From 0bbfa7261e5e84e75307d70e785aee85348bd3f1 Mon Sep 17 00:00:00 2001 From: xbl Date: Tue, 17 Feb 2026 23:23:42 +0100 Subject: [PATCH] modified: index.html modified: js/config.js modified: js/entities.js modified: js/game.js modified: js/helpers.js modified: js/ui.js --- index.html | 4 +++ js/config.js | 15 ++++++++-- js/entities.js | 78 ++++++++++++++++++++++++++++++++++++++++++++------ js/game.js | 43 ++++++++++++++++++++++++++-- js/helpers.js | 3 +- js/ui.js | 32 +++++++++++++++++++-- 6 files changed, 159 insertions(+), 16 deletions(-) diff --git a/index.html b/index.html index 3c6fb27..81ca817 100644 --- a/index.html +++ b/index.html @@ -102,6 +102,10 @@ a1.7 1.7 0 0 0-1.5 1z">
+
+
+ +
diff --git a/js/config.js b/js/config.js index 95495e6..16e0c23 100644 --- a/js/config.js +++ b/js/config.js @@ -37,8 +37,8 @@ var CONFIG = { PLANET_BASE_COST: 1e24, GIANT_BASE_COST: 1e27, MTYPE_BASE_COST: 1e29, - KTYPE_BASE_COST: 1e31, - GTYPE_BASE_COST: 1e33, + KTYPE_BASE_COST: 1e30, + GTYPE_BASE_COST: 1e31, // Upgrade scaling UPGRADE_COST_MULTIPLIER_ASTER: 1.25, @@ -75,6 +75,17 @@ var CONFIG = { gtype: [1.59e30, 2.07e30] // K-Type: 0.8-1.04 M☉ }, + // Visual Sizes + VISUAL_SIZE_SMALL: 1, + VISUAL_SIZE_MEDIUM: 2, + VISUAL_SIZE_LARGE: 3, + VISUAL_SIZE_COMET: 3, + VISUAL_SIZE_PLANT: 5, + VISUAL_SIZE_GIANT: 8, + VISUAL_SIZE_MTYPE: 12, + VISUAL_SIZE_KTYPE: 14, + VISUAL_SIZE_GTYPE: 16, + // Planet color schemes PLANET_COLORS: [ { light: '#6b7c87', mid: '#3d4d5a', dark: '#1f2a33' }, diff --git a/js/entities.js b/js/entities.js index 0e674cf..fdc6cb5 100644 --- a/js/entities.js +++ b/js/entities.js @@ -66,11 +66,11 @@ function Asteroid(type, blackHole, canvas) { } Asteroid.prototype.initializeTypeSpecificProperties = function() { - // Visual size (unchanged) + // Visual size if (this.type === 'comet') { - this.size = 3 + Math.random() * 1; + this.size = CONFIG.VISUAL_SIZE_COMET + Math.random() * 1; } else if (this.type === 'mtype') { - this.size = 15 + Math.random() * 3; + this.size = CONFIG.VISUAL_SIZE_MTYPE + Math.random() * 2; this.orbitSpeed *= 0.2; this.decayRate *= 0.3; this.planetColors = CONFIG.MTYPE_COLORS[Math.floor(Math.random() * CONFIG.MTYPE_COLORS.length)]; @@ -100,7 +100,7 @@ Asteroid.prototype.initializeTypeSpecificProperties = function() { }); } } else if (this.type === 'ktype') { - this.size = 16 + Math.random() * 4; + this.size = CONFIG.VISUAL_SIZE_KTYPE + Math.random() * 2; this.orbitSpeed *= 0.15; this.decayRate *= 0.22; this.planetColors = CONFIG.KTYPE_COLORS[Math.floor(Math.random() * CONFIG.KTYPE_COLORS.length)]; @@ -138,6 +138,64 @@ Asteroid.prototype.initializeTypeSpecificProperties = function() { }); } + // Sparkles — simulating spikes toward the viewer + this.sparkles = []; + var sparkleCount = 4 + Math.floor(Math.random() * 4); // 4–8 + for (var k = 0; k < sparkleCount; k++) { + var angle = Math.random() * Math.PI * 2; + var dist = Math.random() * this.size * 0.75; + this.sparkles.push({ + x: Math.cos(angle) * dist, + y: Math.sin(angle) * dist, + dist: dist, // ← add + angle: angle, // ← add + wanderSpeed: (Math.random() - 0.5) * 0.008, // ← add, slow angular drift + size: 0.4 + Math.random() * 0.8, + phase: Math.random() * Math.PI * 2, + speed: 0.05 + Math.random() * 1.8, + armLen: 0.4 + Math.random() * 8, + armWidth: 0.2 + Math.random() * 0.4 + }); + } + } else if (this.type === 'gtype') { + this.size = CONFIG.VISUAL_SIZE_GTYPE + Math.random() * 2; + this.orbitSpeed *= 0.13; + this.decayRate *= 0.20; + this.planetColors = CONFIG.GTYPE_COLORS[Math.floor(Math.random() * CONFIG.GTYPE_COLORS.length)]; + + // Surface granulation cells + this.granules = []; + var granCount = 8 + Math.floor(Math.random() * 10); + for (var i = 0; i < granCount; i++) { + var angle = Math.random() * Math.PI * 2; + var dist = Math.random() * this.size * 0.8; + this.granules.push({ + x: Math.cos(angle) * dist, + y: Math.sin(angle) * dist, + radius: 1.5 + Math.random() * this.size * 0.2, + phase: Math.random() * Math.PI * 2 + }); + } + + // Spikes — each one fully independent + this.spikes = []; + var spikeCount = 22 + Math.floor(Math.random() * 11); // 22–33 + for (var s = 0; s < spikeCount; s++) { + this.spikes.push({ + baseAngle: (s / spikeCount) * Math.PI * 2 + (Math.random() - 0.5) * 0.4, + rotSpeed: (Math.random() - 0.5) * 0.015, // some drift CW, some CCW + lenBase: 0.35 + Math.random() * 0.45, // 0.35–0.80 × size + lenAmp: 0.05 + Math.random() * 0.20, // oscillation range + lenSpeed: 0.3 + Math.random() * 1.0, // oscillation speed + lenPhase: Math.random() * Math.PI * 2, + widthBase: 0.1 + Math.random() * 0.045, // tip width + alphaBase: 0.10 + Math.random() * 0.14, + alphaAmp: 0.03 + Math.random() * 0.07, + alphaPhase: Math.random() * Math.PI * 2, + alphaSpeed: 0.4 + Math.random() * 0.9 + }); + } + // Sparkles — simulating spikes toward the viewer this.sparkles = []; var sparkleCount = 4 + Math.floor(Math.random() * 4); // 4–8 @@ -158,7 +216,7 @@ Asteroid.prototype.initializeTypeSpecificProperties = function() { }); } } else if (this.type === 'giant') { - this.size = 10 + Math.random() * 5; + this.size = CONFIG.VISUAL_SIZE_GIANT + Math.random() * 5; this.orbitSpeed *= 0.3; this.decayRate *= 0.4; this.planetColors = CONFIG.GIANT_COLORS[Math.floor(Math.random() * CONFIG.GIANT_COLORS.length)]; @@ -174,16 +232,16 @@ Asteroid.prototype.initializeTypeSpecificProperties = function() { }); } } else if (this.type === 'planet') { - this.size = 6 + Math.random() * 4; + this.size = CONFIG.VISUAL_SIZE_PLANT + Math.random() * 4; this.orbitSpeed *= 0.5; this.decayRate *= 0.6; this.planetColors = CONFIG.PLANET_COLORS[Math.floor(Math.random() * CONFIG.PLANET_COLORS.length)]; } else if (this.type === 'large') { - this.size = 3 + Math.random() * 1; + this.size = CONFIG.VISUAL_SIZE_LARGE + Math.random() * 1; } else if (this.type === 'medium') { - this.size = 2 + Math.random() * 1; + this.size = CONFIG.VISUAL_SIZE_MEDIUM + Math.random() * 1; } else { - this.size = 1 + Math.random() * 1; + this.size = CONFIG.VISUAL_SIZE_SMALL + Math.random() * 1; } // Physical mass (kg) @@ -206,6 +264,8 @@ Asteroid.prototype.draw = function(ctx) { this.drawMType(ctx, performance.now() * 0.002); } else if (this.type === 'ktype') { this.drawKType(ctx, performance.now() * 0.002); + } else if (this.type === 'gtype') { + this.drawKType(ctx, performance.now() * 0.002); } else if (this.type === 'giant') { // Initialize per-giant tilt if not already done if (this._ringTiltBase === undefined) { diff --git a/js/game.js b/js/game.js index 0590ee9..460a020 100644 --- a/js/game.js +++ b/js/game.js @@ -20,12 +20,15 @@ var Game = (function() { mtypeUpgradeCost: CONFIG.MTYPE_BASE_COST, ktypeUpgradeLevel: 0, ktypeUpgradeCost: CONFIG.KTYPE_BASE_COST, + gtypeUpgradeLevel: 0, + gtypeUpgradeCost: CONFIG.GTYPE_BASE_COST, currentAsteroidSpawnInterval: CONFIG.BASE_ASTEROID_SPAWN_INTERVAL, currentCometSpawnInterval: CONFIG.BASE_COMET_SPAWN_INTERVAL, currentPlanetSpawnInterval: CONFIG.BASE_PLANET_SPAWN_INTERVAL, currentGiantSpawnInterval: CONFIG.BASE_GIANT_SPAWN_INTERVAL, currentMtypeSpawnInterval: CONFIG.BASE_MTYPE_SPAWN_INTERVAL, currentKtypeSpawnInterval: CONFIG.BASE_KTYPE_SPAWN_INTERVAL, + currentGtypeSpawnInterval: CONFIG.BASE_GTYPE_SPAWN_INTERVAL, asteroidSpawnCount: 0, lastAsteroidSpawn: Date.now(), lastCometSpawn: Date.now(), @@ -33,6 +36,7 @@ var Game = (function() { lastGiantSpawn: Date.now(), lastMtypeSpawn: Date.now(), lastKtypeSpawn: Date.now(), + lastGtypeSpawn: Date.now(), sM: 0, sT: Date.now(), mM: 0, mT: Date.now(), lM: 0, lT: Date.now(), @@ -45,7 +49,8 @@ var Game = (function() { planetUnlocked: false, giantUnlocked: false, mtypeUnlocked: false, - ktypeUnlocked: false + ktypeUnlocked: false, + gtypeUnlocked: false }; var blackHole; @@ -93,7 +98,8 @@ var Game = (function() { planet: handlePlanetUpgrade, giant: handleGiantUpgrade, mtype: handleMtypeUpgrade, - ktype: handleKtypeUpgrade + ktype: handleKtypeUpgrade, + gtype: handleGtypeUpgrade }); Server.init() @@ -164,6 +170,7 @@ var Game = (function() { state.giantUpgradeLevel = savedState.giantUpgradeLevel || 0; state.mtypeUpgradeLevel = savedState.mtypeUpgradeLevel || 0; state.ktypeUpgradeLevel = savedState.ktypeUpgradeLevel || 0; + state.gtypeUpgradeLevel = savedState.gtypeUpgradeLevel || 0; // Derive unlock states from levels state.cometUnlocked = state.asteroidUpgradeLevel >= 20; @@ -171,6 +178,7 @@ var Game = (function() { state.giantUnlocked = state.planetUpgradeLevel >= 10; state.mtypeUnlocked = state.giantUpgradeLevel >= 5; state.ktypeUnlocked = state.mtypeUpgradeLevel >= 5; + state.gtypeUnlocked = state.ktypeUpgradeLevel >= 5; // Derive costs and intervals from levels + CONFIG recalculateUpgradeCosts(); @@ -183,6 +191,7 @@ var Game = (function() { state.lastGiantSpawn = savedState.lastGiantSpawn || Date.now(); state.lastMtypeSpawn = savedState.lastMtypeSpawn || Date.now(); state.lastKtypeSpawn = savedState.lastKtypeSpawn || Date.now(); + state.lastGtypeSpawn = savedState.lastGtypeSpawn || Date.now(); // Consumption rates state.sM = savedState.sM || 0; @@ -260,6 +269,8 @@ var Game = (function() { (1 + state.mtypeUpgradeLevel * CONFIG.UPGRADE_BONUS_PER_LEVEL_MTYPE); state.currentKtypeSpawnInterval = CONFIG.BASE_KTYPE_SPAWN_INTERVAL / (1 + state.ktypeUpgradeLevel * CONFIG.UPGRADE_BONUS_PER_LEVEL_KTYPE); + state.currentGtypeSpawnInterval = CONFIG.BASE_GTYPE_SPAWN_INTERVAL / + (1 + state.gtypeUpgradeLevel * CONFIG.UPGRADE_BONUS_PER_LEVEL_GTYPE); } function recalculateUpgradeCosts() { @@ -269,6 +280,7 @@ var Game = (function() { 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)); + state.gtypeUpgradeCost = Math.floor(CONFIG.GTYPE_BASE_COST * Math.pow(CONFIG.UPGRADE_COST_MULTIPLIER_GTYPE, state.gtypeUpgradeLevel)); } function handleAsteroidUpgrade() { @@ -374,6 +386,27 @@ var Game = (function() { state.ktypeUpgradeCost = Math.floor( CONFIG.KTYPE_BASE_COST * Math.pow(CONFIG.UPGRADE_COST_MULTIPLIER_KTYPE, state.ktypeUpgradeLevel) ); + + // Unlock K-Type at m-type level 5 + if (state.ktypeUpgradeLevel >= 5 && !state.gtypeUnlocked) { + state.gtypeUnlocked = true; + state.lastGtypeSpawn = Date.now(); + showUnlockNotification('gtype'); + asteroids.push(new Asteroid('gtype', blackHole, canvas)); + } + updateSpawnIntervals(); + UI.update(state, CONFIG); + } + } + + function handleGtypeUpgrade() { + if (state.totalMassConsumed >= state.gtypeUpgradeCost) { + state.totalMassConsumed -= state.gtypeUpgradeCost; + state.gtypeUpgradeLevel++; + state.gtypeUpgradeCost = Math.floor( + CONFIG.GTYPE_BASE_COST * Math.pow(CONFIG.UPGRADE_COST_MULTIPLIER_GTYPE, state.gtypeUpgradeLevel) + ); + updateSpawnIntervals(); UI.update(state, CONFIG); } @@ -432,6 +465,12 @@ var Game = (function() { asteroids.push(new Asteroid('ktype', blackHole, canvas)); } } + if (state.gtypeUnlocked) { + if (currentTime - state.lastGtypeSpawn > state.currentGtypeSpawnInterval) { + state.lastGtypeSpawn = currentTime; + asteroids.push(new Asteroid('gtype', blackHole, canvas)); + } + } } diff --git a/js/helpers.js b/js/helpers.js index fcb5caf..4653787 100644 --- a/js/helpers.js +++ b/js/helpers.js @@ -222,7 +222,8 @@ function showUnlockNotification(type) { planet: 'Planets Unlocked', giant: 'Gas & Ice Giants Unlocked', mtype: 'M-Type Stars Unlocked', - ktype: 'K-Type Stars Unlocked' + ktype: 'K-Type Stars Unlocked', + gtype: 'G-Type Stars Unlocked' }; showNotification({ diff --git a/js/ui.js b/js/ui.js index b2e5047..4d3c733 100644 --- a/js/ui.js +++ b/js/ui.js @@ -15,12 +15,14 @@ var UI = { giantLevel: document.getElementById('giant-level'), mtypeLevel: document.getElementById('mtype-level'), ktypeLevel: document.getElementById('ktype-level'), + gtypeLevel: document.getElementById('gtype-level'), asteroidUpgradeBtn: document.getElementById('asteroid-upgrade-btn'), cometUpgradeBtn: document.getElementById('comet-upgrade-btn'), planetUpgradeBtn: document.getElementById('planet-upgrade-btn'), giantUpgradeBtn: document.getElementById('giant-upgrade-btn'), mtypeUpgradeBtn: document.getElementById('mtype-upgrade-btn'), ktypeUpgradeBtn: document.getElementById('ktype-upgrade-btn'), + gtypeUpgradeBtn: document.getElementById('gtype-upgrade-btn'), gearIcon: document.getElementById('gear-icon'), settingsMenu: document.getElementById('settings-menu'), resetBtn: document.getElementById('reset-btn'), @@ -205,6 +207,7 @@ var UI = { this.elements.giantUpgradeBtn.addEventListener('click', handlers.giant); this.elements.mtypeUpgradeBtn.addEventListener('click', handlers.mtype); this.elements.ktypeUpgradeBtn.addEventListener('click', handlers.ktype); + this.elements.gtypeUpgradeBtn.addEventListener('click', handlers.gtype); }, update: function(gameState, config) { @@ -305,13 +308,22 @@ var UI = { this.elements.ktypeLevel.parentElement.style.display = 'none'; } + // Only show gtype upgrade if unlocked + if (gameState.gtypeUnlocked) { + this.updateGtypeUpgrade(gameState); + this.elements.gtypeLevel.parentElement.style.display = ''; + } else { + this.elements.gtypeLevel.parentElement.style.display = 'none'; + } + var canAffordAny = gameState.totalMassConsumed >= gameState.asteroidUpgradeCost || (gameState.cometUnlocked && gameState.totalMassConsumed >= gameState.cometUpgradeCost) || (gameState.planetUnlocked && gameState.totalMassConsumed >= gameState.planetUpgradeCost) || (gameState.giantUnlocked && gameState.totalMassConsumed >= gameState.giantUpgradeCost) || (gameState.mtypeUnlocked && gameState.totalMassConsumed >= gameState.mtypeUpgradeCost) || - (gameState.ktypeUnlocked && gameState.totalMassConsumed >= gameState.ktypeUpgradeCost); + (gameState.ktypeUnlocked && gameState.totalMassConsumed >= gameState.ktypeUpgradeCost) || + (gameState.gtypeUnlocked && gameState.totalMassConsumed >= gameState.gtypeUpgradeCost); var holeIcon = document.getElementById('hole-icon'); if (holeIcon) { @@ -333,7 +345,8 @@ var UI = { gameState.planetUpgradeLevel + gameState.giantUpgradeLevel + gameState.mtypeUpgradeLevel + - gameState.ktypeUpgradeLevel; + gameState.ktypeUpgradeLevel + + gameState.gtypeUpgradeLevel; el.innerHTML = '' + @@ -462,6 +475,7 @@ var UI = { 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.

Rate: ' + rate + '/2days
Bonus: ' + bonusPercent + '%'; + if (!gameState.gtypeUnlocked) tooltipText += '
Unlocks G-Type at level 5'; this.elements.ktypeLevel.innerHTML = 'K-Type: Level ' + gameState.ktypeUpgradeLevel + @@ -472,6 +486,20 @@ var UI = { gameState.totalMassConsumed < gameState.ktypeUpgradeCost; }, + updateGtypeUpgrade: function(gameState) { + var rate = (CONFIG.BASE_GTYPE_SPAWN_INTERVAL / gameState.currentGtypeSpawnInterval).toFixed(2); + var bonusPercent = (gameState.gtypeUpgradeLevel * CONFIG.UPGRADE_BONUS_PER_LEVEL_GTYPE * 100).toFixed(0); + var tooltipText = 'G-Types, also known as yellow dwarfs, are medium-sized stars that are about the size of our Sun (~1 M☉). The term yellow dwarf is a misnomer, as most G-Types (including the Sun) are in fact white. These stars are stable and long-lived, making them strong candidates for hosting habitable planets.

Rate: ' + rate + '/3days
Bonus: ' + bonusPercent + '%'; + + this.elements.gtypeLevel.innerHTML = 'G-Type: Level ' + + gameState.gtypeUpgradeLevel + + '' + tooltipText + ''; + this.elements.gtypeUpgradeBtn.textContent = + 'Upgrade (Cost: ' + this.formatMass(gameState.gtypeUpgradeCost) + ')'; + this.elements.gtypeUpgradeBtn.disabled = + gameState.totalMassConsumed < gameState.gtypeUpgradeCost; + }, + formatMass: function(massKg, options = {}) { if (massKg == null) return '0'; // fallback const SOLAR_SWITCH_KG = CONFIG.SOLAR_MASS_KG * 0.01; // ~1% solar mass