diff --git a/Client/js/GameInitializer.js b/Client/js/GameInitializer.js
index c4e6517..763cf67 100644
--- a/Client/js/GameInitializer.js
+++ b/Client/js/GameInitializer.js
@@ -155,13 +155,37 @@ class GameInitializer {
});
this.socket.on('onlineIdleRewards', (data) => {
- console.log('[GAME INITIALIZER] Online idle rewards received:', data);
+ if (data.credits > 0 || data.experience > 0) {
+ console.log('[GAME INITIALIZER] Online idle rewards received:', data);
+ }
this.onOnlineIdleRewards(data);
});
+ // Quest completion events
+ this.socket.on('quest_completed', (data) => {
+ console.log('[GAME INITIALIZER] Quest completed:', data);
+ this.onQuestCompleted(data);
+ });
+
+ // Player stat update events
+ this.socket.on('player_stat_update', (data) => {
+ console.log('[GAME INITIALIZER] Player stat update:', data);
+ this.onPlayerStatUpdate(data);
+ });
+
+ // Quest data events
+ this.socket.on('quests_data', (data) => {
+ console.log('[GAME INITIALIZER] Quest data received:', data);
+ this.onQuestsData(data);
+ });
+
// PlayTime events
this.socket.on('playTimeUpdated', (data) => {
- console.log('[GAME INITIALIZER] PlayTime updated from server:', data);
+ // Only log playtime updates every minute (600,000 ms) to reduce spam
+ if (!this.lastPlayTimeLog || Date.now() - this.lastPlayTimeLog > 60000) {
+ console.log('[GAME INITIALIZER] PlayTime updated:', `${Math.floor(data.playTime / 3600000)}h ${Math.floor((data.playTime % 3600000) / 60000)}m`);
+ this.lastPlayTimeLog = Date.now();
+ }
this.onPlayTimeUpdated(data);
});
@@ -342,6 +366,12 @@ class GameInitializer {
window.game.loadServerPlayerData(this.serverPlayerData);
console.log('[GAME INITIALIZER] Server player data applied to GameEngine');
+ // CRITICAL: Force immediate economy sync
+ if (window.game.systems && window.game.systems.economy && window.game.systems.economy.syncWithServerData) {
+ console.log('[GAME INITIALIZER] Forcing immediate economy sync with server data');
+ window.game.systems.economy.syncWithServerData(this.serverPlayerData);
+ }
+
// 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');
@@ -730,65 +760,65 @@ class GameInitializer {
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);
+ // Try to get from localStorage as fallback
+ const storedUser = localStorage.getItem('currentUser');
+ if (storedUser) {
+ try {
+ const user = JSON.parse(storedUser);
+ const username = user.username || 'anonymous';
+ } catch (e) {
+ console.warn('[GAME INITIALIZER] Failed to parse stored user, using default');
}
} 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) {
+ // Only log if there are actual rewards to process
+ if (data.credits > 0 || data.experience > 0 || data.energy > 0) {
+ console.log('[GAME INITIALIZER] Processing online idle rewards:', data);
+ }
+
// Update player balance with online idle rewards
if (data.credits > 0) {
- // The server already updated the balance, just show notification
+ // Update the Economy system's local credits
+ if (window.game.systems.economy) {
+ window.game.systems.economy.credits += data.credits;
+
+ // Update UI immediately
+ if (window.game.ui) {
+ window.game.ui.updatePlayerStats();
+ console.log('[GAME INITIALIZER] UI updated with new credits');
+ }
+ } else {
+ console.warn('[GAME INITIALIZER] Economy system not available for credit update');
+ }
+
+ // Show notification
window.game.showNotification(`+${data.credits} credits (online idle)`, 'success', 2000);
}
+
+ // Update experience if provided
+ if (data.experience > 0 && window.game.systems.player) {
+ window.game.systems.player.addExperience(data.experience);
+ console.log('[GAME INITIALIZER] Added idle experience:', data.experience);
+ }
+ } else {
+ console.warn('[GAME INITIALIZER] Game systems not available for idle rewards');
}
}
onPlayTimeUpdated(data) {
- console.log('[GAME INITIALIZER] PlayTime updated from server:', data);
-
+ // PlayTime updates are handled in the socket listener with throttled logging
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();
}
@@ -807,6 +837,72 @@ class GameInitializer {
economy.gems = data.newBalance;
}
+ // Add item to inventory if it's a consumable or material
+ if (data.item && (data.item.type === 'consumable' || data.item.type === 'material')) {
+ if (window.game.systems.inventory) {
+ console.log('[GAME INITIALIZER] Adding item to inventory:', data.item);
+ window.game.systems.inventory.addItem(data.item, data.quantity || 1);
+ window.game.showNotification(`${data.item.name} added to inventory!`, 'success', 3000);
+ }
+ }
+
+ // Update owned ships if it's a ship
+ if (data.item && data.item.type === 'ship') {
+ if (!economy.ownedShips) economy.ownedShips = [];
+ if (!economy.ownedShips.includes(data.item.id)) {
+ economy.ownedShips.push(data.item.id);
+ console.log('[GAME INITIALIZER] Ship added to owned ships:', data.item.id);
+
+ // Add ship to BaseSystem ship gallery
+ if (window.game.systems.baseSystem) {
+ console.log('[GAME INITIALIZER] BaseSystem available, adding ship to gallery');
+ const shipData = {
+ id: data.item.id,
+ name: data.item.name,
+ class: data.item.name.replace(/\s+/g, '_').toLowerCase(), // Generate class from name
+ level: 1,
+ stats: data.item.stats || {},
+ texture: data.item.texturePath || `assets/textures/ships/${data.item.id}.png`,
+ isCurrent: false,
+ rarity: data.item.rarity || 'common'
+ };
+
+ console.log('[GAME INITIALIZER] Ship data prepared:', shipData);
+
+ // Initialize ship gallery if needed
+ if (!window.game.systems.baseSystem.purchasedShips) {
+ console.log('[GAME INITIALIZER] Initializing ship gallery');
+ window.game.systems.baseSystem.initializeShipGallery();
+ }
+
+ // Check if ship already exists
+ const existingShip = window.game.systems.baseSystem.purchasedShips.find(s => s.id === shipData.id);
+ if (!existingShip) {
+ // Add ship to gallery
+ window.game.systems.baseSystem.purchasedShips.push(shipData);
+ console.log('[GAME INITIALIZER] Ship added to gallery. Total ships:', window.game.systems.baseSystem.purchasedShips.length);
+
+ // Update the ship gallery UI
+ window.game.systems.baseSystem.updateShipGallery();
+ console.log('[GAME INITIALIZER] Ship gallery UI updated');
+ } else {
+ console.log('[GAME INITIALIZER] Ship already exists in gallery:', shipData.id);
+ }
+ } else {
+ console.error('[GAME INITIALIZER] BaseSystem not available for ship gallery');
+ }
+ }
+ }
+
+ // Update owned cosmetics if it's a cosmetic
+ if (data.item && data.item.type === 'cosmetic') {
+ if (!economy.ownedCosmetics) economy.ownedCosmetics = [];
+ if (!economy.ownedCosmetics.includes(data.item.id)) {
+ economy.ownedCosmetics.push(data.item.id);
+ console.log('[GAME INITIALIZER] Cosmetic added to owned cosmetics:', data.item.id);
+ }
+ }
+
// Request fresh economy data from server to ensure sync
if (economy.requestEconomyData) {
setTimeout(() => {
@@ -814,9 +910,21 @@ class GameInitializer {
}, 500);
}
+ // Also request economy data immediately to prevent reset
+ if (economy.requestEconomyData) {
+ economy.requestEconomyData();
+ }
+
// Update UI
economy.updateUI();
+ // Update inventory UI if item was added
+ if (data.item && (data.item.type === 'consumable' || data.item.type === 'material')) {
+ if (window.game.systems.inventory) {
+ window.game.systems.inventory.updateUI();
+ }
+ }
+
// Show success message
window.game.showNotification(`Purchased ${data.item.name}!`, 'success', 3000);
}
@@ -828,8 +936,28 @@ class GameInitializer {
onShopItemsReceived(data) {
if (data.success && window.game && window.game.systems && window.game.systems.itemSystem) {
+ console.log('[GAME INITIALIZER] Processing shop items data structure:', Object.keys(data));
+
+ // Handle both old (data.items) and new (data.shopItems) structures
+ let itemsToProcess = null;
+
+ if (data.shopItems && typeof data.shopItems === 'object') {
+ // New structure: categorized items
+ console.log('[GAME INITIALIZER] Using new shop structure');
+ itemsToProcess = Object.values(data.shopItems).flat();
+ console.log('[GAME INITIALIZER] Flattened', itemsToProcess.length, 'items from categories');
+ } else if (data.items && Array.isArray(data.items)) {
+ // Old structure: flat array
+ console.log('[GAME INITIALIZER] Using old shop structure');
+ itemsToProcess = data.items;
+ console.log('[GAME INITIALIZER] Processing', itemsToProcess.length, 'items from flat array');
+ } else {
+ console.warn('[GAME INITIALIZER] Invalid shop items structure:', data);
+ return;
+ }
+
// Update ItemSystem with server data
- window.game.systems.itemSystem.processServerItems(data.items);
+ window.game.systems.itemSystem.processServerItems(itemsToProcess);
console.log('[GAME INITIALIZER] ItemSystem updated with server shop items');
// Update Economy shop UI
@@ -872,6 +1000,101 @@ class GameInitializer {
this.currentUser = null;
this.serverPlayerData = null;
}
+
+ onQuestCompleted(data) {
+ console.log('[GAME INITIALIZER] Processing quest completion:', data);
+
+ // Show quest completion notification
+ if (window.game && window.game.showNotification) {
+ const questName = data.questId.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
+ window.game.showNotification(`Quest Completed: ${questName}! 🎯`, 'success', 5000);
+ }
+
+ // Award quest rewards
+ if (data.rewards) {
+ if (data.rewards.experience > 0 && window.game.systems.player) {
+ window.game.systems.player.addExperience(data.rewards.experience);
+ console.log('[GAME INITIALIZER] Awarded quest experience:', data.rewards.experience);
+ }
+
+ if (data.rewards.credits > 0 && window.game.systems.economy) {
+ window.game.systems.economy.credits += data.rewards.credits;
+ console.log('[GAME INITIALIZER] Awarded quest credits:', data.rewards.credits);
+ }
+ }
+
+ // Update quest UI if quest system exists
+ if (window.game && window.game.systems && window.game.systems.questSystem) {
+ window.game.systems.questSystem.completeQuest(data.questId, data.rewards);
+
+ // Force fetch fresh quest data from server (Fix #1 - Corrected)
+ console.log('[GAME INITIALIZER] Requesting fresh quest data from server after completion');
+
+ // Request fresh quest data from server
+ if (window.gameInitializer && window.gameInitializer.socket) {
+ window.gameInitializer.socket.emit('get_quests');
+ }
+ }
+
+ // Force quest UI refresh for server-driven quests
+ if (typeof updateQuestDisplay === 'function') {
+ updateQuestDisplay();
+ }
+
+ // Update UI
+ if (window.game && window.game.ui) {
+ window.game.ui.updatePlayerStats();
+ }
+ }
+
+ onPlayerStatUpdate(data) {
+ console.log('[GAME INITIALIZER] Processing player stat update:', data);
+
+ if (window.game && window.game.systems && window.game.systems.player && window.game.systems.player.stats) {
+ // Update the player stat
+ window.game.systems.player.stats[data.stat] = data.value;
+ console.log('[GAME INITIALIZER] Updated player stat:', data.stat, '=', data.value);
+
+ // Update UI to reflect the change
+ if (window.game.ui) {
+ window.game.ui.updatePlayerStats();
+ }
+
+ // Update quest system to check for quest progress
+ if (window.game.systems.questSystem) {
+ window.game.systems.questSystem.checkQuestAvailability();
+ }
+ }
+ }
+
+ onQuestsData(data) {
+ console.log('[GAME INITIALIZER] Processing quest data from server:', data);
+ console.log('[GAME INITIALIZER] Quest data keys:', Object.keys(data || {}));
+ console.log('[GAME INITIALIZER] Main quests count:', (data?.mainQuests || []).length);
+ console.log('[GAME INITIALIZER] Daily quests count:', (data?.dailyQuests || []).length);
+ console.log('[GAME INITIALIZER] Weekly quests count:', (data?.weeklyQuests || []).length);
+
+ if (window.game && window.game.systems && window.game.systems.questSystem) {
+ // Load quest data into the quest system
+ if (window.game.systems.questSystem.loadServerQuests) {
+ console.log('[GAME INITIALIZER] Calling loadServerQuests with data');
+ window.game.systems.questSystem.loadServerQuests(data);
+ console.log('[GAME INITIALIZER] Loaded quest data into quest system');
+ } else {
+ console.log('[GAME INITIALIZER] loadServerQuests method not found');
+ }
+
+ // Update quest UI
+ if (typeof updateQuestDisplay === 'function') {
+ console.log('[GAME INITIALIZER] Calling updateQuestDisplay');
+ updateQuestDisplay();
+ } else {
+ console.log('[GAME INITIALIZER] updateQuestDisplay function not found');
+ }
+ } else {
+ console.log('[GAME INITIALIZER] Quest system not available');
+ }
+ }
}
// Create global instance
diff --git a/Client/js/core/Economy.js b/Client/js/core/Economy.js
index 963f784..9c97d92 100644
--- a/Client/js/core/Economy.js
+++ b/Client/js/core/Economy.js
@@ -8,28 +8,25 @@ class Economy {
constructor(gameEngine) {
this.game = gameEngine;
- // 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;
+ // Currency - don't override in multiplayer mode, will be set by server data
+ if (window.smartSaveManager?.isMultiplayer) {
+ this.credits = 0; // Will be updated by server
+ this.gems = 0; // Will be updated by server
+ this.premiumCurrency = 0; // Will be updated by server
+ } else {
+ this.credits = 10000; // Starting credits for singleplayer
+ this.gems = 50; // Starting premium currency
+ this.premiumCurrency = 0; // Additional premium currency
+ }
- // Player resources
- this.credits = preservedCredits;
- this.gems = preservedGems;
- this.premiumCurrency = 0;
-
- // Transaction tracking
- this.transactionHistory = [];
- this.transactions = []; // Add missing transactions array
-
- // Owned cosmetics
- this.ownedCosmetics = []; // Add missing owned cosmetics array
+ // Transaction history
+ this.transactions = [];
// Shop categories
this.shopCategories = {
ships: 'Ships',
- weapons: 'Weapons',
- modules: 'Modules',
+ weapons: 'Weapons',
+ armors: 'Armors',
cosmetics: 'Cosmetics',
consumables: 'Consumables',
materials: 'Materials'
@@ -40,6 +37,7 @@ class Economy {
this.shopRefreshInterval = null; // Timer for 2-hour refresh
this.shopHeartbeatInterval = null; // Timer for live countdown updates
this.lastShopRefresh = null; // Timestamp of last refresh
+ this.currentShopData = null; // Current shop data from server
this.SHOP_REFRESH_INTERVAL = 2 * 60 * 60 * 1000; // 2 hours in milliseconds
this.MAX_ITEMS_PER_CATEGORY = 8;
this.categoryPurchaseLimits = {}; // Track purchases per category per refresh
@@ -50,18 +48,92 @@ class Economy {
// Owned cosmetics
this.ownedCosmetics = [];
- console.log('[ECONOMY] Economy system initialized with server-side ItemSystem');
- console.log('[ECONOMY] Preserved values - Credits:', this.credits, 'Gems:', this.gems);
+ // Owned ships
+ this.ownedShips = [];
- // Set up socket listeners for economy sync
- this.setupSocketListeners();
+ console.log('[ECONOMY] Economy system initialized');
- // Request fresh economy data after a short delay to ensure sync
- if (window.smartSaveManager?.isMultiplayer) {
- setTimeout(() => {
- this.requestEconomyData();
- }, 1000);
- }
+ // Initialize global purchase function
+ Economy.initGlobalPurchaseFunction();
+ }
+
+ // Create global purchase function for shop buttons
+ static initGlobalPurchaseFunction() {
+ window.purchaseShopItem = function(itemId) {
+ console.log('[GLOBAL] Purchase shop item called:', itemId);
+
+ if (window.game && window.game.systems && window.game.systems.economy) {
+ window.game.systems.economy.purchaseItem(itemId, 1);
+ } else {
+ console.error('[GLOBAL] Economy system not available for purchase');
+ }
+ };
+
+ // Add test function for idle system
+ window.testIdleRewards = function() {
+ console.log('[GLOBAL] Testing idle rewards...');
+
+ if (window.game && window.game.socket) {
+ window.game.socket.emit('testIdleRewards', {});
+
+ // Listen for response
+ window.game.socket.once('testIdleRewards', (data) => {
+ console.log('[GLOBAL] Test idle rewards response:', data);
+ });
+ } else {
+ console.error('[GLOBAL] No socket available for idle test');
+ }
+ };
+
+ // Add socket event monitor
+ window.monitorSocketEvents = function() {
+ if (window.game && window.game.socket) {
+ console.log('[GLOBAL] Monitoring socket events...');
+
+ // Monitor all incoming events
+ const originalOn = window.game.socket.on;
+ window.game.socket.on = function(event, callback) {
+ const wrappedCallback = function(data) {
+ if (event === 'onlineIdleRewards' || event === 'economy_data') {
+ console.log('[SOCKET MONITOR] Received event:', event, data);
+ }
+ return callback(data);
+ };
+ return originalOn.call(this, event, wrappedCallback);
+ };
+
+ console.log('[GLOBAL] Socket event monitoring enabled');
+ } else {
+ console.error('[GLOBAL] No socket available for monitoring');
+ }
+ };
+
+ // Add function to give player energy for testing dungeons
+ window.addEnergy = function(amount = 50) {
+ if (window.game && window.game.systems && window.game.systems.player) {
+ const player = window.game.systems.player;
+ const oldEnergy = player.attributes.energy || 0;
+ player.attributes.energy = Math.min(oldEnergy + amount, player.attributes.maxEnergy || 100);
+ console.log('[GLOBAL] Added energy:', oldEnergy, '->', player.attributes.energy);
+
+ // Update UI
+ if (player.updateUI) {
+ player.updateUI();
+ }
+
+ // Update dungeon UI if available
+ if (window.game.systems.dungeonSystem && window.game.systems.dungeonSystem.updateUI) {
+ window.game.systems.dungeonSystem.updateUI();
+ }
+
+ return player.attributes.energy;
+ } else {
+ console.error('[GLOBAL] Player system not available');
+ return 0;
+ }
+ };
+
+ console.log('[GLOBAL] Global functions initialized - purchaseShopItem() and testIdleRewards() available');
}
/**
@@ -76,9 +148,15 @@ class Economy {
// Listen for economy data updates from server
this.game.socket.on('economy_data', (data) => {
console.log('[ECONOMY] Received economy data from server:', data);
+ console.log('[ECONOMY] Current credits before update:', this.credits);
+ console.log('[ECONOMY] Current gems before update:', this.gems);
+
this.credits = data.credits || 0;
this.gems = data.gems || 0;
+ console.log('[ECONOMY] Updated credits:', this.credits);
+ console.log('[ECONOMY] Updated gems:', this.gems);
+
// Update UI immediately
if (this.game.ui) {
this.game.ui.updatePlayerStats();
@@ -86,6 +164,18 @@ class Economy {
console.log('[ECONOMY] Economy synced - Credits:', this.credits, 'Gems:', this.gems);
});
+
+ // Note: onlineIdleRewards is handled by GameInitializer to avoid duplicate event handling
+
+ // Listen for play time updates from server
+ this.game.socket.on('playTimeUpdated', (data) => {
+ console.log('[ECONOMY] Received play time update from server:', data);
+
+ // Update player stats if available
+ if (this.game.systems.player && this.game.systems.player.stats) {
+ this.game.systems.player.stats.playTime = data.playTime;
+ }
+ });
}
/**
@@ -314,6 +404,33 @@ class Economy {
player.ownedShips.push(ship.id);
}
+ // Add ship to BaseSystem ship gallery (singleplayer)
+ if (this.game.systems.baseSystem) {
+ const shipData = {
+ id: ship.id,
+ name: ship.name,
+ class: ship.name.replace(/\s+/g, '_').toLowerCase(), // Generate class from name
+ level: 1,
+ stats: ship.stats || {},
+ texture: ship.texture || `assets/textures/ships/${ship.id}.png`,
+ isCurrent: false,
+ rarity: ship.rarity || 'common'
+ };
+
+ // Initialize ship gallery if needed
+ if (!this.game.systems.baseSystem.purchasedShips) {
+ this.game.systems.baseSystem.initializeShipGallery();
+ }
+
+ // Add ship to gallery
+ this.game.systems.baseSystem.purchasedShips.push(shipData);
+
+ // Update the ship gallery UI
+ this.game.systems.baseSystem.updateShipGallery();
+
+ console.log('[ECONOMY] Ship added to BaseSystem gallery (singleplayer):', shipData.name);
+ }
+
if (debugLogger) debugLogger.logStep('Ship purchase completed', {
shipId: ship.id,
shipName: ship.name,
@@ -472,8 +589,39 @@ class Economy {
}
}
+ // Manual sync with server data - call this to force update
+ syncWithServerData(serverPlayerData) {
+ console.log('[ECONOMY] Manual sync with server data:', {
+ serverCredits: serverPlayerData?.stats?.credits,
+ serverGems: serverPlayerData?.stats?.gems,
+ currentCredits: this.credits,
+ currentGems: this.gems
+ });
+
+ if (serverPlayerData?.stats?.credits !== undefined) {
+ this.credits = serverPlayerData.stats.credits;
+ console.log('[ECONOMY] Updated credits from server:', this.credits);
+ }
+
+ if (serverPlayerData?.stats?.gems !== undefined) {
+ this.gems = serverPlayerData.stats.gems;
+ console.log('[ECONOMY] Updated gems from server:', this.gems);
+ }
+
+ // Update UI after sync
+ this.updateUI();
+ }
+
// UI updates
updateUI() {
+ // Debug logging to track current values
+ console.log('[ECONOMY] updateUI called - Current values:', {
+ credits: this.credits,
+ gems: this.gems,
+ gameSystemsAvailable: !!(this.game && this.game.systems),
+ uiSystemAvailable: !!(this.game && this.game.systems && this.game.systems.ui)
+ });
+
// Update resource display
if (this.game.systems.ui) {
this.game.systems.ui.updateResourceDisplay();
@@ -487,28 +635,33 @@ class Economy {
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;
- }
+ if (this.game.multiplayerMode && this.game.itemSystem && this.game.itemSystem.catalog) {
+ console.log('[ECONOMY] Multiplayer mode:', true);
+ console.log('[ECONOMY] ItemSystem available:', !!this.game.itemSystem);
+ console.log('[ECONOMY] ItemSystem catalog:', !!this.game.itemSystem.catalog);
- // 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);
+ const shopItems = this.game.itemSystem.catalog;
+ console.log('[ECONOMY] Got categorized shop items:', Object.keys(shopItems));
+
+ // Get current active category
+ const activeCategory = this.game.itemSystem.activeCategory || 'ships';
+ console.log('[ECONOMY] Active shop category:', activeCategory);
+
+ // Filter items for active category
+ const categoryItems = shopItems[activeCategory] || [];
+ console.log('[ECONOMY] Using new shop structure - found', categoryItems.length, 'categories');
+ console.log('[ECONOMY] Filtered items for category', activeCategory, ':', categoryItems.length, 'items');
+ console.log('[ECONOMY] Item types in category:', categoryItems.map(item => item.type));
+
+ this.renderShopItems(categoryItems);
} 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);
+
+ // Convert to categorized structure for consistency
+ const categorizedItems = this.randomShopItems || {};
+ this.renderShopItems(categorizedItems);
}
}
@@ -518,25 +671,27 @@ class Economy {
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'
- };
+ // Handle new shop data structure (items by category) or old structure (flat array)
+ let categoryItems = [];
- const targetItemType = categoryTypeMap[activeCategory] || activeCategory;
- console.log('[ECONOMY] Mapped category', activeCategory, 'to item type', targetItemType);
+ if (items && typeof items === 'object' && !Array.isArray(items)) {
+ // New structure: { ships: [...], weapons: [...], ... }
+ categoryItems = items[activeCategory] || [];
+ console.log('[ECONOMY] Using new shop structure - found', Object.keys(items).length, 'categories');
+ } else if (Array.isArray(items)) {
+ // Old structure: flat array of items
+ const targetItemType = activeCategory.slice(0, -1); // Remove 's' from 'ships', 'weapons', etc.
+ categoryItems = items.filter(item => item.type === targetItemType);
+ console.log('[ECONOMY] Using old shop structure - filtered', items.length, 'total items');
+ } else {
+ console.warn('[ECONOMY] Invalid shop items structure:', typeof items);
+ shopItemsElement.innerHTML = '
No items available
';
+ return;
+ }
- const categoryItems = items.filter(item => item.type === targetItemType);
- console.log('[ECONOMY] Filtered items for category', activeCategory, '(type:', targetItemType, ') :', categoryItems.length, 'items');
+ console.log('[ECONOMY] Filtered items for category', activeCategory, ':', categoryItems.length, 'items');
+ console.log('[ECONOMY] Item types in category:', categoryItems.map(item => item.type));
if (categoryItems.length === 0) {
shopItemsElement.innerHTML = 'No items available in this category
';
@@ -573,6 +728,7 @@ class Economy {
`;
}).join('');
+
+ // Add event listeners to purchase buttons
+ shopItemsElement.querySelectorAll('.shop-item-purchase-btn').forEach(button => {
+ button.addEventListener('click', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ const itemId = button.getAttribute('data-item-id');
+ if (itemId && !button.disabled) {
+ console.log('[ECONOMY] Purchase button clicked for item:', itemId);
+ this.purchaseItem(itemId, 1);
+ }
+ });
+ });
}
formatPrice(item) {
diff --git a/Client/js/core/GameEngine.js b/Client/js/core/GameEngine.js
index 713040a..7be3764 100644
--- a/Client/js/core/GameEngine.js
+++ b/Client/js/core/GameEngine.js
@@ -184,7 +184,10 @@ class GameEngine extends EventTarget {
console.error('[GAME ENGINE] Failed to initialize DungeonSystem:', error);
});
}
- // REMOVED: QuestSystem should be server-driven only
+ if (typeof QuestSystem !== 'undefined') {
+ this.systems.questSystem = new QuestSystem(this);
+ console.log('[GAME ENGINE] QuestSystem created');
+ }
if (typeof CraftingSystem !== 'undefined') {
this.systems.crafting = new CraftingSystem(this);
}
@@ -426,7 +429,22 @@ class GameEngine extends EventTarget {
console.log('[GAME ENGINE] Auto-saving game...');
try {
- await this.save();
+ // In multiplayer mode, save to server
+ if (window.smartSaveManager?.isMultiplayer) {
+ console.log('[GAME ENGINE] Auto-saving to server...');
+ if (this.socket) {
+ this.socket.emit('saveGameData', {
+ timestamp: Date.now(),
+ gameTime: this.gameTime
+ });
+ } else {
+ console.warn('[GAME ENGINE] No socket available for server save');
+ }
+ } else {
+ // Singleplayer mode - local save (not implemented yet)
+ console.log('[GAME ENGINE] Local auto-save not implemented');
+ }
+
this.showNotification('Game auto-saved', 'info', 2000);
console.log('[GAME ENGINE] Auto-save completed successfully');
@@ -644,6 +662,12 @@ class GameEngine extends EventTarget {
console.log('[GAME ENGINE] Applied gems from server:', playerData.stats.gems);
}
+ // Force manual sync to ensure economy is updated
+ if (this.systems.economy && this.systems.economy.syncWithServerData) {
+ console.log('[GAME ENGINE] Forcing manual economy sync');
+ this.systems.economy.syncWithServerData(playerData);
+ }
+
// Request fresh economy data from server to ensure sync
if (this.systems.economy && this.systems.economy.requestEconomyData) {
setTimeout(() => {
@@ -662,6 +686,25 @@ class GameEngine extends EventTarget {
console.log('[GAME ENGINE] Applied max energy from server:', playerData.stats.maxEnergy);
}
+ // Ensure player has minimum energy for dungeon access
+ if (this.systems.player.attributes) {
+ // Check if energy is missing or too low
+ if (!this.systems.player.attributes.energy || this.systems.player.attributes.energy < 10) {
+ const oldEnergy = this.systems.player.attributes.energy;
+ this.systems.player.attributes.energy = 100;
+ this.systems.player.attributes.maxEnergy = Math.max(this.systems.player.attributes.maxEnergy || 0, 100);
+ console.log('[GAME ENGINE] Set minimum energy for dungeon access:', oldEnergy, '->', this.systems.player.attributes.energy);
+ }
+
+ // Also ensure currentEnergy is set if it exists
+ if (this.systems.player.attributes.currentEnergy !== undefined) {
+ if (this.systems.player.attributes.currentEnergy < 10) {
+ this.systems.player.attributes.currentEnergy = 100;
+ console.log('[GAME ENGINE] Set minimum currentEnergy for dungeon access');
+ }
+ }
+ }
+
console.log('[GAME ENGINE] Final player stats after application:', this.systems.player.stats);
} else {
console.log('[GAME ENGINE] Missing player stats or player system');
diff --git a/Client/js/systems/DungeonSystem.js b/Client/js/systems/DungeonSystem.js
index 6f4bddd..ada1e64 100644
--- a/Client/js/systems/DungeonSystem.js
+++ b/Client/js/systems/DungeonSystem.js
@@ -3,6 +3,69 @@
* Server-driven dungeon management client
*/
+// Create global function for dungeon start that's more reliable
+window.startDungeon = function(dungeonId) {
+ console.log('[DUNGEON SYSTEM] startDungeon called with:', dungeonId);
+ console.log('[DUNGEON SYSTEM] Game available:', !!window.game);
+ console.log('[DUNGEON SYSTEM] Game systems available:', !!(window.game && window.game.systems));
+ console.log('[DUNGEON SYSTEM] Dungeon system available:', !!(window.game && window.game.systems && window.game.systems.dungeonSystem));
+
+ if (window.game && window.game.systems && window.game.systems.dungeonSystem) {
+ return window.game.systems.dungeonSystem.startDungeon(dungeonId);
+ }
+
+ console.warn('[DUNGEON SYSTEM] Game systems not available for dungeon start');
+};
+
+// Create global function for process encounter that's more reliable
+window.processEncounter = function() {
+ console.log('[DUNGEON SYSTEM] processEncounter called');
+
+ if (window.game && window.game.systems && window.game.systems.dungeonSystem) {
+ return window.game.systems.dungeonSystem.processEncounter();
+ }
+
+ console.warn('[DUNGEON SYSTEM] Game systems not available for process encounter');
+};
+
+// Create global function for dungeon toggle that's more reliable
+window.toggleDungeonSection = function(sectionId) {
+ // Try to use the dungeon system if available
+ if (window.game && window.game.systems && window.game.systems.dungeonSystem) {
+ return window.game.systems.dungeonSystem.toggleDungeonSection(sectionId);
+ }
+
+ // Fallback: Direct DOM manipulation
+ const section = document.getElementById(sectionId);
+ const indicator = document.getElementById(`${sectionId}-indicator`);
+
+ if (!section || !indicator) {
+ console.warn('[DUNGEON SYSTEM] Section or indicator not found:', sectionId);
+ return;
+ }
+
+ const isCollapsed = section.classList.contains('collapsed');
+
+ if (isCollapsed) {
+ // Expand
+ section.classList.remove('collapsed');
+ indicator.classList.remove('fa-chevron-right');
+ indicator.classList.add('fa-chevron-down');
+ } else {
+ // Collapse
+ section.classList.add('collapsed');
+ indicator.classList.remove('fa-chevron-down');
+ indicator.classList.add('fa-chevron-right');
+ }
+
+ // Save the state in the dungeon system if available
+ if (window.game && window.game.systems && window.game.systems.dungeonSystem) {
+ window.game.systems.dungeonSystem.collapseStates.set(sectionId, !isCollapsed);
+ }
+
+ console.log(`[DUNGEON SYSTEM] Toggled section ${sectionId}: ${isCollapsed ? 'expanded' : 'collapsed'}`);
+};
+
class DungeonSystem {
constructor(gameEngine) {
this.game = gameEngine;
@@ -13,8 +76,23 @@ class DungeonSystem {
this.dungeonProgress = 0;
this.isExploring = false;
- // Server data loaded from server
- this.serverDungeons = [];
+ // Debouncing to prevent multiple rapid clicks
+ this.lastProcessTime = 0;
+ this.processCooldown = 1000; // 1 second cooldown
+
+ // Prevent duplicate event processing
+ this.lastEncounterData = null;
+ this.lastNextRoomData = null;
+
+ // Store collapse states to preserve them during regeneration
+ this.collapseStates = new Map();
+
+ // Track last generation to prevent unnecessary regenerations
+ this.lastGenerationTime = 0;
+ this.generationThrottle = 500; // 500ms throttle
+
+ // Server dungeons data
+ this.serverDungeons = null;
this.roomTypes = {};
this.enemyTemplates = {};
@@ -39,14 +117,16 @@ class DungeonSystem {
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();
+ this.forceGenerateDungeonList();
});
// 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');
+ console.log(`[DUNGEON SYSTEM] Loaded ${Object.keys(this.roomTypes).length} room types from server`);
+ // Update UI when room data is loaded
+ this.forceGenerateDungeonList();
});
// Listen for enemy templates response
@@ -55,22 +135,157 @@ class DungeonSystem {
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();
+ this.forceGenerateDungeonList();
});
// Listen for dungeon start response
this.game.socket.on('dungeon_started', (data) => {
console.log('[DUNGEON SYSTEM] Dungeon started:', data);
+
+ // Handle error responses
+ if (data.success === false) {
+ console.error('[DUNGEON SYSTEM] Failed to start dungeon:', data.error);
+ if (this.game && this.game.showNotification) {
+ this.game.showNotification(data.error, 'error', 5000);
+ }
+ return;
+ }
+
+ // Clear any existing dungeon state first
+ if (this.currentDungeon) {
+ console.warn('[DUNGEON SYSTEM] Clearing existing dungeon state before starting new one');
+ this.currentDungeon = null;
+ this.currentRoom = null;
+ this.isExploring = false;
+ this.dungeonProgress = 0;
+ }
+
this.currentDungeon = data.instance;
this.isExploring = true;
this.dungeonProgress = 0;
+
+ console.log('[DUNGEON SYSTEM] About to update UI - State:', {
+ currentDungeon: !!this.currentDungeon,
+ isExploring: this.isExploring,
+ dungeonProgress: this.dungeonProgress,
+ gameUIManager: !!this.game.systems.ui,
+ instanceId: this.currentDungeon?.id
+ });
+
+ // Update UI to show dungeon exploration
+ this.updateUI();
+
+ // Show notification to player
+ if (this.game && this.game.showNotification) {
+ this.game.showNotification(`Entered ${data.instance.dungeonId} dungeon!`, 'success', 3000);
+ }
});
// Listen for encounter response
this.game.socket.on('encounter_data', (data) => {
+ // Skip duplicate events
+ if (this.lastEncounterData &&
+ this.lastEncounterData.encounterIndex === data.encounterIndex &&
+ this.lastEncounterData.encounter?.name === data.encounter?.name) {
+ console.log('[DUNGEON SYSTEM] Skipping duplicate encounter data');
+ return;
+ }
+ this.lastEncounterData = data;
+
console.log('[DUNGEON SYSTEM] Encounter received:', data);
+ console.log('[DUNGEON SYSTEM] Current state before update:', {
+ currentDungeonId: this.currentDungeon?.id,
+ currentProgress: this.dungeonProgress,
+ newEncounterIndex: data.encounterIndex,
+ encounterType: data.encounter?.type,
+ encounterName: data.encounter?.name
+ });
+
this.currentRoom = data.encounter;
- this.dungeonProgress++;
+ this.dungeonProgress = data.encounterIndex; // Use server data, not local increment
+
+ // Update UI to show the new encounter
+ this.updateUI();
+ });
+
+ // Listen for encounter completion (auto-combat)
+ this.game.socket.on('encounter_completed', (data) => {
+ console.log('[DUNGEON SYSTEM] Encounter completed:', data);
+ if (data.success) {
+ // Check if dungeon is complete
+ if (data.isComplete) {
+ console.log('[DUNGEON SYSTEM] Dungeon completed!');
+
+ // Clear all dungeon state
+ this.currentDungeon = null;
+ this.currentRoom = null;
+ this.dungeonProgress = 0;
+ this.isExploring = false;
+ this.lastEncounterData = null;
+ this.lastNextRoomData = null;
+
+ // Show completion notification
+ if (this.game && this.game.showNotification) {
+ this.game.showNotification('Dungeon completed! 🎉', 'success', 5000);
+ }
+
+ // Force UI to show dungeon list
+ setTimeout(() => {
+ this.updateUI();
+ }, 1000);
+ } else {
+ this.currentRoom = data.nextEncounter;
+ this.dungeonProgress = data.encounterIndex;
+
+ // Show rewards notification
+ if (data.rewards && (data.rewards.credits > 0 || data.rewards.experience > 0)) {
+ const rewardText = [];
+ if (data.rewards.credits > 0) rewardText.push(`${data.rewards.credits} credits`);
+ if (data.rewards.experience > 0) rewardText.push(`${data.rewards.experience} exp`);
+
+ if (this.game && this.game.showNotification) {
+ this.game.showNotification(`Combat complete! Gained: ${rewardText.join(', ')}`, 'success', 3000);
+ }
+ }
+ }
+
+ // Update UI to show the new state
+ this.updateUI();
+ } else {
+ console.error('[DUNGEON SYSTEM] Error completing encounter:', data.error);
+ }
+ });
+
+ // Listen for next room response
+ this.game.socket.on('next_room_data', (data) => {
+ // Skip duplicate events
+ if (this.lastNextRoomData &&
+ this.lastNextRoomData.encounterIndex === data.encounterIndex &&
+ this.lastNextRoomData.encounter?.name === data.encounter?.name) {
+ console.log('[DUNGEON SYSTEM] Skipping duplicate next room data');
+ return;
+ }
+ this.lastNextRoomData = data;
+
+ console.log('[DUNGEON SYSTEM] Next room received:', data);
+ console.log('[DUNGEON SYSTEM] Current state before update:', {
+ currentDungeonId: this.currentDungeon?.id,
+ currentProgress: this.dungeonProgress,
+ newEncounterIndex: data.encounterIndex,
+ encounterType: data.encounter?.type,
+ encounterName: data.encounter?.name,
+ isComplete: data.isComplete
+ });
+
+ if (data.success) {
+ this.currentRoom = data.encounter;
+ this.dungeonProgress = data.encounterIndex;
+
+ // Update UI to show the new room
+ this.updateUI();
+ } else {
+ console.error('[DUNGEON SYSTEM] Error moving to next room:', data.error);
+ }
});
// Listen for dungeon completion response
@@ -169,7 +384,7 @@ class DungeonSystem {
// Send packet to start dungeon
this.game.socket.emit('start_dungeon', {
dungeonId: dungeonId,
- userId: this.game.player?.id || 'anonymous'
+ userId: this.game.systems.player?.id || 'anonymous'
});
console.log('[DUNGEON SYSTEM] Dungeon start packet sent');
@@ -182,15 +397,24 @@ class DungeonSystem {
}
/**
- * Process current room encounter using Socket.IO packets
+ * Process encounter in current dungeon room
*/
async processEncounter() {
- if (!this.currentDungeon || !this.isExploring) {
- console.warn('[DUNGEON SYSTEM] No active dungeon to process');
+ // Debounce to prevent multiple rapid clicks
+ const now = Date.now();
+ if (now - this.lastProcessTime < this.processCooldown) {
+ console.log('[DUNGEON SYSTEM] Process throttled, please wait...');
return null;
}
+ this.lastProcessTime = now;
try {
+ // Safety check - make sure we have an active dungeon
+ if (!this.currentDungeon) {
+ console.error('[DUNGEON SYSTEM] No active dungeon to process encounter for');
+ return null;
+ }
+
console.log(`[DUNGEON SYSTEM] Processing encounter for dungeon: ${this.currentDungeon.id}`);
if (!this.game.socket) {
@@ -201,7 +425,7 @@ class DungeonSystem {
// Send packet to process encounter
this.game.socket.emit('process_encounter', {
instanceId: this.currentDungeon.id,
- userId: this.game.player?.id || 'anonymous'
+ userId: this.game.systems.player?.id || 'anonymous'
});
console.log('[DUNGEON SYSTEM] Encounter process packet sent');
@@ -233,7 +457,7 @@ class DungeonSystem {
// Send packet to complete dungeon
this.game.socket.emit('complete_dungeon', {
instanceId: this.currentDungeon.id,
- userId: this.game.player?.id || 'anonymous'
+ userId: this.game.systems.player?.id || 'anonymous'
});
console.log('[DUNGEON SYSTEM] Dungeon completion packet sent');
@@ -257,7 +481,7 @@ class DungeonSystem {
// Send packet to get dungeon status
this.game.socket.emit('get_dungeon_status', {
- userId: this.game.player?.id || 'anonymous'
+ userId: this.game.systems.player?.id || 'anonymous'
});
console.log('[DUNGEON SYSTEM] Dungeon status request packet sent');
@@ -270,11 +494,27 @@ class DungeonSystem {
return null;
}
+ /**
+ * Force generate dungeon list (bypasses throttle)
+ */
+ forceGenerateDungeonList() {
+ this.lastGenerationTime = 0; // Reset throttle
+ this.generateDungeonList();
+ }
+
/**
* Generate dungeon list UI using server data
*/
generateDungeonList() {
- console.log('[DUNGEON SYSTEM] Generating dungeon list UI with server data...');
+ const now = Date.now();
+
+ // Throttle generation to prevent excessive calls
+ if (now - this.lastGenerationTime < this.generationThrottle) {
+ return; // Silently skip instead of logging
+ }
+
+ this.lastGenerationTime = now;
+ // console.log('[DUNGEON SYSTEM] Generating dungeon list UI');
const dungeonListElement = document.getElementById('dungeonList');
if (!dungeonListElement) {
@@ -300,13 +540,22 @@ class DungeonSystem {
const difficultyTitle = difficulty === 'tutorial' ? 'Tutorial Dungeons' :
difficulty.charAt(0).toUpperCase() + difficulty.slice(1) + ' Dungeons';
const difficultyIcon = this.getDifficultyIcon(difficulty);
+ const sectionId = `dungeon-section-${difficulty}`;
- // Add difficulty header
+ // Add collapsible difficulty header
html += `
-
+
+
+
`;
dungeons.forEach(dungeon => {
@@ -332,16 +581,90 @@ class DungeonSystem {
`;
});
+
+ // Close the section
+ html += `
+
+
+ `;
});
dungeonListElement.innerHTML = html;
- console.log('[DUNGEON SYSTEM] Dungeon list UI generated successfully');
+
+ // Initialize default collapse states
+ this.initializeDungeonSections();
+ }
+
+ /**
+ * Initialize dungeon sections with saved collapse states
+ */
+ initializeDungeonSections() {
+ // Default states: tutorial and easy expanded, others collapsed
+ const defaultStates = {
+ 'dungeon-section-tutorial': false, // expanded
+ 'dungeon-section-easy': false, // expanded
+ 'dungeon-section-medium': true, // collapsed
+ 'dungeon-section-hard': true, // collapsed
+ 'dungeon-section-extreme': true // collapsed
+ };
+
+ Object.entries(defaultStates).forEach(([sectionId, defaultCollapsed]) => {
+ const section = document.getElementById(sectionId);
+ const indicator = document.getElementById(`${sectionId}-indicator`);
+
+ if (section && indicator) {
+ // Use saved state if available, otherwise use default
+ const shouldCollapse = this.collapseStates.has(sectionId) ?
+ this.collapseStates.get(sectionId) : defaultCollapsed;
+
+ if (shouldCollapse) {
+ section.classList.add('collapsed');
+ indicator.classList.remove('fa-chevron-down');
+ indicator.classList.add('fa-chevron-right');
+ }
+ }
+ });
+ }
+
+ /**
+ * Toggle dungeon section collapse/expand
+ */
+ toggleDungeonSection(sectionId) {
+ // Check if game and systems are available
+ if (!window.game || !window.game.systems || !window.game.systems.dungeonSystem) {
+ console.warn('[DUNGEON SYSTEM] Game systems not available for toggle');
+ return;
+ }
+
+ const section = document.getElementById(sectionId);
+ const indicator = document.getElementById(`${sectionId}-indicator`);
+
+ if (!section || !indicator) return;
+
+ const isCollapsed = section.classList.contains('collapsed');
+
+ if (isCollapsed) {
+ // Expand
+ section.classList.remove('collapsed');
+ indicator.classList.remove('fa-chevron-right');
+ indicator.classList.add('fa-chevron-down');
+ } else {
+ // Collapse
+ section.classList.add('collapsed');
+ indicator.classList.remove('fa-chevron-down');
+ indicator.classList.add('fa-chevron-right');
+ }
+
+ // Save the state
+ this.collapseStates.set(sectionId, !isCollapsed);
+
+ console.log(`[DUNGEON SYSTEM] Toggled section ${sectionId}: ${isCollapsed ? 'expanded' : 'collapsed'}`);
}
/**
@@ -390,45 +713,88 @@ class DungeonSystem {
* Check if player can enter dungeon
*/
canEnterDungeon(dungeon) {
- if (!this.game.player) {
- console.log('[DUNGEON SYSTEM] No player data available');
+ if (!this.game.systems.player) {
return false;
}
- const playerLevel = this.game.player.stats?.level || 1;
+ const playerLevel = this.game.systems.player.stats?.level || 1;
const minLevel = dungeon.minLevel || 1;
const maxLevel = dungeon.maxLevel || 999;
const energyCost = dungeon.energyCost || 0;
- const playerEnergy = this.game.player.stats?.energy || 0;
-
- console.log(`[DUNGEON SYSTEM] Dungeon check for ${dungeon.name}:`, {
- playerLevel,
- minLevel,
- maxLevel,
- playerEnergy,
- energyCost,
- canEnter: playerLevel >= minLevel && playerLevel <= maxLevel && playerEnergy >= energyCost
- });
+ const playerEnergy = this.game.systems.player.attributes?.energy || 0;
return playerLevel >= minLevel &&
playerLevel <= maxLevel &&
playerEnergy >= energyCost;
}
-
+
+ /**
+ * Exit current dungeon
+ */
+ exitDungeon() {
+ console.log('[DUNGEON SYSTEM] Exiting dungeon');
+
+ if (this.currentDungeon) {
+ // Send exit packet to server
+ if (this.game.socket) {
+ this.game.socket.emit('exit_dungeon', {
+ instanceId: this.currentDungeon.id,
+ userId: this.game.systems.player?.id || 'anonymous'
+ });
+ }
+ }
+
+ // Reset local state
+ this.currentDungeon = null;
+ this.currentRoom = null;
+ this.isExploring = false;
+ this.dungeonProgress = 0;
+
+ // Update UI to show dungeon list
+ this.updateUI();
+
+ // Show notification
+ if (this.game && this.game.showNotification) {
+ this.game.showNotification('Exited dungeon', 'info', 2000);
+ }
+ }
+
+ /**
+ * Move to next room (for rooms without enemies)
+ */
+ moveToNextRoom() {
+ console.log('[DUNGEON SYSTEM] Moving to next room');
+
+ if (!this.currentDungeon) {
+ console.warn('[DUNGEON SYSTEM] No active dungeon to continue');
+ return;
+ }
+
+ // Request next room from server
+ if (this.game.socket) {
+ this.game.socket.emit('next_room', {
+ instanceId: this.currentDungeon.id,
+ userId: this.game.systems.player?.id || 'anonymous'
+ });
+ }
+ }
+
/**
* Update UI with current dungeon information
*/
updateUI() {
- if (this.game.uiManager) {
- this.game.uiManager.updateDungeonUI({
+ if (this.game.systems.ui) {
+ this.game.systems.ui.updateDungeonUI({
currentDungeon: this.currentDungeon,
currentRoom: this.currentRoom,
progress: this.dungeonProgress,
isExploring: this.isExploring
});
+ } else {
+ console.warn('[DUNGEON SYSTEM] UI manager not available in game.systems.ui');
}
}
-
+
/**
* Initialize system and load server data
*/
@@ -447,15 +813,19 @@ class DungeonSystem {
console.error('[DUNGEON SYSTEM] Socket still not available after retry');
}
}, 1000);
- } else {
- this.setupSocketListeners();
- await this.loadServerData();
+ return;
}
- console.log('[DUNGEON SYSTEM] Client dungeon system initialization complete');
+ this.setupSocketListeners();
+ await this.loadServerData();
}
}
+// Export DungeonSystem to global scope
+if (typeof window !== 'undefined') {
+ window.DungeonSystem = DungeonSystem;
+}
+
// Export for use in GameEngine
if (typeof module !== 'undefined' && module.exports) {
module.exports = DungeonSystem;
diff --git a/Client/js/systems/ItemSystem.js b/Client/js/systems/ItemSystem.js
index 0c15b49..53af214 100644
--- a/Client/js/systems/ItemSystem.js
+++ b/Client/js/systems/ItemSystem.js
@@ -9,7 +9,8 @@ class ItemSystem {
// Item storage
this.itemCatalog = new Map(); // itemId -> item data
- this.shopItems = []; // Array of shop items
+ this.shopItems = []; // Array of shop items (legacy)
+ this.shopItemsByCategory = {}; // Categorized shop items (new structure)
this.lastUpdated = null;
// Loading state
@@ -56,10 +57,31 @@ class ItemSystem {
// Load shop items from server
const shopItems = await this.fetchShopItems();
- console.log('[ITEM SYSTEM] Received', shopItems.length, 'items from server');
-
- // Process and store items
- this.processServerItems(shopItems);
+ // Handle new shop structure (categorized) vs old structure (flat array)
+ let totalItems = 0;
+ if (Array.isArray(shopItems)) {
+ // Old structure: flat array
+ totalItems = shopItems.length;
+ console.log('[ITEM SYSTEM] Received', totalItems, 'items from server (old structure)');
+ this.processServerItems(shopItems);
+ this.shopItemsByCategory = {}; // Clear categorized data
+ } else if (shopItems && typeof shopItems === 'object') {
+ // New structure: categorized object
+ totalItems = Object.values(shopItems).reduce((sum, categoryItems) => sum + categoryItems.length, 0);
+ console.log('[ITEM SYSTEM] Received', totalItems, 'items from server (new structure)');
+ console.log('[ITEM SYSTEM] Categories:', Object.keys(shopItems));
+
+ // Store categorized data
+ this.shopItemsByCategory = shopItems;
+
+ // Flatten all items for processing
+ const allItems = Object.values(shopItems).flat();
+ this.processServerItems(allItems);
+ } else {
+ console.warn('[ITEM SYSTEM] Invalid shop items structure received:', typeof shopItems);
+ totalItems = 0;
+ this.shopItemsByCategory = {};
+ }
this.lastUpdated = Date.now();
console.log(`[ITEM SYSTEM] Successfully loaded ${this.itemCatalog.size} items from server`);
@@ -113,9 +135,36 @@ class ItemSystem {
reject(new Error('Server request timeout'));
}, 10000);
+ // Test server connection first
+ console.log('[ITEM SYSTEM] Testing server connection...');
+ window.game.socket.emit('ping', { timestamp: Date.now() });
+
+ // Listen for ping response
+ const pingHandler = (data) => {
+ console.log('[ITEM SYSTEM] Ping response received:', data);
+ console.log('[ITEM SYSTEM] Server is responding! Ping roundtrip:', Date.now() - data.received, 'ms');
+ window.game.socket.off('ping', pingHandler);
+ window.game.socket.off('pong', pingHandler);
+ };
+ window.game.socket.on('ping', pingHandler);
+
+ // Listen for pong response (backup)
+ const pongHandler = (data) => {
+ console.log('[ITEM SYSTEM] Pong response received:', data);
+ console.log('[ITEM SYSTEM] Server is responding! Pong roundtrip:', Date.now() - data.timestamp, 'ms');
+ window.game.socket.off('pong', pongHandler);
+ };
+ window.game.socket.on('pong', pongHandler);
+
// Request shop items from server
console.log('[ITEM SYSTEM] Emitting getShopItems request');
+ console.log('[ITEM SYSTEM] Socket state:', {
+ connected: window.game.socket.connected,
+ id: window.game.socket.id
+ });
+
window.game.socket.emit('getShopItems', {});
+ console.log('[ITEM SYSTEM] Request sent, waiting for response...');
// Listen for response
const handleResponse = (data) => {
@@ -124,13 +173,20 @@ class ItemSystem {
window.game.socket.off('shopItemsReceived', handleResponse);
console.log('[ITEM SYSTEM] Response success:', data.success);
- console.log('[ITEM SYSTEM] Response items count:', data.items?.length || 0);
+ console.log('[ITEM SYSTEM] Response shopItems keys:', data.shopItems ? Object.keys(data.shopItems) : 'none');
if (data.success) {
- console.log('[ITEM SYSTEM] Successfully received', data.items?.length || 0, 'items');
+ console.log('[ITEM SYSTEM] Successfully received shop data');
console.log('[ITEM SYSTEM] Response timestamp:', data.timestamp);
- console.log('[ITEM SYSTEM] Sample item:', data.items?.[0]);
- resolve(data.items || []);
+
+ // Log item counts per category
+ if (data.shopItems) {
+ Object.entries(data.shopItems).forEach(([category, items]) => {
+ console.log(`[ITEM SYSTEM] ${category}: ${items.length} items`);
+ });
+ }
+
+ resolve(data.shopItems || {});
} else {
console.error('[ITEM SYSTEM] Server returned error:', data.error);
reject(new Error(data.error || 'Failed to load shop items'));
@@ -139,6 +195,10 @@ class ItemSystem {
console.log('[ITEM SYSTEM] Setting up shopItemsReceived listener');
window.game.socket.on('shopItemsReceived', handleResponse);
+
+ // Verify the listener was set up
+ const listeners = window.game.socket.listeners('shopItemsReceived');
+ console.log('[ITEM SYSTEM] shopItemsReceived listeners count:', listeners.length);
});
}
@@ -180,6 +240,13 @@ class ItemSystem {
* Process items received from server
*/
processServerItems(items) {
+ // Safety check for items parameter
+ if (!items || !Array.isArray(items)) {
+ console.error('[ITEM SYSTEM] Invalid items parameter:', items);
+ console.error('[ITEM SYSTEM] Expected array, got:', typeof items);
+ return;
+ }
+
console.log('[ITEM SYSTEM] Processing', items.length, 'items from server');
console.log('[ITEM SYSTEM] Sample items:', items.slice(0, 3));
@@ -195,14 +262,14 @@ class ItemSystem {
this.shopItems.push(item);
}
- console.log('[ITEM SYSTEM] Added item:', {
- id: item.id,
- name: item.name,
- type: item.type,
- rarity: item.rarity,
- price: item.price,
- categories: item.categories
- });
+ // console.log('[ITEM SYSTEM] Added item:', {
+ // id: item.id,
+ // name: item.name,
+ // type: item.type,
+ // rarity: item.rarity,
+ // price: item.price,
+ // categories: item.categories
+ // });
}
console.log('[ITEM SYSTEM] Processing complete - Catalog:', this.itemCatalog.size, 'Shop items:', this.shopItems.length);
@@ -238,6 +305,13 @@ class ItemSystem {
return [...this.shopItems];
}
+ /**
+ * Get shop items by category (new structure)
+ */
+ getShopItemsByCategory() {
+ return this.shopItemsByCategory || {};
+ }
+
/**
* Get items by category
*/
diff --git a/Client/js/systems/QuestSystem.js b/Client/js/systems/QuestSystem.js
index cdd0b2b..aaddf2b 100644
--- a/Client/js/systems/QuestSystem.js
+++ b/Client/js/systems/QuestSystem.js
@@ -39,682 +39,56 @@ class QuestSystem {
questStatus: Object.keys(this.questStatus)
});
- // Main story quests
- this.mainQuests = [
- {
- id: 'tutorial_complete',
- name: 'First Steps',
- description: 'Complete the tutorial dungeon and learn the basics',
- type: 'main',
- status: 'available',
- requirements: { level: 1 },
- objectives: [
- { id: 'clear_tutorial_dungeon', description: 'Complete the tutorial dungeon', target: 1, current: 0, type: 'tutorial_dungeon' },
- { id: 'reach_level_2', description: 'Reach level 2', target: 2, current: 0, type: 'level' }
- ],
- rewards: { credits: 500, experience: 100, gems: 5 },
- nextQuest: 'first_ship_upgrade'
- },
- {
- id: 'first_ship_upgrade',
- name: 'Ship Enhancement',
- description: 'Upgrade your ship for better performance',
- type: 'main',
- status: 'available',
- requirements: { quest: 'tutorial_complete' },
- objectives: [
- { id: 'upgrade_weapon', description: 'Upgrade ship weapons', target: 1, current: 0, type: 'upgrade' },
- { id: 'upgrade_shield', description: 'Upgrade ship shields', target: 1, current: 0, type: 'upgrade' }
- ],
- rewards: { credits: 1000, experience: 200, gems: 10 },
- nextQuest: 'join_guild'
- },
- {
- id: 'join_guild',
- name: 'Guild Recruitment',
- description: 'Join a guild and participate in guild activities',
- type: 'main',
- status: 'available',
- requirements: { quest: 'first_ship_upgrade', level: 5 },
- objectives: [
- { id: 'join_guild', description: 'Join a guild', target: 1, current: 0, type: 'guild' },
- { id: 'guild_contribution', description: 'Contribute to guild', target: 100, current: 0, type: 'contribution' }
- ],
- rewards: { credits: 2000, experience: 500, gems: 20 },
- nextQuest: 'master_commander'
- },
- {
- id: 'master_commander',
- name: 'Master Commander',
- description: 'Become a master commander and lead your fleet to victory',
- type: 'main',
- status: 'available',
- requirements: { quest: 'join_guild', level: 10 },
- objectives: [
- { id: 'reach_level_10', description: 'Reach level 10', target: 10, current: 0, type: 'level' },
- { id: 'clear_10_dungeons', description: 'Clear 10 dungeons', target: 10, current: 0, type: 'dungeon' },
- { id: 'max_skill', description: 'Max out one skill', target: 10, current: 0, type: 'skill' }
- ],
- rewards: { credits: 5000, experience: 1000, gems: 50, item: 'legendary_weapon' }
- }
- ];
+ // Main story quests - populated by server
+ this.mainQuests = [];
- // All possible daily quests (20 total)
- this.allDailyQuests = [
- // Easy quests (difficulty: 1)
- {
- id: 'daily_dungeon_easy',
- name: 'Quick Dungeon Run',
- description: 'Complete any dungeon',
- type: 'daily',
- difficulty: 1,
- status: 'available',
- objectives: [
- { id: 'clear_dungeon', description: 'Clear 1 dungeon', target: 1, current: 0, type: 'dungeon' }
- ],
- rewards: { credits: 100, experience: 25, gems: 1 }
- },
- {
- id: 'daily_combat_easy',
- name: 'Light Combat',
- description: 'Defeat a few enemies',
- type: 'daily',
- difficulty: 1,
- status: 'available',
- objectives: [
- { id: 'defeat_enemies', description: 'Defeat 10 enemies', target: 10, current: 0, type: 'combat' }
- ],
- rewards: { credits: 80, experience: 20, gems: 1 }
- },
- {
- id: 'daily_crafting_easy',
- name: 'Basic Crafting',
- description: 'Craft some items',
- type: 'daily',
- difficulty: 1,
- status: 'available',
- objectives: [
- { id: 'craft_items', description: 'Craft 2 items', target: 2, current: 0, type: 'crafting' }
- ],
- rewards: { credits: 90, experience: 22, gems: 1 }
- },
- {
- id: 'daily_level_easy',
- name: 'Level Up',
- description: 'Gain experience and level up',
- type: 'daily',
- difficulty: 1,
- status: 'available',
- objectives: [
- { id: 'gain_level', description: 'Gain 1 level', target: 1, current: 0, type: 'level' }
- ],
- rewards: { credits: 120, experience: 30, gems: 2 }
- },
- {
- id: 'daily_energy_easy',
- name: 'Energy Management',
- description: 'Use energy efficiently',
- type: 'daily',
- difficulty: 1,
- status: 'available',
- objectives: [
- { id: 'use_energy', description: 'Use 50 energy', target: 50, current: 0, type: 'energy' }
- ],
- rewards: { credits: 70, experience: 18, gems: 1 }
- },
- // Medium quests (difficulty: 2)
- {
- id: 'daily_dungeon_medium',
- name: 'Dungeon Explorer',
- description: 'Complete multiple dungeons',
- type: 'daily',
- difficulty: 2,
- status: 'available',
- objectives: [
- { id: 'clear_dungeons', description: 'Clear 3 dungeons', target: 3, current: 0, type: 'dungeon' }
- ],
- rewards: { credits: 300, experience: 75, gems: 3 }
- },
- {
- id: 'daily_combat_medium',
- name: 'Combat Training',
- description: 'Defeat enemies in combat',
- type: 'daily',
- difficulty: 2,
- status: 'available',
- objectives: [
- { id: 'defeat_enemies', description: 'Defeat 20 enemies', target: 20, current: 0, type: 'combat' }
- ],
- rewards: { credits: 150, experience: 40, gems: 1 }
- },
- {
- id: 'daily_combat_hard',
- name: 'Combat Veteran',
- description: 'Defeat many enemies',
- type: 'daily',
- difficulty: 2,
- status: 'available',
- objectives: [
- { id: 'defeat_enemies', description: 'Defeat 50 enemies', target: 50, current: 0, type: 'combat' }
- ],
- rewards: { credits: 250, experience: 60, gems: 3 }
- },
- {
- id: 'daily_crafting_medium',
- name: 'Master Crafter',
- description: 'Craft many items',
- type: 'daily',
- difficulty: 2,
- status: 'available',
- objectives: [
- { id: 'craft_items', description: 'Craft 5 items', target: 5, current: 0, type: 'crafting' }
- ],
- rewards: { credits: 280, experience: 70, gems: 3 }
- },
- {
- id: 'daily_upgrade_medium',
- name: 'Equipment Upgrade',
- description: 'Upgrade your equipment',
- type: 'daily',
- difficulty: 2,
- status: 'available',
- objectives: [
- { id: 'upgrade_items', description: 'Upgrade 3 items', target: 3, current: 0, type: 'upgrade' }
- ],
- rewards: { credits: 320, experience: 80, gems: 4 }
- },
- {
- id: 'daily_wealth_medium',
- name: 'Wealth Accumulator',
- description: 'Earn credits',
- type: 'daily',
- difficulty: 2,
- status: 'available',
- objectives: [
- { id: 'earn_credits', description: 'Earn 1000 credits', target: 1000, current: 0, type: 'credits' }
- ],
- rewards: { credits: 400, experience: 50, gems: 3 }
- },
- // Hard quests (difficulty: 3)
- {
- id: 'daily_dungeon_hard',
- name: 'Dungeon Master',
- description: 'Complete many dungeons',
- type: 'daily',
- difficulty: 3,
- status: 'available',
- objectives: [
- { id: 'clear_dungeons', description: 'Clear 5 dungeons', target: 5, current: 0, type: 'dungeon' }
- ],
- rewards: { credits: 600, experience: 150, gems: 6 }
- },
- {
- id: 'daily_combat_hard',
- name: 'Combat Master',
- description: 'Defeat many powerful enemies',
- type: 'daily',
- difficulty: 3,
- status: 'available',
- objectives: [
- { id: 'defeat_enemies', description: 'Defeat 100 enemies', target: 100, current: 0, type: 'combat' }
- ],
- rewards: { credits: 500, experience: 120, gems: 5 }
- },
- {
- id: 'daily_level_hard',
- name: 'Power Leveling',
- description: 'Gain multiple levels',
- type: 'daily',
- difficulty: 3,
- status: 'available',
- objectives: [
- { id: 'gain_levels', description: 'Gain 3 levels', target: 3, current: 0, type: 'level' }
- ],
- rewards: { credits: 700, experience: 200, gems: 7 }
- },
- {
- id: 'daily_boss_hard',
- name: 'Boss Hunter',
- description: 'Defeat boss enemies',
- type: 'daily',
- difficulty: 3,
- status: 'available',
- objectives: [
- { id: 'defeat_bosses', description: 'Defeat 3 bosses', target: 3, current: 0, type: 'boss' }
- ],
- rewards: { credits: 800, experience: 180, gems: 8 }
- },
- {
- id: 'daily_collection_hard',
- name: 'Master Collector',
- description: 'Collect rare items',
- type: 'daily',
- difficulty: 3,
- status: 'available',
- objectives: [
- { id: 'collect_rare', description: 'Collect 10 rare items', target: 10, current: 0, type: 'collection' }
- ],
- rewards: { credits: 650, experience: 140, gems: 6 }
- },
- // Special quests (difficulty: 4)
- {
- id: 'daily_speedrun',
- name: 'Speed Runner',
- description: 'Complete dungeons quickly',
- type: 'daily',
- difficulty: 4,
- status: 'available',
- objectives: [
- { id: 'fast_dungeon', description: 'Complete 2 dungeons under 5 minutes', target: 2, current: 0, type: 'speedrun' }
- ],
- rewards: { credits: 1000, experience: 250, gems: 10 }
- },
- {
- id: 'daily_perfection',
- name: 'Perfectionist',
- description: 'Complete objectives without taking damage',
- type: 'daily',
- difficulty: 4,
- status: 'available',
- objectives: [
- { id: 'perfect_runs', description: '3 perfect dungeon runs', target: 3, current: 0, type: 'perfect' }
- ],
- rewards: { credits: 1200, experience: 300, gems: 12 }
- },
- {
- id: 'daily_multitask',
- name: 'Multitask Master',
- description: 'Complete multiple quest types',
- type: 'daily',
- difficulty: 4,
- status: 'available',
- objectives: [
- { id: 'dungeon_task', description: 'Clear 2 dungeons', target: 2, current: 0, type: 'dungeon' },
- { id: 'combat_task', description: 'Defeat 30 enemies', target: 30, current: 0, type: 'combat' },
- { id: 'craft_task', description: 'Craft 2 items', target: 2, current: 0, type: 'crafting' }
- ],
- rewards: { credits: 1500, experience: 400, gems: 15 }
- },
- {
- id: 'daily_endurance',
- name: 'Endurance Test',
- description: 'Complete long activities',
- type: 'daily',
- difficulty: 4,
- status: 'available',
- objectives: [
- { id: 'long_dungeon', description: 'Complete 1 dungeon without healing', target: 1, current: 0, type: 'endurance' }
- ],
- rewards: { credits: 1100, experience: 280, gems: 11 }
- },
- {
- id: 'daily_legendary',
- name: 'Legendary Challenge',
- description: 'Complete legendary difficulty content',
- type: 'daily',
- difficulty: 4,
- status: 'available',
- objectives: [
- { id: 'legendary_content', description: 'Complete 1 legendary dungeon', target: 1, current: 0, type: 'legendary' }
- ],
- rewards: { credits: 2000, experience: 500, gems: 20, item: 'rare_material' }
- }
- ];
-
- // Weekly quests (25 total quests with varied objectives)
- this.allWeeklyQuests = [
- // Combat-focused weekly quests
- {
- id: 'weekly_combat_basic',
- name: 'Weekly Combat Duty',
- description: 'Complete combat objectives throughout the week',
- type: 'weekly',
- difficulty: 1,
- status: 'available',
- objectives: [
- { id: 'defeat_enemies', description: 'Defeat 100 enemies', target: 100, current: 0, type: 'combat' }
- ],
- rewards: { credits: 800, experience: 200, gems: 8 }
- },
- {
- id: 'weekly_combat_elite',
- name: 'Elite Hunter Weekly',
- description: 'Hunt down elite enemies',
- type: 'weekly',
- difficulty: 3,
- status: 'available',
- objectives: [
- { id: 'defeat_elites', description: 'Defeat 25 elite enemies', target: 25, current: 0, type: 'elite_combat' }
- ],
- rewards: { credits: 1500, experience: 400, gems: 15 }
- },
- {
- id: 'weekly_boss_hunter',
- name: 'Boss Hunter Weekly',
- description: 'Defeat powerful boss enemies',
- type: 'weekly',
- difficulty: 4,
- status: 'available',
- objectives: [
- { id: 'defeat_bosses', description: 'Defeat 10 bosses', target: 10, current: 0, type: 'boss' }
- ],
- rewards: { credits: 2500, experience: 600, gems: 25, item: 'boss_material' }
- },
-
- // Dungeon-focused weekly quests
- {
- id: 'weekly_dungeon_explorer',
- name: 'Weekly Dungeon Explorer',
- description: 'Explore various dungeons',
- type: 'weekly',
- difficulty: 2,
- status: 'available',
- objectives: [
- { id: 'clear_dungeons', description: 'Clear 15 dungeons', target: 15, current: 0, type: 'dungeon' }
- ],
- rewards: { credits: 1200, experience: 300, gems: 12 }
- },
- {
- id: 'weekly_dungeon_master',
- name: 'Weekly Dungeon Master',
- description: 'Master difficult dungeons',
- type: 'weekly',
- difficulty: 3,
- status: 'available',
- objectives: [
- { id: 'clear_hard_dungeons', description: 'Clear 10 hard dungeons', target: 10, current: 0, type: 'hard_dungeon' }
- ],
- rewards: { credits: 2000, experience: 500, gems: 20 }
- },
- {
- id: 'weekly_dungeon_extreme',
- name: 'Extreme Dungeon Challenge',
- description: 'Conquer extreme difficulty dungeons',
- type: 'weekly',
- difficulty: 4,
- status: 'available',
- objectives: [
- { id: 'clear_extreme_dungeons', description: 'Clear 5 extreme dungeons', target: 5, current: 0, type: 'extreme_dungeon' }
- ],
- rewards: { credits: 3500, experience: 800, gems: 35, item: 'extreme_material' }
- },
-
- // Crafting and upgrade weekly quests
- {
- id: 'weekly_crafting_basic',
- name: 'Weekly Crafting Session',
- description: 'Craft items throughout the week',
- type: 'weekly',
- difficulty: 1,
- status: 'available',
- objectives: [
- { id: 'craft_items', description: 'Craft 20 items', target: 20, current: 0, type: 'crafting' }
- ],
- rewards: { credits: 600, experience: 150, gems: 6 }
- },
- {
- id: 'weekly_crafting_master',
- name: 'Master Crafter Weekly',
- description: 'Craft advanced items',
- type: 'weekly',
- difficulty: 3,
- status: 'available',
- objectives: [
- { id: 'craft_advanced', description: 'Craft 10 advanced items', target: 10, current: 0, type: 'advanced_crafting' }
- ],
- rewards: { credits: 1800, experience: 450, gems: 18 }
- },
- {
- id: 'weekly_upgrade_specialist',
- name: 'Weekly Upgrade Specialist',
- description: 'Upgrade equipment and systems',
- type: 'weekly',
- difficulty: 2,
- status: 'available',
- objectives: [
- { id: 'upgrade_items', description: 'Upgrade 15 items', target: 15, current: 0, type: 'upgrade' }
- ],
- rewards: { credits: 1400, experience: 350, gems: 14 }
- },
-
- // Progression weekly quests
- {
- id: 'weekly_level_up',
- name: 'Weekly Level Up Challenge',
- description: 'Gain levels throughout the week',
- type: 'weekly',
- difficulty: 2,
- status: 'available',
- objectives: [
- { id: 'gain_levels', description: 'Gain 5 levels', target: 5, current: 0, type: 'level' }
- ],
- rewards: { credits: 1000, experience: 250, gems: 10 }
- },
- {
- id: 'weekly_skill_master',
- name: 'Weekly Skill Mastery',
- description: 'Improve your skills',
- type: 'weekly',
- difficulty: 3,
- status: 'available',
- objectives: [
- { id: 'improve_skills', description: 'Gain 20 skill points', target: 20, current: 0, type: 'skill' }
- ],
- rewards: { credits: 1600, experience: 400, gems: 16 }
- },
-
- // Resource and wealth weekly quests
- {
- id: 'weekly_wealth_collector',
- name: 'Weekly Wealth Collector',
- description: 'Accumulate wealth',
- type: 'weekly',
- difficulty: 2,
- status: 'available',
- objectives: [
- { id: 'earn_credits', description: 'Earn 5000 credits', target: 5000, current: 0, type: 'credits' }
- ],
- rewards: { credits: 2000, experience: 300, gems: 12 }
- },
- {
- id: 'weekly_resource_gatherer',
- name: 'Weekly Resource Gathering',
- description: 'Collect valuable resources',
- type: 'weekly',
- difficulty: 2,
- status: 'available',
- objectives: [
- { id: 'collect_resources', description: 'Collect 500 resources', target: 500, current: 0, type: 'collection' }
- ],
- rewards: { credits: 1200, experience: 320, gems: 13 }
- },
-
- // Special activity weekly quests
- {
- id: 'weekly_energy_management',
- name: 'Weekly Energy Management',
- description: 'Use energy efficiently',
- type: 'weekly',
- difficulty: 1,
- status: 'available',
- objectives: [
- { id: 'use_energy', description: 'Use 500 energy', target: 500, current: 0, type: 'energy' }
- ],
- rewards: { credits: 800, experience: 180, gems: 8 }
- },
- {
- id: 'weekly_speed_demon',
- name: 'Weekly Speed Demon',
- description: 'Complete activities quickly',
- type: 'weekly',
- difficulty: 3,
- status: 'available',
- objectives: [
- { id: 'speed_runs', description: 'Complete 10 speed runs', target: 10, current: 0, type: 'speedrun' }
- ],
- rewards: { credits: 2200, experience: 550, gems: 22 }
- },
- {
- id: 'weekly_perfectionist',
- name: 'Weekly Perfectionist',
- description: 'Complete flawless runs',
- type: 'weekly',
- difficulty: 4,
- status: 'available',
- objectives: [
- { id: 'perfect_runs', description: 'Complete 8 perfect runs', target: 8, current: 0, type: 'perfect' }
- ],
- rewards: { credits: 3000, experience: 700, gems: 30, item: 'perfection_material' }
- },
-
- // Multi-objective weekly quests
- {
- id: 'weekly_all_rounder',
- name: 'Weekly All-Rounder',
- description: 'Complete various activities',
- type: 'weekly',
- difficulty: 3,
- status: 'available',
- objectives: [
- { id: 'dungeon_task', description: 'Clear 8 dungeons', target: 8, current: 0, type: 'dungeon' },
- { id: 'combat_task', description: 'Defeat 50 enemies', target: 50, current: 0, type: 'combat' },
- { id: 'craft_task', description: 'Craft 5 items', target: 5, current: 0, type: 'crafting' }
- ],
- rewards: { credits: 2500, experience: 600, gems: 25 }
- },
- {
- id: 'weekly_specialist',
- name: 'Weekly Specialist',
- description: 'Focus on specialized activities',
- type: 'weekly',
- difficulty: 3,
- status: 'available',
- objectives: [
- { id: 'special_dungeons', description: 'Clear 5 themed dungeons', target: 5, current: 0, type: 'themed_dungeon' },
- { id: 'special_crafting', description: 'Craft 8 themed items', target: 8, current: 0, type: 'themed_crafting' }
- ],
- rewards: { credits: 2300, experience: 580, gems: 23 }
- },
-
- // Exploration and discovery weekly quests
- {
- id: 'weekly_explorer',
- name: 'Weekly Explorer',
- description: 'Explore new areas and content',
- type: 'weekly',
- difficulty: 2,
- status: 'available',
- objectives: [
- { id: 'explore_areas', description: 'Explore 20 new areas', target: 20, current: 0, type: 'exploration' }
- ],
- rewards: { credits: 1300, experience: 340, gems: 13 }
- },
- {
- id: 'weekly_discovery',
- name: 'Weekly Discovery',
- description: 'Discover hidden secrets',
- type: 'weekly',
- difficulty: 3,
- status: 'available',
- objectives: [
- { id: 'discover_secrets', description: 'Discover 15 secrets', target: 15, current: 0, type: 'discovery' }
- ],
- rewards: { credits: 1900, experience: 480, gems: 19 }
- },
-
- // Endurance and challenge weekly quests
- {
- id: 'weekly_endurance',
- name: 'Weekly Endurance Test',
- description: 'Complete long-form challenges',
- type: 'weekly',
- difficulty: 3,
- status: 'available',
- objectives: [
- { id: 'endurance_runs', description: 'Complete 5 endurance runs', target: 5, current: 0, type: 'endurance' }
- ],
- rewards: { credits: 2100, experience: 530, gems: 21 }
- },
- {
- id: 'weekly_survivor',
- name: 'Weekly Survivor',
- description: 'Survive challenging conditions',
- type: 'weekly',
- difficulty: 4,
- status: 'available',
- objectives: [
- { id: 'survival_runs', description: 'Complete 3 survival runs', target: 3, current: 0, type: 'survival' }
- ],
- rewards: { credits: 3200, experience: 750, gems: 32, item: 'survival_material' }
- },
-
- // Social and community weekly quests
- {
- id: 'weekly_helper',
- name: 'Weekly Helper',
- description: 'Assist other players',
- type: 'weekly',
- difficulty: 2,
- status: 'available',
- objectives: [
- { id: 'assist_players', description: 'Assist 10 players', target: 10, current: 0, type: 'assist' }
- ],
- rewards: { credits: 1100, experience: 280, gems: 11 }
- },
- {
- id: 'weekly_leader',
- name: 'Weekly Leader',
- description: 'Lead group activities',
- type: 'weekly',
- difficulty: 3,
- status: 'available',
- objectives: [
- { id: 'lead_activities', description: 'Lead 5 group activities', target: 5, current: 0, type: 'leadership' }
- ],
- rewards: { credits: 1700, experience: 430, gems: 17 }
- },
-
- // Legendary weekly quests
- {
- id: 'weekly_legendary',
- name: 'Weekly Legendary Challenge',
- description: 'Complete legendary difficulty content',
- type: 'weekly',
- difficulty: 5,
- status: 'available',
- objectives: [
- { id: 'legendary_content', description: 'Complete 3 legendary dungeons', target: 3, current: 0, type: 'legendary' }
- ],
- rewards: { credits: 5000, experience: 1200, gems: 50, item: 'legendary_material' }
- },
- {
- id: 'weekly_mythic',
- name: 'Weekly Mythic Trial',
- description: 'Face mythic level challenges',
- type: 'weekly',
- difficulty: 5,
- status: 'available',
- objectives: [
- { id: 'mythic_trials', description: 'Complete 2 mythic trials', target: 2, current: 0, type: 'mythic' }
- ],
- rewards: { credits: 6000, experience: 1500, gems: 60, item: 'mythic_material' }
- }
- ];
-
- // Currently active daily quests (3 random from allDailyQuests)
+ // Daily quests - populated by server
this.dailyQuests = [];
- this.selectedDailyQuests = [];
-
- // Currently active weekly quests (5 random from allWeeklyQuests)
this.weeklyQuests = [];
- this.selectedWeeklyQuests = [];
-
- // Current active quests
this.activeQuests = [];
this.completedQuests = [];
this.failedQuests = [];
- this.completedDailyQuests = []; // History of completed daily quests
- this.completedWeeklyQuests = []; // History of completed weekly quests
+
+ // Quest tracking arrays
+ this.selectedDailyQuests = [];
+ this.selectedWeeklyQuests = [];
+ this.completedDailyQuests = [];
+ this.completedWeeklyQuests = [];
+
+ // Procedural quest templates (server-driven)
+ this.proceduralTemplates = {};
+
+ // Quest generation settings
+ this.maxProceduralQuests = 3;
+ this.proceduralQuestRefresh = 30 * 60 * 1000; // 30 minutes
+
+ // Initialize stats
+ this.stats = {
+ questsCompleted: 0,
+ dailyQuestsCompleted: 0,
+ weeklyQuestsCompleted: 0,
+ totalRewardsEarned: { credits: 0, experience: 0, gems: 0 },
+ lastDailyReset: this.getServerTime(),
+ lastWeeklyReset: this.getServerTime()
+ };
+
+ console.log('[QUEST SYSTEM] Client quest system initialized - waiting for server data');
+
+ if (debugLogger) debugLogger.endStep('QuestSystem.constructor', {
+ mainQuestsCount: this.mainQuests.length,
+ dailyQuestsCount: this.dailyQuests.length,
+ weeklyQuestsCount: this.weeklyQuests.length,
+ maxProceduralQuests: this.maxProceduralQuests,
+ proceduralQuestRefresh: this.proceduralQuestRefresh,
+ initialStats: this.stats,
+ dailyQuestsInitialized: this.dailyQuests.length,
+ weeklyQuestsInitialized: this.weeklyQuests.length
+ });
+
+ // Start countdown timers for server-driven quests
+ console.log('[QUEST SYSTEM] Starting countdown timers');
+ this.startDailyCountdown();
+ this.startWeeklyCountdown();
// Initialize daily quests with safety check
try {
diff --git a/Client/js/ui/UIManager.js b/Client/js/ui/UIManager.js
index 9b3bdc2..cc8e93c 100644
--- a/Client/js/ui/UIManager.js
+++ b/Client/js/ui/UIManager.js
@@ -1712,7 +1712,9 @@ class UIManager {
if (creditsElement) {
const oldCredits = creditsElement.textContent;
const creditsAmount = economy.credits || 0;
- // console.log('[UI MANAGER] Credits debug - economy.credits:', economy.credits, 'creditsAmount:', creditsAmount);
+ if (oldCredits !== this.game.formatNumber(creditsAmount)) {
+ console.log('[UI MANAGER] Credits updated:', this.game.formatNumber(creditsAmount));
+ }
creditsElement.textContent = this.game.formatNumber(creditsAmount);
elementsUpdated++;
@@ -1732,7 +1734,9 @@ class UIManager {
if (gemsElement) {
const oldGems = gemsElement.textContent;
const gemsAmount = economy.gems || 0;
- // console.log('[UI MANAGER] Gems debug - economy.gems:', economy.gems, 'gemsAmount:', gemsAmount);
+ if (oldGems !== this.game.formatNumber(gemsAmount)) {
+ console.log('[UI MANAGER] Gems updated:', this.game.formatNumber(gemsAmount));
+ }
gemsElement.textContent = this.game.formatNumber(gemsAmount);
elementsUpdated++;
@@ -1753,8 +1757,11 @@ class UIManager {
const oldEnergy = energyElement.textContent;
const currentEnergy = player.attributes.currentEnergy || player.attributes.energy || 0;
const maxEnergy = player.attributes.maxEnergy || 100;
- // console.log('[UI MANAGER] Energy debug - player.attributes.currentEnergy:', player.attributes.currentEnergy, 'player.attributes.maxEnergy:', player.attributes.maxEnergy);
- energyElement.textContent = `${currentEnergy}/${maxEnergy}`;
+ const newEnergy = `${currentEnergy}/${maxEnergy}`;
+ if (oldEnergy !== newEnergy) {
+ console.log('[UI MANAGER] Energy updated:', newEnergy);
+ }
+ energyElement.textContent = newEnergy;
elementsUpdated++;
if (debugLogger) debugLogger.logStep('Energy updated', {
@@ -2630,6 +2637,94 @@ class UIManager {
`;
}
+
+ // Update dungeon UI when player enters/exits dungeons
+ updateDungeonUI(dungeonData) {
+ console.log('[UI MANAGER] Updating dungeon UI:', dungeonData);
+ console.log('[UI MANAGER] Dungeon state check:', {
+ isExploring: dungeonData.isExploring,
+ hasCurrentDungeon: !!dungeonData.currentDungeon,
+ hasCurrentRoom: !!dungeonData.currentRoom,
+ progress: dungeonData.progress,
+ dungeonViewElement: !!document.getElementById('dungeonView')
+ });
+
+ // Check if dungeon is completed (progress = -1)
+ if (dungeonData.progress === -1 || !dungeonData.isExploring) {
+ // Player is not in dungeon or dungeon is completed - show normal dungeon list
+ console.log('[UI MANAGER] Player not exploring or dungeon completed, showing dungeon list');
+ this.switchTab('dungeons');
+ return;
+ }
+
+ // Player is in dungeon - show exploration interface
+ const dungeonView = document.getElementById('dungeonView');
+ if (dungeonView) {
+ console.log('[UI MANAGER] Found dungeonView element, creating exploration UI');
+ const dungeon = dungeonData.currentDungeon;
+ const progress = dungeonData.progress || 0;
+ const currentRoom = dungeonData.currentRoom;
+
+ let content = `
+
+
+ `;
+
+ if (currentRoom) {
+ content += `
+
+
${currentRoom.name}
+
${currentRoom.description}
+
+
Enemies:
+ ${currentRoom.enemies && currentRoom.enemies.length > 0 ?
+ currentRoom.enemies.map((enemy, index) => {
+ console.log(`[UI MANAGER] Rendering enemy ${index}:`, enemy);
+ return `
+
+
${enemy.name || 'Unknown Enemy'}
+
HP: ${enemy.health || '??'} | ATK: ${enemy.attack || '??'} | DEF: ${enemy.defense || '??'}
+
+ `}).join('') :
+ '
No enemies in this room
'
+ }
+
+ ${currentRoom.enemies && currentRoom.enemies.length > 0 ?
+ `
` :
+ `
`
+ }
+
+ `;
+ } else {
+ content += `
+
+
Loading next room...
+
+
+ `;
+ }
+
+ content += '
';
+ dungeonView.innerHTML = content;
+ console.log('[UI MANAGER] Dungeon exploration UI applied successfully');
+ } else {
+ console.error('[UI MANAGER] dungeonView element not found!');
+ }
+ }
}
// Export UIManager to global scope
diff --git a/Client/styles/components.css b/Client/styles/components.css
index bc076f6..deecfc2 100644
--- a/Client/styles/components.css
+++ b/Client/styles/components.css
@@ -1533,6 +1533,134 @@ body.fullscreen .shop-container {
color: var(--text-secondary);
}
+/* Collapsible Dungeon Sections */
+.dungeon-section {
+ margin-bottom: 1rem;
+ border: 1px solid var(--border-color);
+ border-radius: 8px;
+ overflow: hidden;
+ background: var(--bg-secondary);
+}
+
+.difficulty-header.collapsible {
+ padding: 1rem;
+ cursor: pointer;
+ user-select: none;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ transition: background-color 0.3s ease;
+ margin: 0;
+ border: none;
+ border-radius: 0;
+}
+
+.difficulty-header.collapsible:hover {
+ background: var(--bg-tertiary);
+}
+
+.header-content {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.header-content i {
+ font-size: 1.2rem;
+}
+
+.header-content span {
+ font-weight: 600;
+ font-size: 1.1rem;
+}
+
+.dungeon-count {
+ background: rgba(255, 255, 255, 0.2);
+ padding: 0.2rem 0.5rem;
+ border-radius: 12px;
+ font-size: 0.8rem;
+ font-weight: 600;
+ min-width: 2rem;
+ text-align: center;
+}
+
+.collapse-indicator {
+ transition: transform 0.3s ease;
+ background: rgba(255, 255, 255, 0.1);
+ width: 2rem;
+ height: 2rem;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.collapse-indicator i {
+ font-size: 0.9rem;
+ color: rgba(255, 255, 255, 0.9);
+}
+
+.difficulty-header.collapsible:hover .collapse-indicator {
+ background: rgba(255, 255, 255, 0.2);
+ transform: scale(1.1);
+}
+
+.dungeon-content {
+ padding: 0 1rem 1rem;
+ max-height: 2000px;
+ overflow: hidden;
+ transition: all 0.3s ease;
+ opacity: 1;
+}
+
+.dungeon-content.collapsed {
+ max-height: 0;
+ padding: 0 1rem;
+ opacity: 0;
+}
+
+/* Difficulty-specific colors for collapsible headers */
+.difficulty-header.tutorial {
+ background: linear-gradient(135deg, var(--info-color), #2c5aa0);
+ color: white;
+}
+
+.difficulty-header.easy {
+ background: linear-gradient(135deg, var(--success-color), #27ae60);
+ color: white;
+}
+
+.difficulty-header.medium {
+ background: linear-gradient(135deg, var(--warning-color), #f39c12);
+ color: white;
+}
+
+.difficulty-header.hard {
+ background: linear-gradient(135deg, var(--error-color), #e74c3c);
+ color: white;
+}
+
+.difficulty-header.extreme {
+ background: linear-gradient(135deg, #8e44ad, #9b59b6);
+ color: white;
+}
+
+/* Enhanced Dungeon Items in Collapsible Sections */
+.dungeon-content .dungeon-item {
+ margin-bottom: 0.75rem;
+ border-left: 4px solid transparent;
+ transition: all 0.3s ease;
+}
+
+.dungeon-content .dungeon-item:hover {
+ border-left-color: var(--primary-color);
+ transform: translateX(4px);
+}
+
+.dungeon-content .dungeon-item:last-child {
+ margin-bottom: 0;
+}
+
/* Shop Items */
.shop-item {
background: var(--bg-tertiary);
diff --git a/GameServer/server.js b/GameServer/server.js
index 3efe609..694b3f6 100644
--- a/GameServer/server.js
+++ b/GameServer/server.js
@@ -54,6 +54,9 @@ const io = socketIo(server, {
}
});
+// Pass io instance to systems that need it
+dungeonSystem.setIO(io);
+
// Game state
const gameInstances = new Map();
const connectedClients = new Map();
@@ -180,7 +183,7 @@ app.get('/api/shop/items', (req, res) => {
const shopItems = itemSystem.getRandomShopItems();
res.status(200).json({
success: true,
- items: shopItems,
+ shopItems: shopItems,
timestamp: new Date().toISOString()
});
} catch (error) {
@@ -195,12 +198,12 @@ app.get('/api/shop/items', (req, res) => {
app.get('/api/shop/items/:category', (req, res) => {
try {
const { category } = req.params;
- const items = itemSystem.getItemsByType(category);
+ const items = itemSystem.getRandomItemsByCategory(category);
res.status(200).json({
success: true,
- items: items,
category: category,
+ items: items,
timestamp: new Date().toISOString()
});
} catch (error) {
@@ -299,6 +302,7 @@ app.use('/api/base', require('./routes/base'));
// Socket.IO handlers (based on LocalServer)
io.on('connection', (socket) => {
+ console.log('[GAME SERVER] === NEW CLIENT CONNECTION ===');
console.log('[GAME SERVER] Client connected:', socket.id);
connectedClients.set(socket.id, {
connectedAt: Date.now(),
@@ -329,6 +333,13 @@ io.on('connection', (socket) => {
clearTimeout(authTimeout);
});
+ // Log all incoming events for debugging
+ socket.onAny((eventName, ...args) => {
+ if (eventName !== 'ping' && eventName !== 'pong') {
+ console.log(`[GAME SERVER] Event received: ${eventName} from ${socket.id}`, args);
+ }
+ });
+
// Authentication (similar to LocalServer)
socket.on('authenticate', async (data) => {
console.log('[GAME SERVER] Authenticating client:', socket.id, data);
@@ -376,6 +387,10 @@ io.on('connection', (socket) => {
if (playerData.stats.totalKills === undefined) playerData.stats.totalKills = 0;
if (playerData.stats.questsCompleted === undefined) playerData.stats.questsCompleted = 0;
+ // Ensure idle system production rates are initialized
+ idleSystem.initializePlayerData(playerData.userId);
+ console.log('[GAME SERVER] Idle system initialized for player:', playerData.username);
+
await savePlayerData(playerData.userId, playerData);
// In production, validate with API server
@@ -459,26 +474,63 @@ io.on('connection', (socket) => {
});
});
+ // Test idle system manually
+ socket.on('testIdleRewards', (data) => {
+ console.log('[GAME SERVER] Testing idle rewards for:', socket.id);
+
+ const clientData = connectedClients.get(socket.id);
+ if (clientData && clientData.userId) {
+ const onlineRewards = idleSystem.generateOnlineIdleRewards(clientData.userId, 10000);
+ console.log('[GAME SERVER] Test idle rewards:', onlineRewards);
+
+ socket.emit('testIdleRewards', {
+ rewards: onlineRewards,
+ productionRates: idleSystem.playerProductionRates.get(clientData.userId)
+ });
+ } else {
+ socket.emit('testIdleRewards', { error: 'Not authenticated' });
+ }
+ });
+
// Shop and item system events
+ socket.on('ping', (data) => {
+ console.log('[GAME SERVER] Ping received from:', socket.id, data);
+ socket.emit('pong', {
+ timestamp: Date.now(),
+ received: data.timestamp,
+ serverTime: new Date().toISOString()
+ });
+ });
+
socket.on('getShopItems', (data) => {
+ console.log('[GAME SERVER] === getShopItems REQUEST START ===');
console.log('[GAME SERVER] getShopItems request received from:', socket.id);
console.log('[GAME SERVER] Request data:', data);
+ console.log('[GAME SERVER] ItemSystem available:', !!itemSystem);
+ console.log('[GAME SERVER] ItemSystem.getRandomShopItems available:', typeof itemSystem.getRandomShopItems);
try {
console.log('[GAME SERVER] Getting shop items from ItemSystem...');
const shopItems = itemSystem.getRandomShopItems();
- console.log('[GAME SERVER] Got shop items:', shopItems.length, 'items');
- console.log('[GAME SERVER] Sample item:', shopItems[0]);
+ console.log('[GAME SERVER] Got shop items by category:', Object.keys(shopItems));
+
+ // Log item counts per category
+ Object.entries(shopItems).forEach(([category, items]) => {
+ console.log(`[GAME SERVER] ${category}: ${items.length} items`);
+ });
const response = {
success: true,
- items: shopItems,
+ shopItems: shopItems, // Changed from 'items' to 'shopItems'
timestamp: new Date().toISOString()
};
console.log('[GAME SERVER] Sending response:', response);
+ console.log('[GAME SERVER] About to emit shopItemsReceived to:', socket.id);
+
socket.emit('shopItemsReceived', response);
- console.log('[GAME SERVER] Response sent successfully');
+ console.log('[GAME SERVER] shopItemsReceived emitted successfully');
+ console.log('[GAME SERVER] === getShopItems REQUEST COMPLETE ===');
} catch (error) {
console.error('[GAME SERVER] Error sending shop items:', error);
console.error('[GAME SERVER] Error stack:', error.stack);
@@ -486,6 +538,44 @@ io.on('connection', (socket) => {
success: false,
error: 'Failed to load shop items'
});
+ console.log('[GAME SERVER] Error response sent');
+ }
+ });
+
+ socket.on('getShopCategory', (data) => {
+ console.log('[GAME SERVER] getShopCategory request received from:', socket.id);
+ console.log('[GAME SERVER] Request data:', data);
+
+ try {
+ const { category } = data;
+ if (!category) {
+ socket.emit('shopCategoryReceived', {
+ success: false,
+ error: 'Category required'
+ });
+ return;
+ }
+
+ console.log('[GAME SERVER] Getting items for category:', category);
+ const categoryItems = itemSystem.getRandomItemsByCategory(category);
+ console.log('[GAME SERVER] Got', categoryItems.length, 'items for category', category);
+
+ const response = {
+ success: true,
+ category: category,
+ items: categoryItems,
+ timestamp: new Date().toISOString()
+ };
+
+ console.log('[GAME SERVER] Sending category response:', response);
+ socket.emit('shopCategoryReceived', response);
+ console.log('[GAME SERVER] Category response sent successfully');
+ } catch (error) {
+ console.error('[GAME SERVER] Error sending category items:', error);
+ socket.emit('shopCategoryReceived', {
+ success: false,
+ error: 'Failed to load category items'
+ });
}
});
@@ -601,7 +691,7 @@ io.on('connection', (socket) => {
}
try {
- const playerData = await loadPlayerData(clientData.userId);
+ const playerData = await loadPlayerData(clientData.userId, clientData.username || 'Player');
const offlineRewards = idleSystem.calculateOfflineRewards(clientData.userId);
if (offlineRewards.offlineTime > 0 && offlineRewards.rewards.credits > 0) {
@@ -665,7 +755,17 @@ io.on('connection', (socket) => {
}
// Load current player data
- const playerData = await loadPlayerData(clientData.userId);
+ console.log('[GAME SERVER] Loading player data for purchase:', {
+ userId: clientData.userId,
+ username: clientData.username
+ });
+
+ const playerData = await loadPlayerData(clientData.userId, clientData.username || 'Player');
+ if (!playerData) {
+ console.log('[GAME SERVER] Purchase failed - could not load player data');
+ socket.emit('purchaseCompleted', { success: false, error: 'Failed to load player data' });
+ return;
+ }
console.log('[GAME SERVER] Player data loaded:', {
username: playerData.username,
credits: playerData.stats.credits,
@@ -801,7 +901,18 @@ io.on('connection', (socket) => {
// Send success response
const response = {
success: true,
- item: item,
+ item: {
+ id: item.id,
+ name: item.name,
+ type: item.type,
+ rarity: item.rarity,
+ description: item.description,
+ price: item.price,
+ currency: item.currency,
+ quantity: quantity,
+ stats: item.stats || {},
+ texturePath: item.texturePath || item.texture
+ },
quantity: quantity,
totalCost: totalCost,
currency: currency,
@@ -865,6 +976,20 @@ io.on('connection', (socket) => {
console.log('[GAME SERVER] Starting dungeon for:', socket.id, data);
try {
const { dungeonId, userId } = data;
+
+ // Check if dungeon is one-time only and already completed
+ const dungeon = dungeonSystem.getDungeon(dungeonId);
+ if (dungeon && dungeon.oneTimeOnly) {
+ const completedDungeons = dungeonSystem.getPlayerCompletedDungeons(userId);
+ if (completedDungeons.includes(dungeonId)) {
+ socket.emit('dungeon_started', {
+ success: false,
+ error: 'This dungeon can only be completed once per character.'
+ });
+ return;
+ }
+ }
+
const instance = dungeonSystem.createInstance(dungeonId, userId, []);
socket.emit('dungeon_started', { instance });
} catch (error) {
@@ -877,8 +1002,29 @@ io.on('connection', (socket) => {
console.log('[GAME SERVER] Processing encounter for:', socket.id, data);
try {
const { instanceId, userId } = data;
- const encounter = dungeonSystem.startEncounter(instanceId, userId);
- socket.emit('encounter_data', { encounter });
+ const result = dungeonSystem.startEncounter(instanceId, userId);
+
+ // Auto-complete combat for enemies with 0 attack
+ if (result.encounter.enemies && result.encounter.enemies.length > 0) {
+ const allZeroAttack = result.encounter.enemies.every(enemy => enemy.attack === 0);
+ if (allZeroAttack) {
+ console.log('[GAME SERVER] Auto-combat: All enemies have 0 attack, completing encounter');
+ const completionResult = dungeonSystem.completeEncounter(instanceId, userId, { victory: true });
+ socket.emit('encounter_completed', {
+ success: true,
+ rewards: completionResult.rewards,
+ nextEncounter: completionResult.nextEncounter,
+ encounterIndex: completionResult.encounterIndex
+ });
+ return;
+ }
+ }
+
+ socket.emit('encounter_data', {
+ encounter: result.encounter,
+ encounterIndex: result.encounterIndex,
+ instance: result.instance
+ });
} catch (error) {
console.error('[GAME SERVER] Error processing encounter:', error);
socket.emit('encounter_data', { success: false, error: error.message });
@@ -897,6 +1043,45 @@ io.on('connection', (socket) => {
}
});
+ socket.on('get_quests', (data) => {
+ console.log('[GAME SERVER] Getting quests for:', socket.id, data);
+ try {
+ // Get quest data from quest system
+ const questData = questSystem.getPlayerQuests(socket.userId || 'anonymous');
+ socket.emit('quests_data', questData);
+ } catch (error) {
+ console.error('[GAME SERVER] Error getting quests:', error);
+ socket.emit('quests_data', { success: false, error: error.message });
+ }
+ });
+
+ // Listen for quest completion events from other systems
+ io.on('quest_completed', (data) => {
+ console.log('[GAME SERVER] Quest completion event received:', data);
+ try {
+ const result = questSystem.completeQuest(data.userId, data.questId, data.rewards);
+ if (result.success) {
+ console.log('[GAME SERVER] Quest completed successfully on server:', data.questId);
+ } else {
+ console.error('[GAME SERVER] Failed to complete quest:', result.error);
+ }
+ } catch (error) {
+ console.error('[GAME SERVER] Error processing quest completion:', error);
+ }
+ });
+
+ socket.on('next_room', (data) => {
+ console.log('[GAME SERVER] Moving to next room for:', socket.id, data);
+ try {
+ const { instanceId, userId } = data;
+ const result = dungeonSystem.moveToNextRoom(instanceId, userId);
+ socket.emit('next_room_data', result);
+ } catch (error) {
+ console.error('[GAME SERVER] Error moving to next room:', error);
+ socket.emit('next_room_data', { success: false, error: error.message });
+ }
+ });
+
socket.on('get_dungeon_status', (data) => {
console.log('[GAME SERVER] Getting dungeon status for:', socket.id, data);
try {
@@ -1224,9 +1409,12 @@ async function startServer() {
// Start online idle rewards generation (every 10 seconds)
setInterval(async () => {
+ console.log('[GAME SERVER] Idle reward timer triggered - checking', connectedClients.size, 'connected clients');
+
for (const [socketId, clientData] of connectedClients.entries()) {
if (clientData.userId && clientData.playerData) {
try {
+ console.log('[GAME SERVER] Processing idle rewards for client:', socketId, 'user:', clientData.username);
// Update playTime for active players
const sessionTime = clientData.playerData.updatePlayTime();
console.log(`[GAME SERVER] Updated playTime for ${clientData.username}: +${sessionTime}ms, Total: ${clientData.playerData.stats.playTime}ms`);
@@ -1239,11 +1427,15 @@ async function startServer() {
const onlineRewards = idleSystem.generateOnlineIdleRewards(clientData.userId, 10000); // 10 seconds
+ console.log('[GAME SERVER] Generated online rewards for', clientData.username, ':', onlineRewards);
+
if (onlineRewards.credits > 0) {
// Update player data with online rewards
clientData.playerData.stats.credits = (clientData.playerData.stats.credits || 0) + onlineRewards.credits;
clientData.playerData.stats.experience = (clientData.playerData.stats.experience || 0) + onlineRewards.experience;
+ console.log('[GAME SERVER] Applied idle rewards - Credits:', onlineRewards.credits, 'New balance:', clientData.playerData.stats.credits);
+
// Send update to client
io.to(socketId).emit('onlineIdleRewards', {
credits: onlineRewards.credits,
@@ -1251,6 +1443,10 @@ async function startServer() {
newBalance: clientData.playerData.stats.credits,
playTime: clientData.playerData.stats.playTime
});
+
+ console.log('[GAME SERVER] Sent onlineIdleRewards to client:', socketId);
+ } else {
+ console.log('[GAME SERVER] No idle rewards generated for', clientData.username);
}
} catch (error) {
console.error(`[GAME SERVER] Error generating online idle rewards for ${socketId}:`, error);
diff --git a/GameServer/systems/DungeonSystem.js b/GameServer/systems/DungeonSystem.js
index e163cb1..b8c0df4 100644
--- a/GameServer/systems/DungeonSystem.js
+++ b/GameServer/systems/DungeonSystem.js
@@ -1024,10 +1024,121 @@ class DungeonSystem {
console.log(`[DUNGEON SYSTEM] Initialized ${this.dungeons.size} dungeons`);
}
+ setIO(io) {
+ this.io = io;
+ console.log('[DUNGEON SYSTEM] Socket.IO instance set for quest events');
+ }
+
+ generateDungeonEncounters(dungeon) {
+ console.log('[DUNGEON SYSTEM] Generating encounters for dungeon:', dungeon.id);
+
+ const encounters = [];
+ const encounterCount = Math.max(3, Math.floor(dungeon.estimatedTime / 5)); // 1 encounter per ~5 minutes
+
+ console.log('[DUNGEON SYSTEM] Encounter count:', encounterCount, 'for difficulty:', dungeon.difficulty);
+
+ // Always start with entrance
+ encounters.push({
+ type: 'entrance',
+ name: 'Dungeon Entrance',
+ description: 'The entrance to the dungeon',
+ enemies: [],
+ rewards: false
+ });
+
+ // Generate random encounters based on dungeon difficulty
+ for (let i = 1; i < encounterCount - 1; i++) {
+ const encounterTypes = ['corridor', 'chamber'];
+ if (i === Math.floor(encounterCount / 2)) {
+ encounterTypes.push('treasure'); // Mid-dungeon treasure room
+ }
+
+ const type = encounterTypes[Math.floor(Math.random() * encounterTypes.length)];
+ const template = this.roomTypes[type];
+
+ encounters.push({
+ type,
+ name: template.name,
+ description: `A ${template.name.toLowerCase()} in the dungeon`,
+ enemies: this.generateEnemies(dungeon.enemyTypes, template.enemies),
+ rewards: template.rewards
+ });
+ }
+
+ // Always end with boss room (except tutorial)
+ if (dungeon.difficulty !== 'tutorial') {
+ encounters.push({
+ type: 'boss',
+ name: 'Boss Chamber',
+ description: 'The final chamber with a powerful enemy',
+ enemies: this.generateEnemies(dungeon.enemyTypes, 1, true),
+ rewards: true,
+ isBoss: true
+ });
+ } else {
+ // Tutorial ends with simple chamber (only practice targets for auto-completion)
+ encounters.push({
+ type: 'chamber',
+ name: 'Training Room',
+ description: 'The final training room',
+ enemies: this.generateEnemies(['practice_target'], 1), // Only practice targets
+ rewards: true
+ });
+ }
+
+ console.log('[DUNGEON SYSTEM] Generated encounters:', encounters.map((e, i) => ({
+ index: i,
+ type: e.type,
+ name: e.name,
+ enemyCount: e.enemies?.length || 0
+ })));
+
+ return encounters;
+ }
+
+ generateEnemies(enemyTypes, count, isBoss = false) {
+ console.log('[DUNGEON SYSTEM] Generating enemies:', { enemyTypes, count, isBoss });
+ console.log('[DUNGEON SYSTEM] Available enemy templates:', Object.keys(this.enemyTemplates));
+
+ const enemies = [];
+
+ for (let i = 0; i < count; i++) {
+ const enemyTypeId = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
+ const template = this.enemyTemplates[enemyTypeId];
+
+ console.log(`[DUNGEON SYSTEM] Generating enemy ${i}: type=${enemyTypeId}, template=`, template);
+
+ if (!template) {
+ console.warn(`[DUNGEON SYSTEM] Enemy template not found for type: ${enemyTypeId}`);
+ continue;
+ }
+
+ // Scale enemy for boss fights
+ const enemy = { ...template };
+ if (isBoss) {
+ enemy.health *= 3;
+ enemy.attack *= 2;
+ enemy.defense *= 1.5;
+ enemy.experience *= 5;
+ enemy.credits *= 3;
+ enemy.name = `Elite ${enemy.name}`;
+ }
+
+ enemies.push(enemy);
+ }
+
+ console.log('[DUNGEON SYSTEM] Generated enemies:', enemies);
+ return enemies;
+ }
+
addDungeon(id, dungeon) {
+ // Generate encounters for this dungeon
+ const encounters = this.generateDungeonEncounters(dungeon);
+
this.dungeons.set(id, {
id,
...dungeon,
+ encounters,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
});
@@ -1076,11 +1187,19 @@ class DungeonSystem {
}
createInstance(dungeonId, creatorId, playerIds = []) {
+ console.log('[DUNGEON SYSTEM] Creating instance:', { dungeonId, creatorId, playerIds });
+
const dungeon = this.getDungeon(dungeonId);
if (!dungeon) {
throw new Error('Dungeon not found');
}
+ console.log('[DUNGEON SYSTEM] Dungeon found:', {
+ id: dungeon.id,
+ encountersCount: dungeon.encounters?.length || 0,
+ encounters: dungeon.encounters?.map((e, i) => ({ index: i, type: e.type, name: e.name }))
+ });
+
// Validate requirements
if (playerIds.length > dungeon.maxPlayers) {
throw new Error('Too many players for this dungeon');
@@ -1235,6 +1354,7 @@ class DungeonSystem {
return {
success: true,
nextEncounter: instance.currentEncounter < dungeon.encounters.length ? dungeon.encounters[instance.currentEncounter] : null,
+ encounterIndex: instance.currentEncounter,
instance
};
}
@@ -1258,16 +1378,58 @@ class DungeonSystem {
// Remove players from instance tracking
for (const playerId of instance.players) {
this.playerInstances.delete(playerId);
+
+ // Check for quest completion
+ this.checkQuestCompletion(playerId, instance.dungeonId);
}
return {
success: true,
dungeon,
rewards,
- instance
+ instance,
+ encounterIndex: -1, // Dungeon complete
+ isComplete: true
};
}
+ // Check if dungeon completion should trigger quests
+ checkQuestCompletion(userId, dungeonId) {
+ // Tutorial dungeon completion should trigger main story progression
+ if (dungeonId === 'tutorial') {
+ console.log('[DUNGEON SYSTEM] Tutorial dungeon completed, triggering quest progression');
+
+ // Set player stat for tutorial completion
+ if (this.io) {
+ this.io.emit('player_stat_update', {
+ userId: userId,
+ stat: 'tutorialDungeonCompleted',
+ value: true
+ });
+
+ // Emit quest completion event for quest system
+ this.io.emit('quest_completed', {
+ userId: userId,
+ questId: 'main_story_first_dungeon',
+ questType: 'dungeon',
+ dungeonId: dungeonId,
+ rewards: {
+ experience: 50,
+ credits: 25
+ }
+ });
+ }
+ }
+ }
+
+ getPlayerCompletedDungeons(userId) {
+ const instances = Array.from(this.instances.values()).filter(instance =>
+ instance.players.has(userId) && instance.status === 'completed'
+ );
+
+ return instances.map(instance => instance.dungeonId);
+ }
+
calculateRewards(dungeon, instance) {
const rewards = {
experience: 0,
@@ -1275,10 +1437,21 @@ class DungeonSystem {
items: []
};
+ // Check if dungeon has rewards defined
+ if (!dungeon.rewards) {
+ console.warn('[DUNGEON SYSTEM] No rewards defined for dungeon:', dungeon.id);
+ return rewards;
+ }
+
// Calculate base rewards
const expRange = dungeon.rewards.experience;
const creditRange = dungeon.rewards.credits;
+ if (!expRange || !creditRange) {
+ console.warn('[DUNGEON SYSTEM] Incomplete rewards defined for dungeon:', dungeon.id);
+ return rewards;
+ }
+
rewards.experience = Math.floor(Math.random() * (expRange.max - expRange.min + 1)) + expRange.min;
rewards.credits = Math.floor(Math.random() * (creditRange.max - creditRange.min + 1)) + creditRange.min;
@@ -1292,7 +1465,7 @@ class DungeonSystem {
const itemChance = Math.max(0.1, 0.5 - (totalDeaths * 0.1)); // Lower chance with more deaths
if (Math.random() < itemChance) {
const itemPool = dungeon.rewards.items;
- if (itemPool.length > 0) {
+ if (itemPool && itemPool.length > 0) {
rewards.items.push(itemPool[Math.floor(Math.random() * itemPool.length)]);
}
}
@@ -1308,6 +1481,58 @@ class DungeonSystem {
);
}
+ moveToNextRoom(instanceId, userId) {
+ console.log('[DUNGEON SYSTEM] moveToNextRoom called:', { instanceId, userId });
+
+ const instance = this.getInstance(instanceId);
+ if (!instance) {
+ throw new Error('Instance not found');
+ }
+
+ if (!instance.players.has(userId)) {
+ throw new Error('Player not in instance');
+ }
+
+ const dungeon = this.getDungeon(instance.dungeonId);
+
+ console.log('[DUNGEON SYSTEM] Current state:', {
+ currentEncounter: instance.currentEncounter,
+ totalEncounters: dungeon.encounters.length,
+ dungeonId: dungeon.id
+ });
+
+ // Move to next encounter
+ instance.currentEncounter++;
+
+ console.log('[DUNGEON SYSTEM] After increment:', {
+ newEncounter: instance.currentEncounter,
+ isComplete: instance.currentEncounter >= dungeon.encounters.length
+ });
+
+ // Check if dungeon is complete
+ if (instance.currentEncounter >= dungeon.encounters.length) {
+ return this.completeDungeon(instanceId);
+ }
+
+ // Get next encounter
+ const encounter = dungeon.encounters[instance.currentEncounter];
+
+ console.log('[DUNGEON SYSTEM] Next encounter:', {
+ encounterIndex: instance.currentEncounter,
+ encounterType: encounter.type,
+ encounterName: encounter.name,
+ enemies: encounter.enemies
+ });
+
+ return {
+ success: true,
+ encounter,
+ encounterIndex: instance.currentEncounter,
+ instance,
+ isComplete: false
+ };
+ }
+
getDungeonStatistics(userId) {
const instances = Array.from(this.instances.values()).filter(instance =>
instance.players.has(userId)
diff --git a/GameServer/systems/IdleSystem.js b/GameServer/systems/IdleSystem.js
index 9926bfe..938a822 100644
--- a/GameServer/systems/IdleSystem.js
+++ b/GameServer/systems/IdleSystem.js
@@ -50,15 +50,25 @@ class IdleSystem {
}
initializePlayerData(userId) {
+ console.log('[IDLE SYSTEM] initializePlayerData called for userId:', userId);
+ console.log('[IDLE SYSTEM] Current playerProductionRates size:', this.playerProductionRates.size);
+
if (!this.playerLastActive.has(userId)) {
+ console.log('[IDLE SYSTEM] Initializing new player data for:', userId);
this.playerLastActive.set(userId, Date.now());
this.playerProductionRates.set(userId, { ...this.defaultProductionRates });
+ console.log('[IDLE SYSTEM] Set production rates for', userId, ':', this.defaultProductionRates);
this.playerAchievements.set(userId, {
totalOfflineTime: 0,
maxOfflineSession: 0,
totalIdleCredits: 0,
totalIdleExperience: 0
});
+ console.log('[IDLE SYSTEM] Player data initialized successfully for:', userId);
+ } else {
+ console.log('[IDLE SYSTEM] Player data already exists for:', userId);
+ const existingRates = this.playerProductionRates.get(userId);
+ console.log('[IDLE SYSTEM] Existing production rates:', existingRates);
}
}
@@ -250,8 +260,13 @@ class IdleSystem {
// Generate online idle rewards (called periodically while player is online)
generateOnlineIdleRewards(userId, deltaTimeMs) {
+ console.log('[IDLE SYSTEM] generateOnlineIdleRewards called for userId:', userId, 'deltaTimeMs:', deltaTimeMs);
+
const productionRates = this.playerProductionRates.get(userId);
+ console.log('[IDLE SYSTEM] Production rates for', userId, ':', productionRates);
+
if (!productionRates) {
+ console.log('[IDLE SYSTEM] No production rates found for', userId, '- returning 0 rewards');
return {
credits: 0,
experience: 0,
@@ -260,12 +275,18 @@ class IdleSystem {
}
const deltaTimeSeconds = deltaTimeMs / 1000;
+ console.log('[IDLE SYSTEM] Delta time seconds:', deltaTimeSeconds);
+ console.log('[IDLE SYSTEM] Raw calculation:', productionRates.credits, '*', deltaTimeSeconds, '=', productionRates.credits * deltaTimeSeconds);
+
+ const creditsBeforeFloor = productionRates.credits * deltaTimeSeconds;
const rewards = {
- credits: Math.floor(productionRates.credits * deltaTimeSeconds),
+ credits: Math.floor(creditsBeforeFloor),
experience: Math.floor(productionRates.experience * deltaTimeSeconds),
energy: Math.floor(productionRates.energy * deltaTimeSeconds)
};
+ console.log('[IDLE SYSTEM] Credits before floor:', creditsBeforeFloor, 'after floor:', rewards.credits);
+ console.log('[IDLE SYSTEM] Calculated rewards for', userId, ':', rewards);
return rewards;
}
diff --git a/GameServer/systems/ItemSystem.js b/GameServer/systems/ItemSystem.js
index 54e236d..22d411e 100644
--- a/GameServer/systems/ItemSystem.js
+++ b/GameServer/systems/ItemSystem.js
@@ -81,6 +81,20 @@ class ItemSystem {
categories: ['shop', 'dungeon_reward'],
requirements: { level: 7 },
stackable: false
+ },
+ {
+ id: 'heavy_cruiser_rare',
+ name: 'Heavy Cruiser',
+ type: 'ship',
+ rarity: 'rare',
+ price: 35000,
+ currency: 'credits',
+ description: 'Powerful heavy cruiser with superior firepower and armor',
+ texturePath: 'images/ships/heavy_cruiser_rare.png',
+ stats: { attack: 28, speed: 8, defense: 25, hull: 200 },
+ categories: ['shop', 'dungeon_reward'],
+ requirements: { level: 12 },
+ stackable: false
}
],
@@ -145,6 +159,81 @@ class ItemSystem {
stackable: true,
maxStack: 10,
effects: { attack: 20, defense: 20 }
+ },
+ {
+ id: 'titanium_alloy',
+ name: 'Titanium Alloy',
+ type: 'material',
+ rarity: 'uncommon',
+ price: 300,
+ currency: 'credits',
+ description: 'Lightweight yet durable metal for ship construction',
+ texture: 'http://localhost:3002/images/items/materials/titanium_alloy.png',
+ categories: ['shop', 'dungeon_loot', 'crafting'],
+ requirements: { level: 8 },
+ stackable: true,
+ maxStack: 75,
+ effects: { defense: 8, speed: 3 }
+ },
+ {
+ id: 'plasma_coil',
+ name: 'Plasma Coil',
+ type: 'material',
+ rarity: 'rare',
+ price: 750,
+ currency: 'credits',
+ description: 'High-energy component for weapon systems',
+ texture: 'http://localhost:3002/images/items/materials/plasma_coil.png',
+ categories: ['shop', 'dungeon_loot', 'crafting'],
+ requirements: { level: 12 },
+ stackable: true,
+ maxStack: 30,
+ effects: { attack: 12, energy: 8 }
+ },
+ {
+ id: 'nanite_gel',
+ name: 'Nanite Gel',
+ type: 'material',
+ rarity: 'epic',
+ price: 1200,
+ currency: 'gems',
+ description: 'Self-repairing nanomachines in gel form',
+ texture: 'http://localhost:3002/images/items/materials/nanite_gel.png',
+ categories: ['shop', 'dungeon_loot', 'crafting'],
+ requirements: { level: 18 },
+ stackable: true,
+ maxStack: 15,
+ effects: { hull_repair: 25, defense: 5 }
+ },
+ {
+ id: 'cryo_crystal',
+ name: 'Cryo Crystal',
+ type: 'material',
+ rarity: 'rare',
+ price: 600,
+ currency: 'credits',
+ description: 'Frozen crystal that enhances cooling systems',
+ texture: 'http://localhost:3002/images/items/materials/cryo_crystal.png',
+ categories: ['shop', 'dungeon_loot', 'crafting'],
+ requirements: { level: 10 },
+ stackable: true,
+ maxStack: 40,
+ effects: { energy: 15, speed: 5 }
+ },
+ {
+ id: 'void_shard',
+ name: 'Void Shard',
+ type: 'material',
+ rarity: 'legendary',
+ price: 2500,
+ currency: 'gems',
+ description: 'Fragment of void space with reality-bending properties',
+ texture: 'http://localhost:3002/images/items/materials/void_shard.png',
+ categories: ['shop', 'dungeon_loot', 'crafting'],
+ requirements: { level: 20 },
+ stackable: true,
+ maxStack: 8,
+ effects: { attack: 15, defense: 15, speed: 10 }
}
],
@@ -217,6 +306,91 @@ class ItemSystem {
effects: { damage_multiplier: 1.5, duration: 45000 }, // 45 seconds
consumable: true,
cooldown: 120000 // 2 minutes
+ },
+ {
+ id: 'speed_boost',
+ name: 'Speed Boost',
+ type: 'consumable',
+ rarity: 'uncommon',
+ price: 200,
+ currency: 'credits',
+ description: 'Temporary speed increase for quick escapes',
+ texture: 'http://localhost:3002/images/items/consumables/speed_boost.png',
+ categories: ['shop', 'dungeon_loot'],
+ requirements: { level: 6 },
+ stackable: true,
+ maxStack: 8,
+ effects: { speed_multiplier: 1.3, duration: 30000 }, // 30 seconds
+ consumable: true,
+ cooldown: 90000 // 1.5 minutes
+ },
+ {
+ id: 'nanite_repair',
+ name: 'Nanite Repair',
+ type: 'consumable',
+ rarity: 'epic',
+ price: 500,
+ currency: 'credits',
+ description: 'Advanced nanites that continuously repair hull damage',
+ texture: 'http://localhost:3002/images/items/consumables/nanite_repair.png',
+ categories: ['shop', 'dungeon_loot'],
+ requirements: { level: 12 },
+ stackable: true,
+ maxStack: 3,
+ effects: { hull_repair_rate: 10, duration: 60000 }, // 10 HP/sec for 1 minute
+ consumable: true,
+ cooldown: 180000 // 3 minutes
+ },
+ {
+ id: 'stealth_field',
+ name: 'Stealth Field Generator',
+ type: 'consumable',
+ rarity: 'rare',
+ price: 400,
+ currency: 'credits',
+ description: 'Temporary invisibility to enemy sensors',
+ texture: 'http://localhost:3002/images/items/consumables/stealth_field.png',
+ categories: ['shop', 'dungeon_loot'],
+ requirements: { level: 10 },
+ stackable: true,
+ maxStack: 4,
+ effects: { stealth: true, duration: 20000 }, // 20 seconds
+ consumable: true,
+ cooldown: 150000 // 2.5 minutes
+ },
+ {
+ id: 'emergency_warp',
+ name: 'Emergency Warp Core',
+ type: 'consumable',
+ rarity: 'legendary',
+ price: 1000,
+ currency: 'gems',
+ description: 'Instant emergency teleport to safety',
+ texture: 'http://localhost:3002/images/items/consumables/emergency_warp.png',
+ categories: ['shop', 'dungeon_loot'],
+ requirements: { level: 15 },
+ stackable: true,
+ maxStack: 2,
+ effects: { emergency_escape: true },
+ consumable: true,
+ cooldown: 300000 // 5 minutes
+ },
+ {
+ id: 'combat_stim',
+ name: 'Combat Stimulant',
+ type: 'consumable',
+ rarity: 'uncommon',
+ price: 250,
+ currency: 'credits',
+ description: 'Enhances combat abilities for short duration',
+ texture: 'http://localhost:3002/images/items/consumables/combat_stim.png',
+ categories: ['shop', 'dungeon_loot'],
+ requirements: { level: 7 },
+ stackable: true,
+ maxStack: 6,
+ effects: { attack: 10, speed: 5, duration: 45000 }, // 45 seconds
+ consumable: true,
+ cooldown: 120000 // 2 minutes
}
],
@@ -281,6 +455,81 @@ class ItemSystem {
stackable: false,
cosmetic: true,
slot: 'engine'
+ },
+ {
+ id: 'skull_decal',
+ name: 'Skull Decal',
+ type: 'cosmetic',
+ rarity: 'uncommon',
+ price: 1500,
+ currency: 'credits',
+ description: 'Intimidating skull decal for your ship',
+ texture: 'http://localhost:3002/images/items/cosmetics/skull_decal.png',
+ categories: ['shop'],
+ requirements: { level: 3 },
+ stackable: false,
+ cosmetic: true,
+ slot: 'decal'
+ },
+ {
+ id: 'neon_lights',
+ name: 'Neon Underglow',
+ type: 'cosmetic',
+ rarity: 'rare',
+ price: 3000,
+ currency: 'credits',
+ description: 'Colorful neon lights under your ship',
+ texture: 'http://localhost:3002/images/items/cosmetics/neon_lights.png',
+ categories: ['shop'],
+ requirements: { level: 8 },
+ stackable: false,
+ cosmetic: true,
+ slot: 'lights'
+ },
+ {
+ id: 'chrome_finish',
+ name: 'Chrome Finish',
+ type: 'cosmetic',
+ rarity: 'epic',
+ price: 5000,
+ currency: 'credits',
+ description: 'Reflective chrome coating for premium look',
+ texture: 'http://localhost:3002/images/items/cosmetics/chrome_finish.png',
+ categories: ['shop'],
+ requirements: { level: 12 },
+ stackable: false,
+ cosmetic: true,
+ slot: 'finish'
+ },
+ {
+ id: 'holographic_display',
+ name: 'Holographic Display',
+ type: 'cosmetic',
+ rarity: 'legendary',
+ price: 8000,
+ currency: 'gems',
+ description: 'Advanced holographic projection system',
+ texture: 'http://localhost:3002/images/items/cosmetics/holographic_display.png',
+ categories: ['shop'],
+ requirements: { level: 15 },
+ stackable: false,
+ cosmetic: true,
+ slot: 'display'
+ },
+ {
+ id: 'carbon_fiber',
+ name: 'Carbon Fiber Body',
+ type: 'cosmetic',
+ rarity: 'uncommon',
+ price: 2000,
+ currency: 'credits',
+ description: 'Lightweight carbon fiber ship body',
+ texture: 'http://localhost:3002/images/items/cosmetics/carbon_fiber.png',
+ categories: ['shop'],
+ requirements: { level: 6 },
+ stackable: false,
+ cosmetic: true,
+ slot: 'body'
}
],
@@ -327,6 +576,76 @@ class ItemSystem {
requirements: { level: 10 },
stackable: false,
stats: { attack: 30, range: 150, fire_rate: 1 }
+ },
+ {
+ id: 'ion_cannon_uncommon',
+ name: 'Ion Cannon',
+ type: 'weapon',
+ rarity: 'uncommon',
+ price: 3500,
+ currency: 'credits',
+ description: 'Ion-based weapon effective against shields',
+ texture: 'http://localhost:3002/images/weapons/ion_cannon_uncommon.png',
+ categories: ['shop', 'dungeon_loot'],
+ requirements: { level: 6 },
+ stackable: false,
+ stats: { attack: 22, range: 110, fire_rate: 1.8, shield_damage: 1.5 }
+ },
+ {
+ id: 'missile_launcher_rare',
+ name: 'Missile Launcher',
+ type: 'weapon',
+ rarity: 'rare',
+ price: 8000,
+ currency: 'credits',
+ description: 'Homing missile system for heavy damage',
+ texture: 'http://localhost:3002/images/weapons/missile_launcher_rare.png',
+ categories: ['shop', 'dungeon_loot'],
+ requirements: { level: 12 },
+ stackable: false,
+ stats: { attack: 45, range: 200, fire_rate: 0.8, explosive: true }
+ },
+ {
+ id: 'plasma_thrower_epic',
+ name: 'Plasma Thrower',
+ type: 'weapon',
+ rarity: 'epic',
+ price: 12000,
+ currency: 'credits',
+ description: 'Continuous plasma stream weapon',
+ texture: 'http://localhost:3002/images/weapons/plasma_thrower_epic.png',
+ categories: ['shop', 'dungeon_loot'],
+ requirements: { level: 15 },
+ stackable: false,
+ stats: { attack: 35, range: 80, fire_rate: 5, continuous: true }
+ },
+ {
+ id: 'railgun_legendary',
+ name: 'Railgun',
+ type: 'weapon',
+ rarity: 'legendary',
+ price: 20000,
+ currency: 'gems',
+ description: 'Electromagnetic railgun with piercing shots',
+ texture: 'http://localhost:3002/images/weapons/railgun_legendary.png',
+ categories: ['shop', 'dungeon_loot'],
+ requirements: { level: 18 },
+ stackable: false,
+ stats: { attack: 60, range: 300, fire_rate: 0.5, piercing: true }
+ },
+ {
+ id: 'pulse_cannon_common',
+ name: 'Pulse Cannon',
+ type: 'weapon',
+ rarity: 'common',
+ price: 1500,
+ currency: 'credits',
+ description: 'Rapid-fire pulse weapon',
+ texture: 'http://localhost:3002/images/weapons/pulse_cannon_common.png',
+ categories: ['shop', 'dungeon_loot'],
+ requirements: { level: 2 },
+ stackable: false,
+ stats: { attack: 8, range: 90, fire_rate: 4 }
}
],
@@ -373,6 +692,76 @@ class ItemSystem {
requirements: { level: 10 },
stackable: false,
stats: { defense: 25, shield_capacity: 200, recharge_rate: 12 }
+ },
+ {
+ id: 'plasma_barrier_uncommon',
+ name: 'Plasma Barrier',
+ type: 'armor',
+ rarity: 'uncommon',
+ price: 3000,
+ currency: 'credits',
+ description: 'Plasma-based barrier system',
+ texture: 'http://localhost:3002/images/armors/plasma_barrier_uncommon.png',
+ categories: ['shop', 'dungeon_loot'],
+ requirements: { level: 7 },
+ stackable: false,
+ stats: { defense: 18, shield_capacity: 120, recharge_rate: 10, energy_resistance: 0.8 }
+ },
+ {
+ id: 'reactive_armor_epic',
+ name: 'Reactive Armor',
+ type: 'armor',
+ rarity: 'epic',
+ price: 9000,
+ currency: 'credits',
+ description: 'Adaptive armor that responds to damage types',
+ texture: 'http://localhost:3002/images/armors/reactive_armor_epic.png',
+ categories: ['shop', 'dungeon_loot'],
+ requirements: { level: 14 },
+ stackable: false,
+ stats: { defense: 35, shield_capacity: 250, recharge_rate: 15, adaptive: true }
+ },
+ {
+ id: 'kinetic_dampener_rare',
+ name: 'Kinetic Dampener',
+ type: 'armor',
+ rarity: 'rare',
+ price: 6000,
+ currency: 'credits',
+ description: 'Specialized armor against kinetic weapons',
+ texture: 'http://localhost:3002/images/armors/kinetic_dampener_rare.png',
+ categories: ['shop', 'dungeon_loot'],
+ requirements: { level: 11 },
+ stackable: false,
+ stats: { defense: 22, shield_capacity: 180, recharge_rate: 11, kinetic_resistance: 0.7 }
+ },
+ {
+ id: 'phase_shifter_legendary',
+ name: 'Phase Shifter',
+ type: 'armor',
+ rarity: 'legendary',
+ price: 15000,
+ currency: 'gems',
+ description: 'Experimental phase-shifting armor system',
+ texture: 'http://localhost:3002/images/armors/phase_shifter_legendary.png',
+ categories: ['shop', 'dungeon_loot'],
+ requirements: { level: 18 },
+ stackable: false,
+ stats: { defense: 40, shield_capacity: 300, recharge_rate: 20, phase_shift: 0.2 }
+ },
+ {
+ id: 'carbon_plate_common',
+ name: 'Carbon Plate Armor',
+ type: 'armor',
+ rarity: 'common',
+ price: 1200,
+ currency: 'credits',
+ description: 'Lightweight carbon composite armor',
+ texture: 'http://localhost:3002/images/armors/carbon_plate_common.png',
+ categories: ['shop', 'dungeon_loot'],
+ requirements: { level: 3 },
+ stackable: false,
+ stats: { defense: 12, shield_capacity: 70, recharge_rate: 6, weight_reduction: 0.1 }
}
],
@@ -689,17 +1078,44 @@ class ItemSystem {
}
/**
- * Get random shop items with processed URLs
+ * Get random shop items with processed URLs - 6 items per category
*/
- getRandomShopItems(count = 8) {
- const allItems = Object.values(this.itemCatalog).flat();
+ getRandomShopItems(count = 6) {
+ const shopItems = {};
+
+ // Get 6 random items from each category
+ Object.keys(this.itemCatalog).forEach(category => {
+ const categoryItems = this.itemCatalog[category] || [];
+ const selectedItems = [];
+
+ if (categoryItems.length > 0) {
+ // Randomly select items from this category
+ const shuffled = [...categoryItems].sort(() => Math.random() - 0.5);
+
+ for (let i = 0; i < Math.min(count, shuffled.length); i++) {
+ selectedItems.push(this.processItem(shuffled[i]));
+ }
+ }
+
+ shopItems[category] = selectedItems;
+ });
+
+ return shopItems;
+ }
+
+ /**
+ * Get random items for a specific category
+ */
+ getRandomItemsByCategory(category, count = 6) {
+ const categoryItems = this.itemCatalog[category] || [];
const selectedItems = [];
- // Randomly select items
- const shuffled = [...allItems].sort(() => Math.random() - 0.5);
-
- for (let i = 0; i < Math.min(count, shuffled.length); i++) {
- selectedItems.push(this.processItem(shuffled[i]));
+ if (categoryItems.length > 0) {
+ const shuffled = [...categoryItems].sort(() => Math.random() - 0.5);
+
+ for (let i = 0; i < Math.min(count, shuffled.length); i++) {
+ selectedItems.push(this.processItem(shuffled[i]));
+ }
}
return selectedItems;
diff --git a/GameServer/systems/QuestSystem.js b/GameServer/systems/QuestSystem.js
index 582ae61..f115046 100644
--- a/GameServer/systems/QuestSystem.js
+++ b/GameServer/systems/QuestSystem.js
@@ -660,6 +660,149 @@ class QuestSystem {
return { success: true, message: 'Daily quests reset' };
}
+ getPlayerQuests(userId) {
+ console.log('[QUEST SYSTEM] Getting quests for user:', userId);
+
+ // Get or create player data
+ let playerData = this.playerQuests.get(userId);
+ if (!playerData) {
+ playerData = {
+ activeQuests: new Map(),
+ completedQuests: new Map(),
+ failedQuests: new Map()
+ };
+ this.playerQuests.set(userId, playerData);
+ }
+
+ // Prepare quest data for client
+ const questData = {
+ mainQuests: [],
+ dailyQuests: [],
+ weeklyQuests: [],
+ activeQuests: [],
+ completedQuests: [],
+ failedQuests: []
+ };
+
+ // Add main quests
+ for (const [questId, quest] of this.quests) {
+ if (quest.type === 'main') {
+ const playerQuest = playerData.activeQuests.get(questId) || playerData.completedQuests.get(questId) || playerData.failedQuests.get(questId);
+ const status = playerQuest ? playerQuest.status : 'available';
+
+ questData.mainQuests.push({
+ ...quest,
+ status: status,
+ objectives: quest.objectives.map(obj => ({
+ ...obj,
+ current: playerQuest && playerQuest.objectives && playerQuest.objectives[obj.id] ? playerQuest.objectives[obj.id].current || 0 : 0
+ }))
+ });
+ }
+ }
+
+ // Add daily quests
+ for (const [questId, quest] of this.quests) {
+ if (quest.type === 'daily') {
+ const playerQuest = playerData.activeQuests.get(questId) || playerData.completedQuests.get(questId) || playerData.failedQuests.get(questId);
+ const status = playerQuest ? playerQuest.status : 'available';
+
+ questData.dailyQuests.push({
+ ...quest,
+ status: status,
+ objectives: quest.objectives.map(obj => ({
+ ...obj,
+ current: playerQuest && playerQuest.objectives && playerQuest.objectives[obj.id] ? playerQuest.objectives[obj.id].current || 0 : 0
+ }))
+ });
+ }
+ }
+
+ // Add weekly quests
+ for (const [questId, quest] of this.quests) {
+ if (quest.type === 'weekly') {
+ const playerQuest = playerData.activeQuests.get(questId) || playerData.completedQuests.get(questId) || playerData.failedQuests.get(questId);
+ const status = playerQuest ? playerQuest.status : 'available';
+
+ questData.weeklyQuests.push({
+ ...quest,
+ status: status,
+ objectives: quest.objectives.map(obj => ({
+ ...obj,
+ current: playerQuest && playerQuest.objectives && playerQuest.objectives[obj.id] ? playerQuest.objectives[obj.id].current || 0 : 0
+ }))
+ });
+ }
+ }
+
+ // Add active quests (for compatibility)
+ questData.activeQuests = Array.from(playerData.activeQuests.values());
+ questData.completedQuests = Array.from(playerData.completedQuests.values());
+ questData.failedQuests = Array.from(playerData.failedQuests.values());
+
+ console.log('[QUEST SYSTEM] Returning quest data:', {
+ mainQuests: questData.mainQuests.length,
+ dailyQuests: questData.dailyQuests.length,
+ weeklyQuests: questData.weeklyQuests.length,
+ activeQuests: questData.activeQuests.length,
+ completedQuests: questData.completedQuests.length
+ });
+
+ return questData;
+ }
+
+ completeQuest(userId, questId, rewards = null) {
+ console.log('[QUEST SYSTEM] Completing quest:', questId, 'for user:', userId);
+
+ const playerData = this.playerQuests.get(userId);
+ if (!playerData) {
+ console.log('[QUEST SYSTEM] Player data not found for user:', userId);
+ return { success: false, error: 'Player not found' };
+ }
+
+ const quest = this.quests.get(questId);
+ if (!quest) {
+ console.log('[QUEST SYSTEM] Quest not found:', questId);
+ return { success: false, error: 'Quest not found' };
+ }
+
+ // Move quest from active to completed
+ const activeQuest = playerData.activeQuests.get(questId);
+ if (activeQuest) {
+ // Mark all objectives as completed
+ const completedQuest = {
+ ...activeQuest,
+ status: 'completed',
+ completedAt: Date.now(),
+ objectives: quest.objectives.map(obj => ({
+ ...obj,
+ current: obj.target
+ }))
+ };
+
+ playerData.activeQuests.delete(questId);
+ playerData.completedQuests.set(questId, completedQuest);
+
+ console.log('[QUEST SYSTEM] Quest completed successfully:', questId);
+ return { success: true, quest: completedQuest };
+ } else {
+ // Quest might not be active, try to complete it anyway
+ const completedQuest = {
+ ...quest,
+ status: 'completed',
+ completedAt: Date.now(),
+ objectives: quest.objectives.map(obj => ({
+ ...obj,
+ current: obj.target
+ }))
+ };
+
+ playerData.completedQuests.set(questId, completedQuest);
+ console.log('[QUEST SYSTEM] Quest force-completed:', questId);
+ return { success: true, quest: completedQuest };
+ }
+ }
+
resetWeeklyQuests(userId) {
const playerData = this.getPlayerData(userId);