/** * Cosmic Garden - Main Game Engine * Handles game state, tick loop, and core logic */ const Game = { // Core game state state: { stardust: 0, totalStardust: 0, totalClicks: 0, clickPower: 1, startTime: Date.now(), lastSaveTime: Date.now(), buildings: {}, upgrades: [], achievements: [] }, // Configuration config: { tickRate: 100, // ms per tick (10 ticks/sec) autoSaveInterval: 30000, // 30 seconds costScaling: 1.15 // Cost multiplier per building level }, /** * Initialize the game */ init() { this.loadState(); this.setupEventListeners(); this.startGameLoop(); this.startAutoSave(); Audio.init(); UI.init(); Stats.init(); console.log('🌌 Cosmic Garden initialized!'); }, /** * Setup event listeners */ setupEventListeners() { // Harvest button document.getElementById('harvest-btn').addEventListener('click', (e) => { this.harvest(e); }); // Save buttons document.getElementById('save-btn').addEventListener('click', () => { this.save(); UI.showNotification('Game saved!', 'success'); }); document.getElementById('export-btn').addEventListener('click', () => { SaveSystem.exportSave(); }); document.getElementById('import-btn').addEventListener('click', () => { UI.showImportModal(); }); document.getElementById('confirm-import').addEventListener('click', () => { const data = document.getElementById('import-data').value; if (data) { this.loadFromData(data); UI.hideImportModal(); UI.showNotification('Save imported!', 'success'); } }); document.getElementById('cancel-import').addEventListener('click', () => { UI.hideImportModal(); }); document.getElementById('reset-btn').addEventListener('click', () => { if (confirm('Are you sure you want to reset all progress? This cannot be undone!')) { this.reset(); } }); // Tab switching document.querySelectorAll('.tab-btn').forEach(btn => { btn.addEventListener('click', (e) => { const tab = e.target.dataset.tab; document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); e.target.classList.add('active'); document.getElementById(`${tab}-tab`).classList.add('active'); }); }); }, /** * Harvest stardust by clicking */ harvest(event) { const amount = this.getClickPower(); this.addStardust(amount); this.state.totalClicks++; // Audio feedback Audio.playClick(); // Visual feedback UI.showFloatingText(event.clientX, event.clientY, `+${this.formatNumber(amount)}`); Particles.createClickParticles(event.clientX, event.clientY, 8); UI.update(); }, /** * Add stardust to the game */ addStardust(amount) { this.state.stardust += amount; this.state.totalStardust += amount; }, /** * Get current click power */ getClickPower() { let power = 1; // Apply upgrade bonuses for (const upgradeId of this.state.upgrades) { const upgrade = UPGRADES[upgradeId]; if (upgrade && upgrade.clickPowerBonus) { power *= upgrade.clickPowerBonus; } } return Math.floor(power); }, /** * Calculate production per second */ getProductionPerSecond() { let total = 0; for (const [buildingId, count] of Object.entries(this.state.buildings)) { if (count === 0) continue; const building = BUILDINGS[buildingId]; if (!building) continue; let production = building.baseProduction * count; // Apply upgrade bonuses for (const upgradeId of this.state.upgrades) { const upgrade = UPGRADES[upgradeId]; if (upgrade && upgrade.buildingBonus && upgrade.buildingBonus[buildingId]) { production *= upgrade.buildingBonus[buildingId]; } if (upgrade && upgrade.globalBonus) { production *= upgrade.globalBonus; } } total += production; } return total; }, /** * Get building cost */ getBuildingCost(buildingId) { const building = BUILDINGS[buildingId]; const count = this.state.buildings[buildingId] || 0; return Math.floor(building.baseCost * Math.pow(this.config.costScaling, count)); }, /** * Buy a building */ buyBuilding(buildingId) { const cost = this.getBuildingCost(buildingId); if (this.state.stardust >= cost) { this.state.stardust -= cost; this.state.buildings[buildingId] = (this.state.buildings[buildingId] || 0) + 1; UI.showNotification(`Bought ${BUILDINGS[buildingId].name}!`, 'success'); // Audio feedback Audio.playPurchase(); // Particle effect on building purchase const card = document.getElementById(`building-${buildingId}`); if (card) { const rect = card.getBoundingClientRect(); Particles.createBuildingEffect(rect.left + rect.width / 2, rect.top + rect.height / 2); } UI.update(); return true; } return false; }, /** * Buy an upgrade */ buyUpgrade(upgradeId) { const upgrade = UPGRADES[upgradeId]; if (!upgrade || this.state.upgrades.includes(upgradeId)) { return false; } if (this.state.stardust >= upgrade.cost) { // Check requirements if (upgrade.requirements) { for (const [reqId, reqCount] of Object.entries(upgrade.requirements)) { const currentCount = this.state.buildings[reqId] || 0; if (currentCount < reqCount) { UI.showNotification('Requirements not met!', 'warning'); return false; } } } this.state.stardust -= upgrade.cost; this.state.upgrades.push(upgradeId); UI.showNotification(`Unlocked: ${upgrade.name}!`, 'success'); UI.update(); return true; } return false; }, /** * Game tick - called every config.tickRate ms */ tick() { const production = this.getProductionPerSecond(); const tickSeconds = this.config.tickRate / 1000; if (production > 0) { this.addStardust(production * tickSeconds); } UI.update(); Achievements.check(); }, /** * Start the game loop */ startGameLoop() { setInterval(() => this.tick(), this.config.tickRate); }, /** * Start auto-save */ startAutoSave() { setInterval(() => { this.save(); document.getElementById('last-saved').textContent = `Last saved: ${new Date().toLocaleTimeString()}`; }, this.config.autoSaveInterval); }, /** * Save game state */ save() { this.state.lastSaveTime = Date.now(); localStorage.setItem('cosmicGardenSave', JSON.stringify(this.state)); }, /** * Load game state */ loadState() { const saved = localStorage.getItem('cosmicGardenSave'); if (saved) { try { const parsed = JSON.parse(saved); this.state = { ...this.state, ...parsed }; // Calculate offline progress const offlineTime = (Date.now() - this.state.lastSaveTime) / 1000; if (offlineTime > 60) { const production = this.getProductionPerSecond(); const offlineEarnings = production * offlineTime * 0.5; // 50% offline efficiency if (offlineEarnings > 0) { this.addStardust(offlineEarnings); UI.showNotification( `Welcome back! Earned ${this.formatNumber(offlineEarnings)} ✨ while away`, 'success' ); } } } catch (e) { console.error('Failed to load save:', e); } } }, /** * Load from imported data */ loadFromData(data) { try { const parsed = JSON.parse(atob(data)); this.state = { ...this.state, ...parsed }; UI.update(); } catch (e) { UI.showNotification('Invalid save data!', 'warning'); } }, /** * Export save as base64 */ exportSave() { this.save(); const data = btoa(JSON.stringify(this.state)); navigator.clipboard.writeText(data).then(() => { UI.showNotification('Save copied to clipboard!', 'success'); }); }, /** * Reset game */ reset() { localStorage.removeItem('cosmicGardenSave'); this.state = { stardust: 0, totalStardust: 0, totalClicks: 0, clickPower: 1, startTime: Date.now(), lastSaveTime: Date.now(), buildings: {}, upgrades: [], achievements: [] }; UI.update(); UI.showNotification('Game reset!', 'warning'); }, /** * Format numbers with commas */ formatNumber(num) { if (num >= 1e12) return (num / 1e12).toFixed(2) + 'T'; if (num >= 1e9) return (num / 1e9).toFixed(2) + 'B'; if (num >= 1e6) return (num / 1e6).toFixed(2) + 'M'; if (num >= 1e3) return (num / 1e3).toFixed(2) + 'K'; return Math.floor(num).toLocaleString(); } }; // Make Game globally accessible window.Game = Game;