/** * Galaxy Strike Online - Economy System * Manages credits, gems, transactions, and shop */ class Economy { constructor(gameEngine) { const debugLogger = window.debugLogger; if (debugLogger) debugLogger.log('Economy constructor called'); this.game = gameEngine; // Currency this.credits = 1000; this.gems = 10; this.premiumCurrency = 0; // For future premium features // Transaction history this.transactionHistory = []; this.transactions = []; // Add missing transactions array // Owned cosmetics this.ownedCosmetics = []; // Add missing owned cosmetics array // Shop categories this.shopCategories = { ships: 'Ships', weapons: 'Weapons', armors: 'Armors', materials: 'Materials', cosmetics: 'Cosmetics', // upgrades: 'Upgrades', // Temporarily disabled consumables: 'Consumables', buildings: 'Buildings' }; // Random shop system this.randomShopItems = {}; // Current random items per category this.shopRefreshInterval = null; // Timer for 2-hour refresh this.shopHeartbeatInterval = null; // Timer for live countdown updates this.lastShopRefresh = null; // Timestamp of last refresh this.SHOP_REFRESH_INTERVAL = 2 * 60 * 60 * 1000; // 2 hours in milliseconds this.MAX_ITEMS_PER_CATEGORY = 6; this.categoryPurchaseLimits = {}; // Track purchases per category per refresh // Shop items this.shopItems = { ships: [ // Starter Cruiser Variants { id: 'starter_cruiser_common', name: 'Starter Cruiser', type: 'ship', rarity: 'common', price: 5000, currency: 'credits', description: 'Reliable starter cruiser for new pilots', texture: 'assets/textures/ships/starter_cruiser.png', stats: { attack: 15, speed: 10, defense: 12, hull: 100 } }, { id: 'starter_cruiser_uncommon', name: 'Starter Cruiser II', type: 'ship', rarity: 'uncommon', price: 12000, currency: 'credits', description: 'Upgraded starter cruiser with enhanced systems', texture: 'assets/textures/ships/starter_cruiser.png', stats: { attack: 18, speed: 12, defense: 15, hull: 120 } }, { id: 'starter_cruiser_rare', name: 'Starter Cruiser III', type: 'ship', rarity: 'rare', price: 25000, currency: 'credits', description: 'Elite starter cruiser with advanced weaponry', texture: 'assets/textures/ships/starter_cruiser.png', stats: { attack: 22, speed: 14, defense: 18, hull: 145 } }, { id: 'starter_cruiser_epic', name: 'Starter Cruiser IV', type: 'ship', rarity: 'epic', price: 45000, currency: 'credits', description: 'Master starter cruiser with elite modifications', texture: 'assets/textures/ships/starter_cruiser.png', stats: { attack: 26, speed: 16, defense: 22, hull: 175 } }, { id: 'starter_cruiser_legendary', name: 'Starter Cruiser V', type: 'ship', rarity: 'legendary', price: 75000, currency: 'credits', description: 'Legendary starter cruiser with unparalleled performance', texture: 'assets/textures/ships/starter_cruiser.png', stats: { attack: 32, speed: 18, defense: 28, hull: 210 } }, // Light Destroyer Variants { id: 'light_destroyer_common', name: 'Light Destroyer', type: 'ship', rarity: 'common', price: 12000, currency: 'credits', description: 'Fast and maneuverable light destroyer', texture: 'assets/textures/ships/light_destroyer.png', stats: { attack: 18, speed: 18, defense: 10, hull: 80 } }, { id: 'light_destroyer_uncommon', name: 'Light Destroyer II', type: 'ship', rarity: 'uncommon', price: 28000, currency: 'credits', description: 'Enhanced light destroyer with improved speed', texture: 'assets/textures/ships/light_destroyer.png', stats: { attack: 22, speed: 22, defense: 12, hull: 95 } }, { id: 'light_destroyer_rare', name: 'Light Destroyer III', type: 'ship', rarity: 'rare', price: 55000, currency: 'credits', description: 'Elite light destroyer with superior agility', texture: 'assets/textures/ships/light_destroyer.png', stats: { attack: 26, speed: 26, defense: 15, hull: 115 } }, { id: 'light_destroyer_epic', name: 'Light Destroyer IV', type: 'ship', rarity: 'epic', price: 95000, currency: 'credits', description: 'Master light destroyer with exceptional speed', texture: 'assets/textures/ships/light_destroyer.png', stats: { attack: 30, speed: 30, defense: 18, hull: 140 } }, { id: 'light_destroyer_legendary', name: 'Light Destroyer V', type: 'ship', rarity: 'legendary', price: 150000, currency: 'credits', description: 'Legendary light destroyer with unmatched velocity', texture: 'assets/textures/ships/light_destroyer.png', stats: { attack: 36, speed: 36, defense: 22, hull: 170 } }, // Heavy Destroyer Variants { id: 'heavy_destroyer_common', name: 'Heavy Destroyer', type: 'ship', rarity: 'common', price: 25000, currency: 'credits', description: 'Powerful heavy destroyer with strong weapons', texture: 'assets/textures/ships/heavy_destroyer.png', stats: { attack: 25, speed: 12, defense: 18, hull: 120 } }, { id: 'heavy_destroyer_uncommon', name: 'Heavy Destroyer II', type: 'ship', rarity: 'uncommon', price: 55000, currency: 'credits', description: 'Upgraded heavy destroyer with enhanced firepower', texture: 'assets/textures/ships/heavy_destroyer.png', stats: { attack: 30, speed: 14, defense: 22, hull: 145 } }, { id: 'heavy_destroyer_rare', name: 'Heavy Destroyer III', type: 'ship', rarity: 'rare', price: 110000, currency: 'credits', description: 'Elite heavy destroyer with devastating weapons', texture: 'assets/textures/ships/heavy_destroyer.png', stats: { attack: 35, speed: 16, defense: 26, hull: 175 } }, { id: 'heavy_destroyer_epic', name: 'Heavy Destroyer IV', type: 'ship', rarity: 'epic', price: 190000, currency: 'credits', description: 'Master heavy destroyer with overwhelming power', texture: 'assets/textures/ships/heavy_destroyer.png', stats: { attack: 40, speed: 18, defense: 30, hull: 210 } }, { id: 'heavy_destroyer_legendary', name: 'Heavy Destroyer V', type: 'ship', rarity: 'legendary', price: 300000, currency: 'credits', description: 'Legendary heavy destroyer with ultimate destruction', texture: 'assets/textures/ships/heavy_destroyer.png', stats: { attack: 48, speed: 20, defense: 36, hull: 255 } }, // Heavy Cruiser Variants { id: 'heavy_cruiser_common', name: 'Heavy Cruiser', type: 'ship', rarity: 'common', price: 45000, currency: 'credits', description: 'Massive heavy cruiser with excellent defense', texture: 'assets/textures/ships/heavy_cruiser.png', stats: { attack: 22, speed: 8, defense: 25, hull: 150 } }, { id: 'heavy_cruiser_uncommon', name: 'Heavy Cruiser II', type: 'ship', rarity: 'uncommon', price: 95000, currency: 'credits', description: 'Enhanced heavy cruiser with superior armor', texture: 'assets/textures/ships/heavy_cruiser.png', stats: { attack: 26, speed: 9, defense: 30, hull: 180 } }, { id: 'heavy_cruiser_rare', name: 'Heavy Cruiser III', type: 'ship', rarity: 'rare', price: 190000, currency: 'credits', description: 'Elite heavy cruiser with fortress-like defense', texture: 'assets/textures/ships/heavy_cruiser.png', stats: { attack: 30, speed: 10, defense: 36, hull: 220 } }, { id: 'heavy_cruiser_epic', name: 'Heavy Cruiser IV', type: 'ship', rarity: 'epic', price: 320000, currency: 'credits', description: 'Master heavy cruiser with impenetrable armor', texture: 'assets/textures/ships/heavy_cruiser.png', stats: { attack: 34, speed: 11, defense: 42, hull: 265 } }, { id: 'heavy_cruiser_legendary', name: 'Heavy Cruiser V', type: 'ship', rarity: 'legendary', price: 500000, currency: 'credits', description: 'Legendary heavy cruiser with ultimate defense', texture: 'assets/textures/ships/heavy_cruiser.png', stats: { attack: 40, speed: 12, defense: 50, hull: 320 } } ], weapons: [ // Starter Blaster Variants { id: 'starter_blaster_common', name: 'Starter Blaster', type: 'weapon', rarity: 'common', price: 2000, currency: 'credits', description: 'Basic blaster for new pilots', texture: 'assets/textures/weapons/starter_blaster.png', stats: { damage: 10, fireRate: 2, range: 5, energy: 5 } }, { id: 'starter_blaster_uncommon', name: 'Starter Blaster II', type: 'weapon', rarity: 'uncommon', price: 5000, currency: 'credits', description: 'Enhanced starter blaster with better fire rate', texture: 'assets/textures/weapons/starter_blaster.png', stats: { damage: 12, fireRate: 2.5, range: 5.5, energy: 6 } }, { id: 'starter_blaster_rare', name: 'Starter Blaster III', type: 'weapon', rarity: 'rare', price: 12000, currency: 'credits', description: 'Elite starter blaster with improved damage', texture: 'assets/textures/weapons/starter_blaster.png', stats: { damage: 15, fireRate: 3, range: 6, energy: 7 } }, { id: 'starter_blaster_epic', name: 'Starter Blaster IV', type: 'weapon', rarity: 'epic', price: 25000, currency: 'credits', description: 'Master starter blaster with superior performance', texture: 'assets/textures/weapons/starter_blaster.png', stats: { damage: 18, fireRate: 3.5, range: 6.5, energy: 8 } }, { id: 'starter_blaster_legendary', name: 'Starter Blaster V', type: 'weapon', rarity: 'legendary', price: 50000, currency: 'credits', description: 'Legendary starter blaster with ultimate power', texture: 'assets/textures/weapons/starter_blaster.png', stats: { damage: 22, fireRate: 4, range: 7, energy: 10 } }, // Laser Pistol Variants { id: 'laser_pistol_common', name: 'Laser Pistol', type: 'weapon', rarity: 'common', price: 3000, currency: 'credits', description: 'Compact laser pistol for close combat', texture: 'assets/textures/weapons/laser_pistol.png', stats: { damage: 8, fireRate: 3, range: 4, energy: 3 } }, { id: 'laser_pistol_uncommon', name: 'Laser Pistol II', type: 'weapon', rarity: 'uncommon', price: 7500, currency: 'credits', description: 'Enhanced laser pistol with better accuracy', texture: 'assets/textures/weapons/laser_pistol.png', stats: { damage: 10, fireRate: 3.5, range: 4.5, energy: 4 } }, { id: 'laser_pistol_rare', name: 'Laser Pistol III', type: 'weapon', rarity: 'rare', price: 18000, currency: 'credits', description: 'Elite laser pistol with piercing shots', texture: 'assets/textures/weapons/laser_pistol.png', stats: { damage: 13, fireRate: 4, range: 5, energy: 5 } }, { id: 'laser_pistol_epic', name: 'Laser Pistol IV', type: 'weapon', rarity: 'epic', price: 35000, currency: 'credits', description: 'Master laser pistol with rapid fire capability', texture: 'assets/textures/weapons/laser_pistol.png', stats: { damage: 16, fireRate: 4.5, range: 5.5, energy: 6 } }, { id: 'laser_pistol_legendary', name: 'Laser Pistol V', type: 'weapon', rarity: 'legendary', price: 70000, currency: 'credits', description: 'Legendary laser pistol with devastating power', texture: 'assets/textures/weapons/laser_pistol.png', stats: { damage: 20, fireRate: 5, range: 6, energy: 8 } }, // Laser Sniper Rifle Variants { id: 'laser_sniper_rifle_common', name: 'Laser Sniper Rifle', type: 'weapon', rarity: 'common', price: 8000, currency: 'credits', description: 'Long-range laser sniper rifle for precision attacks', texture: 'assets/textures/weapons/laser_sniper_rifle.png', stats: { damage: 25, fireRate: 1, range: 10, energy: 8 } }, { id: 'laser_sniper_rifle_uncommon', name: 'Laser Sniper Rifle II', type: 'weapon', rarity: 'uncommon', price: 20000, currency: 'credits', description: 'Enhanced laser sniper rifle with better penetration', texture: 'assets/textures/weapons/laser_sniper_rifle.png', stats: { damage: 30, fireRate: 1.2, range: 11, energy: 9 } }, { id: 'laser_sniper_rifle_rare', name: 'Laser Sniper Rifle III', type: 'weapon', rarity: 'rare', price: 50000, currency: 'credits', description: 'Elite laser sniper rifle with deadly accuracy', texture: 'assets/textures/weapons/laser_sniper_rifle.png', stats: { damage: 36, fireRate: 1.4, range: 12, energy: 10 } }, { id: 'laser_sniper_rifle_epic', name: 'Laser Sniper Rifle IV', type: 'weapon', rarity: 'epic', price: 100000, currency: 'credits', description: 'Master laser sniper rifle with extreme range', texture: 'assets/textures/weapons/laser_sniper_rifle.png', stats: { damage: 42, fireRate: 1.6, range: 13, energy: 12 } }, { id: 'laser_sniper_rifle_legendary', name: 'Laser Sniper Rifle V', type: 'weapon', rarity: 'legendary', price: 200000, currency: 'credits', description: 'Legendary laser sniper rifle with unparalleled precision', texture: 'assets/textures/weapons/laser_sniper_rifle.png', stats: { damage: 50, fireRate: 2, range: 15, energy: 15 } } ], armors: [ // Basic Armor Variants { id: 'basic_armor_common', name: 'Basic Armor', type: 'armor', rarity: 'common', price: 1500, currency: 'credits', description: 'Light protection for beginners', texture: 'assets/textures/armors/basic_armor.png', stats: { defense: 5, durability: 20, weight: 2, energyShield: 0 } }, { id: 'basic_armor_uncommon', name: 'Basic Armor II', type: 'armor', rarity: 'uncommon', price: 4000, currency: 'credits', description: 'Improved basic armor with better durability', texture: 'assets/textures/armors/basic_armor.png', stats: { defense: 7, durability: 25, weight: 2.2, energyShield: 2 } }, { id: 'basic_armor_rare', name: 'Basic Armor III', type: 'armor', rarity: 'rare', price: 10000, currency: 'credits', description: 'Elite basic armor with energy shielding', texture: 'assets/textures/armors/basic_armor.png', stats: { defense: 10, durability: 30, weight: 2.5, energyShield: 5 } }, { id: 'basic_armor_epic', name: 'Basic Armor IV', type: 'armor', rarity: 'epic', price: 20000, currency: 'credits', description: 'Master basic armor with advanced protection', texture: 'assets/textures/armors/basic_armor.png', stats: { defense: 13, durability: 35, weight: 2.8, energyShield: 8 } }, { id: 'basic_armor_legendary', name: 'Basic Armor V', type: 'armor', rarity: 'legendary', price: 40000, currency: 'credits', description: 'Legendary basic armor with ultimate defense', texture: 'assets/textures/armors/basic_armor.png', stats: { defense: 17, durability: 40, weight: 3, energyShield: 12 } }, // Medium Armor Variants { id: 'medium_armor_common', name: 'Medium Armor', type: 'armor', rarity: 'common', price: 5000, currency: 'credits', description: 'Balanced armor for general combat', texture: 'assets/textures/armors/medium_armor.png', stats: { defense: 12, durability: 40, weight: 5, energyShield: 3 } }, { id: 'medium_armor_uncommon', name: 'Medium Armor II', type: 'armor', rarity: 'uncommon', price: 12000, currency: 'credits', description: 'Enhanced medium armor with better shielding', texture: 'assets/textures/armors/medium_armor.png', stats: { defense: 15, durability: 50, weight: 5.5, energyShield: 6 } }, { id: 'medium_armor_rare', name: 'Medium Armor III', type: 'armor', rarity: 'rare', price: 30000, currency: 'credits', description: 'Elite medium armor with advanced systems', texture: 'assets/textures/armors/medium_armor.png', stats: { defense: 19, durability: 60, weight: 6, energyShield: 10 } }, { id: 'medium_armor_epic', name: 'Medium Armor IV', type: 'armor', rarity: 'epic', price: 60000, currency: 'credits', description: 'Master medium armor with superior protection', texture: 'assets/textures/armors/medium_armor.png', stats: { defense: 23, durability: 70, weight: 6.5, energyShield: 15 } }, { id: 'medium_armor_legendary', name: 'Medium Armor V', type: 'armor', rarity: 'legendary', price: 100000, currency: 'credits', description: 'Legendary medium armor with ultimate defense', texture: 'assets/textures/armors/medium_armor.png', stats: { defense: 28, durability: 80, weight: 7, energyShield: 20 } }, // Heavy Armor Variants { id: 'heavy_armor_common', name: 'Heavy Armor', type: 'armor', rarity: 'common', price: 10000, currency: 'credits', description: 'Maximum protection with increased weight', texture: 'assets/textures/armors/heavy_armor.png', stats: { defense: 20, durability: 60, weight: 8, energyShield: 5 } }, { id: 'heavy_armor_uncommon', name: 'Heavy Armor II', type: 'armor', rarity: 'uncommon', price: 25000, currency: 'credits', description: 'Upgraded heavy armor with better energy shielding', texture: 'assets/textures/armors/heavy_armor.png', stats: { defense: 25, durability: 75, weight: 9, energyShield: 10 } }, { id: 'heavy_armor_rare', name: 'Heavy Armor III', type: 'armor', rarity: 'rare', price: 60000, currency: 'credits', description: 'Elite heavy armor with advanced protection systems', texture: 'assets/textures/armors/heavy_armor.png', stats: { defense: 31, durability: 90, weight: 10, energyShield: 16 } }, { id: 'heavy_armor_epic', name: 'Heavy Armor IV', type: 'armor', rarity: 'epic', price: 120000, currency: 'credits', description: 'Master heavy armor with superior defense capabilities', texture: 'assets/textures/armors/heavy_armor.png', stats: { defense: 37, durability: 105, weight: 11, energyShield: 23 } }, { id: 'heavy_armor_legendary', name: 'Heavy Armor V', type: 'armor', rarity: 'legendary', price: 200000, currency: 'credits', description: 'Legendary heavy armor with ultimate protection', texture: 'assets/textures/armors/heavy_armor.png', stats: { defense: 45, durability: 120, weight: 12, energyShield: 30 } } ], cosmetics: [ { id: 'blue_paint', name: 'Blue Paint Job', type: 'cosmetic', rarity: 'common', price: 100, currency: 'gems', description: 'Custom blue paint for your ship' }, { id: 'golden_trim', name: 'Golden Trim', type: 'cosmetic', rarity: 'rare', price: 500, currency: 'gems', description: 'Luxurious golden trim for your ship' } ], consumables: [ { id: 'health_kit', name: 'Health Kit', type: 'consumable', rarity: 'common', price: 15, currency: 'credits', description: 'Restores 50 health points', texture: 'assets/textures/items/health_pack.png', effect: { heal: 50 } }, { id: 'mega_health_kit', name: 'Mega Health Kit', type: 'consumable', rarity: 'uncommon', price: 50, currency: 'credits', description: 'Restores full health', texture: 'assets/textures/items/mega_health_pack.png', effect: { heal: 999 } } ], buildings: [ { id: 'command_center', name: 'Command Center', type: 'building', rarity: 'uncommon', price: 50000, currency: 'credits', description: 'Central command facility for base operations and coordination', texture: 'assets/textures/base/command_center.png', stats: { command: 10, efficiency: 15, capacity: 20 } }, { id: 'mining_facility', name: 'Mining Facility', type: 'building', rarity: 'common', price: 25000, currency: 'credits', description: 'Automated mining facility for resource extraction and processing', texture: 'assets/textures/base/mining_facility.png', stats: { production: 12, efficiency: 8, storage: 15 } } ], materials: [ { id: 'iron_ore', name: 'Iron Ore', type: 'material', rarity: 'common', price: 10, currency: 'credits', description: 'Basic metal ore used for crafting weapons and armor', texture: 'assets/textures/items/iron_ore.png', stackable: true }, { id: 'copper_ore', name: 'Copper Ore', type: 'material', rarity: 'common', price: 8, currency: 'credits', description: 'Conductive metal ore used for wiring and electronics', texture: 'assets/textures/items/copper_ore.png', stackable: true }, { id: 'tin_bar', name: 'Tin Bar', type: 'material', rarity: 'common', price: 12, currency: 'credits', description: 'Refined tin bar used for alloys and soldering', texture: 'assets/textures/items/tin_bar.png', stackable: true }, { id: 'copper_wire', name: 'Copper Wire', type: 'material', rarity: 'common', price: 8, currency: 'credits', description: 'Conductive wiring for electronic components', texture: 'assets/textures/items/copper_wire.png', stackable: true }, { id: 'energy_crystal', name: 'Energy Crystal', type: 'material', rarity: 'uncommon', price: 25, currency: 'credits', description: 'Crystallized energy source for power systems', texture: 'assets/textures/items/energy_crystal.png', stackable: true }, { id: 'leather', name: 'Leather', type: 'material', rarity: 'common', price: 12, currency: 'credits', description: 'Durable material for crafting armor and accessories', texture: 'assets/textures/items/leather.png', stackable: true }, { id: 'herbs', name: 'Herbs', type: 'material', rarity: 'common', price: 5, currency: 'credits', description: 'Medicinal herbs used for healing items', texture: 'assets/textures/items/herbs.png', stackable: true }, { id: 'bandages', name: 'Bandages', type: 'material', rarity: 'common', price: 3, currency: 'credits', description: 'Medical bandages used for crafting healing items', texture: 'assets/textures/items/bandages.png', stackable: true }, { id: 'steel_plate', name: 'Steel Plate', type: 'material', rarity: 'uncommon', price: 30, currency: 'credits', description: 'Reinforced steel plates used for advanced armor and ship components', texture: 'assets/textures/items/stell_plate.png', stackable: true }, { id: 'advanced_component', name: 'Advanced Component', type: 'material', rarity: 'rare', price: 75, currency: 'credits', description: 'High-tech component used for advanced equipment and upgrades', texture: 'assets/textures/items/advanced_component.png', stackable: true }, { id: 'advanced_components', name: 'Advanced Components Set', type: 'material', rarity: 'epic', price: 150, currency: 'credits', description: 'Complete set of advanced components for high-end manufacturing', texture: 'assets/textures/items/advanced_components.png', stackable: true }, { id: 'basic_circuitboard', name: 'Basic Circuit Board', type: 'material', rarity: 'common', price: 20, currency: 'credits', description: 'Basic electronic circuit board for simple devices', texture: 'assets/textures/items/basic_circuitboard.png', stackable: true }, { id: 'common_circuitboard', name: 'Common Circuit Board', type: 'material', rarity: 'common', price: 35, currency: 'credits', description: 'Standard circuit board for most electronic equipment', texture: 'assets/textures/items/common_circuitboard.png', stackable: true }, { id: 'advanced_circuitboard', name: 'Advanced Circuit Board', type: 'material', rarity: 'rare', price: 100, currency: 'credits', description: 'High-performance circuit board for advanced systems', texture: 'assets/textures/items/advanced_circuitboard.png', stackable: true }, { id: 'battery', name: 'Battery', type: 'material', rarity: 'uncommon', price: 35, currency: 'credits', description: 'Power batteries used for energy-based equipment', texture: 'assets/textures/items/battery.png', stackable: true }, { id: 'advanced_components', name: 'Advanced Components', type: 'material', rarity: 'rare', price: 150, currency: 'credits', description: 'Sophisticated electronic components for advanced ship systems', stackable: true } ] }; // Owned cosmetics this.ownedCosmetics = []; if (debugLogger) debugLogger.log('Economy constructor completed', { initialCredits: this.credits, initialGems: this.gems, shopCategoriesCount: Object.keys(this.shopCategories).length, totalShopItems: Object.values(this.shopItems).reduce((total, category) => total + category.length, 0) }); } async initialize() { const debugLogger = window.debugLogger; console.log('[ECONOMY] Economy system initializing'); if (debugLogger) await debugLogger.startStep('economyInitialize'); try { if (debugLogger) await debugLogger.logStep('Economy initialization started', { currentCredits: this.credits, currentGems: this.gems, transactionHistoryLength: this.transactionHistory.length }); // Setup shop purchase event listeners this.setupShopEventListeners(); // Initialize random shop system this.initializeRandomShop(); console.log('[ECONOMY] Economy system initialization completed'); if (debugLogger) await debugLogger.endStep('economyInitialize'); } catch (error) { console.error('[ECONOMY] Error during initialization:', error); if (debugLogger) await debugLogger.errorEvent(error, 'Economy Initialize'); } } initializeRandomShop() { const debugLogger = window.debugLogger; console.log('[ECONOMY] Initializing random shop system'); // Shop data is now loaded through the main save/load system in load() // Only initialize if no shop data exists (new game) if (!this.lastShopRefresh || Object.keys(this.randomShopItems).length === 0) { console.log('[ECONOMY] No shop data found, generating new shop'); this.refreshRandomShop(); } else { console.log('[ECONOMY] Shop data already loaded from save'); // Start the refresh timer for ongoing updates this.startShopRefreshTimer(); } if (debugLogger) debugLogger.logStep('Random shop system initialized', { hasShopData: Object.keys(this.randomShopItems).length > 0, lastRefresh: this.lastShopRefresh }); } refreshRandomShop() { const debugLogger = window.debugLogger; console.log('[ECONOMY] Refreshing random shop items'); const categories = Object.keys(this.shopCategories); this.randomShopItems = {}; categories.forEach(category => { const categoryItems = this.shopItems[category] || []; if (categoryItems.length === 0) return; // Group items by their base ID (remove tier suffixes) const baseItemGroups = {}; categoryItems.forEach(item => { // Extract base ID by removing tier suffixes (common, uncommon, rare, epic, legendary) const baseId = item.id.replace(/_(common|uncommon|rare|epic|legendary)$/, ''); if (!baseItemGroups[baseId]) { baseItemGroups[baseId] = []; } baseItemGroups[baseId].push(item); }); // Get all unique base items const uniqueBaseItems = Object.keys(baseItemGroups); // Randomly select up to MAX_ITEMS_PER_CATEGORY base items const shuffledBaseItems = [...uniqueBaseItems].sort(() => Math.random() - 0.5); const selectedBaseItems = shuffledBaseItems.slice(0, Math.min(this.MAX_ITEMS_PER_CATEGORY, shuffledBaseItems.length)); // For each selected base item, randomly pick one tier variant this.randomShopItems[category] = selectedBaseItems.map(baseId => { const variants = baseItemGroups[baseId]; const selectedVariant = variants[Math.floor(Math.random() * variants.length)]; return { ...selectedVariant, id: `${selectedVariant.id}_random_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, originalId: selectedVariant.id, baseId: baseId, price: Math.round(selectedVariant.price * (0.8 + Math.random() * 0.4)), // ±20% price variation isRandomItem: true, refreshTimestamp: Date.now() }; }); // Reset purchase limits for this category this.categoryPurchaseLimits[category] = 0; if (debugLogger) debugLogger.logStep('Random shop category refreshed', { category: category, totalBaseItems: uniqueBaseItems.length, baseItemsSelected: selectedBaseItems.length, itemsGenerated: this.randomShopItems[category].length, selectedVariants: this.randomShopItems[category].map(item => ({ baseId: item.baseId, variantId: item.originalId, rarity: item.rarity, name: item.name })), purchaseLimitReset: true }); }); this.lastShopRefresh = Date.now(); this.saveRandomShopData(); this.startShopRefreshTimer(); // Update UI if shop tab is active this.updateShopUI(); this.game.showNotification('Shop inventory refreshed!', 'info', 3000); if (debugLogger) debugLogger.logStep('Random shop refresh completed', { categoriesRefreshed: categories.length, totalItemsGenerated: Object.values(this.randomShopItems).flat().length, nextRefresh: new Date(this.lastShopRefresh + this.SHOP_REFRESH_INTERVAL) }); } startShopRefreshTimer() { // Clear existing timers if (this.shopRefreshInterval) { clearInterval(this.shopRefreshInterval); } if (this.shopHeartbeatInterval) { clearInterval(this.shopHeartbeatInterval); } // Set up heartbeat timer for live countdown updates (every second) this.shopHeartbeatInterval = setInterval(() => { this.updateShopCountdown(); }, 1000); // Set up refresh timer (every 2 hours) this.shopRefreshInterval = setInterval(() => { this.refreshRandomShop(); }, this.SHOP_REFRESH_INTERVAL); console.log('[ECONOMY] Shop refresh timers started'); } updateShopCountdown() { // Only update if shop tab is active and using random shop const shopTab = document.getElementById('shop-tab'); if (!shopTab || shopTab.style.display === 'none') { return; } const activeCategory = document.querySelector('.shop-cat-btn.active')?.dataset.category || 'ships'; if (!this.randomShopItems[activeCategory]) { return; } // Find and update the countdown element const countdownElement = document.querySelector('.refresh-countdown'); if (countdownElement) { countdownElement.innerHTML = ` Next refresh in: ${this.getShopRefreshCountdown()} `; } } stopShopRefreshTimer() { if (this.shopRefreshInterval) { clearInterval(this.shopRefreshInterval); this.shopRefreshInterval = null; } if (this.shopHeartbeatInterval) { clearInterval(this.shopHeartbeatInterval); this.shopHeartbeatInterval = null; } console.log('[ECONOMY] Shop refresh timers stopped'); } loadRandomShopData() { try { const savedData = localStorage.getItem('gso_random_shop'); if (savedData) { const data = JSON.parse(savedData); this.randomShopItems = data.randomShopItems || {}; this.categoryPurchaseLimits = data.categoryPurchaseLimits || {}; this.lastShopRefresh = data.lastShopRefresh || null; console.log('[ECONOMY] Random shop data loaded from localStorage'); } } catch (error) { console.error('[ECONOMY] Error loading random shop data:', error); this.randomShopItems = {}; this.categoryPurchaseLimits = {}; this.lastShopRefresh = null; } } saveRandomShopData() { try { const data = { randomShopItems: this.randomShopItems, categoryPurchaseLimits: this.categoryPurchaseLimits, lastShopRefresh: this.lastShopRefresh }; localStorage.setItem('gso_random_shop', JSON.stringify(data)); console.log('[ECONOMY] Random shop data saved to localStorage'); } catch (error) { console.error('[ECONOMY] Error saving random shop data:', error); } } shouldRefreshShop() { // Check if enough time has passed since the last actual refresh if (!this.lastShopRefresh) { return true; // No previous refresh, need to refresh } const now = Date.now(); const timeSinceRefresh = now - this.lastShopRefresh; return timeSinceRefresh >= this.SHOP_REFRESH_INTERVAL; } getShopRefreshCountdown() { const schedule = this.getRefreshSchedule(); const now = Date.now(); // Calculate time until next server refresh const timeUntilRefresh = schedule.nextRefresh.getTime() - now; if (timeUntilRefresh <= 0) { return 'Refreshing...'; } return this.formatTimeRemaining(timeUntilRefresh); } getNextRefreshTime() { // Calculate the next refresh time based on server time const now = Date.now(); if (!this.lastShopRefresh) { // If no last refresh, next refresh is now + interval return new Date(now + this.SHOP_REFRESH_INTERVAL); } // Calculate next refresh time const timeSinceRefresh = now - this.lastShopRefresh; const intervalsPassed = Math.floor(timeSinceRefresh / this.SHOP_REFRESH_INTERVAL); const nextRefreshTime = this.lastShopRefresh + ((intervalsPassed + 1) * this.SHOP_REFRESH_INTERVAL); return new Date(nextRefreshTime); } getRefreshSchedule() { // Generate a consistent refresh schedule based on server time const now = new Date(); const currentHour = now.getHours(); // Refresh at even hours: 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22 let nextRefreshHour; if (currentHour % 2 === 0 && now.getMinutes() === 0 && now.getSeconds() === 0) { // Exactly on an even hour at :00:00, next refresh is in 2 hours nextRefreshHour = currentHour + 2; } else { // Find the next even hour nextRefreshHour = currentHour + (2 - (currentHour % 2)); } // Handle midnight wrap-around if (nextRefreshHour >= 24) { nextRefreshHour = nextRefreshHour % 24; } const nextRefresh = new Date(now); nextRefresh.setHours(nextRefreshHour, 0, 0, 0); // If the calculated time is in the past (shouldn't happen but just in case), add 2 hours if (nextRefresh <= now) { nextRefresh.setHours(nextRefresh.getHours() + 2); } console.log('[ECONOMY] Current time:', now.toLocaleTimeString()); console.log('[ECONOMY] Next refresh at:', nextRefresh.toLocaleTimeString()); return { nextRefresh: nextRefresh, interval: 2 * 60 * 60 * 1000, // 2 hours schedule: 'Every 2 hours at even hours (2,4,6,8,10,12,14,16,18,20,22,00)' }; } formatTimeRemaining(timeUntilRefresh) { const hours = Math.floor(timeUntilRefresh / (1000 * 60 * 60)); const minutes = Math.floor((timeUntilRefresh % (1000 * 60 * 60)) / (1000 * 60)); const seconds = Math.floor((timeUntilRefresh % (1000 * 60)) / 1000); if (hours > 0) { return `${hours}h ${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; } else { return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; } } getItemCategory(item) { // Determine category based on item type switch (item.type) { case 'ship': return 'ships'; case 'weapon': return 'weapons'; case 'armor': return 'armors'; case 'material': return 'materials'; case 'cosmetic': return 'cosmetics'; case 'upgrade': return 'upgrades'; case 'consumable': return 'consumables'; case 'building': return 'buildings'; default: // For random items, check if we can find them in shop categories for (const [category, items] of Object.entries(this.shopItems)) { if (items.some(shopItem => shopItem.id === item.originalId || shopItem.id === item.id)) { return category; } } return 'unknown'; } } setupShopEventListeners() { const debugLogger = window.debugLogger; console.log('[ECONOMY] Setting up shop event listeners'); // Remove existing listeners to prevent duplicates const shopItemsElement = document.getElementById('shopItems'); if (shopItemsElement) { // Clone the element to remove all event listeners const newElement = shopItemsElement.cloneNode(true); shopItemsElement.parentNode.replaceChild(newElement, shopItemsElement); // Setup shop purchase button event delegation on the new element newElement.addEventListener('click', (event) => { const purchaseBtn = event.target.closest('.shop-item-purchase-btn'); if (purchaseBtn && !purchaseBtn.disabled) { const itemId = purchaseBtn.getAttribute('data-item-id'); console.log(`[ECONOMY] Shop purchase button clicked for item: ${itemId}`); if (itemId) { this.purchaseItem(itemId, 1); } else { console.error('[ECONOMY] No item ID found on purchase button'); } } }); console.log('[ECONOMY] Shop event listeners setup completed'); } else { console.error('[ECONOMY] Shop items element not found'); } } // Currency management addCredits(amount, source = 'unknown') { const oldCredits = this.credits; this.credits += amount; this.addTransaction('credits', amount, source); console.log(`[ECONOMY] Added ${amount} credits from ${source}. New total: ${this.credits}`); // Update UI to show new credit amount if (this.game.systems.ui && this.game.shouldUpdateGUI()) { this.game.systems.ui.updateUI(); } this.game.showNotification(`+${this.game.formatNumber(amount)} credits`, 'success', 2000); } removeCredits(amount) { const oldCredits = this.credits; if (this.credits < amount) { this.game.showNotification('Not enough credits!', 'error', 3000); return false; } this.credits -= amount; this.addTransaction('credits', -amount, 'purchase'); console.log(`[ECONOMY] Removed ${amount} credits. New total: ${this.credits}`); // Update UI to show new credit amount if (this.game.systems.ui && this.game.shouldUpdateGUI()) { this.game.systems.ui.updateUI(); } return true; } addGems(amount, source = 'unknown') { const oldGems = this.gems; this.gems += amount; this.addTransaction('gems', amount, source); console.log(`[ECONOMY] Added ${amount} gems from ${source}. New total: ${this.gems}`); // Update UI to show new gem amount if (this.game.systems.ui && this.game.shouldUpdateGUI()) { this.game.systems.ui.updateUI(); } this.game.showNotification(`+${this.game.formatNumber(amount)} gems`, 'success', 2000); } removeGems(amount) { const oldGems = this.gems; if (this.gems < amount) { this.game.showNotification('Not enough gems!', 'error', 3000); return false; } this.gems -= amount; this.addTransaction('gems', -amount, 'purchase'); console.log(`[ECONOMY] Removed ${amount} gems. New total: ${this.gems}`); // Update UI to show new gem amount if (this.game.systems.ui && this.game.shouldUpdateGUI()) { this.game.systems.ui.updateUI(); } return true; } // Transaction tracking addTransaction(currency, amount, source) { const debugLogger = window.debugLogger; const transaction = { id: Date.now() + Math.random().toString(36).substr(2, 9), currency: currency, amount: amount, source: source, timestamp: Date.now() }; this.transactionHistory.unshift(transaction); // Keep only last 100 transactions const oldLength = this.transactionHistory.length; if (this.transactionHistory.length > 100) { this.transactionHistory = this.transactionHistory.slice(0, 100); } if (debugLogger) debugLogger.logStep('Transaction recorded', { transactionId: transaction.id, currency: currency, amount: amount, source: source, timestamp: transaction.timestamp, historyLength: this.transactionHistory.length, wasTrimmed: oldLength > 100, removedCount: oldLength > 100 ? oldLength - 100 : 0 }); } // Shop functionality purchaseItem(itemId, quantity = 1) { const debugLogger = window.debugLogger; const item = this.findShopItem(itemId); if (!item) { if (debugLogger) debugLogger.logStep('Item purchase failed - item not found', { itemId: itemId, quantity: quantity }); this.game.showNotification('Item not found in shop', 'error', 3000); return false; } const totalCost = item.price * quantity; const currency = item.currency; const oldCredits = this.credits; const oldGems = this.gems; if (debugLogger) debugLogger.logStep('Item purchase attempted', { itemId: itemId, itemName: item.name, itemType: item.type, quantity: quantity, unitPrice: item.price, totalCost: totalCost, currency: currency, currentCredits: oldCredits, currentGems: oldGems }); // Check if player can afford if (currency === 'credits' && this.credits < totalCost) { if (debugLogger) debugLogger.logStep('Item purchase failed - insufficient credits', { totalCost: totalCost, currentCredits: oldCredits, deficit: totalCost - oldCredits }); this.game.showNotification('Not enough credits!', 'error', 3000); return false; } if (currency === 'gems' && this.gems < totalCost) { if (debugLogger) debugLogger.logStep('Item purchase failed - insufficient gems', { totalCost: totalCost, currentGems: oldGems, deficit: totalCost - oldGems }); this.game.showNotification('Not enough gems!', 'error', 3000); return false; } // Check if already owns this cosmetic if (item.type === 'cosmetic' && this.ownedCosmetics.includes(item.id)) { this.game.showNotification('You already own this cosmetic!', 'error', 3000); return false; } // Process payment and give item based on type if (currency === 'credits') { this.credits -= totalCost; } else if (currency === 'gems') { this.gems -= totalCost; } switch (item.type) { case 'ship': this.purchaseShip(item, quantity); break; case 'cosmetic': this.purchaseCosmetic(item, quantity); break; case 'consumable': this.purchaseConsumable(item, quantity); break; case 'material': this.purchaseMaterial(item, quantity); break; default: console.warn(`[ECONOMY] Unknown item type: ${item.type}`); return false; } // Random shop items don't have purchase limits - unlimited purchases allowed if (item.isRandomItem) { const category = this.getItemCategory(item); if (debugLogger) debugLogger.logStep('Random shop purchase completed', { itemId: itemId, itemName: item.name, category: category }); } // Update inventory UI to show the new item if (this.game.systems.ui && this.game.systems.ui.updateInventory) { this.game.systems.ui.updateInventory(); } else if (this.game.systems.ui && this.game.systems.ui.updateUI) { this.game.systems.ui.updateUI(); } else { console.warn('No UI update method available after purchase'); } if (debugLogger) debugLogger.logStep('Item purchase completed successfully', { itemId: itemId, itemName: item.name, itemType: item.type, quantity: quantity, totalCost: totalCost, currency: currency, oldCredits: oldCredits, newCredits: this.credits, oldGems: oldGems, newGems: this.gems }); // Update UI without calling updateShopUI to avoid circular updates return true; } findShopItem(itemId) { const debugLogger = window.debugLogger; for (const category of Object.values(this.shopItems)) { const item = category.find(i => i.id === itemId); if (item) { if (debugLogger) debugLogger.logStep('Shop item found', { itemId: itemId, itemName: item.name, itemType: item.type, itemPrice: item.price, itemCurrency: item.currency }); return item; } } if (debugLogger) debugLogger.logStep('Shop item not found', { itemId: itemId, availableCategories: Object.keys(this.shopItems) }); return null; } purchaseShip(ship) { const debugLogger = window.debugLogger; const player = this.game.systems.player; const oldShipName = player.ship.name; const oldShipClass = player.ship.class; const oldAttributes = { ...player.attributes }; if (debugLogger) debugLogger.logStep('Ship purchase processing', { shipId: ship.id, shipName: ship.name, shipType: ship.type, shipStats: ship.stats, oldShipName: oldShipName, oldShipClass: oldShipClass }); // Add ship to player's ship collection // Add ship to base gallery if (this.game.systems.baseSystem) { this.game.systems.baseSystem.addShipToGallery(ship); if (debugLogger) debugLogger.logStep('Ship added to base gallery'); } // Replace current ship (no upgrade functionality) player.ship.name = ship.name; player.ship.class = ship.type; player.ship.level = 1; // Reset level for new ship player.ship.texture = ship.texture; // Copy texture for image display // Set ship-specific stats (not just attributes) if (ship.stats.health) { player.ship.maxHealth = ship.stats.health; player.ship.health = ship.stats.health; } if (ship.stats.attack) { player.ship.attack = ship.stats.attack; } if (ship.stats.defense) { player.ship.defense = ship.stats.defense; } if (ship.stats.speed) { player.ship.speed = ship.stats.speed; } if (ship.stats.criticalChance) { player.ship.criticalChance = ship.stats.criticalChance; } if (ship.stats.criticalDamage) { player.ship.criticalDamage = ship.stats.criticalDamage; } // Also update player attributes for compatibility for (const [stat, value] of Object.entries(ship.stats)) { if (player.attributes[stat] !== undefined) { player.attributes[stat] = value; // Replace stats instead of adding } } player.updateUI(); // Also update ShipSystem display if (this.game.systems.ship && this.game.systems.ship.updateCurrentShipDisplay) { this.game.systems.ship.updateCurrentShipDisplay(); } if (debugLogger) debugLogger.logStep('Ship purchase completed', { shipId: ship.id, oldShipName: oldShipName, newShipName: player.ship.name, oldShipClass: oldShipClass, newShipClass: player.ship.class, attributeChanges: Object.entries(ship.stats).map(([stat, value]) => ({ stat: stat, oldValue: oldAttributes[stat], newValue: player.attributes[stat], change: value })) }); } purchaseCosmetic(cosmetic) { const debugLogger = window.debugLogger; const oldOwnedCount = this.ownedCosmetics.length; // Add to owned cosmetics this.ownedCosmetics.push(cosmetic.id); this.game.showNotification(`Cosmetic unlocked: ${cosmetic.name}`, 'success', 3000); if (debugLogger) debugLogger.logStep('Cosmetic purchase completed', { cosmeticId: cosmetic.id, cosmeticName: cosmetic.name, oldOwnedCount: oldOwnedCount, newOwnedCount: this.ownedCosmetics.length, totalOwnedCosmetics: this.ownedCosmetics.length }); } purchaseConsumable(consumable, quantity) { const debugLogger = window.debugLogger; const inventory = this.game.systems.inventory; const oldInventorySize = inventory ? inventory.items.length : 0; if (debugLogger) debugLogger.logStep('Consumable purchase processing', { consumableId: consumable.id, consumableName: consumable.name, quantity: quantity, effect: consumable.effect, oldInventorySize: oldInventorySize }); // Add to inventory for (let i = 0; i < quantity; i++) { const item = { id: `${consumable.id}_${Date.now()}_${i}`, name: consumable.name, type: 'consumable', rarity: consumable.rarity, quantity: 1, description: consumable.description, consumable: true, effect: consumable.effect }; inventory.addItem(item); } if (debugLogger) debugLogger.logStep('Consumable purchase completed', { consumableId: consumable.id, quantity: quantity, oldInventorySize: oldInventorySize, newInventorySize: inventory ? inventory.items.length : 0, itemsAdded: quantity }); } purchaseEquipment(equipment, quantity = 1) { const inventory = this.game.systems.inventory; const oldInventorySize = inventory ? inventory.items.length : 0; // Add to inventory for (let i = 0; i < quantity; i++) { const item = { id: `${equipment.id}_${Date.now()}_${i}`, name: equipment.name, type: equipment.type, rarity: equipment.rarity, quantity: 1, description: equipment.description, texture: equipment.texture, stats: equipment.stats || {}, equipable: true }; inventory.addItem(item); } } purchaseMaterial(material, quantity = 1) { const inventory = this.game.systems.inventory; const debugLogger = window.debugLogger; if (!inventory) { console.error('[ECONOMY] Inventory system not available for material purchase'); return false; } try { const oldInventorySize = inventory.items.length; console.log(`[DEBUG] Inventory state before purchase: ${oldInventorySize} items`); // Create item object for inventory const item = { id: material.id, name: material.name, type: 'material', rarity: material.rarity || 'common', quantity: quantity, description: material.description || `A ${material.name} material`, stackable: true, stats: {}, equipable: false, slot: null }; console.log(`[DEBUG] Adding item to inventory: ${item.name}`); const added = inventory.addItem(item); console.log(`[DEBUG] addItem() returned: ${added}, new inventory size: ${inventory.items.length}`); if (debugLogger) debugLogger.logStep('Material purchase completed', { materialId: material.id, materialName: material.name, quantity: quantity, oldInventorySize: oldInventorySize, newInventorySize: inventory.items.length, addedSuccessfully: added }); if (!added) { console.error('Failed to add material to inventory'); return false; } else { // Update inventory UI to show the new material if (this.game.systems.ui && this.game.systems.ui.updateInventory) { this.game.systems.ui.updateInventory(); } else if (this.game.systems.ui && this.game.systems.ui.updateUI) { this.game.systems.ui.updateUI(); } else { console.warn('No UI update method available after material purchase'); } console.log('[DEBUG] Material purchase completed and UI update called'); return true; } } catch (error) { console.error('Exception in purchaseMaterial:', error); if (debugLogger) debugLogger.errorEvent('Purchase Material Exception', error, { materialId: material.id, materialName: material.name }); return false; } } // Reward generation generateRewards(difficulty = 'normal', source = 'dungeon') { const debugLogger = window.debugLogger; if (debugLogger) debugLogger.startStep('generateRewards', { difficulty: difficulty, source: source }); const baseRewards = { easy: { credits: [50, 150], gems: [0, 1], experience: [20, 50] }, normal: { credits: [100, 300], gems: [1, 3], experience: [50, 100] }, hard: { credits: [200, 500], gems: [2, 5], experience: [100, 200] }, extreme: { credits: [500, 1000], gems: [5, 10], experience: [200, 400] } }; const rewards = baseRewards[difficulty] || baseRewards.normal; const generatedRewards = {}; // Generate credits generatedRewards.credits = Math.floor( Math.random() * (rewards.credits[1] - rewards.credits[0] + 1) + rewards.credits[0] ); // Generate gems generatedRewards.gems = Math.floor( Math.random() * (rewards.gems[1] - rewards.gems[0] + 1) + rewards.gems[0] ); // Generate experience generatedRewards.experience = Math.floor( Math.random() * (rewards.experience[1] - rewards.experience[0] + 1) + rewards.experience[0] ); // Bonus for premium currency (rare) if (Math.random() < 0.05) { // 5% chance generatedRewards.premiumCurrency = 1; } if (debugLogger) debugLogger.endStep('generateRewards', { difficulty: difficulty, source: source, generatedRewards: generatedRewards, rewardRanges: rewards }); return generatedRewards; } giveRewards(rewards, source = 'unknown') { const debugLogger = window.debugLogger; const player = this.game.systems.player; const oldCredits = this.credits; const oldGems = this.gems; const oldExperience = player.stats.experience; const oldLevel = player.stats.level; if (debugLogger) debugLogger.startStep('giveRewards', { source: source, rewards: rewards, oldPlayerState: { credits: oldCredits, gems: oldGems, experience: oldExperience, level: oldLevel } }); let totalRewards = []; // Add credits if (rewards.credits > 0) { this.addCredits(rewards.credits, source); totalRewards.push(`${rewards.credits} credits`); } // Add gems if (rewards.gems > 0) { this.addGems(rewards.gems, source); totalRewards.push(`${rewards.gems} gems`); } // Add premium currency if (rewards.premiumCurrency > 0) { this.addPremiumCurrency(rewards.premiumCurrency, source); totalRewards.push(`${rewards.premiumCurrency} premium currency`); } // Add experience if (rewards.experience > 0) { player.addExperience(rewards.experience); totalRewards.push(`${rewards.experience} experience`); } // Add materials if (rewards.materials && rewards.materials.length > 0) { const inventory = this.game.systems.inventory; alert(`[DUNGEON DEBUG] Adding ${rewards.materials.length} materials to inventory\nInventory available: ${!!inventory}\nMaterials: ${JSON.stringify(rewards.materials, null, 2)}`); for (const material of rewards.materials) { // Create proper item object like in purchaseMaterial const itemObject = { id: material.id, name: material.id.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase()), // Convert ID to readable name type: 'material', rarity: 'common', quantity: material.quantity, description: `A ${material.id.replace('_', ' ')} material`, stackable: true, stats: {}, equipable: false, slot: null }; alert(`[DUNGEON DEBUG] Fixed! Adding material as full item object:\n${JSON.stringify(itemObject, null, 2)}`); inventory.addItem(itemObject); totalRewards.push(`${material.quantity}x ${material.id}`); } } // Add items if (rewards.items && rewards.items.length > 0) { const inventory = this.game.systems.inventory; for (const item of rewards.items) { inventory.addItem(item.id, item.quantity || 1); totalRewards.push(`${item.quantity || 1}x ${item.id}`); } } // Show reward notification if (totalRewards.length > 0) { const rewardText = totalRewards.join(', '); this.game.showNotification(`Rewards: ${rewardText}`, 'success', 3000); } if (debugLogger) debugLogger.endStep('giveRewards', { source: source, rewardsGiven: rewards, totalRewardsText: totalRewards.join(', '), currencyChanges: { credits: { old: oldCredits, new: this.credits, change: this.credits - oldCredits }, gems: { old: oldGems, new: this.gems, change: this.gems - oldGems } }, playerChanges: { experience: { old: oldExperience, new: player.stats.experience, change: player.stats.experience - oldExperience }, level: { old: oldLevel, new: player.stats.level, leveledUp: player.stats.level > oldLevel } } }); } // Daily rewards and bonuses claimDailyReward() { const debugLogger = window.debugLogger; const lastClaim = localStorage.getItem('lastDailyReward'); const today = new Date().toDateString(); if (debugLogger) debugLogger.startStep('claimDailyReward', { lastClaim: lastClaim, today: today, alreadyClaimed: lastClaim === today }); if (lastClaim === today) { this.game.showNotification('Daily reward already claimed!', 'warning', 3000); if (debugLogger) debugLogger.endStep('claimDailyReward', { success: false, reason: 'Already claimed today' }); return false; } // Calculate reward based on consecutive days const consecutiveDays = parseInt(localStorage.getItem('consecutiveDailyRewards') || '0') + 1; const baseReward = 100; const bonusMultiplier = Math.min(consecutiveDays * 0.1, 2); // Max 2x bonus const credits = Math.floor(baseReward * (1 + bonusMultiplier)); const gems = Math.min(Math.floor(consecutiveDays / 7), 5); // 1 gem per week, max 5 if (debugLogger) debugLogger.logStep('Daily reward calculation', { consecutiveDays: consecutiveDays, baseReward: baseReward, bonusMultiplier: bonusMultiplier, creditsReward: credits, gemsReward: gems }); // Give rewards this.addCredits(credits, 'daily_reward'); this.addGems(gems, 'daily_reward'); // Update daily reward tracking localStorage.setItem('lastDailyReward', today); localStorage.setItem('consecutiveDailyRewards', consecutiveDays.toString()); // Show notification const rewardText = `${credits} credits${gems > 0 ? ` and ${gems} gems` : ''}`; this.game.showNotification(`Daily reward claimed: ${rewardText}! (${consecutiveDays} day streak)`, 'success', 4000); if (debugLogger) debugLogger.endStep('claimDailyReward', { success: true, consecutiveDays: consecutiveDays, creditsReward: credits, gemsReward: gems, rewardText: rewardText, newLastClaim: today, newConsecutiveDays: consecutiveDays }); return true; } // UI updates updateUI() { const debugLogger = window.debugLogger; if (debugLogger) debugLogger.startStep('updateUI', { currentCredits: this.credits, currentGems: this.gems, currentPremiumCurrency: this.premiumCurrency }); // Update resource displays const creditsElement = document.getElementById('credits'); if (creditsElement) { creditsElement.textContent = this.game.formatNumber(this.credits); } const gemsElement = document.getElementById('gems'); if (gemsElement) { gemsElement.textContent = this.game.formatNumber(this.gems); } const premiumElement = document.getElementById('premiumCurrency'); if (premiumElement) { premiumElement.textContent = this.game.formatNumber(this.premiumCurrency); } // Update shop UI when shop tab is active this.updateShopUI(); if (debugLogger) debugLogger.endStep('updateUI', { creditsUpdated: !!creditsElement, gemsUpdated: !!gemsElement, premiumUpdated: !!premiumElement, shopUIUpdated: true }); } // Reset economy (for new game) reset() { const debugLogger = window.debugLogger; const oldState = { credits: this.credits, gems: this.gems, premiumCurrency: this.premiumCurrency, transactionCount: this.transactions.length, ownedCosmeticsCount: this.ownedCosmetics.length }; if (debugLogger) debugLogger.startStep('reset', { oldState: oldState }); this.credits = 1000; this.gems = 10; this.premiumCurrency = 0; this.transactions = []; this.ownedCosmetics = []; // Reset daily rewards localStorage.removeItem('lastDailyReward'); localStorage.removeItem('consecutiveDailyRewards'); if (debugLogger) debugLogger.endStep('reset', { oldState: oldState, newState: { credits: this.credits, gems: this.gems, premiumCurrency: this.premiumCurrency, transactionCount: this.transactions.length, ownedCosmeticsCount: this.ownedCosmetics.length } }); } // Clear all data clear() { const debugLogger = window.debugLogger; const oldState = { credits: this.credits, gems: this.gems, premiumCurrency: this.premiumCurrency, transactionCount: this.transactions.length, ownedCosmeticsCount: this.ownedCosmetics.length }; if (debugLogger) debugLogger.startStep('clear', { oldState: oldState }); this.credits = 0; this.gems = 0; this.premiumCurrency = 0; this.transactions = []; this.ownedCosmetics = []; if (debugLogger) debugLogger.endStep('clear', { oldState: oldState, newState: { credits: this.credits, gems: this.gems, premiumCurrency: this.premiumCurrency, transactionCount: this.transactions.length, ownedCosmeticsCount: this.ownedCosmetics.length } }); } updateShopUI() { const debugLogger = window.debugLogger; const shopItemsElement = document.getElementById('shopItems'); if (!shopItemsElement) { if (debugLogger) debugLogger.log('updateShopUI: Shop items element not found'); return; } const activeCategory = document.querySelector('.shop-cat-btn.active')?.dataset.category || 'ships'; // Always use random shop items when the system is available // If no random items exist for this category, trigger a refresh if (!this.randomShopItems[activeCategory] || this.randomShopItems[activeCategory].length === 0) { console.log(`[ECONOMY] No random items for ${activeCategory}, triggering refresh`); this.refreshRandomShop(); } const items = this.randomShopItems[activeCategory] || []; const isRandomShop = true; // Always random shop when system is available if (debugLogger) debugLogger.startStep('updateShopUI', { activeCategory: activeCategory, itemCount: items.length, isRandomShop: isRandomShop, currentCredits: this.credits, currentGems: this.gems, currentPremiumCurrency: this.premiumCurrency }); shopItemsElement.innerHTML = ''; // Add shop refresh info if using random shop if (isRandomShop) { const refreshInfo = document.createElement('div'); refreshInfo.className = 'shop-refresh-info'; refreshInfo.innerHTML = `