diff --git a/index.html b/index.html index b63f12f..3c6fb27 100644 --- a/index.html +++ b/index.html @@ -40,13 +40,12 @@ a1.7 1.7 0 0 0-1.5 1z">
- + -
diff --git a/js/config.js b/js/config.js index f96141d..e6fc38f 100644 --- a/js/config.js +++ b/js/config.js @@ -21,7 +21,7 @@ var CONFIG = { BASE_PLANET_SPAWN_INTERVAL: 3600000, // 1 hour BASE_GIANT_SPAWN_INTERVAL: 21600000, // 6 hours BASE_MTYPE_SPAWN_INTERVAL: 86400000, // 1 day - BASE_KTYPE_SPAWN_INTERVAL: 259200000, // 3 days + BASE_KTYPE_SPAWN_INTERVAL: 172800000, // 2 days // Rate tracking windows (in milliseconds) RATE_WINDOWS: { diff --git a/js/game.js b/js/game.js index 4b6a705..0b7fb1a 100644 --- a/js/game.js +++ b/js/game.js @@ -151,104 +151,95 @@ var Game = (function() { function loadGameState() { Server.loadGameState().then(function(savedState) { if (savedState) { - state.blackHoleTotalMass = savedState.blackHoleTotalMass; + // Restore only what cannot be derived + state.blackHoleTotalMass = savedState.blackHoleTotalMass; state.totalMassConsumedEver = savedState.totalMassConsumedEver; - state.totalMassConsumed = savedState.totalMassConsumed; - state.asteroidUpgradeLevel = savedState.asteroidUpgradeLevel; - state.asteroidUpgradeCost = savedState.asteroidUpgradeCost; - state.cometUpgradeLevel = savedState.cometUpgradeLevel; - state.cometUpgradeCost = savedState.cometUpgradeCost; - state.planetUpgradeLevel = savedState.planetUpgradeLevel; - state.planetUpgradeCost = savedState.planetUpgradeCost; - state.giantUpgradeLevel = savedState.giantUpgradeLevel; - state.giantUpgradeCost = savedState.giantUpgradeCost; - state.mtypeUpgradeLevel = savedState.mtypeUpgradeLevel || 0; - state.ktypeUpgradeLevel = savedState.ktypeUpgradeLevel || 0; - state.mtypeUpgradeCost = savedState.mtypeUpgradeCost || CONFIG.MTYPE_BASE_COST; - state.ktypeUpgradeCost = savedState.ktypeUpgradeCost || CONFIG.KTYPE_BASE_COST; - state.asteroidSpawnCount = savedState.asteroidSpawnCount; - - // Load unlock states (with backward compatibility) - state.cometUnlocked = savedState.cometUnlocked !== undefined ? savedState.cometUnlocked : (savedState.asteroidUpgradeLevel >= 20); - state.planetUnlocked = savedState.planetUnlocked !== undefined ? savedState.planetUnlocked : (savedState.cometUpgradeLevel >= 15); - state.giantUnlocked = savedState.giantUnlocked !== undefined ? savedState.giantUnlocked : (savedState.planetUpgradeLevel >= 10); - state.mtypeUnlocked = savedState.mtypeUnlocked !== undefined ? savedState.mtypeUnlocked : (savedState.giantUpgradeLevel >= 5); - state.ktypeUnlocked = savedState.ktypeUnlocked !== undefined ? savedState.ktypeUnlocked : (savedState.mtypeUpgradeLevel >= 5); - - state.lastAsteroidSpawn = savedState.lastAsteroidSpawn || Date.now(); - state.lastCometSpawn = savedState.lastCometSpawn || Date.now(); - state.lastPlanetSpawn = savedState.lastPlanetSpawn || Date.now(); - state.lastGiantSpawn = savedState.lastGiantSpawn || Date.now(); - state.lastMtypeSpawn = savedState.lastMtypeSpawn || Date.now(); - state.lastKtypeSpawn = savedState.lastKtypeSpawn || Date.now(); + state.totalMassConsumed = savedState.totalMassConsumed; + state.asteroidSpawnCount = savedState.asteroidSpawnCount; - // Load consumption rates + // Upgrade levels — the only upgrade data we store + state.asteroidUpgradeLevel = savedState.asteroidUpgradeLevel || 0; + state.cometUpgradeLevel = savedState.cometUpgradeLevel || 0; + state.planetUpgradeLevel = savedState.planetUpgradeLevel || 0; + state.giantUpgradeLevel = savedState.giantUpgradeLevel || 0; + state.mtypeUpgradeLevel = savedState.mtypeUpgradeLevel || 0; + state.ktypeUpgradeLevel = savedState.ktypeUpgradeLevel || 0; + + // Derive unlock states from levels + state.cometUnlocked = state.asteroidUpgradeLevel >= 20; + state.planetUnlocked = state.cometUpgradeLevel >= 15; + state.giantUnlocked = state.planetUpgradeLevel >= 10; + state.mtypeUnlocked = state.giantUpgradeLevel >= 5; + state.ktypeUnlocked = state.mtypeUpgradeLevel >= 5; + + // Derive costs and intervals from levels + CONFIG + recalculateUpgradeCosts(); + updateSpawnIntervals(); + + // Spawn timestamps + state.lastAsteroidSpawn = savedState.lastAsteroidSpawn || Date.now(); + state.lastCometSpawn = savedState.lastCometSpawn || Date.now(); + state.lastPlanetSpawn = savedState.lastPlanetSpawn || Date.now(); + state.lastGiantSpawn = savedState.lastGiantSpawn || Date.now(); + state.lastMtypeSpawn = savedState.lastMtypeSpawn || Date.now(); + state.lastKtypeSpawn = savedState.lastKtypeSpawn || Date.now(); + + // Consumption rates state.sM = savedState.sM || 0; state.mM = savedState.mM || 0; state.lM = savedState.lM || 0; var now = Date.now(); - state.sT = savedState.sT || now; state.mT = savedState.mT || now; state.lT = savedState.lT || now; - state.rateShort = savedState.rateShort || 0; + state.rateShort = savedState.rateShort || 0; state.rateShortTrend = savedState.rateShortTrend || 'same'; - state.rateMedium = savedState.rateMedium || 0; - state.rateLong = savedState.rateLong || 0; - - state.tabHiddenAt = savedState.tabHiddenAt || null; + state.rateMedium = savedState.rateMedium || 0; + state.rateLong = savedState.rateLong || 0; - // Calculate offline progression ONLY if tab was actually hidden - var offlineTime = 0; - - if (savedState.tabHiddenAt) { - // Tab was hidden - calculate time between hiding and now - offlineTime = now - savedState.tabHiddenAt; - } - - if (offlineTime > 1000) { - var rateToUse = 0; - - if (state.rateLong > 0 && (now - state.lT) > 3600000) { - rateToUse = state.rateLong; - } else if (state.rateMedium > 0 && (now - state.mT) > 300000) { - rateToUse = state.rateMedium; - } else { - rateToUse = state.rateShort || 0; - } - - if (rateToUse > 0) { - var offlineMass = rateToUse * (offlineTime / 1000); - - state.blackHoleTotalMass += offlineMass; - state.totalMassConsumedEver += offlineMass; - state.totalMassConsumed += offlineMass; - - state.sM += offlineMass; - state.mM += offlineMass; - state.lM += offlineMass; - - showOfflineNotification( - formatOfflineTime(offlineTime), - UI.formatMass(offlineMass), - 'offline' - ); - } - } - - // Clear the hidden timestamp after processing - state.tabHiddenAt = null; - - updateSpawnIntervals(); + state.tabHiddenAt = savedState.tabHiddenAt || null; + + // Offline progression + var offlineTime = 0; + if (savedState.tabHiddenAt) { + offlineTime = now - savedState.tabHiddenAt; + } + + if (offlineTime > 1000) { + var rateToUse = 0; + if (state.rateLong > 0 && (now - state.lT) > 3600000) { + rateToUse = state.rateLong; + } else if (state.rateMedium > 0 && (now - state.mT) > 300000) { + rateToUse = state.rateMedium; + } else { + rateToUse = state.rateShort || 0; + } + + if (rateToUse > 0) { + var offlineMass = rateToUse * (offlineTime / 1000); + state.blackHoleTotalMass += offlineMass; + state.totalMassConsumedEver += offlineMass; + state.totalMassConsumed += offlineMass; + state.sM += offlineMass; + state.mM += offlineMass; + state.lM += offlineMass; + showOfflineNotification( + formatOfflineTime(offlineTime), + UI.formatMass(offlineMass), + 'offline' + ); + } + } + + state.tabHiddenAt = null; // Restore black hole size - blackHole.radius = 0.5 * (state.blackHoleTotalMass / CONFIG.SOLAR_MASS_KG) - - // Send checkpoint after loading + blackHole.radius = 0.5 * (state.blackHoleTotalMass / CONFIG.SOLAR_MASS_KG); + Server.checkpoint(state).then(function() { - state.isReady = true; // Mark as ready after checkpoint + state.isReady = true; }); } else { // No saved state - send initial checkpoint for new player @@ -275,6 +266,15 @@ var Game = (function() { state.currentKtypeSpawnInterval = CONFIG.BASE_KTYPE_SPAWN_INTERVAL / (1 + state.ktypeUpgradeLevel * CONFIG.UPGRADE_BONUS_PER_LEVEL_KTYPE); } + + function recalculateUpgradeCosts() { + state.asteroidUpgradeCost = Math.floor(CONFIG.ASTEROID_BASE_COST * Math.pow(CONFIG.UPGRADE_COST_MULTIPLIER_ASTER, state.asteroidUpgradeLevel)); + state.cometUpgradeCost = Math.floor(CONFIG.COMET_BASE_COST * Math.pow(CONFIG.UPGRADE_COST_MULTIPLIER_COMET, state.cometUpgradeLevel)); + state.planetUpgradeCost = Math.floor(CONFIG.PLANET_BASE_COST * Math.pow(CONFIG.UPGRADE_COST_MULTIPLIER_PLANT, state.planetUpgradeLevel)); + state.giantUpgradeCost = Math.floor(CONFIG.GIANT_BASE_COST * Math.pow(CONFIG.UPGRADE_COST_MULTIPLIER_GIANT, state.giantUpgradeLevel)); + state.mtypeUpgradeCost = Math.floor(CONFIG.MTYPE_BASE_COST * Math.pow(CONFIG.UPGRADE_COST_MULTIPLIER_MTYPE, state.mtypeUpgradeLevel)); + state.ktypeUpgradeCost = Math.floor(CONFIG.KTYPE_BASE_COST * Math.pow(CONFIG.UPGRADE_COST_MULTIPLIER_KTYPE, state.ktypeUpgradeLevel)); + } function handleAsteroidUpgrade() { if (state.totalMassConsumed >= state.asteroidUpgradeCost) { diff --git a/js/ui.js b/js/ui.js index 263d6fc..f9227a1 100644 --- a/js/ui.js +++ b/js/ui.js @@ -391,9 +391,10 @@ var UI = { }, updateAsteroidUpgrade: function(gameState) { - var rate = (1 / gameState.currentAsteroidSpawnInterval * 1000).toFixed(2); - var bonusPercent = (gameState.asteroidUpgradeLevel * 10); - var tooltipText = 'Rate: ' + rate + '/sec
Bonus: ' + bonusPercent + '%'; + var rate = (1000 / gameState.currentAsteroidSpawnInterval).toFixed(2); + var bonusPercent = (gameState.asteroidUpgradeLevel * CONFIG.UPGRADE_BONUS_PER_LEVEL_ASTER * 100).toFixed(0); + var tooltipText = 'Asteroids are rocky, airless bodies that range in size from tiny pebbles to hundreds of kilometers across. Some asteroids have moons or even binary companions.

Rate: ' + rate + '/sec
Bonus: ' + bonusPercent + '%'; + if (!gameState.cometUnlocked) tooltipText += '
Unlocks Comets at level 20'; this.elements.asteroidLevel.innerHTML = 'Asteroids: Level ' + gameState.asteroidUpgradeLevel + @@ -407,9 +408,10 @@ var UI = { }, updateCometUpgrade: function(gameState) { - var rate = (1 / gameState.currentCometSpawnInterval * 60000).toFixed(2); - var bonusPercent = (gameState.cometUpgradeLevel * 10); - var tooltipText = 'Rate: ' + rate + '/min
Bonus: ' + bonusPercent + '%'; + var rate = (60000 / gameState.currentCometSpawnInterval).toFixed(2); + var bonusPercent = (gameState.cometUpgradeLevel * CONFIG.UPGRADE_BONUS_PER_LEVEL_COMET * 100).toFixed(0); + var tooltipText = 'Comets are small icy bodies that typically release gas and dust, forming a glowing coma and often a tail. They are composed mainly of ice, rock, and organic compounds.

Rate: ' + rate + '/min
Bonus: ' + bonusPercent + '%'; + if (!gameState.planetUnlocked) tooltipText += '
Unlocks Planets at level 15'; this.elements.cometLevel.innerHTML = 'Comets: Level ' + gameState.cometUpgradeLevel + @@ -423,9 +425,10 @@ var UI = { }, updatePlanetUpgrade: function(gameState) { - var rate = (3600000 / gameState.currentPlanetSpawnInterval).toFixed(2); - var bonusPercent = (gameState.planetUpgradeLevel * 10); - var tooltipText = 'Rate: ' + rate + '/hour
Bonus: ' + bonusPercent + '%'; + var rate = (CONFIG.BASE_PLANET_SPAWN_INTERVAL / gameState.currentPlanetSpawnInterval).toFixed(2); + var bonusPercent = (gameState.planetUpgradeLevel * CONFIG.UPGRADE_BONUS_PER_LEVEL_PLANT * 100).toFixed(0); + var tooltipText = 'Planets are roughly spherical accumulations of rock and metal, with solid surfaces and relatively thin atmospheres. Local examples include Mercury, Venus, Earth, and Mars. They may vary greatly in size.

Rate: ' + rate + '/hour
Bonus: ' + bonusPercent + '%'; + if (!gameState.giantUnlocked) tooltipText += '
Unlocks Giants at level 10'; this.elements.planetLevel.innerHTML = 'Planets: Level ' + gameState.planetUpgradeLevel + @@ -439,9 +442,10 @@ var UI = { }, updateGiantUpgrade: function(gameState) { - var rate = (21600000 / gameState.currentGiantSpawnInterval).toFixed(2); - var bonusPercent = (gameState.giantUpgradeLevel * 10); - var tooltipText = 'Spawn Rate: ' + rate + '/6hours
Bonus: ' + bonusPercent + '%'; + var rate = (CONFIG.BASE_GIANT_SPAWN_INTERVAL / gameState.currentGiantSpawnInterval).toFixed(2); + var bonusPercent = (gameState.giantUpgradeLevel * CONFIG.UPGRADE_BONUS_PER_LEVEL_GIANT * 100).toFixed(0); + var tooltipText = 'Gas giants are large planets made mostly of hydrogen and helium, with thick atmospheres and no solid surface; Jupiter and Saturn are examples.
Ice giants are similar but contain higher amounts of frozen materials like water, ammonia, and methane beneath their atmospheres; Uranus and Neptune fall into this category.

Spawn Rate: ' + rate + '/6hours
Bonus: ' + bonusPercent + '%'; + if (!gameState.mtypeUnlocked) tooltipText += '
Unlocks M-Type at level 5'; this.elements.giantLevel.innerHTML = 'Giants: Level ' + gameState.giantUpgradeLevel + @@ -455,9 +459,11 @@ var UI = { }, updateMtypeUpgrade: function(gameState) { - var rate = (86400000 / gameState.currentMtypeSpawnInterval).toFixed(2); - var bonusPercent = (gameState.mtypeUpgradeLevel * 0.5); - var tooltipText = 'Spawn Rate: ' + rate + '/day
Bonus: ' + bonusPercent + '%'; + var rate = (CONFIG.BASE_MTYPE_SPAWN_INTERVAL / gameState.currentMtypeSpawnInterval).toFixed(2); + var bonusPercent = (gameState.mtypeUpgradeLevel * CONFIG.UPGRADE_BONUS_PER_LEVEL_MTYPE * 100).toFixed(0); + var tooltipText = 'M-types, also known as red dwarfs, are the smallest and coolest stars with masses ranging from about 0.08 to 0.45 M☉.
They are the most common star in the universe, making up roughly three quarters of all main-sequence stars, but are not easily visible due to their low luminosity.

Spawn Rate: ' + rate + '/day
Bonus: ' + bonusPercent + '%'; + if (!gameState.ktypeUnlocked) tooltipText += '
Unlocks K-Type at level 5'; + this.elements.mtypeLevel.innerHTML = 'M-Type: Level ' + gameState.mtypeUpgradeLevel + '' + tooltipText + ''; @@ -468,9 +474,10 @@ var UI = { }, updateKtypeUpgrade: function(gameState) { - var rate = (259200000 / gameState.currentKtypeSpawnInterval).toFixed(2); - var bonusPercent = (gameState.ktypeUpgradeLevel * 3); - var tooltipText = 'Spawn Rate: ' + rate + '/3days
Bonus: ' + bonusPercent + '%'; + var rate = (CONFIG.BASE_KTYPE_SPAWN_INTERVAL / gameState.currentKtypeSpawnInterval).toFixed(2); + var bonusPercent = (gameState.ktypeUpgradeLevel * CONFIG.UPGRADE_BONUS_PER_LEVEL_KTYPE * 100).toFixed(0); + var tooltipText = 'K-types, also known as orange dwarfs, are medium-sized stars that are cooler than the Sun, with masses ranging from about 0.45 to 0.8 M☉. They are known for their stability and long lifespans (20 to 70 billion years) making them potential candidates for supporting inhabited planets.

Spawn Rate: ' + rate + '/2days
Bonus: ' + bonusPercent + '%'; + this.elements.ktypeLevel.innerHTML = 'K-Type: Level ' + gameState.ktypeUpgradeLevel + '' + tooltipText + ''; diff --git a/style.css b/style.css index 849e425..86d4bc4 100644 --- a/style.css +++ b/style.css @@ -47,7 +47,7 @@ canvas { .tooltip { display: none; position: absolute; - background: rgba(20, 20, 30, 0.3); + background: rgba(20, 20, 30, 0.69); backdrop-filter: blur(5px); border: 1px solid rgba(255, 255, 255, 0.2); padding: 8px 12px;