modified: index.html
modified: js/config.js modified: js/game.js modified: js/ui.js modified: style.css
This commit is contained in:
@ -40,13 +40,12 @@ a1.7 1.7 0 0 0-1.5 1z"></path>
|
||||
<div id="settings-menu">
|
||||
<div class="menu-subtitle">Hoel</div>
|
||||
<div class="menu-text">This game uses cookies to save your progress across sessions. By using this website, you consent to storing cookies on your device.</div>
|
||||
<div class="menu-text">The game periodically sends pseudonymous information about your session to the server to populate the neighbour chart.</div>
|
||||
<div class="menu-text">The game periodically sends anonymous information about your session to the server to populate the neighbour chart.</div>
|
||||
<div class="menu-text">Sessions are hidden from the chart after 5 days and deleted from the database after 30 days of inactivity.</div>
|
||||
<div class="menu-divider"></div>
|
||||
<div class="menu-subtitle">Session Reset</div>
|
||||
<div class="menu-text">Terminate progress, purge registers, annihilate archives, unweave circuits, shatter syntax, invert polarity, nullify existence, restart systems, initialize framework.</div>
|
||||
<button id="reset-btn" class="menu-btn danger">Reset</button>
|
||||
<div id="save-status" style="margin-top: 12px; font-size: 10px; color: rgba(100, 200, 100, 0.6);"></div>
|
||||
<div class="menu-divider"></div>
|
||||
<div class="menu-subtitle">Session Transfer</div>
|
||||
<div class="menu-text">Duplicate progress, start migration, redirect access, shift registry, authorize device, clone instance, restore snapshot, deploy replica, mount payload, fuse endpoints.</div>
|
||||
|
||||
@ -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: {
|
||||
|
||||
164
js/game.js
164
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) {
|
||||
|
||||
43
js/ui.js
43
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 <br>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.<br><br>Rate: ' + rate + '/sec<br>Bonus: ' + bonusPercent + '%';
|
||||
if (!gameState.cometUnlocked) tooltipText += '<br>Unlocks Comets at level 20';
|
||||
|
||||
this.elements.asteroidLevel.innerHTML = '<span class="tooltip-trigger">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 <br>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.<br><br>Rate: ' + rate + '/min<br>Bonus: ' + bonusPercent + '%';
|
||||
if (!gameState.planetUnlocked) tooltipText += '<br>Unlocks Planets at level 15';
|
||||
|
||||
this.elements.cometLevel.innerHTML = '<span class="tooltip-trigger">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 <br>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.<br><br>Rate: ' + rate + '/hour<br>Bonus: ' + bonusPercent + '%';
|
||||
if (!gameState.giantUnlocked) tooltipText += '<br>Unlocks Giants at level 10';
|
||||
|
||||
this.elements.planetLevel.innerHTML = '<span class="tooltip-trigger">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 <br>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.<br>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.<br><br>Spawn Rate: ' + rate + '/6hours<br>Bonus: ' + bonusPercent + '%';
|
||||
if (!gameState.mtypeUnlocked) tooltipText += '<br>Unlocks M-Type at level 5';
|
||||
|
||||
this.elements.giantLevel.innerHTML = '<span class="tooltip-trigger">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 <br>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☉.<br>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.<br><br>Spawn Rate: ' + rate + '/day<br>Bonus: ' + bonusPercent + '%';
|
||||
if (!gameState.ktypeUnlocked) tooltipText += '<br>Unlocks K-Type at level 5';
|
||||
|
||||
this.elements.mtypeLevel.innerHTML = '<span class="tooltip-trigger">M-Type: Level ' +
|
||||
gameState.mtypeUpgradeLevel +
|
||||
'<span class="tooltip">' + tooltipText + '</span></span>';
|
||||
@ -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 <br>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.<br><br>Spawn Rate: ' + rate + '/2days<br>Bonus: ' + bonusPercent + '%';
|
||||
|
||||
this.elements.ktypeLevel.innerHTML = '<span class="tooltip-trigger">K-Type: Level ' +
|
||||
gameState.ktypeUpgradeLevel +
|
||||
'<span class="tooltip">' + tooltipText + '</span></span>';
|
||||
|
||||
Reference in New Issue
Block a user