DeepSeek V4 Pro
DeepSeekApril 2026
What it wrote
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cosmic Forge — Incremental</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg: #0a0a14;
--surface: #12122a;
--surface2: #1a1a3a;
--primary: #7c3aed;
--primary-glow: #a78bfa;
--gold: #f59e0b;
--gold-glow: #fcd34d;
--cyan: #06b6d4;
--cyan-glow: #67e8f9;
--pink: #ec4899;
--pink-glow: #f9a8d4;
--green: #10b981;
--red: #ef4444;
--text: #e2e8f0;
--text-dim: #94a3b8;
--font: 'Segoe UI', system-ui, -apple-system, sans-serif;
--radius: 12px;
--radius-sm: 8px;
}
html, body { height: 100%; overflow: hidden; }
body {
font-family: var(--font);
background: var(--bg);
color: var(--text);
display: flex;
flex-direction: column;
user-select: none;
-webkit-user-select: none;
}
#stars-canvas {
position: fixed;
inset: 0;
z-index: 0;
pointer-events: none;
}
#app {
position: relative;
z-index: 1;
display: flex;
height: 100%;
overflow: hidden;
}
/* ── SIDEBAR ── */
#sidebar {
width: 340px;
min-width: 340px;
background: var(--surface);
border-right: 1px solid rgba(124,58,237,0.2);
display: flex;
flex-direction: column;
overflow-y: auto;
padding: 20px 16px;
gap: 16px;
}
#sidebar::-webkit-scrollbar { width: 4px; }
#sidebar::-webkit-scrollbar-thumb { background: rgba(124,58,237,0.4); border-radius: 4px; }
.resource-panel {
background: var(--surface2);
border-radius: var(--radius);
padding: 16px;
border: 1px solid rgba(124,58,237,0.15);
text-align: center;
}
.resource-label {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 2px;
color: var(--text-dim);
margin-bottom: 6px;
}
.resource-value {
font-size: 32px;
font-weight: 800;
background: linear-gradient(135deg, var(--primary-glow), var(--gold-glow));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.resource-rate {
font-size: 13px;
color: var(--green);
margin-top: 4px;
}
/* ── CLICK BUTTON ── */
.click-area {
display: flex;
justify-content: center;
align-items: center;
padding: 10px 0;
}
#click-btn {
width: 140px;
height: 140px;
border-radius: 50%;
border: 3px solid var(--primary);
background: radial-gradient(circle at 40% 35%, rgba(124,58,237,0.4), rgba(124,58,237,0.05));
cursor: pointer;
position: relative;
transition: transform 0.1s, box-shadow 0.1s;
box-shadow: 0 0 30px rgba(124,58,237,0.3), inset 0 0 30px rgba(124,58,237,0.1);
}
#click-btn:hover {
transform: scale(1.05);
box-shadow: 0 0 50px rgba(124,58,237,0.5), inset 0 0 40px rgba(124,58,237,0.15);
}
#click-btn:active {
transform: scale(0.92);
box-shadow: 0 0 60px rgba(252,211,77,0.6), inset 0 0 50px rgba(252,211,77,0.2);
border-color: var(--gold);
}
#click-btn .icon {
font-size: 48px;
pointer-events: none;
position: relative;
z-index: 1;
}
#click-btn .ring {
position: absolute;
inset: -6px;
border-radius: 50%;
border: 1px solid rgba(124,58,237,0.3);
animation: ring-pulse 2s ease-in-out infinite;
}
#click-btn .ring:nth-child(3) { animation-delay: 0.6s; }
#click-btn .ring:nth-child(4) { animation-delay: 1.2s; }
@keyframes ring-pulse {
0%, 100% { transform: scale(1); opacity: 0.5; }
50% { transform: scale(1.08); opacity: 0; }
}
.click-power {
text-align: center;
font-size: 12px;
color: var(--text-dim);
margin-top: 4px;
}
/* ── MAIN CONTENT ── */
#main {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
#tabs {
display: flex;
border-bottom: 1px solid rgba(124,58,237,0.2);
background: var(--surface);
padding: 0 20px;
gap: 4px;
flex-shrink: 0;
}
.tab-btn {
background: none;
border: none;
color: var(--text-dim);
font-family: var(--font);
font-size: 13px;
font-weight: 600;
padding: 14px 20px;
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.2s;
letter-spacing: 0.5px;
}
.tab-btn:hover { color: var(--text); }
.tab-btn.active {
color: var(--primary-glow);
border-bottom-color: var(--primary);
}
.tab-content {
display: none;
flex: 1;
overflow-y: auto;
padding: 20px;
gap: 12px;
}
.tab-content.active { display: flex; flex-direction: column; }
.tab-content::-webkit-scrollbar { width: 4px; }
.tab-content::-webkit-scrollbar-thumb { background: rgba(124,58,237,0.4); border-radius: 4px; }
/* ── BUILDING CARD ── */
.building-card {
background: var(--surface2);
border: 1px solid rgba(124,58,237,0.1);
border-radius: var(--radius);
padding: 16px 20px;
display: flex;
align-items: center;
gap: 16px;
transition: all 0.2s;
cursor: pointer;
position: relative;
overflow: hidden;
}
.building-card:hover {
border-color: rgba(124,58,237,0.3);
background: var(--surface);
}
.building-card.locked {
opacity: 0.4;
cursor: not-allowed;
filter: grayscale(0.5);
}
.building-card .b-icon {
font-size: 36px;
width: 50px;
text-align: center;
flex-shrink: 0;
}
.building-card .b-info {
flex: 1;
min-width: 0;
}
.building-card .b-name {
font-weight: 700;
font-size: 15px;
}
.building-card .b-desc {
font-size: 12px;
color: var(--text-dim);
margin-top: 2px;
}
.building-card .b-stats {
font-size: 11px;
color: var(--green);
margin-top: 4px;
}
.building-card .b-cost {
background: var(--surface);
border-radius: var(--radius-sm);
padding: 10px 14px;
text-align: center;
flex-shrink: 0;
min-width: 90px;
font-weight: 700;
font-size: 14px;
color: var(--gold);
}
.building-card .b-count {
font-size: 11px;
color: var(--text-dim);
margin-top: 2px;
}
.building-card .b-cost.affordable {
animation: pulse-afford 1.5s ease-in-out infinite;
}
@keyframes pulse-afford {
0%, 100% { box-shadow: 0 0 0 rgba(245,158,11,0); }
50% { box-shadow: 0 0 15px rgba(245,158,11,0.3); }
}
/* ── UPGRADE CARD ── */
.upgrade-card {
background: var(--surface2);
border: 1px solid rgba(6,182,212,0.15);
border-radius: var(--radius);
padding: 14px 20px;
display: flex;
align-items: center;
gap: 14px;
cursor: pointer;
transition: all 0.2s;
}
.upgrade-card:hover {
border-color: rgba(6,182,212,0.4);
background: var(--surface);
}
.upgrade-card.purchased {
opacity: 0.5;
cursor: default;
border-color: rgba(16,185,129,0.3);
}
.upgrade-card .u-icon {
font-size: 28px;
width: 40px;
text-align: center;
flex-shrink: 0;
}
.upgrade-card .u-info { flex: 1; min-width: 0; }
.upgrade-card .u-name { font-weight: 700; font-size: 14px; }
.upgrade-card .u-desc { font-size: 11px; color: var(--text-dim); margin-top: 2px; }
.upgrade-card .u-cost {
font-weight: 700;
color: var(--cyan);
font-size: 13px;
flex-shrink: 0;
}
/* ── PRESTIGE ── */
.prestige-panel {
background: linear-gradient(135deg, rgba(236,72,153,0.1), rgba(124,58,237,0.1));
border: 1px solid rgba(236,72,153,0.3);
border-radius: var(--radius);
padding: 20px;
text-align: center;
}
.prestige-panel h2 {
font-size: 20px;
background: linear-gradient(135deg, var(--pink), var(--primary-glow));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 10px;
}
.prestige-panel .p-desc {
font-size: 12px;
color: var(--text-dim);
margin-bottom: 14px;
line-height: 1.5;
}
.prestige-panel .p-reward {
font-size: 14px;
color: var(--pink-glow);
margin-bottom: 14px;
}
#prestige-btn {
background: linear-gradient(135deg, var(--pink), var(--primary));
border: none;
color: white;
font-family: var(--font);
font-size: 15px;
font-weight: 700;
padding: 12px 32px;
border-radius: 50px;
cursor: pointer;
transition: all 0.2s;
letter-spacing: 1px;
}
#prestige-btn:hover {
transform: scale(1.05);
box-shadow: 0 0 30px rgba(236,72,153,0.4);
}
#prestige-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
/* ── STATS ── */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 10px;
}
.stat-card {
background: var(--surface2);
border-radius: var(--radius-sm);
padding: 12px 16px;
border: 1px solid rgba(124,58,237,0.1);
}
.stat-card .stat-label {
font-size: 10px;
text-transform: uppercase;
letter-spacing: 1px;
color: var(--text-dim);
}
.stat-card .stat-value {
font-size: 18px;
font-weight: 700;
margin-top: 4px;
}
/* ── PARTICLES ── */
.particle {
position: fixed;
pointer-events: none;
z-index: 100;
font-weight: 800;
font-size: 16px;
animation: particle-up 0.8s ease-out forwards;
color: var(--gold);
text-shadow: 0 0 8px var(--gold-glow);
}
@keyframes particle-up {
0% { opacity: 1; transform: translateY(0) scale(1); }
100% { opacity: 0; transform: translateY(-80px) scale(1.4); }
}
/* ── TOAST ── */
.toast {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background: var(--surface2);
border: 1px solid var(--primary);
border-radius: var(--radius);
padding: 12px 24px;
font-weight: 700;
z-index: 200;
animation: toast-in 0.3s ease-out, toast-out 0.3s ease-in 2s forwards;
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
}
@keyframes toast-in { from { opacity: 0; transform: translateX(-50%) translateY(-20px); } }
@keyframes toast-out { to { opacity: 0; transform: translateX(-50%) translateY(-20px); } }
/* ── RESPONSIVE ── */
@media (max-width: 768px) {
#app { flex-direction: column; }
#sidebar {
width: 100%;
min-width: 100%;
max-height: 30%;
flex-shrink: 0;
}
#click-btn { width: 100px; height: 100px; }
#click-btn .icon { font-size: 32px; }
.resource-value { font-size: 24px; }
}
</style>
</head>
<body>
<canvas id="stars-canvas"></canvas>
<div id="app">
<aside id="sidebar">
<div class="resource-panel">
<div class="resource-label">⚡ Stellar Energy</div>
<div class="resource-value" id="energy-display">0</div>
<div class="resource-rate" id="rate-display">+0/s</div>
</div>
<div class="click-area">
<button id="click-btn">
<span class="icon">💫</span>
<span class="ring"></span>
<span class="ring"></span>
<span class="ring"></span>
</button>
</div>
<div class="click-power">Click: +<span id="click-power-display">1</span></div>
<div class="resource-panel" id="prestige-mini" style="display:none;">
<div class="resource-label">🌌 Dark Matter</div>
<div class="resource-value" style="background:linear-gradient(135deg,var(--pink),var(--primary-glow));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;" id="dm-display">0</div>
<div class="resource-rate" style="color:var(--pink);">+<span id="dm-bonus-display">0</span>% prod bonus</div>
</div>
</aside>
<main id="main">
<nav id="tabs">
<button class="tab-btn active" data-tab="generators">🏭 Generators</button>
<button class="tab-btn" data-tab="upgrades">⚡ Upgrades</button>
<button class="tab-btn" data-tab="prestige">🌌 Prestige</button>
<button class="tab-btn" data-tab="stats">📊 Stats</button>
</nav>
<div class="tab-content active" id="tab-generators"></div>
<div class="tab-content" id="tab-upgrades"></div>
<div class="tab-content" id="tab-prestige"></div>
<div class="tab-content" id="tab-stats"></div>
</main>
</div>
<script>
(function() {
const state = {
energy: 0,
totalEnergy: 0,
clickPower: 1,
clickMultiplier: 1,
globalMultiplier: 1,
generators: {},
upgrades: {},
darkMatter: 0,
clicks: 0,
startTime: Date.now(),
lastEnergyGain: 0,
};
const GENERATORS = [
{ id: 'solar', name: 'Solar Array', icon: '☀️', baseCost: 15, costMult: 1.15, production: 0.1, desc: 'Harnesses the nearest star.' },
{ id: 'wind', name: 'Fusion Turbine', icon: '🌀', baseCost: 100, costMult: 1.18, production: 0.5, desc: 'Miniature fusion in a bottle.' },
{ id: 'reactor', name: 'Quantum Reactor', icon: '⚛️', baseCost: 700, costMult: 1.20, production: 3, desc: 'Taps zero-point energy.' },
{ id: 'collider', name: 'Particle Collider', icon: '💥', baseCost: 5000, costMult: 1.22, production: 18, desc: 'Smash atoms, harvest scraps.' },
{ id: 'dyson', name: 'Dyson Swarm', icon: '🪐', baseCost: 40000,costMult: 1.25, production: 120, desc: 'A cloud of solar satellites.' },
{ id: 'singularity',name: 'Singularity Core', icon: '🕳️', baseCost: 350000,costMult:1.28, production: 900, desc: 'Harvests Hawking radiation.' },
{ id: 'universe', name: 'Universe Forge', icon: '🌟', baseCost: 5e6, costMult: 1.30, production: 7500, desc: 'Creates micro-universes for fuel.' },
{ id: 'multiverse', name: 'Multiverse Engine', icon: '🌌', baseCost: 8e7, costMult: 1.32, production: 70000,desc: 'Burns parallel realities.' },
];
const UPGRADES = [
{ id: 'click1', name: 'Enhanced Focus', icon: '🎯', cost: 50, desc: '2x click power', effect: () => state.clickMultiplier *= 2 },
{ id: 'click2', name: 'Quantum Clicks', icon: '👆', cost: 500, desc: '3x click power', effect: () => state.clickMultiplier *= 3 },
{ id: 'click3', name: 'Reality Bending', icon: '🌀', cost: 8000, desc: '5x click power', effect: () => state.clickMultiplier *= 5 },
{ id: 'click4', name: 'Dimensional Tap', icon: '💎', cost: 150000, desc: '10x click power', effect: () => state.clickMultiplier *= 10 },
{ id: 'global1', name: 'Efficiency Matrix', icon: '🔷', cost: 200, desc: '2x all production', effect: () => state.globalMultiplier *= 2 },
{ id: 'global2', name: 'Temporal Accelerator', icon: '⏳', cost: 3000, desc: '3x all production', effect: () => state.globalMultiplier *= 3 },
{ id: 'global3', name: 'Cosmic Alignment', icon: '✨', cost: 50000, desc: '4x all production', effect: () => state.globalMultiplier *= 4 },
{ id: 'solar1', name: 'Solar Optimization', icon: '🔆', cost: 500, desc: '5x Solar Arrays', effect: () => applyGenBoost('solar', 5) },
{ id: 'reactor1', name: 'Quantum Overclock', icon: '⚡', cost: 30000, desc: '5x Quantum Reactors', effect: () => applyGenBoost('reactor', 5) },
{ id: 'dyson1', name: 'Swarm Expansion', icon: '🛰️', cost: 500000, desc: '5x Dyson Swarm', effect: () => applyGenBoost('dyson', 5) },
];
function applyGenBoost(genId, mult) {
const g = GENERATORS.find(g => g.id === genId);
g._boost = (g._boost || 1) * mult;
}
function initState() {
GENERATORS.forEach(g => { state.generators[g.id] = 0; g._boost = 1; });
UPGRADES.forEach(u => { state.upgrades[u.id] = false; });
}
function fmt(n) {
if (n === Infinity) return '∞';
if (n < 1000) return Math.floor(n).toLocaleString();
const suffixes = ['', 'K', 'M', 'B', 'T', 'Qa', 'Qi', 'Sx', 'Sp', 'Oc', 'No', 'Dc'];
let tier = 0;
while (n >= 1000 && tier < suffixes.length - 1) { n /= 1000; tier++; }
return n.toFixed(tier > 0 ? 2 : 0) + ' ' + suffixes[tier];
}
function fmtDecimal(n, decimals) {
if (n >= 1000) return fmt(n);
return n.toFixed(decimals);
}
function calcGeneratorProduction(g) {
return g.production * (g._boost || 1) * state.globalMultiplier;
}
function calcTotalRate() {
let rate = 0;
GENERATORS.forEach(g => {
rate += calcGeneratorProduction(g) * state.generators[g.id];
});
rate *= (1 + state.darkMatter * 0.5);
return rate;
}
function calcClickPower() {
return state.clickPower * state.clickMultiplier * state.globalMultiplier * (1 + state.darkMatter * 0.5);
}
function calcGeneratorCost(g) {
const owned = state.generators[g.id];
return Math.floor(g.baseCost * Math.pow(g.costMult, owned));
}
function canAfford(cost) { return state.energy >= cost; }
function spend(cost) {
if (!canAfford(cost)) return false;
state.energy -= cost;
return true;
}
function buyGenerator(g) {
const cost = calcGeneratorCost(g);
if (!spend(cost)) return false;
state.generators[g.id]++;
return true;
}
function buyUpgrade(u) {
if (state.upgrades[u.id]) return false;
if (!spend(u.cost)) return false;
state.upgrades[u.id] = true;
u.effect();
return true;
}
function calcDarkMatterGain() {
const gain = Math.floor(Math.sqrt(state.totalEnergy / 1e7));
return Math.max(0, gain - state.darkMatter);
}
function prestigeReset() {
const gain = Math.floor(Math.sqrt(state.totalEnergy / 1e7));
if (gain <= state.darkMatter) return;
state.darkMatter = gain;
state.energy = 0;
state.totalEnergy = 0;
state.clickPower = 1;
state.clickMultiplier = 1;
state.globalMultiplier = 1;
state.clicks = 0;
state.startTime = Date.now();
GENERATORS.forEach(g => { state.generators[g.id] = 0; g._boost = 1; });
UPGRADES.forEach(u => { state.upgrades[u.id] = false; });
showToast(`🌌 Reset! +${gain} Dark Matter (${Math.floor(gain * 50)}% bonus)`);
}
function showToast(msg) {
const el = document.createElement('div');
el.className = 'toast';
el.textContent = msg;
document.body.appendChild(el);
setTimeout(() => el.remove(), 2500);
}
function spawnParticle(x, y, text) {
const el = document.createElement('div');
el.className = 'particle';
el.textContent = text;
el.style.left = x + 'px';
el.style.top = y + 'px';
document.body.appendChild(el);
setTimeout(() => el.remove(), 800);
}
function renderGenerators() {
const container = document.getElementById('tab-generators');
container.innerHTML = GENERATORS.map(g => {
const owned = state.generators[g.id];
const cost = calcGeneratorCost(g);
const prod = calcGeneratorProduction(g);
const totalProd = prod * owned;
const affordable = canAfford(cost);
return `
<div class="building-card ${owned === 0 && !affordable ? 'locked' : ''}" data-gen="${g.id}">
<div class="b-icon">${g.icon}</div>
<div class="b-info">
<div class="b-name">${g.name}</div>
<div class="b-desc">${g.desc}</div>
<div class="b-stats">Each: +${fmtDecimal(prod, 2)}/s | Total: +${fmtDecimal(totalProd, 2)}/s</div>
</div>
<div class="b-cost ${affordable ? 'affordable' : ''}">
${fmt(cost)} ⚡
<div class="b-count">Owned: ${fmt(owned)}</div>
</div>
</div>
`;
}).join('');
container.querySelectorAll('.building-card').forEach(card => {
card.addEventListener('click', () => {
const genId = card.dataset.gen;
const g = GENERATORS.find(g => g.id === genId);
if (buyGenerator(g)) renderAll();
});
});
}
function renderUpgrades() {
const container = document.getElementById('tab-upgrades');
container.innerHTML = UPGRADES.map(u => {
const purchased = state.upgrades[u.id];
const affordable = !purchased && canAfford(u.cost);
return `
<div class="upgrade-card ${purchased ? 'purchased' : ''}" data-upgrade="${u.id}">
<div class="u-icon">${u.icon}</div>
<div class="u-info">
<div class="u-name">${u.name}</div>
<div class="u-desc">${u.desc}</div>
</div>
<div class="u-cost">${purchased ? '✅ Owned' : fmt(u.cost) + ' ⚡'}</div>
</div>
`;
}).join('');
container.querySelectorAll('.upgrade-card:not(.purchased)').forEach(card => {
card.addEventListener('click', () => {
const uId = card.dataset.upgrade;
const u = UPGRADES.find(u => u.id === uId);
if (buyUpgrade(u)) {
showToast(`🔓 ${u.name} unlocked!`);
renderAll();
}
});
});
}
function renderPrestige() {
const container = document.getElementById('tab-prestige');
const gain = calcDarkMatterGain();
const canPrestige = gain > 0;
container.innerHTML = `
<div class="prestige-panel">
<h2>🌌 Initiate Big Crunch</h2>
<p class="p-desc">Reset all progress in exchange for Dark Matter.<br>Each Dark Matter grants a permanent +50% production bonus.</p>
<p class="p-reward" id="prestige-gain-text">
${canPrestige
? `You will gain <strong>+${gain}</strong> Dark Matter (total: ${state.darkMatter + gain})`
: `Need at least ${fmt(1e7 * Math.pow(state.darkMatter + 1, 2))} total energy earned to gain more Dark Matter.`}
</p>
<button id="prestige-btn" ${canPrestige ? '' : 'disabled'}>Big Crunch</button>
</div>
`;
const btn = document.getElementById('prestige-btn');
if (btn && canPrestige) {
btn.addEventListener('click', () => {
if (confirm('This will reset ALL progress except Dark Matter. Continue?')) {
prestigeReset();
renderAll();
}
});
}
}
function renderStats() {
const container = document.getElementById('tab-stats');
const elapsed = Math.floor((Date.now() - state.startTime) / 1000);
const hours = Math.floor(elapsed / 3600);
const mins = Math.floor((elapsed % 3600) / 60);
const secs = elapsed % 60;
const timeStr = `${hours}h ${mins}m ${secs}s`;
container.innerHTML = `
<div class="stats-grid">
<div class="stat-card"><div class="stat-label">Total Energy Earned</div><div class="stat-value">${fmt(state.totalEnergy)}</div></div>
<div class="stat-card"><div class="stat-label">Current Energy</div><div class="stat-value">${fmt(state.energy)}</div></div>
<div class="stat-card"><div class="stat-label">Production Rate</div><div class="stat-value" style="color:var(--green)">${fmtDecimal(calcTotalRate(), 2)}/s</div></div>
<div class="stat-card"><div class="stat-label">Click Power</div><div class="stat-value">${fmt(calcClickPower())}</div></div>
<div class="stat-card"><div class="stat-label">Total Clicks</div><div class="stat-value">${fmt(state.clicks)}</div></div>
<div class="stat-card"><div class="stat-label">Dark Matter</div><div class="stat-value" style="color:var(--pink)">${fmt(state.darkMatter)}</div></div>
<div class="stat-card"><div class="stat-label">DM Bonus</div><div class="stat-value" style="color:var(--pink)">${Math.floor(state.darkMatter * 50)}%</div></div>
<div class="stat-card"><div class="stat-label">Time Played</div><div class="stat-value">${timeStr}</div></div>
<div class="stat-card"><div class="stat-label">Global Multiplier</div><div class="stat-value">${fmt(state.globalMultiplier)}x</div></div>
<div class="stat-card"><div class="stat-label">Click Multiplier</div><div class="stat-value">${fmt(state.clickMultiplier)}x</div></div>
${GENERATORS.map(g => `
<div class="stat-card">
<div class="stat-label">${g.icon} ${g.name}</div>
<div class="stat-value">${fmt(state.generators[g.id])} owned</div>
</div>
`).join('')}
</div>
`;
}
function renderHUD() {
document.getElementById('energy-display').textContent = fmt(Math.floor(state.energy));
document.getElementById('rate-display').textContent = `+${fmtDecimal(calcTotalRate(), 2)}/s`;
document.getElementById('click-power-display').textContent = fmt(calcClickPower());
const dmMini = document.getElementById('prestige-mini');
if (state.darkMatter > 0) {
dmMini.style.display = '';
document.getElementById('dm-display').textContent = fmt(state.darkMatter);
document.getElementById('dm-bonus-display').textContent = Math.floor(state.darkMatter * 50);
} else {
dmMini.style.display = 'none';
}
}
function renderAll() {
renderHUD();
renderGenerators();
renderUpgrades();
renderPrestige();
renderStats();
}
function gameTick() {
const rate = calcTotalRate();
const gain = rate / 10;
state.energy += gain;
if (gain > 0) state.totalEnergy += gain;
state.lastEnergyGain = gain;
renderHUD();
document.querySelectorAll('.building-card .b-cost').forEach(el => {
const card = el.closest('.building-card');
if (!card) return;
const g = GENERATORS.find(g => g.id === card.dataset.gen);
if (g && canAfford(calcGeneratorCost(g))) {
el.classList.add('affordable');
} else {
el.classList.remove('affordable');
}
});
document.querySelectorAll('.upgrade-card .u-cost').forEach(el => {
const card = el.closest('.upgrade-card');
if (!card || card.classList.contains('purchased')) return;
const u = UPGRADES.find(u => u.id === card.dataset.upgrade);
if (u && canAfford(u.cost)) {
el.style.color = 'var(--cyan-glow)';
} else {
el.style.color = 'var(--cyan)';
}
});
}
function setupClickButton() {
const btn = document.getElementById('click-btn');
btn.addEventListener('click', (e) => {
const power = calcClickPower();
state.energy += power;
state.totalEnergy += power;
state.clicks++;
spawnParticle(e.clientX, e.clientY, `+${fmt(power)}`);
renderHUD();
btn.style.transform = 'scale(0.92)';
btn.style.boxShadow = '0 0 60px rgba(252,211,77,0.6), inset 0 0 50px rgba(252,211,77,0.2)';
btn.style.borderColor = 'var(--gold)';
setTimeout(() => {
btn.style.transform = '';
btn.style.boxShadow = '';
btn.style.borderColor = '';
}, 100);
});
}
function setupTabs() {
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
btn.classList.add('active');
document.getElementById('tab-' + btn.dataset.tab).classList.add('active');
if (btn.dataset.tab === 'generators') renderGenerators();
if (btn.dataset.tab === 'upgrades') renderUpgrades();
if (btn.dataset.tab === 'prestige') renderPrestige();
if (btn.dataset.tab === 'stats') renderStats();
});
});
}
function setupStarfield() {
const canvas = document.getElementById('stars-canvas');
const ctx = canvas.getContext('2d');
let stars = [];
let resizeTimeout;
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
initStars();
}
function initStars() {
stars = [];
const count = Math.floor((canvas.width * canvas.height) / 2000);
for (let i = 0; i < count; i++) {
stars.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
r: Math.random() * 1.5 + 0.5,
twinkle: Math.random() * Math.PI * 2,
speed: Math.random() * 0.02 + 0.005,
hue: Math.random() < 0.1 ? 260 + Math.random() * 40 : 30 + Math.random() * 20,
});
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
stars.forEach(s => {
s.twinkle += s.speed;
const alpha = 0.3 + Math.sin(s.twinkle) * 0.4 + 0.3;
ctx.fillStyle = `hsla(${s.hue}, 80%, 70%, ${alpha})`;
ctx.beginPath();
ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2);
ctx.fill();
});
requestAnimationFrame(draw);
}
window.addEventListener('resize', () => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(resize, 200);
});
resize();
draw();
}
function saveGame() {
const save = {
energy: state.energy,
totalEnergy: state.totalEnergy,
clickPower: state.clickPower,
clickMultiplier: state.clickMultiplier,
globalMultiplier: state.globalMultiplier,
generators: state.generators,
upgrades: state.upgrades,
darkMatter: state.darkMatter,
clicks: state.clicks,
startTime: state.startTime,
genBoosts: Object.fromEntries(GENERATORS.map(g => [g.id, g._boost])),
};
localStorage.setItem('cosmicforge_save', JSON.stringify(save));
}
function loadGame() {
const raw = localStorage.getItem('cosmicforge_save');
if (!raw) return;
try {
const save = JSON.parse(raw);
state.energy = save.energy || 0;
state.totalEnergy = save.totalEnergy || 0;
state.clickPower = save.clickPower || 1;
state.clickMultiplier = save.clickMultiplier || 1;
state.globalMultiplier = save.globalMultiplier || 1;
state.generators = save.generators || {};
state.upgrades = save.upgrades || {};
state.darkMatter = save.darkMatter || 0;
state.clicks = save.clicks || 0;
state.startTime = save.startTime || Date.now();
if (save.genBoosts) {
Object.entries(save.genBoosts).forEach(([id, boost]) => {
const g = GENERATORS.find(g => g.id === id);
if (g) g._boost = boost;
});
}
} catch (e) { /* ignore corrupt saves */ }
}
function catchUpOffline() {
const elapsed = (Date.now() - state.startTime) / 1000;
if (elapsed < 5) return;
const simulatedRate = calcTotalRate() * 0.5;
const offlineGain = simulatedRate * elapsed;
if (offlineGain > 0) {
state.energy += offlineGain;
state.totalEnergy += offlineGain;
const mins = Math.floor(elapsed / 60);
const hrs = Math.floor(mins / 60);
let timeAgo = mins < 1 ? `${Math.floor(elapsed)}s` : mins < 60 ? `${mins}m` : `${hrs}h ${mins % 60}m`;
showToast(`⏰ Welcome back! Earned ${fmt(Math.floor(offlineGain))} energy while away (${timeAgo})`);
}
}
initState();
loadGame();
setupClickButton();
setupTabs();
setupStarfield();
renderAll();
catchUpOffline();
setInterval(() => {
gameTick();
saveGame();
}, 100);
window.addEventListener('beforeunload', saveGame);
setInterval(() => {
renderStats();
renderPrestige();
}, 5000);
})();
</script>
</body>
</html>