modified: admin.html
modified: js/config.js modified: js/entities.js modified: js/game.js modified: js/helpers.js modified: js/ui.js
This commit is contained in:
@ -29,6 +29,7 @@
|
||||
<button onclick="adminSpawn('giant')">Giant</button>
|
||||
<button onclick="adminSpawn('mtype')">M-Type</button>
|
||||
<button onclick="adminSpawn('ktype')">K-Type</button>
|
||||
<button onclick="adminSpawn('gtype')">G-Type</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
18
js/config.js
18
js/config.js
@ -22,6 +22,7 @@ var CONFIG = {
|
||||
BASE_GIANT_SPAWN_INTERVAL: 21600000, // 6 hours
|
||||
BASE_MTYPE_SPAWN_INTERVAL: 86400000, // 1 day
|
||||
BASE_KTYPE_SPAWN_INTERVAL: 172800000, // 2 days
|
||||
BASE_GTYPE_SPAWN_INTERVAL: 259200000, // 3 days
|
||||
|
||||
// Rate tracking windows (in milliseconds)
|
||||
RATE_WINDOWS: {
|
||||
@ -36,7 +37,8 @@ var CONFIG = {
|
||||
PLANET_BASE_COST: 1e24,
|
||||
GIANT_BASE_COST: 1e27,
|
||||
MTYPE_BASE_COST: 1e29,
|
||||
KTYPE_BASE_COST: 1e30,
|
||||
KTYPE_BASE_COST: 1e31,
|
||||
GTYPE_BASE_COST: 1e33,
|
||||
|
||||
// Upgrade scaling
|
||||
UPGRADE_COST_MULTIPLIER_ASTER: 1.25,
|
||||
@ -45,6 +47,7 @@ var CONFIG = {
|
||||
UPGRADE_COST_MULTIPLIER_GIANT: 2,
|
||||
UPGRADE_COST_MULTIPLIER_MTYPE: 2.1,
|
||||
UPGRADE_COST_MULTIPLIER_KTYPE: 2.2,
|
||||
UPGRADE_COST_MULTIPLIER_GTYPE: 2.3,
|
||||
|
||||
UPGRADE_BONUS_PER_LEVEL_ASTER: 0.1,
|
||||
UPGRADE_BONUS_PER_LEVEL_COMET: 0.1,
|
||||
@ -52,6 +55,7 @@ var CONFIG = {
|
||||
UPGRADE_BONUS_PER_LEVEL_GIANT: 0.03,
|
||||
UPGRADE_BONUS_PER_LEVEL_MTYPE: 0.03,
|
||||
UPGRADE_BONUS_PER_LEVEL_KTYPE: 0.03,
|
||||
UPGRADE_BONUS_PER_LEVEL_GTYPE: 0.03,
|
||||
|
||||
// Asteroid spawn patterns
|
||||
ASTEROID_SPAWN_PATTERNS: {
|
||||
@ -67,7 +71,8 @@ var CONFIG = {
|
||||
planet: [1e22, 1e25], // Moons & Planets
|
||||
giant: [1e26, 1e28], // Gas & Ice Giants
|
||||
mtype: [1.589e29, 8.95e29],// M-Type: 0.08-0.45 M☉
|
||||
ktype: [8.95e29, 1.59e30] // K-Type: 0.45-0.8 M☉
|
||||
ktype: [8.95e29, 1.59e30], // K-Type: 0.45-0.8 M☉
|
||||
gtype: [1.59e30, 2.07e30] // K-Type: 0.8-1.04 M☉
|
||||
},
|
||||
|
||||
// Planet color schemes
|
||||
@ -122,6 +127,15 @@ var CONFIG = {
|
||||
{ light: '#ffe8b0', mid: '#ffc055', dark: '#e08820' },
|
||||
{ light: '#ffd070', mid: '#e8952a', dark: '#c87018' }
|
||||
],
|
||||
|
||||
// G-Type star color schemes (yellow to yellowish-white)
|
||||
GTYPE_COLORS: [
|
||||
{ light: '#fff9e6', mid: '#ffe066', dark: '#ffcc33' },
|
||||
{ light: '#fffbe8', mid: '#ffeb99', dark: '#ffd24d' },
|
||||
{ light: '#fffde0', mid: '#fff176', dark: '#ffd54f' },
|
||||
{ light: '#fff8cc', mid: '#ffe57f', dark: '#ffc107' },
|
||||
{ light: '#fffff0', mid: '#fff59d', dark: '#ffca28' }
|
||||
],
|
||||
|
||||
// Star color distribution
|
||||
STAR_COLORS: [
|
||||
|
||||
179
js/entities.js
179
js/entities.js
@ -164,12 +164,12 @@ Asteroid.prototype.initializeTypeSpecificProperties = function() {
|
||||
this.planetColors = CONFIG.GIANT_COLORS[Math.floor(Math.random() * CONFIG.GIANT_COLORS.length)];
|
||||
// Initialize rings
|
||||
this.rings = [];
|
||||
var ringCount = Math.floor(Math.random() * 4) + 1; // 1–4 rings
|
||||
var ringCount = Math.floor(Math.random() * 3) + 1; // 1–3 rings
|
||||
for (var i = 0; i < ringCount; i++) {
|
||||
this.rings.push({
|
||||
color: CONFIG.GIANT_RING_COLORS[Math.floor(Math.random() * CONFIG.GIANT_RING_COLORS.length)],
|
||||
thickness: 1 + Math.random() * 9, // random thickness
|
||||
radiusOffset: 10 + i * 5 + Math.random() * 5, // spread rings outward
|
||||
radiusOffset: 5 + i * 5 + Math.random() * 5, // spread rings outward
|
||||
alpha: 0.3 + Math.random() * 0.4 // stored permanently
|
||||
});
|
||||
}
|
||||
@ -614,6 +614,181 @@ Asteroid.prototype.drawGiant = function(ctx) {
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
Asteroid.prototype.drawGType = function(ctx, time) {
|
||||
var massRange = CONFIG.ASTEROID_MASS_RANGES.gtype;
|
||||
var massNormalized = (this.massKg - massRange[0]) / (massRange[1] - massRange[0]);
|
||||
|
||||
var pulse = 0.06 * Math.sin(time * 1.4 + this.massKg * 0.001) + 1;
|
||||
var slowPulse = 0.04 * Math.sin(time * 0.4) + 1;
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(this.x, this.y);
|
||||
|
||||
// ── Layer 1: tight ambient halo ──────────────────────────────────────
|
||||
var haloSize = this.size * (2.2 + massNormalized * 0.5) * slowPulse;
|
||||
var halo = ctx.createRadialGradient(0, 0, this.size * 0.9, 0, 0, haloSize);
|
||||
halo.addColorStop(0, 'rgba(255, 160, 40, 0.10)');
|
||||
halo.addColorStop(0.5, 'rgba(255, 130, 20, 0.04)');
|
||||
halo.addColorStop(1, 'rgba(240, 100, 10, 0)');
|
||||
ctx.fillStyle = halo;
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, haloSize, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
// ── Layer 2: spikes ──────────────────────────────────────────────────
|
||||
for (var i = 0; i < this.spikes.length; i++) {
|
||||
var sp = this.spikes[i];
|
||||
|
||||
var spikeAngle = sp.baseAngle + time * sp.rotSpeed;
|
||||
var spikeLen = this.size * (sp.lenBase + sp.lenAmp * Math.sin(time * sp.lenSpeed + sp.lenPhase));
|
||||
var alpha = sp.alphaBase + sp.alphaAmp * Math.sin(time * sp.alphaSpeed + sp.alphaPhase);
|
||||
var tipWidth = this.size * sp.widthBase;
|
||||
|
||||
ctx.save();
|
||||
ctx.rotate(spikeAngle);
|
||||
|
||||
var spikeGrad = ctx.createLinearGradient(
|
||||
this.size, 0,
|
||||
this.size + spikeLen, 0
|
||||
);
|
||||
spikeGrad.addColorStop(0, 'rgba(255, 210, 110, ' + alpha + ')');
|
||||
spikeGrad.addColorStop(0.4, 'rgba(255, 185, 70, ' + (alpha * 0.6) + ')');
|
||||
spikeGrad.addColorStop(1, 'rgba(240, 150, 30, 0)');
|
||||
|
||||
ctx.fillStyle = spikeGrad;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(this.size * 0.95, -tipWidth);
|
||||
ctx.quadraticCurveTo(
|
||||
this.size + spikeLen * 0.5, -tipWidth * 0.3,
|
||||
this.size + spikeLen, 0
|
||||
);
|
||||
ctx.quadraticCurveTo(
|
||||
this.size + spikeLen * 0.5, tipWidth * 0.3,
|
||||
this.size * 0.95, tipWidth
|
||||
);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
// ── Layer 3: inner corona ring ───────────────────────────────────────
|
||||
var coronaGrad = ctx.createRadialGradient(0, 0, this.size * 0.9, 0, 0, this.size * 1.3 * pulse);
|
||||
coronaGrad.addColorStop(0, 'rgba(255, 200, 100, 0.18)');
|
||||
coronaGrad.addColorStop(0.5, 'rgba(255, 170, 60, 0.07)');
|
||||
coronaGrad.addColorStop(1, 'rgba(255, 140, 30, 0)');
|
||||
ctx.fillStyle = coronaGrad;
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, this.size * 1.3 * pulse, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
// ── Layer 4: main body ───────────────────────────────────────────────
|
||||
var bodyGrad = ctx.createRadialGradient(
|
||||
-this.size * 0.15, -this.size * 0.15, 0,
|
||||
0, 0, this.size
|
||||
);
|
||||
bodyGrad.addColorStop(0, this.planetColors.light);
|
||||
bodyGrad.addColorStop(0.45, this.planetColors.mid);
|
||||
bodyGrad.addColorStop(1, this.planetColors.dark);
|
||||
ctx.fillStyle = bodyGrad;
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, this.size, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
// ── Layer 5: surface granulation ────────────────────────────────────
|
||||
for (var g = 0; g < this.granules.length; g++) {
|
||||
var gran = this.granules[g];
|
||||
var gAlpha = 0.05 + 0.03 * Math.sin(time * 0.35 + gran.phase);
|
||||
var granGrad = ctx.createRadialGradient(
|
||||
gran.x, gran.y, 0,
|
||||
gran.x, gran.y, gran.radius
|
||||
);
|
||||
granGrad.addColorStop(0, 'rgba(255, 248, 200, ' + gAlpha + ')');
|
||||
granGrad.addColorStop(1, 'rgba(255, 200, 100, 0)');
|
||||
ctx.fillStyle = granGrad;
|
||||
ctx.beginPath();
|
||||
ctx.arc(gran.x, gran.y, gran.radius, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
// ── Layer 6a: limb darkening ──────────────────────────────────────────
|
||||
var limbGrad = ctx.createRadialGradient(0, 0, this.size * 0.45, 0, 0, this.size * 1.04);
|
||||
limbGrad.addColorStop(0, 'rgba(0, 0, 0, 0)');
|
||||
limbGrad.addColorStop(0.6, 'rgba(0, 0, 0, 0)');
|
||||
limbGrad.addColorStop(0.82, 'rgba(0, 0, 0, 0.12)');
|
||||
limbGrad.addColorStop(0.93, 'rgba(0, 0, 0, 0.20)');
|
||||
limbGrad.addColorStop(1, 'rgba(0, 0, 0, 0)');
|
||||
|
||||
// ── Layer 6b: sparkles (viewer-facing spikes) ────────────────────────
|
||||
for (var k = 0; k < this.sparkles.length; k++) {
|
||||
var sp = this.sparkles[k];
|
||||
|
||||
// Drift angle slowly over time
|
||||
sp.angle += sp.wanderSpeed;
|
||||
sp.x = Math.cos(sp.angle) * sp.dist;
|
||||
sp.y = Math.sin(sp.angle) * sp.dist;
|
||||
|
||||
var bright = 0.5 + 0.5 * Math.sin(time * sp.speed + sp.phase);
|
||||
var sAlpha = bright * bright; // squaring gives snappier flash
|
||||
|
||||
if (sAlpha < 0.05) continue; // skip when nearly invisible
|
||||
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, this.size, 0, Math.PI * 2);
|
||||
ctx.clip(); // keep sparkles inside the disc
|
||||
|
||||
ctx.translate(sp.x, sp.y);
|
||||
|
||||
// Four-pointed cross — two perpendicular arms
|
||||
for (var arm = 0; arm < 2; arm++) {
|
||||
ctx.save();
|
||||
ctx.rotate(arm * Math.PI / 2);
|
||||
var armGrad = ctx.createLinearGradient(-sp.armLen, 0, sp.armLen, 0);
|
||||
armGrad.addColorStop(0, 'rgba(255, 255, 240, 0)');
|
||||
armGrad.addColorStop(0.35, 'rgba(255, 250, 210, ' + (sAlpha * 0.5) + ')');
|
||||
armGrad.addColorStop(0.5, 'rgba(255, 255, 255, ' + sAlpha + ')');
|
||||
armGrad.addColorStop(0.65, 'rgba(255, 250, 210, ' + (sAlpha * 0.5) + ')');
|
||||
armGrad.addColorStop(1, 'rgba(255, 255, 240, 0)');
|
||||
ctx.fillStyle = armGrad;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-sp.armLen, 0);
|
||||
ctx.lineTo(0, -sp.armWidth);
|
||||
ctx.lineTo( sp.armLen, 0);
|
||||
ctx.lineTo(0, sp.armWidth);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
// Bright centre dot
|
||||
var dotGrad = ctx.createRadialGradient(0, 0, 0, 0, 0, sp.size);
|
||||
dotGrad.addColorStop(0, 'rgba(255, 255, 255, ' + sAlpha + ')');
|
||||
dotGrad.addColorStop(0.5, 'rgba(255, 245, 200, ' + (sAlpha * 0.5) + ')');
|
||||
dotGrad.addColorStop(1, 'rgba(255, 230, 150, 0)');
|
||||
ctx.fillStyle = dotGrad;
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, sp.size, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
// ── Layer 7: bright core ─────────────────────────────────────────────
|
||||
var coreGrad = ctx.createRadialGradient(0, 0, 0, 0, 0, this.size * 0.45 * pulse);
|
||||
coreGrad.addColorStop(0, 'rgba(255, 255, 230, 0.55)');
|
||||
coreGrad.addColorStop(0.35,'rgba(255, 235, 160, 0.25)');
|
||||
coreGrad.addColorStop(0.7, 'rgba(255, 200, 100, 0.08)');
|
||||
coreGrad.addColorStop(1, 'rgba(255, 170, 50, 0)');
|
||||
ctx.fillStyle = coreGrad;
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, this.size * 0.45 * pulse, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
|
||||
|
||||
Asteroid.prototype.drawPlanet = function(ctx) {
|
||||
ctx.save();
|
||||
ctx.translate(this.x, this.y);
|
||||
|
||||
45
js/game.js
45
js/game.js
@ -207,31 +207,26 @@ var Game = (function() {
|
||||
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'
|
||||
);
|
||||
}
|
||||
}
|
||||
if (offlineTime > 1000) {
|
||||
var rateToUse = calculateTheoreticalRate(state, CONFIG);
|
||||
|
||||
if (rateToUse > 0) {
|
||||
var offlineMass = rateToUse * (offlineTime / 1000);
|
||||
state.blackHoleTotalMass += offlineMass;
|
||||
state.totalMassConsumedEver += offlineMass;
|
||||
state.totalMassConsumed += offlineMass;
|
||||
|
||||
state.sM = 0; state.sT = now;
|
||||
state.mM = 0; state.mT = now;
|
||||
state.lM = 0; state.lT = now;
|
||||
|
||||
showOfflineNotification(
|
||||
formatOfflineTime(offlineTime),
|
||||
UI.formatMass(offlineMass),
|
||||
'offline'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
state.tabHiddenAt = null;
|
||||
|
||||
|
||||
@ -46,65 +46,105 @@ function saturateColor(r, g, b, saturationBoost) {
|
||||
return { r: r, g: g, b: b };
|
||||
}
|
||||
|
||||
function calculateTheoreticalRate(state, config, windowMs) {
|
||||
var rate = 0;
|
||||
var ranges = config.ASTEROID_MASS_RANGES;
|
||||
var patterns = config.ASTEROID_SPAWN_PATTERNS;
|
||||
|
||||
var avgLarge = (ranges.large[0] + ranges.large[1]) / 2;
|
||||
var avgMedium = (ranges.medium[0] + ranges.medium[1]) / 2;
|
||||
var avgSmall = (ranges.small[0] + ranges.small[1]) / 2;
|
||||
var mediumCount = (patterns.LARGE_EVERY / patterns.MEDIUM_EVERY) - 1;
|
||||
var smallCount = patterns.LARGE_EVERY - mediumCount - 1;
|
||||
var avgMassPerAsteroid = (avgLarge + mediumCount * avgMedium + smallCount * avgSmall) / patterns.LARGE_EVERY;
|
||||
rate += avgMassPerAsteroid / (state.currentAsteroidSpawnInterval / 1000);
|
||||
|
||||
// Only include object types whose spawn interval fits within the measurement window.
|
||||
// When windowMs is not provided (display/offline use), limit is Infinity so all types included.
|
||||
var limit = windowMs || Infinity;
|
||||
|
||||
if (state.cometUnlocked && state.currentCometSpawnInterval &&
|
||||
state.currentCometSpawnInterval <= limit) {
|
||||
rate += ((ranges.comet[0] + ranges.comet[1]) / 2) / (state.currentCometSpawnInterval / 1000);
|
||||
}
|
||||
if (state.planetUnlocked && state.currentPlanetSpawnInterval &&
|
||||
state.currentPlanetSpawnInterval <= limit) {
|
||||
rate += ((ranges.planet[0] + ranges.planet[1]) / 2) / (state.currentPlanetSpawnInterval / 1000);
|
||||
}
|
||||
if (state.giantUnlocked && state.currentGiantSpawnInterval &&
|
||||
state.currentGiantSpawnInterval <= limit) {
|
||||
rate += ((ranges.giant[0] + ranges.giant[1]) / 2) / (state.currentGiantSpawnInterval / 1000);
|
||||
}
|
||||
if (state.mtypeUnlocked && state.currentMtypeSpawnInterval &&
|
||||
state.currentMtypeSpawnInterval <= limit) {
|
||||
rate += ((ranges.mtype[0] + ranges.mtype[1]) / 2) / (state.currentMtypeSpawnInterval / 1000);
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
function calculateConsumptionRates(state, CONFIG, consumedMassKg) {
|
||||
const now = Date.now();
|
||||
|
||||
// Initialize tracking windows if needed
|
||||
if (!state.sM) state.sM = 0;
|
||||
if (!state.sT) state.sT = now;
|
||||
if (!state.mM) state.mM = 0;
|
||||
if (!state.mT) state.mT = now;
|
||||
if (!state.lM) state.lM = 0;
|
||||
if (!state.lT) state.lT = now;
|
||||
|
||||
// Add mass to all windows
|
||||
// Comparison window for trend coloring
|
||||
if (!state.cM) state.cM = 0;
|
||||
if (!state.cT) state.cT = now;
|
||||
|
||||
state.sM += consumedMassKg;
|
||||
state.mM += consumedMassKg;
|
||||
state.lM += consumedMassKg;
|
||||
|
||||
// Update per-second rate (with trend coloring)
|
||||
state.cM += consumedMassKg;
|
||||
|
||||
// Per-second rate (display only — no longer drives trend)
|
||||
const elapsedShort = now - state.sT;
|
||||
if (elapsedShort >= 1000) {
|
||||
const newRate = state.sM / (elapsedShort / 1000);
|
||||
|
||||
// Compare to previous rate for trend
|
||||
if (state.rateShort === undefined) {
|
||||
state.rateShort = newRate;
|
||||
state.rateShortTrend = 'same';
|
||||
} else {
|
||||
// Determine trend with 5% threshold to avoid jitter
|
||||
if (newRate > state.rateShort * 1.05) {
|
||||
state.rateShort = state.sM / (elapsedShort / 1000);
|
||||
state.sM = 0;
|
||||
state.sT = now;
|
||||
}
|
||||
|
||||
// Trend: actual vs theoretical over 60-second window
|
||||
const elapsedComparison = now - state.cT;
|
||||
if (elapsedComparison >= 1000) {
|
||||
const actualRate = state.cM / (elapsedComparison / 1000);
|
||||
const theoreticalRate = calculateTheoreticalRate(state, CONFIG, elapsedComparison);
|
||||
|
||||
if (theoreticalRate > 0) {
|
||||
const ratio = actualRate / theoreticalRate;
|
||||
if (ratio > 1.05) {
|
||||
state.rateShortTrend = 'up';
|
||||
} else if (newRate < state.rateShort * 0.95) {
|
||||
} else if (ratio < 0.95) {
|
||||
state.rateShortTrend = 'down';
|
||||
} else {
|
||||
state.rateShortTrend = 'same';
|
||||
}
|
||||
state.rateShort = newRate;
|
||||
}
|
||||
|
||||
// Reset short window
|
||||
state.sM = 0;
|
||||
state.sT = now;
|
||||
|
||||
state.cM = 0;
|
||||
state.cT = now;
|
||||
}
|
||||
|
||||
// Calculate hourly average (rolling 1-hour window)
|
||||
|
||||
// Hourly average
|
||||
const elapsedMedium = now - state.mT;
|
||||
if (elapsedMedium > 0) {
|
||||
state.rateMedium = state.mM / (elapsedMedium / 1000);
|
||||
}
|
||||
// Reset hourly window after 1 hour
|
||||
if (elapsedMedium >= CONFIG.RATE_WINDOWS.MEDIUM) {
|
||||
state.mM = 0;
|
||||
state.mT = now;
|
||||
}
|
||||
|
||||
// Calculate daily average (rolling 24-hour window)
|
||||
|
||||
// Daily average
|
||||
const elapsedLong = now - state.lT;
|
||||
if (elapsedLong > 0) {
|
||||
state.rateLong = state.lM / (elapsedLong / 1000);
|
||||
}
|
||||
// Reset daily window after 24 hours
|
||||
if (elapsedLong >= CONFIG.RATE_WINDOWS.LONG) {
|
||||
state.lM = 0;
|
||||
state.lT = now;
|
||||
|
||||
49
js/ui.js
49
js/ui.js
@ -237,38 +237,23 @@ var UI = {
|
||||
},
|
||||
|
||||
updateRateDisplay: function(state, config) {
|
||||
const now = Date.now();
|
||||
const theoretical = calculateTheoreticalRate(state, config);
|
||||
const windowedTheoretical = calculateTheoreticalRate(state, config, 1000);
|
||||
const trend = state.rateShortTrend || 'same';
|
||||
|
||||
// Format per-second rate (always real)
|
||||
const rateShortText = this.formatMass(state.rateShort || 0) + '/s';
|
||||
const rateShortText = this.formatMass(theoretical) + '/s';
|
||||
const rateMediumText = this.formatMass(theoretical * 3600) + '/h';
|
||||
const rateLongText = this.formatMass(theoretical * 86400) + '/d';
|
||||
|
||||
// Check if we have enough data for hourly average
|
||||
const hourlyElapsed = now - (state.mT || now);
|
||||
let rateMediumText;
|
||||
if (hourlyElapsed < 300000) { // Less than 5 minutes of data
|
||||
// Use estimated value from per-second rate
|
||||
rateMediumText = '~' + this.formatMass((state.rateShort || 0) * 3600) + '/h';
|
||||
} else {
|
||||
// Use real average
|
||||
rateMediumText = this.formatMass((state.rateMedium || 0) * 3600) + '/h';
|
||||
}
|
||||
const delta = (state.rateShort || 0) - windowedTheoretical;
|
||||
const sign = delta >= 0 ? '+' : '-';
|
||||
const deltaText = sign + this.formatMass(Math.abs(delta)) + '/s';
|
||||
const deltaClass = trend === 'same' ? 'rate-same' : 'rate-' + trend;
|
||||
|
||||
// Check if we have enough data for daily average
|
||||
const dailyElapsed = now - (state.lT || now);
|
||||
let rateLongText;
|
||||
if (dailyElapsed < 3600000) { // Less than 1 hour of data
|
||||
// Use estimated value from best available rate
|
||||
const baseRate = (hourlyElapsed >= 300000) ? state.rateMedium : state.rateShort;
|
||||
rateLongText = '~' + this.formatMass((baseRate || 0) * 86400) + '/d';
|
||||
} else {
|
||||
// Use real average
|
||||
rateLongText = this.formatMass((state.rateLong || 0) * 86400) + '/d';
|
||||
}
|
||||
|
||||
// Only color the per-second rate based on trend
|
||||
const rateText = `
|
||||
<span class="${'rate-' + (state.rateShortTrend || 'same')}">${rateShortText}</span>
|
||||
</br><span> ${rateMediumText}</span>
|
||||
<span class="rate-same">${rateShortText}</span>
|
||||
<span class="${deltaClass}"> ${deltaText}</span>
|
||||
</br><span> ${rateMediumText}</span>
|
||||
</br><span> ${rateLongText}</span>
|
||||
`;
|
||||
|
||||
@ -427,7 +412,7 @@ var UI = {
|
||||
updatePlanetUpgrade: function(gameState) {
|
||||
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 + '%';
|
||||
var tooltipText = 'Planets are roughly spherical accumulations of rock and metal, with solid surfaces and relatively thin atmospheres. Local examples include Mercury, Earth, and Pluto. 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 ' +
|
||||
@ -444,7 +429,7 @@ var UI = {
|
||||
updateGiantUpgrade: function(gameState) {
|
||||
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 + '%';
|
||||
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>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 ' +
|
||||
@ -461,7 +446,7 @@ var UI = {
|
||||
updateMtypeUpgrade: function(gameState) {
|
||||
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 + '%';
|
||||
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>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 ' +
|
||||
@ -476,7 +461,7 @@ var UI = {
|
||||
updateKtypeUpgrade: function(gameState) {
|
||||
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 + '%';
|
||||
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>Rate: ' + rate + '/2days<br>Bonus: ' + bonusPercent + '%';
|
||||
|
||||
this.elements.ktypeLevel.innerHTML = '<span class="tooltip-trigger">K-Type: Level ' +
|
||||
gameState.ktypeUpgradeLevel +
|
||||
|
||||
Reference in New Issue
Block a user