238 lines
6.8 KiB
JavaScript
238 lines
6.8 KiB
JavaScript
// Helper functions for the game
|
|
|
|
// Helper function to increase saturation of RGB color
|
|
function saturateColor(r, g, b, saturationBoost) {
|
|
// Normalize RGB to 0-1
|
|
r /= 255; g /= 255; b /= 255;
|
|
|
|
// Convert to HSL
|
|
var max = Math.max(r, g, b);
|
|
var min = Math.min(r, g, b);
|
|
var h, s, l = (max + min) / 2;
|
|
|
|
if (max === min) {
|
|
h = s = 0; // achromatic
|
|
} else {
|
|
var d = max - min;
|
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
|
|
switch (max) {
|
|
case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break;
|
|
case g: h = ((b - r) / d + 2) / 6; break;
|
|
case b: h = ((r - g) / d + 4) / 6; break;
|
|
}
|
|
}
|
|
|
|
// Boost saturation
|
|
s = Math.min(1, s * saturationBoost);
|
|
|
|
// Convert back to RGB
|
|
function hue2rgb(p, q, t) {
|
|
if (t < 0) t += 1;
|
|
if (t > 1) t -= 1;
|
|
if (t < 1/6) return p + (q - p) * 6 * t;
|
|
if (t < 1/2) return q;
|
|
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
|
|
return p;
|
|
}
|
|
|
|
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
var p = 2 * l - q;
|
|
|
|
r = Math.round(hue2rgb(p, q, h + 1/3) * 255);
|
|
g = Math.round(hue2rgb(p, q, h) * 255);
|
|
b = Math.round(hue2rgb(p, q, h - 1/3) * 255);
|
|
|
|
return { r: r, g: g, b: b };
|
|
}
|
|
|
|
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
|
|
state.sM += consumedMassKg;
|
|
state.mM += consumedMassKg;
|
|
state.lM += consumedMassKg;
|
|
|
|
// Update per-second rate (with trend coloring)
|
|
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.rateShortTrend = 'up';
|
|
} else if (newRate < state.rateShort * 0.95) {
|
|
state.rateShortTrend = 'down';
|
|
} else {
|
|
state.rateShortTrend = 'same';
|
|
}
|
|
state.rateShort = newRate;
|
|
}
|
|
|
|
// Reset short window
|
|
state.sM = 0;
|
|
state.sT = now;
|
|
}
|
|
|
|
// Calculate hourly average (rolling 1-hour window)
|
|
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)
|
|
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;
|
|
}
|
|
}
|
|
|
|
function formatOfflineTime(ms) {
|
|
var seconds = Math.floor(ms / 1000);
|
|
var minutes = Math.floor(seconds / 60);
|
|
var hours = Math.floor(minutes / 60);
|
|
var days = Math.floor(hours / 24);
|
|
|
|
if (days > 0) {
|
|
return days + ' day' + (days > 1 ? 's' : '') + ', ' + (hours % 24) + ' hour' + ((hours % 24) !== 1 ? 's' : '');
|
|
} else if (hours > 0) {
|
|
return hours + ' hour' + (hours > 1 ? 's' : '') + ', ' + (minutes % 60) + ' min';
|
|
} else if (minutes > 0) {
|
|
return minutes + ' minute' + (minutes > 1 ? 's' : '');
|
|
} else {
|
|
return seconds + ' second' + (seconds > 1 ? 's' : '');
|
|
}
|
|
}
|
|
|
|
// Unified notification system
|
|
function showNotification(options) {
|
|
// Default options
|
|
var defaults = {
|
|
message: '',
|
|
type: 'info', // 'unlock', 'offline', 'error', 'success', 'info'
|
|
html: false // if true, message is HTML; if false, it's text
|
|
};
|
|
|
|
// Merge options with defaults
|
|
var config = {};
|
|
for (var key in defaults) {
|
|
config[key] = options[key] !== undefined ? options[key] : defaults[key];
|
|
}
|
|
|
|
// Create notification element
|
|
var notification = document.createElement('div');
|
|
notification.className = 'notification ' + config.type;
|
|
|
|
// Set content
|
|
if (config.html) {
|
|
notification.innerHTML = config.message;
|
|
} else {
|
|
notification.textContent = config.message;
|
|
}
|
|
|
|
// Add to DOM
|
|
document.body.appendChild(notification);
|
|
|
|
// Dismiss on canvas click
|
|
var canvas = document.getElementById('space');
|
|
var dismissHandler = function(e) {
|
|
// Only dismiss if clicking canvas, not the notification
|
|
if (e.target === canvas) {
|
|
notification.remove();
|
|
canvas.removeEventListener('click', dismissHandler);
|
|
}
|
|
};
|
|
|
|
// Small delay to prevent immediate dismissal if notification was triggered by a click
|
|
setTimeout(function() {
|
|
canvas.addEventListener('click', dismissHandler);
|
|
}, 100);
|
|
|
|
return notification;
|
|
}
|
|
|
|
// Helper functions for specific notification types
|
|
function showUnlockNotification(type) {
|
|
var messages = {
|
|
comet: 'Comets Unlocked',
|
|
planet: 'Planets Unlocked',
|
|
giant: 'Gas & Ice Giants Unlocked',
|
|
mtype: 'M-Type Stars Unlocked'
|
|
};
|
|
|
|
showNotification({
|
|
message: messages[type] || 'New content unlocked!',
|
|
type: 'unlock',
|
|
duration: 4000
|
|
});
|
|
}
|
|
|
|
function showOfflineNotification(timeAway, massGained) {
|
|
var message =
|
|
'<div>Inactive for: <strong>' + timeAway + '</strong></div>' +
|
|
'<div>Mass gained: <strong>' + massGained + '</strong></div>';
|
|
|
|
showNotification({
|
|
message: message,
|
|
type: 'offline',
|
|
duration: 5000,
|
|
dismissable: true,
|
|
html: true
|
|
});
|
|
}
|
|
|
|
// Additional helper for errors (useful for debugging/user feedback)
|
|
function showErrorNotification(message) {
|
|
showNotification({
|
|
message: message,
|
|
type: 'error',
|
|
duration: 5000,
|
|
dismissable: true
|
|
});
|
|
}
|
|
|
|
// Additional helper for success messages
|
|
function showSuccessNotification(message) {
|
|
showNotification({
|
|
message: message,
|
|
type: 'success',
|
|
duration: 3000
|
|
});
|
|
}
|
|
|
|
function formatTimeSince(ms) {
|
|
var seconds = Math.floor(ms / 1000);
|
|
var minutes = Math.floor(seconds / 60);
|
|
var hours = Math.floor(minutes / 60);
|
|
var days = Math.floor(hours / 24);
|
|
|
|
if (seconds < 60) return 'Just now';
|
|
if (minutes < 60) return minutes + 'm ago';
|
|
if (hours < 24) return hours + 'h ago';
|
|
return days + 'd ago';
|
|
}
|