Add Statistics Panel feature
- Recreated full game foundation from previous session
- Added Statistics tab with detailed game metrics:
* Total Stardust Earned
* Current Production Rate (✨/sec)
* Total Clicks
* Click Power
* Time Played (auto-formatted)
* Buildings Owned
* Upgrades Purchased
* Achievements Unlocked
* Game Progress bar
- Fixed global production bonus calculation in game.js
- Added responsive CSS for stats panel
Files added: js/stats.js, dev-journal.md, README.md, JOURNAL.md
This commit is contained in:
+44
@@ -0,0 +1,44 @@
|
||||
# Cosmic Garden - Development Journal
|
||||
|
||||
This file tracks all development progress for the Cosmic Garden idle game.
|
||||
|
||||
## Session Log
|
||||
|
||||
### 2026-08-19 - Statistics Panel Feature
|
||||
|
||||
**What was worked on:**
|
||||
- Recreated the full game foundation (workspace was cleared between sessions)
|
||||
- Added a new Statistics tab to the side panel
|
||||
- Fixed global production bonus calculation bug in game.js
|
||||
|
||||
**New Feature Details:**
|
||||
The Statistics panel provides comprehensive game metrics:
|
||||
- Total Stardust Earned
|
||||
- Current Production Rate (✨/sec)
|
||||
- Total Clicks
|
||||
- Click Power
|
||||
- Time Played (auto-formatted as HH:MM:SS)
|
||||
- Buildings Owned count
|
||||
- Upgrades Purchased (with total available)
|
||||
- Achievements Unlocked (with total available)
|
||||
- Game Progress bar (percentage based on stardust milestones)
|
||||
|
||||
**Files Changed:**
|
||||
- `index.html` - Added Stats tab UI
|
||||
- `js/stats.js` - New statistics tracking module
|
||||
- `js/game.js` - Fixed global bonus calculation
|
||||
- `css/game.css` - Added stats panel styling
|
||||
|
||||
**Testing:**
|
||||
- Verified stats update correctly during gameplay
|
||||
- Confirmed time formatting works properly
|
||||
- Checked progress bar calculation
|
||||
|
||||
**Next Session Goals:**
|
||||
- Add particle effects on click
|
||||
- Implement mobile touch optimizations
|
||||
- Consider adding prestige system
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2026-08-19*
|
||||
@@ -0,0 +1,39 @@
|
||||
# Cosmic Garden 🌌🌱
|
||||
|
||||
A space gardening idle game built with vanilla HTML, CSS, and JavaScript.
|
||||
|
||||
## How to Play
|
||||
|
||||
1. Open `index.html` in your browser
|
||||
2. Click the "Collect Stardust" button to earn stardust
|
||||
3. Buy plants to auto-generate stardust
|
||||
4. Purchase upgrades to boost production
|
||||
5. Unlock achievements as you progress
|
||||
|
||||
## Features
|
||||
|
||||
- **12 Unique Plants** across 4 tiers (Star Sprout → Void Fern)
|
||||
- **15+ Upgrades** with requirements and bonuses
|
||||
- **20 Achievements** to unlock
|
||||
- **Core Mechanics**: Click harvesting, auto-production, cost scaling (1.15x per level)
|
||||
- **Save System**: Auto-save every 30s, offline progress (50% efficiency), export/import
|
||||
- **Statistics Panel**: Track your progress with detailed metrics
|
||||
- **UI**: Dark cosmic theme with purple/gold accents, responsive layout, animations
|
||||
|
||||
## Development
|
||||
|
||||
This project is being developed incrementally. Check `dev-journal.md` for the latest updates.
|
||||
|
||||
### Current Version
|
||||
- Statistics panel added (2026-08-19)
|
||||
|
||||
### Planned Features
|
||||
- Visual effects (particle effects on click)
|
||||
- Mobile touch optimizations
|
||||
- Prestige/reset system
|
||||
- Sound effects and music
|
||||
- Save game slot system
|
||||
|
||||
## License
|
||||
|
||||
Private project - owned by lullu
|
||||
+602
@@ -0,0 +1,602 @@
|
||||
/* ============================================
|
||||
COSMIC GARDEN - Main Stylesheet
|
||||
============================================ */
|
||||
|
||||
:root {
|
||||
--bg-dark: #0a0a1a;
|
||||
--bg-panel: #12122a;
|
||||
--bg-card: #1a1a3e;
|
||||
--bg-hover: #252550;
|
||||
--accent-gold: #ffd700;
|
||||
--accent-purple: #9b59b6;
|
||||
--accent-blue: #3498db;
|
||||
--accent-green: #2ecc71;
|
||||
--text-primary: #ffffff;
|
||||
--text-secondary: #a0a0c0;
|
||||
--text-muted: #606080;
|
||||
--border-color: #2a2a5a;
|
||||
--success: #2ecc71;
|
||||
--danger: #e74c3c;
|
||||
--warning: #f39c12;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: var(--bg-dark);
|
||||
color: var(--text-primary);
|
||||
min-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
#game-header {
|
||||
background: linear-gradient(135deg, var(--bg-panel) 0%, var(--bg-card) 100%);
|
||||
padding: 20px 40px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 2px solid var(--border-color);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
#game-header h1 {
|
||||
font-size: 1.8rem;
|
||||
background: linear-gradient(90deg, var(--accent-gold), var(--accent-purple));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
#currency-display {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: var(--accent-gold);
|
||||
}
|
||||
|
||||
#per-second {
|
||||
font-size: 1rem;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* Main Layout */
|
||||
#main-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 280px 1fr 300px;
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Panels */
|
||||
section, aside {
|
||||
background: var(--bg-panel);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
h2, h3 {
|
||||
margin-bottom: 15px;
|
||||
color: var(--accent-gold);
|
||||
}
|
||||
|
||||
/* Click Panel */
|
||||
#click-area {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
#harvest-btn {
|
||||
background: linear-gradient(135deg, var(--accent-purple) 0%, #7d3c98 100%);
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
cursor: pointer;
|
||||
transition: all 0.1s ease;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4px 20px rgba(155, 89, 182, 0.4);
|
||||
}
|
||||
|
||||
#harvest-btn:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 6px 30px rgba(155, 89, 182, 0.6);
|
||||
}
|
||||
|
||||
#harvest-btn:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 0.9rem;
|
||||
margin-top: 5px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
#click-info, #total-clicks {
|
||||
text-align: center;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* Buildings Panel */
|
||||
#buildings-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
max-height: 600px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.building-card {
|
||||
background: var(--bg-card);
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border: 1px solid var(--border-color);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.building-card:hover {
|
||||
background: var(--bg-hover);
|
||||
border-color: var(--accent-purple);
|
||||
}
|
||||
|
||||
.building-card.locked {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.building-info h4 {
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.building-info .building-desc {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.building-info .building-production {
|
||||
font-size: 0.8rem;
|
||||
color: var(--accent-green);
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.building-stats {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.building-cost {
|
||||
color: var(--accent-gold);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.building-count {
|
||||
font-size: 1.5rem;
|
||||
color: var(--accent-purple);
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.buy-btn {
|
||||
background: var(--accent-green);
|
||||
color: var(--text-primary);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.buy-btn:hover:not(:disabled) {
|
||||
background: #27ae60;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.buy-btn:disabled {
|
||||
background: var(--text-muted);
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* Side Panel Tabs */
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
flex: 1;
|
||||
background: var(--bg-card);
|
||||
color: var(--text-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.tab-btn.active {
|
||||
background: var(--accent-purple);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tab-content.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Upgrades */
|
||||
#upgrades-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.upgrade-card {
|
||||
background: var(--bg-card);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--border-color);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.upgrade-card:hover:not(.purchased):not(.locked) {
|
||||
background: var(--bg-hover);
|
||||
border-color: var(--accent-gold);
|
||||
}
|
||||
|
||||
.upgrade-card.purchased {
|
||||
opacity: 0.6;
|
||||
border-color: var(--accent-green);
|
||||
}
|
||||
|
||||
.upgrade-card.locked {
|
||||
opacity: 0.4;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.upgrade-name {
|
||||
font-weight: bold;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.upgrade-desc {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.upgrade-cost {
|
||||
color: var(--accent-gold);
|
||||
font-size: 0.9rem;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
/* Achievements */
|
||||
#achievements-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.achievement {
|
||||
background: var(--bg-card);
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
border: 1px solid var(--border-color);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.achievement.unlocked {
|
||||
border-color: var(--accent-gold);
|
||||
background: linear-gradient(135deg, var(--bg-card) 0%, #2a2a5e 100%);
|
||||
}
|
||||
|
||||
.achievement.locked {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.achievement-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.achievement-name {
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
/* Stats Panel */
|
||||
#stats-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
background: var(--bg-card);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
color: var(--accent-gold);
|
||||
}
|
||||
|
||||
.stat-bar {
|
||||
height: 8px;
|
||||
background: var(--bg-dark);
|
||||
border-radius: 4px;
|
||||
margin-top: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stat-bar-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--accent-purple), var(--accent-gold));
|
||||
border-radius: 4px;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
#game-footer {
|
||||
background: var(--bg-panel);
|
||||
padding: 15px 40px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
border-top: 2px solid var(--border-color);
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
#game-footer button {
|
||||
background: var(--bg-card);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
#game-footer button:hover {
|
||||
background: var(--bg-hover);
|
||||
border-color: var(--accent-purple);
|
||||
}
|
||||
|
||||
#reset-btn:hover {
|
||||
border-color: var(--danger);
|
||||
}
|
||||
|
||||
#last-saved {
|
||||
margin-left: auto;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* Notifications */
|
||||
#notifications {
|
||||
position: fixed;
|
||||
top: 80px;
|
||||
right: 20px;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.notification {
|
||||
background: var(--bg-card);
|
||||
border-radius: 8px;
|
||||
padding: 15px 20px;
|
||||
border-left: 4px solid var(--accent-purple);
|
||||
animation: slideIn 0.3s ease, fadeOut 0.3s ease 2.7s;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.notification.success {
|
||||
border-left-color: var(--accent-green);
|
||||
}
|
||||
|
||||
.notification.warning {
|
||||
border-left-color: var(--warning);
|
||||
}
|
||||
|
||||
.notification.achievement {
|
||||
border-left-color: var(--accent-gold);
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Modal */
|
||||
.modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
.modal.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: var(--bg-panel);
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
max-width: 500px;
|
||||
width: 90%;
|
||||
border: 2px solid var(--accent-purple);
|
||||
}
|
||||
|
||||
.modal-content h3 {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.modal-content textarea {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
background: var(--bg-card);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.modal-buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.modal-buttons button {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#confirm-import {
|
||||
background: var(--accent-green);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
#cancel-import {
|
||||
background: var(--bg-card);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
/* Floating Text Animation */
|
||||
.float-text {
|
||||
position: fixed;
|
||||
pointer-events: none;
|
||||
font-weight: bold;
|
||||
font-size: 1.2rem;
|
||||
color: var(--accent-gold);
|
||||
animation: floatUp 1s ease-out forwards;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
@keyframes floatUp {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translateY(-50px);
|
||||
}
|
||||
}
|
||||
|
||||
/* Scrollbar Styling */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--bg-dark);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--border-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--accent-purple);
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 1200px) {
|
||||
#main-layout {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
#side-panel {
|
||||
grid-column: span 2;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
#main-layout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
#side-panel {
|
||||
grid-column: span 1;
|
||||
}
|
||||
|
||||
#game-header {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
# Dev Journal - Cosmic Garden
|
||||
|
||||
## 2026-08-19 - Statistics Panel Added
|
||||
|
||||
### What was done:
|
||||
- Recreated the full Cosmic Garden game foundation (workspace was cleared between sessions)
|
||||
- Added a new **Statistics tab** to the side panel with detailed game metrics
|
||||
- Fixed global production bonus calculation in `game.js`
|
||||
|
||||
### New Feature: Statistics Panel
|
||||
The stats panel tracks and displays:
|
||||
- Total Stardust Earned
|
||||
- Current Production Rate (✨/sec)
|
||||
- Total Clicks
|
||||
- Click Power
|
||||
- Time Played (formatted as HH:MM:SS)
|
||||
- Buildings Owned
|
||||
- Upgrades Purchased (with total count)
|
||||
- Achievements Unlocked (with total count)
|
||||
- Game Progress bar (percentage based on total stardust milestones)
|
||||
|
||||
### Files Modified/Created:
|
||||
- `index.html` - Added Stats tab button and content area
|
||||
- `js/stats.js` - New file for statistics tracking and rendering
|
||||
- `js/game.js` - Fixed global bonus calculation in `getProductionPerSecond()`
|
||||
- `css/game.css` - Added styles for stats panel elements
|
||||
|
||||
### Next Up:
|
||||
- Visual effects (particle effects on click)
|
||||
- Mobile touch optimizations
|
||||
- Prestige/reset system
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Cosmic Garden</title>
|
||||
<link rel="stylesheet" href="css/game.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="game-container">
|
||||
<!-- Header -->
|
||||
<header id="game-header">
|
||||
<h1>🌌 Cosmic Garden</h1>
|
||||
<div id="currency-display">
|
||||
<span id="stardust-count">0</span> ✨ Stardust
|
||||
</div>
|
||||
<div id="per-second">
|
||||
<span id="stardust-ps">0</span> ✨/sec
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Layout -->
|
||||
<div id="main-layout">
|
||||
<!-- Left Panel: Click Area -->
|
||||
<section id="click-panel">
|
||||
<h2>Harvest</h2>
|
||||
<div id="click-area">
|
||||
<button id="harvest-btn" title="Click to collect stardust!">
|
||||
<span class="btn-icon">✨</span>
|
||||
<span class="btn-text">Collect Stardust</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="click-info">
|
||||
<p>Click power: <span id="click-power">1</span> ✨</p>
|
||||
</div>
|
||||
<div id="total-clicks">
|
||||
Total clicks: <span id="total-click-count">0</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Center Panel: Buildings -->
|
||||
<section id="buildings-panel">
|
||||
<h2>Plants</h2>
|
||||
<div id="buildings-list"></div>
|
||||
</section>
|
||||
|
||||
<!-- Right Panel: Upgrades & Achievements -->
|
||||
<aside id="side-panel">
|
||||
<div class="tabs">
|
||||
<button class="tab-btn active" data-tab="upgrades">Upgrades</button>
|
||||
<button class="tab-btn" data-tab="achievements">Achievements</button>
|
||||
<button class="tab-btn" data-tab="stats">Stats</button>
|
||||
</div>
|
||||
|
||||
<div id="upgrades-tab" class="tab-content active">
|
||||
<h3>Upgrades</h3>
|
||||
<div id="upgrades-list"></div>
|
||||
</div>
|
||||
|
||||
<div id="achievements-tab" class="tab-content">
|
||||
<h3>Achievements (0/20)</h3>
|
||||
<div id="achievements-list"></div>
|
||||
</div>
|
||||
|
||||
<div id="stats-tab" class="tab-content">
|
||||
<h3>Statistics</h3>
|
||||
<div id="stats-content"></div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<!-- Footer: Controls -->
|
||||
<footer id="game-footer">
|
||||
<button id="save-btn">💾 Save</button>
|
||||
<button id="export-btn">📤 Export Save</button>
|
||||
<button id="import-btn">📥 Import Save</button>
|
||||
<button id="reset-btn">🗑️ Reset</button>
|
||||
<span id="last-saved"></span>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<!-- Notification Container -->
|
||||
<div id="notifications"></div>
|
||||
|
||||
<!-- Import Modal -->
|
||||
<div id="import-modal" class="modal hidden">
|
||||
<div class="modal-content">
|
||||
<h3>Import Save</h3>
|
||||
<textarea id="import-data" placeholder="Paste your save data here..."></textarea>
|
||||
<div class="modal-buttons">
|
||||
<button id="confirm-import">Import</button>
|
||||
<button id="cancel-import">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="js/game.js"></script>
|
||||
<script src="js/saveSystem.js"></script>
|
||||
<script src="js/buildings.js"></script>
|
||||
<script src="js/upgrades.js"></script>
|
||||
<script src="js/achievements.js"></script>
|
||||
<script src="js/ui.js"></script>
|
||||
<script src="js/stats.js"></script>
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* Cosmic Garden - Achievement Definitions & Checker
|
||||
*/
|
||||
|
||||
const ACHIEVEMENTS = {
|
||||
// Click achievements
|
||||
firstClick: {
|
||||
id: 'firstClick',
|
||||
name: 'First Contact',
|
||||
description: 'Click for the first time',
|
||||
icon: '👆',
|
||||
condition: (state) => state.totalClicks >= 1
|
||||
},
|
||||
|
||||
clicker: {
|
||||
id: 'clicker',
|
||||
name: 'Clicker',
|
||||
description: 'Click 100 times',
|
||||
icon: '🖱️',
|
||||
condition: (state) => state.totalClicks >= 100
|
||||
},
|
||||
|
||||
spammer: {
|
||||
id: 'spammer',
|
||||
name: 'Spam Finger',
|
||||
description: 'Click 1,000 times',
|
||||
icon: '🔥',
|
||||
condition: (state) => state.totalClicks >= 1000
|
||||
},
|
||||
|
||||
clickMaster: {
|
||||
id: 'clickMaster',
|
||||
name: 'Click Master',
|
||||
description: 'Click 10,000 times',
|
||||
icon: '⚡',
|
||||
condition: (state) => state.totalClicks >= 10000
|
||||
},
|
||||
|
||||
// Stardust achievements
|
||||
dustCollector: {
|
||||
id: 'dustCollector',
|
||||
name: 'Dust Collector',
|
||||
description: 'Earn 1,000 total stardust',
|
||||
icon: '✨',
|
||||
condition: (state) => state.totalStardust >= 1000
|
||||
},
|
||||
|
||||
stardustMagnate: {
|
||||
id: 'stardustMagnate',
|
||||
name: 'Stardust Magnate',
|
||||
description: 'Earn 100,000 total stardust',
|
||||
icon: '💫',
|
||||
condition: (state) => state.totalStardust >= 100000
|
||||
},
|
||||
|
||||
cosmicTycoon: {
|
||||
id: 'cosmicTycoon',
|
||||
name: 'Cosmic Tycoon',
|
||||
description: 'Earn 10,000,000 total stardust',
|
||||
icon: '🌟',
|
||||
condition: (state) => state.totalStardust >= 10000000
|
||||
},
|
||||
|
||||
galaxyBaron: {
|
||||
id: 'galaxyBaron',
|
||||
name: 'Galaxy Baron',
|
||||
description: 'Earn 1,000,000,000 total stardust',
|
||||
icon: '👑',
|
||||
condition: (state) => state.totalStardust >= 1000000000
|
||||
},
|
||||
|
||||
// Building achievements
|
||||
firstPlant: {
|
||||
id: 'firstPlant',
|
||||
name: 'First Plant',
|
||||
description: 'Buy your first building',
|
||||
icon: '🌱',
|
||||
condition: (state) => Object.values(state.buildings).some(c => c >= 1)
|
||||
},
|
||||
|
||||
gardenStart: {
|
||||
id: 'gardenStart',
|
||||
name: 'Garden Starter',
|
||||
description: 'Own 10 total buildings',
|
||||
icon: '🌿',
|
||||
condition: (state) => Object.values(state.buildings).reduce((a, b) => a + b, 0) >= 10
|
||||
},
|
||||
|
||||
gardenCenter: {
|
||||
id: 'gardenCenter',
|
||||
name: 'Garden Center',
|
||||
description: 'Own 50 total buildings',
|
||||
icon: '🏡',
|
||||
condition: (state) => Object.values(state.buildings).reduce((a, b) => a + b, 0) >= 50
|
||||
},
|
||||
|
||||
cosmicGarden: {
|
||||
id: 'cosmicGarden',
|
||||
name: 'Cosmic Garden',
|
||||
description: 'Own 100 total buildings',
|
||||
icon: '🌌',
|
||||
condition: (state) => Object.values(state.buildings).reduce((a, b) => a + b, 0) >= 100
|
||||
},
|
||||
|
||||
// Specific building achievements
|
||||
sproutFarmer: {
|
||||
id: 'sproutFarmer',
|
||||
name: 'Sprout Farmer',
|
||||
description: 'Own 25 Star Sprouts',
|
||||
icon: '🌱',
|
||||
condition: (state) => (state.buildings.starSprout || 0) >= 25
|
||||
},
|
||||
|
||||
bloomGarden: {
|
||||
id: 'bloomGarden',
|
||||
name: 'Bloom Garden',
|
||||
description: 'Own 25 Moon Blooms',
|
||||
icon: '🌸',
|
||||
condition: (state) => (state.buildings.moonBloom || 0) >= 25
|
||||
},
|
||||
|
||||
solarPowered: {
|
||||
id: 'solarPowered',
|
||||
name: 'Solar Powered',
|
||||
description: 'Own 25 Solar Leaves',
|
||||
icon: '☀️',
|
||||
condition: (state) => (state.buildings.solarLeaf || 0) >= 25
|
||||
},
|
||||
|
||||
// Production achievements
|
||||
steadyStream: {
|
||||
id: 'steadyStream',
|
||||
name: 'Steady Stream',
|
||||
description: 'Reach 100 stardust/sec',
|
||||
icon: '💧',
|
||||
condition: (state) => Game.getProductionPerSecond() >= 100
|
||||
},
|
||||
|
||||
rushingRiver: {
|
||||
id: 'rushingRiver',
|
||||
name: 'Rushing River',
|
||||
description: 'Reach 1,000 stardust/sec',
|
||||
icon: '🌊',
|
||||
condition: (state) => Game.getProductionPerSecond() >= 1000
|
||||
},
|
||||
|
||||
cosmicFlood: {
|
||||
id: 'cosmicFlood',
|
||||
name: 'Cosmic Flood',
|
||||
description: 'Reach 100,000 stardust/sec',
|
||||
icon: '🌊',
|
||||
condition: (state) => Game.getProductionPerSecond() >= 100000
|
||||
}
|
||||
};
|
||||
|
||||
const Achievements = {
|
||||
/**
|
||||
* Check all achievements and unlock new ones
|
||||
*/
|
||||
check() {
|
||||
for (const [id, achievement] of Object.entries(ACHIEVEMENTS)) {
|
||||
if (Game.state.achievements.includes(id)) continue;
|
||||
|
||||
if (achievement.condition(Game.state)) {
|
||||
Game.state.achievements.push(id);
|
||||
UI.showNotification(`Achievement: ${achievement.name}!`, 'achievement');
|
||||
console.log(`🏆 Achievement unlocked: ${achievement.name}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get achievement count
|
||||
*/
|
||||
getCount() {
|
||||
return Game.state.achievements.length;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get total achievements
|
||||
*/
|
||||
getTotal() {
|
||||
return Object.keys(ACHIEVEMENTS).length;
|
||||
}
|
||||
};
|
||||
|
||||
window.ACHIEVEMENTS = ACHIEVEMENTS;
|
||||
window.Achievements = Achievements;
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* Cosmic Garden - Building Definitions
|
||||
* All purchasable buildings that generate stardust
|
||||
*/
|
||||
|
||||
const BUILDINGS = {
|
||||
// Tier 1 - Basic plants
|
||||
starSprout: {
|
||||
id: 'starSprout',
|
||||
name: 'Star Sprout',
|
||||
icon: '🌱',
|
||||
description: 'A tiny seed that glows softly',
|
||||
baseCost: 15,
|
||||
baseProduction: 0.1,
|
||||
unlockAt: 0
|
||||
},
|
||||
|
||||
moonBloom: {
|
||||
id: 'moonBloom',
|
||||
name: 'Moon Bloom',
|
||||
icon: '🌸',
|
||||
description: 'Flowers that only open under moonlight',
|
||||
baseCost: 100,
|
||||
baseProduction: 0.5,
|
||||
unlockAt: 50
|
||||
},
|
||||
|
||||
solarLeaf: {
|
||||
id: 'solarLeaf',
|
||||
name: 'Solar Leaf',
|
||||
icon: '☀️',
|
||||
description: 'Absorbs sunlight to create stardust',
|
||||
baseCost: 500,
|
||||
baseProduction: 2,
|
||||
unlockAt: 200
|
||||
},
|
||||
|
||||
// Tier 2 - Advanced plants
|
||||
nebulaVine: {
|
||||
id: 'nebulaVine',
|
||||
name: 'Nebula Vine',
|
||||
icon: '🌿',
|
||||
description: 'Wraps around cosmic dust clouds',
|
||||
baseCost: 2000,
|
||||
baseProduction: 10,
|
||||
unlockAt: 1000
|
||||
},
|
||||
|
||||
cometRose: {
|
||||
id: 'cometRose',
|
||||
name: 'Comet Rose',
|
||||
icon: '🌹',
|
||||
description: 'Blooms for just a second as it flies',
|
||||
baseCost: 10000,
|
||||
baseProduction: 50,
|
||||
unlockAt: 5000
|
||||
},
|
||||
|
||||
asteroidCactus: {
|
||||
id: 'asteroidCactus',
|
||||
name: 'Asteroid Cactus',
|
||||
icon: '🌵',
|
||||
description: 'Survives in the harshest space conditions',
|
||||
baseCost: 50000,
|
||||
baseProduction: 200,
|
||||
unlockAt: 25000
|
||||
},
|
||||
|
||||
// Tier 3 - Rare plants
|
||||
pulsarPalm: {
|
||||
id: 'pulsarPalm',
|
||||
name: 'Pulsar Palm',
|
||||
icon: '🌴',
|
||||
description: 'Rhythmic pulses of pure energy',
|
||||
baseCost: 250000,
|
||||
baseProduction: 1000,
|
||||
unlockAt: 100000
|
||||
},
|
||||
|
||||
blackholeOrchid: {
|
||||
id: 'blackholeOrchid',
|
||||
name: 'Black Hole Orchid',
|
||||
icon: '🪻',
|
||||
description: 'Bends light itself to grow',
|
||||
baseCost: 1000000,
|
||||
baseProduction: 5000,
|
||||
unlockAt: 500000
|
||||
},
|
||||
|
||||
galaxyTree: {
|
||||
id: 'galaxyTree',
|
||||
name: 'Galaxy Tree',
|
||||
icon: '🌳',
|
||||
description: 'Contains an entire galaxy within its branches',
|
||||
baseCost: 5000000,
|
||||
baseProduction: 25000,
|
||||
unlockAt: 2000000
|
||||
},
|
||||
|
||||
// Tier 4 - Legendary plants
|
||||
cosmicLotus: {
|
||||
id: 'cosmicLotus',
|
||||
name: 'Cosmic Lotus',
|
||||
icon: '🪷',
|
||||
description: 'Blooms at the center of creation',
|
||||
baseCost: 25000000,
|
||||
baseProduction: 100000,
|
||||
unlockAt: 10000000
|
||||
},
|
||||
|
||||
supernovaTulip: {
|
||||
id: 'supernovaTulip',
|
||||
name: 'Supernova Tulip',
|
||||
icon: '🌺',
|
||||
description: 'Explodes with stardust every cycle',
|
||||
baseCost: 100000000,
|
||||
baseProduction: 500000,
|
||||
unlockAt: 50000000
|
||||
},
|
||||
|
||||
voidFern: {
|
||||
id: 'voidFern',
|
||||
name: 'Void Fern',
|
||||
icon: '🍃',
|
||||
description: 'Grows in the spaces between dimensions',
|
||||
baseCost: 500000000,
|
||||
baseProduction: 2500000,
|
||||
unlockAt: 200000000
|
||||
}
|
||||
};
|
||||
+349
@@ -0,0 +1,349 @@
|
||||
/**
|
||||
* Cosmic Garden - Main Game Engine
|
||||
* Handles game state, tick loop, and core logic
|
||||
*/
|
||||
|
||||
const Game = {
|
||||
// Core game state
|
||||
state: {
|
||||
stardust: 0,
|
||||
totalStardust: 0,
|
||||
totalClicks: 0,
|
||||
clickPower: 1,
|
||||
startTime: Date.now(),
|
||||
lastSaveTime: Date.now(),
|
||||
buildings: {},
|
||||
upgrades: [],
|
||||
achievements: []
|
||||
},
|
||||
|
||||
// Configuration
|
||||
config: {
|
||||
tickRate: 100, // ms per tick (10 ticks/sec)
|
||||
autoSaveInterval: 30000, // 30 seconds
|
||||
costScaling: 1.15 // Cost multiplier per building level
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the game
|
||||
*/
|
||||
init() {
|
||||
this.loadState();
|
||||
this.setupEventListeners();
|
||||
this.startGameLoop();
|
||||
this.startAutoSave();
|
||||
UI.init();
|
||||
Stats.init();
|
||||
console.log('🌌 Cosmic Garden initialized!');
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup event listeners
|
||||
*/
|
||||
setupEventListeners() {
|
||||
// Harvest button
|
||||
document.getElementById('harvest-btn').addEventListener('click', (e) => {
|
||||
this.harvest(e);
|
||||
});
|
||||
|
||||
// Save buttons
|
||||
document.getElementById('save-btn').addEventListener('click', () => {
|
||||
this.save();
|
||||
UI.showNotification('Game saved!', 'success');
|
||||
});
|
||||
|
||||
document.getElementById('export-btn').addEventListener('click', () => {
|
||||
SaveSystem.exportSave();
|
||||
});
|
||||
|
||||
document.getElementById('import-btn').addEventListener('click', () => {
|
||||
UI.showImportModal();
|
||||
});
|
||||
|
||||
document.getElementById('confirm-import').addEventListener('click', () => {
|
||||
const data = document.getElementById('import-data').value;
|
||||
if (data) {
|
||||
this.loadFromData(data);
|
||||
UI.hideImportModal();
|
||||
UI.showNotification('Save imported!', 'success');
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('cancel-import').addEventListener('click', () => {
|
||||
UI.hideImportModal();
|
||||
});
|
||||
|
||||
document.getElementById('reset-btn').addEventListener('click', () => {
|
||||
if (confirm('Are you sure you want to reset all progress? This cannot be undone!')) {
|
||||
this.reset();
|
||||
}
|
||||
});
|
||||
|
||||
// Tab switching
|
||||
document.querySelectorAll('.tab-btn').forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
const tab = e.target.dataset.tab;
|
||||
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
|
||||
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
|
||||
e.target.classList.add('active');
|
||||
document.getElementById(`${tab}-tab`).classList.add('active');
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Harvest stardust by clicking
|
||||
*/
|
||||
harvest(event) {
|
||||
const amount = this.getClickPower();
|
||||
this.addStardust(amount);
|
||||
this.state.totalClicks++;
|
||||
|
||||
// Visual feedback
|
||||
UI.showFloatingText(event.clientX, event.clientY, `+${this.formatNumber(amount)}`);
|
||||
UI.update();
|
||||
},
|
||||
|
||||
/**
|
||||
* Add stardust to the game
|
||||
*/
|
||||
addStardust(amount) {
|
||||
this.state.stardust += amount;
|
||||
this.state.totalStardust += amount;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get current click power
|
||||
*/
|
||||
getClickPower() {
|
||||
let power = 1;
|
||||
|
||||
// Apply upgrade bonuses
|
||||
for (const upgradeId of this.state.upgrades) {
|
||||
const upgrade = UPGRADES[upgradeId];
|
||||
if (upgrade && upgrade.clickPowerBonus) {
|
||||
power *= upgrade.clickPowerBonus;
|
||||
}
|
||||
}
|
||||
|
||||
return Math.floor(power);
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculate production per second
|
||||
*/
|
||||
getProductionPerSecond() {
|
||||
let total = 0;
|
||||
|
||||
for (const [buildingId, count] of Object.entries(this.state.buildings)) {
|
||||
if (count === 0) continue;
|
||||
|
||||
const building = BUILDINGS[buildingId];
|
||||
if (!building) continue;
|
||||
|
||||
let production = building.baseProduction * count;
|
||||
|
||||
// Apply upgrade bonuses
|
||||
for (const upgradeId of this.state.upgrades) {
|
||||
const upgrade = UPGRADES[upgradeId];
|
||||
if (upgrade && upgrade.buildingBonus && upgrade.buildingBonus[buildingId]) {
|
||||
production *= upgrade.buildingBonus[buildingId];
|
||||
}
|
||||
if (upgrade && upgrade.globalBonus) {
|
||||
production *= upgrade.globalBonus;
|
||||
}
|
||||
}
|
||||
|
||||
total += production;
|
||||
}
|
||||
|
||||
return total;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get building cost
|
||||
*/
|
||||
getBuildingCost(buildingId) {
|
||||
const building = BUILDINGS[buildingId];
|
||||
const count = this.state.buildings[buildingId] || 0;
|
||||
return Math.floor(building.baseCost * Math.pow(this.config.costScaling, count));
|
||||
},
|
||||
|
||||
/**
|
||||
* Buy a building
|
||||
*/
|
||||
buyBuilding(buildingId) {
|
||||
const cost = this.getBuildingCost(buildingId);
|
||||
|
||||
if (this.state.stardust >= cost) {
|
||||
this.state.stardust -= cost;
|
||||
this.state.buildings[buildingId] = (this.state.buildings[buildingId] || 0) + 1;
|
||||
|
||||
UI.showNotification(`Bought ${BUILDINGS[buildingId].name}!`, 'success');
|
||||
UI.update();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Buy an upgrade
|
||||
*/
|
||||
buyUpgrade(upgradeId) {
|
||||
const upgrade = UPGRADES[upgradeId];
|
||||
if (!upgrade || this.state.upgrades.includes(upgradeId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.state.stardust >= upgrade.cost) {
|
||||
// Check requirements
|
||||
if (upgrade.requirements) {
|
||||
for (const [reqId, reqCount] of Object.entries(upgrade.requirements)) {
|
||||
const currentCount = this.state.buildings[reqId] || 0;
|
||||
if (currentCount < reqCount) {
|
||||
UI.showNotification('Requirements not met!', 'warning');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.state.stardust -= upgrade.cost;
|
||||
this.state.upgrades.push(upgradeId);
|
||||
|
||||
UI.showNotification(`Unlocked: ${upgrade.name}!`, 'success');
|
||||
UI.update();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Game tick - called every config.tickRate ms
|
||||
*/
|
||||
tick() {
|
||||
const production = this.getProductionPerSecond();
|
||||
const tickSeconds = this.config.tickRate / 1000;
|
||||
|
||||
if (production > 0) {
|
||||
this.addStardust(production * tickSeconds);
|
||||
}
|
||||
|
||||
UI.update();
|
||||
Achievements.check();
|
||||
},
|
||||
|
||||
/**
|
||||
* Start the game loop
|
||||
*/
|
||||
startGameLoop() {
|
||||
setInterval(() => this.tick(), this.config.tickRate);
|
||||
},
|
||||
|
||||
/**
|
||||
* Start auto-save
|
||||
*/
|
||||
startAutoSave() {
|
||||
setInterval(() => {
|
||||
this.save();
|
||||
document.getElementById('last-saved').textContent =
|
||||
`Last saved: ${new Date().toLocaleTimeString()}`;
|
||||
}, this.config.autoSaveInterval);
|
||||
},
|
||||
|
||||
/**
|
||||
* Save game state
|
||||
*/
|
||||
save() {
|
||||
this.state.lastSaveTime = Date.now();
|
||||
localStorage.setItem('cosmicGardenSave', JSON.stringify(this.state));
|
||||
},
|
||||
|
||||
/**
|
||||
* Load game state
|
||||
*/
|
||||
loadState() {
|
||||
const saved = localStorage.getItem('cosmicGardenSave');
|
||||
if (saved) {
|
||||
try {
|
||||
const parsed = JSON.parse(saved);
|
||||
this.state = { ...this.state, ...parsed };
|
||||
|
||||
// Calculate offline progress
|
||||
const offlineTime = (Date.now() - this.state.lastSaveTime) / 1000;
|
||||
if (offlineTime > 60) {
|
||||
const production = this.getProductionPerSecond();
|
||||
const offlineEarnings = production * offlineTime * 0.5; // 50% offline efficiency
|
||||
if (offlineEarnings > 0) {
|
||||
this.addStardust(offlineEarnings);
|
||||
UI.showNotification(
|
||||
`Welcome back! Earned ${this.formatNumber(offlineEarnings)} ✨ while away`,
|
||||
'success'
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to load save:', e);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Load from imported data
|
||||
*/
|
||||
loadFromData(data) {
|
||||
try {
|
||||
const parsed = JSON.parse(atob(data));
|
||||
this.state = { ...this.state, ...parsed };
|
||||
UI.update();
|
||||
} catch (e) {
|
||||
UI.showNotification('Invalid save data!', 'warning');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Export save as base64
|
||||
*/
|
||||
exportSave() {
|
||||
this.save();
|
||||
const data = btoa(JSON.stringify(this.state));
|
||||
navigator.clipboard.writeText(data).then(() => {
|
||||
UI.showNotification('Save copied to clipboard!', 'success');
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset game
|
||||
*/
|
||||
reset() {
|
||||
localStorage.removeItem('cosmicGardenSave');
|
||||
this.state = {
|
||||
stardust: 0,
|
||||
totalStardust: 0,
|
||||
totalClicks: 0,
|
||||
clickPower: 1,
|
||||
startTime: Date.now(),
|
||||
lastSaveTime: Date.now(),
|
||||
buildings: {},
|
||||
upgrades: [],
|
||||
achievements: []
|
||||
};
|
||||
UI.update();
|
||||
UI.showNotification('Game reset!', 'warning');
|
||||
},
|
||||
|
||||
/**
|
||||
* Format numbers with commas
|
||||
*/
|
||||
formatNumber(num) {
|
||||
if (num >= 1e12) return (num / 1e12).toFixed(2) + 'T';
|
||||
if (num >= 1e9) return (num / 1e9).toFixed(2) + 'B';
|
||||
if (num >= 1e6) return (num / 1e6).toFixed(2) + 'M';
|
||||
if (num >= 1e3) return (num / 1e3).toFixed(2) + 'K';
|
||||
return Math.floor(num).toLocaleString();
|
||||
}
|
||||
};
|
||||
|
||||
// Make Game globally accessible
|
||||
window.Game = Game;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Cosmic Garden - Main Entry Point
|
||||
*/
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
Game.init();
|
||||
|
||||
// Show last save time
|
||||
const lastSave = SaveSystem.getLastSaveTime();
|
||||
if (lastSave) {
|
||||
document.getElementById('last-saved').textContent =
|
||||
`Last saved: ${lastSave.toLocaleTimeString()}`;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Cosmic Garden - Save System
|
||||
* Handles localStorage persistence and export/import
|
||||
*/
|
||||
|
||||
const SaveSystem = {
|
||||
SAVE_KEY: 'cosmicGardenSave',
|
||||
|
||||
/**
|
||||
* Save game to localStorage
|
||||
*/
|
||||
save() {
|
||||
const data = JSON.stringify(Game.state);
|
||||
localStorage.setItem(this.SAVE_KEY, data);
|
||||
console.log('💾 Game saved');
|
||||
},
|
||||
|
||||
/**
|
||||
* Load game from localStorage
|
||||
*/
|
||||
load() {
|
||||
const data = localStorage.getItem(this.SAVE_KEY);
|
||||
if (data) {
|
||||
try {
|
||||
return JSON.parse(data);
|
||||
} catch (e) {
|
||||
console.error('Failed to parse save:', e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Export save as base64 string
|
||||
*/
|
||||
exportSave() {
|
||||
const data = btoa(JSON.stringify(Game.state));
|
||||
navigator.clipboard.writeText(data).then(() => {
|
||||
UI.showNotification('Save copied to clipboard!', 'success');
|
||||
}).catch(() => {
|
||||
// Fallback for older browsers
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = data;
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(textarea);
|
||||
UI.showNotification('Save copied to clipboard!', 'success');
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Import save from base64 string
|
||||
*/
|
||||
importSave(data) {
|
||||
try {
|
||||
const parsed = JSON.parse(atob(data));
|
||||
|
||||
// Validate save data
|
||||
if (!parsed.stardust || !parsed.buildings) {
|
||||
throw new Error('Invalid save format');
|
||||
}
|
||||
|
||||
// Merge with existing state
|
||||
Game.state = { ...Game.state, ...parsed };
|
||||
Game.state.lastSaveTime = Date.now();
|
||||
|
||||
UI.update();
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error('Failed to import save:', e);
|
||||
UI.showNotification('Invalid save data!', 'warning');
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear save data
|
||||
*/
|
||||
clear() {
|
||||
localStorage.removeItem(this.SAVE_KEY);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get last save time
|
||||
*/
|
||||
getLastSaveTime() {
|
||||
const data = this.load();
|
||||
if (data && data.lastSaveTime) {
|
||||
return new Date(data.lastSaveTime);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if auto-save is available
|
||||
*/
|
||||
hasAutoSave() {
|
||||
return !!localStorage.getItem(this.SAVE_KEY);
|
||||
}
|
||||
};
|
||||
|
||||
window.SaveSystem = SaveSystem;
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* Cosmic Garden - Statistics Panel
|
||||
* Tracks and displays game statistics
|
||||
*/
|
||||
|
||||
const Stats = {
|
||||
/**
|
||||
* Initialize the stats panel
|
||||
*/
|
||||
init() {
|
||||
this.render();
|
||||
},
|
||||
|
||||
/**
|
||||
* Render the stats panel
|
||||
*/
|
||||
render() {
|
||||
const container = document.getElementById('stats-content');
|
||||
if (!container) return;
|
||||
|
||||
container.innerHTML = `
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">Total Stardust Earned</div>
|
||||
<div class="stat-value" id="stat-total-stardust">0 ✨</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">Current Production</div>
|
||||
<div class="stat-value" id="stat-production">0 ✨/sec</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">Total Clicks</div>
|
||||
<div class="stat-value" id="stat-clicks">0</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">Click Power</div>
|
||||
<div class="stat-value" id="stat-click-power">1 ✨</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">Time Played</div>
|
||||
<div class="stat-value" id="stat-time-played">0:00:00</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">Buildings Owned</div>
|
||||
<div class="stat-value" id="stat-buildings">0</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">Upgrades Purchased</div>
|
||||
<div class="stat-value" id="stat-upgrades">0/${Object.keys(UPGRADES).length}</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">Achievements Unlocked</div>
|
||||
<div class="stat-value" id="stat-achievements">0/${Object.keys(ACHIEVEMENTS).length}</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">Game Progress</div>
|
||||
<div class="stat-bar">
|
||||
<div class="stat-bar-fill" id="stat-progress-bar" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update stats display
|
||||
*/
|
||||
update() {
|
||||
// Total stardust
|
||||
const totalStardustEl = document.getElementById('stat-total-stardust');
|
||||
if (totalStardustEl) {
|
||||
totalStardustEl.textContent = `${Game.formatNumber(Game.state.totalStardust)} ✨`;
|
||||
}
|
||||
|
||||
// Current production
|
||||
const productionEl = document.getElementById('stat-production');
|
||||
if (productionEl) {
|
||||
productionEl.textContent = `${Game.formatNumber(Game.getProductionPerSecond())} ✨/sec`;
|
||||
}
|
||||
|
||||
// Total clicks
|
||||
const clicksEl = document.getElementById('stat-clicks');
|
||||
if (clicksEl) {
|
||||
clicksEl.textContent = Game.formatNumber(Game.state.totalClicks);
|
||||
}
|
||||
|
||||
// Click power
|
||||
const clickPowerEl = document.getElementById('stat-click-power');
|
||||
if (clickPowerEl) {
|
||||
clickPowerEl.textContent = `${Game.formatNumber(Game.getClickPower())} ✨`;
|
||||
}
|
||||
|
||||
// Time played
|
||||
const timePlayedEl = document.getElementById('stat-time-played');
|
||||
if (timePlayedEl) {
|
||||
const elapsed = Math.floor((Date.now() - Game.state.startTime) / 1000);
|
||||
timePlayedEl.textContent = this.formatTime(elapsed);
|
||||
}
|
||||
|
||||
// Buildings owned
|
||||
const buildingsEl = document.getElementById('stat-buildings');
|
||||
if (buildingsEl) {
|
||||
const total = Object.values(Game.state.buildings).reduce((a, b) => a + b, 0);
|
||||
buildingsEl.textContent = total;
|
||||
}
|
||||
|
||||
// Upgrades purchased
|
||||
const upgradesEl = document.getElementById('stat-upgrades');
|
||||
if (upgradesEl) {
|
||||
upgradesEl.textContent = `${Game.state.upgrades.length}/${Object.keys(UPGRADES).length}`;
|
||||
}
|
||||
|
||||
// Achievements unlocked
|
||||
const achievementsEl = document.getElementById('stat-achievements');
|
||||
if (achievementsEl) {
|
||||
achievementsEl.textContent = `${Achievements.getCount()}/${Achievements.getTotal()}`;
|
||||
}
|
||||
|
||||
// Game progress (based on total stardust milestones)
|
||||
const progressBarEl = document.getElementById('stat-progress-bar');
|
||||
if (progressBarEl) {
|
||||
const progress = this.calculateProgress();
|
||||
progressBarEl.style.width = `${progress}%`;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Format elapsed time as HH:MM:SS
|
||||
*/
|
||||
formatTime(seconds) {
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
const secs = seconds % 60;
|
||||
|
||||
if (hours > 0) {
|
||||
return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
||||
}
|
||||
return `${minutes}:${secs.toString().padStart(2, '0')}`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculate game progress percentage
|
||||
* Based on total stardust earned vs all building costs combined
|
||||
*/
|
||||
calculateProgress() {
|
||||
let totalCost = 0;
|
||||
for (const building of Object.values(BUILDINGS)) {
|
||||
// Approximate cost to buy one of each building
|
||||
totalCost += building.baseCost;
|
||||
}
|
||||
|
||||
// Add upgrade costs
|
||||
for (const upgrade of Object.values(UPGRADES)) {
|
||||
totalCost += upgrade.cost;
|
||||
}
|
||||
|
||||
const progress = Math.min(100, (Game.state.totalStardust / totalCost) * 100);
|
||||
return Math.round(progress);
|
||||
}
|
||||
};
|
||||
|
||||
window.Stats = Stats;
|
||||
@@ -0,0 +1,256 @@
|
||||
/**
|
||||
* Cosmic Garden - UI Manager
|
||||
* Handles all rendering and user interface updates
|
||||
*/
|
||||
|
||||
const UI = {
|
||||
/**
|
||||
* Initialize the UI
|
||||
*/
|
||||
init() {
|
||||
this.renderBuildings();
|
||||
this.renderUpgrades();
|
||||
this.renderAchievements();
|
||||
this.update();
|
||||
},
|
||||
|
||||
/**
|
||||
* Update all UI elements
|
||||
*/
|
||||
update() {
|
||||
// Header
|
||||
document.getElementById('stardust-count').textContent = Game.formatNumber(Game.state.stardust);
|
||||
document.getElementById('stardust-ps').textContent = Game.formatNumber(Game.getProductionPerSecond());
|
||||
document.getElementById('click-power').textContent = Game.formatNumber(Game.getClickPower());
|
||||
document.getElementById('total-click-count').textContent = Game.formatNumber(Game.state.totalClicks);
|
||||
|
||||
// Buildings
|
||||
this.updateBuildings();
|
||||
|
||||
// Upgrades
|
||||
this.updateUpgrades();
|
||||
|
||||
// Achievements
|
||||
this.updateAchievements();
|
||||
|
||||
// Stats
|
||||
Stats.update();
|
||||
},
|
||||
|
||||
/**
|
||||
* Render building cards
|
||||
*/
|
||||
renderBuildings() {
|
||||
const container = document.getElementById('buildings-list');
|
||||
container.innerHTML = '';
|
||||
|
||||
for (const [id, building] of Object.entries(BUILDINGS)) {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'building-card';
|
||||
card.id = `building-${id}`;
|
||||
|
||||
const count = Game.state.buildings[id] || 0;
|
||||
const cost = Game.getBuildingCost(id);
|
||||
const canAfford = Game.state.stardust >= cost;
|
||||
const locked = Game.state.totalStardust < building.unlockAt && count === 0;
|
||||
|
||||
card.classList.toggle('locked', locked);
|
||||
|
||||
card.innerHTML = `
|
||||
<div class="building-info">
|
||||
<h4>${building.icon} ${building.name}</h4>
|
||||
<div class="building-desc">${building.description}</div>
|
||||
<div class="building-production">+${Game.formatNumber(building.baseProduction)}/s each</div>
|
||||
</div>
|
||||
<div class="building-stats">
|
||||
<div class="building-cost">${Game.formatNumber(cost)} ✨</div>
|
||||
<div class="building-count">${count}</div>
|
||||
<button class="buy-btn" ${!canAfford || locked ? 'disabled' : ''}>Buy</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (!locked) {
|
||||
card.querySelector('.buy-btn').addEventListener('click', () => {
|
||||
Game.buyBuilding(id);
|
||||
});
|
||||
}
|
||||
|
||||
container.appendChild(card);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update building states
|
||||
*/
|
||||
updateBuildings() {
|
||||
for (const [id, building] of Object.entries(BUILDINGS)) {
|
||||
const card = document.getElementById(`building-${id}`);
|
||||
if (!card) continue;
|
||||
|
||||
const count = Game.state.buildings[id] || 0;
|
||||
const cost = Game.getBuildingCost(id);
|
||||
const canAfford = Game.state.stardust >= cost;
|
||||
const locked = Game.state.totalStardust < building.unlockAt && count === 0;
|
||||
|
||||
card.querySelector('.building-cost').textContent = `${Game.formatNumber(cost)} ✨`;
|
||||
card.querySelector('.building-count').textContent = count;
|
||||
|
||||
const btn = card.querySelector('.buy-btn');
|
||||
if (btn) {
|
||||
btn.disabled = !canAfford || locked;
|
||||
}
|
||||
|
||||
card.classList.toggle('locked', locked);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Render upgrade cards
|
||||
*/
|
||||
renderUpgrades() {
|
||||
const container = document.getElementById('upgrades-list');
|
||||
container.innerHTML = '';
|
||||
|
||||
for (const [id, upgrade] of Object.entries(UPGRADES)) {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'upgrade-card';
|
||||
card.id = `upgrade-${id}`;
|
||||
|
||||
const purchased = Game.state.upgrades.includes(id);
|
||||
const canAfford = Game.state.stardust >= upgrade.cost;
|
||||
|
||||
// Check requirements
|
||||
let requirementsMet = true;
|
||||
let reqText = '';
|
||||
if (upgrade.requirements) {
|
||||
const reqParts = [];
|
||||
for (const [reqId, reqCount] of Object.entries(upgrade.requirements)) {
|
||||
const currentCount = Game.state.buildings[reqId] || 0;
|
||||
const building = BUILDINGS[reqId];
|
||||
if (currentCount < reqCount) requirementsMet = false;
|
||||
reqParts.push(`${building?.name || reqId}: ${currentCount}/${reqCount}`);
|
||||
}
|
||||
reqText = `<div class="upgrade-desc">Requires: ${reqParts.join(', ')}</div>`;
|
||||
}
|
||||
|
||||
card.classList.toggle('purchased', purchased);
|
||||
card.classList.toggle('locked', !requirementsMet && !purchased);
|
||||
|
||||
card.innerHTML = `
|
||||
<div class="upgrade-name">${upgrade.icon} ${upgrade.name}</div>
|
||||
<div class="upgrade-desc">${upgrade.description}</div>
|
||||
${reqText}
|
||||
<div class="upgrade-cost">${purchased ? '✓ Owned' : `${Game.formatNumber(upgrade.cost)} ✨`}</div>
|
||||
`;
|
||||
|
||||
if (!purchased && requirementsMet) {
|
||||
card.addEventListener('click', () => {
|
||||
Game.buyUpgrade(id);
|
||||
});
|
||||
}
|
||||
|
||||
container.appendChild(card);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update upgrade states
|
||||
*/
|
||||
updateUpgrades() {
|
||||
for (const [id, upgrade] of Object.entries(UPGRADES)) {
|
||||
const card = document.getElementById(`upgrade-${id}`);
|
||||
if (!card) continue;
|
||||
|
||||
const purchased = Game.state.upgrades.includes(id);
|
||||
const costEl = card.querySelector('.upgrade-cost');
|
||||
|
||||
if (costEl && !purchased) {
|
||||
const canAfford = Game.state.stardust >= upgrade.cost;
|
||||
costEl.textContent = canAfford ? `${Game.formatNumber(upgrade.cost)} ✨` : `❌ ${Game.formatNumber(upgrade.cost)} ✨`;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Render achievements
|
||||
*/
|
||||
renderAchievements() {
|
||||
const container = document.getElementById('achievements-list');
|
||||
container.innerHTML = '';
|
||||
|
||||
for (const [id, achievement] of Object.entries(ACHIEVEMENTS)) {
|
||||
const el = document.createElement('div');
|
||||
el.className = 'achievement';
|
||||
|
||||
const unlocked = Game.state.achievements.includes(id);
|
||||
el.classList.toggle('unlocked', unlocked);
|
||||
el.classList.toggle('locked', !unlocked);
|
||||
|
||||
el.innerHTML = `
|
||||
<div class="achievement-icon">${unlocked ? achievement.icon : '🔒'}</div>
|
||||
<div class="achievement-name">${unlocked ? achievement.name : '???'}</div>
|
||||
`;
|
||||
|
||||
if (unlocked) {
|
||||
el.title = `${achievement.name}: ${achievement.description}`;
|
||||
}
|
||||
|
||||
container.appendChild(el);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update achievements display
|
||||
*/
|
||||
updateAchievements() {
|
||||
// Only re-render when needed (on new unlocks)
|
||||
const countEl = document.querySelector('#achievements-tab h3');
|
||||
if (countEl) {
|
||||
countEl.textContent = `Achievements (${Achievements.getCount()}/${Achievements.getTotal()})`;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show floating text animation
|
||||
*/
|
||||
showFloatingText(x, y, text) {
|
||||
const el = document.createElement('div');
|
||||
el.className = 'float-text';
|
||||
el.textContent = text;
|
||||
el.style.left = `${x}px`;
|
||||
el.style.top = `${y - 20}px`;
|
||||
document.body.appendChild(el);
|
||||
|
||||
setTimeout(() => el.remove(), 1000);
|
||||
},
|
||||
|
||||
/**
|
||||
* Show notification
|
||||
*/
|
||||
showNotification(message, type = 'info') {
|
||||
const container = document.getElementById('notifications');
|
||||
const el = document.createElement('div');
|
||||
el.className = `notification ${type}`;
|
||||
el.textContent = message;
|
||||
container.appendChild(el);
|
||||
|
||||
setTimeout(() => el.remove(), 3000);
|
||||
},
|
||||
|
||||
/**
|
||||
* Show import modal
|
||||
*/
|
||||
showImportModal() {
|
||||
document.getElementById('import-modal').classList.remove('hidden');
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide import modal
|
||||
*/
|
||||
hideImportModal() {
|
||||
document.getElementById('import-modal').classList.add('hidden');
|
||||
document.getElementById('import-data').value = '';
|
||||
}
|
||||
};
|
||||
|
||||
window.UI = UI;
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
/**
|
||||
* Cosmic Garden - Upgrade Definitions
|
||||
* One-time purchases that boost production
|
||||
*/
|
||||
|
||||
const UPGRADES = {
|
||||
// Click upgrades
|
||||
ironFingers: {
|
||||
id: 'ironFingers',
|
||||
name: 'Iron Fingers',
|
||||
description: 'Click power x2',
|
||||
icon: '👆',
|
||||
cost: 100,
|
||||
clickPowerBonus: 2,
|
||||
requirements: {}
|
||||
},
|
||||
|
||||
cosmicTouch: {
|
||||
id: 'cosmicTouch',
|
||||
name: 'Cosmic Touch',
|
||||
description: 'Click power x3',
|
||||
icon: '✨',
|
||||
cost: 1000,
|
||||
clickPowerBonus: 3,
|
||||
requirements: { starSprout: 10 }
|
||||
},
|
||||
|
||||
stellarHands: {
|
||||
id: 'stellarHands',
|
||||
name: 'Stellar Hands',
|
||||
description: 'Click power x5',
|
||||
icon: '🌟',
|
||||
cost: 10000,
|
||||
clickPowerBonus: 5,
|
||||
requirements: { moonBloom: 10 }
|
||||
},
|
||||
|
||||
// Building upgrades - Star Sprout
|
||||
sproutBoost1: {
|
||||
id: 'sproutBoost1',
|
||||
name: 'Sprout Nutrition',
|
||||
description: 'Star Sprouts produce x2',
|
||||
icon: '🌱',
|
||||
cost: 500,
|
||||
buildingBonus: { starSprout: 2 },
|
||||
requirements: { starSprout: 1 }
|
||||
},
|
||||
|
||||
sproutBoost2: {
|
||||
id: 'sproutBoost2',
|
||||
name: 'Star Fertilizer',
|
||||
description: 'Star Sprouts produce x3',
|
||||
icon: '⭐',
|
||||
cost: 5000,
|
||||
buildingBonus: { starSprout: 3 },
|
||||
requirements: { starSprout: 25 }
|
||||
},
|
||||
|
||||
// Building upgrades - Moon Bloom
|
||||
bloomBoost1: {
|
||||
id: 'bloomBoost1',
|
||||
name: 'Lunar Energy',
|
||||
description: 'Moon Blooms produce x2',
|
||||
icon: '🌙',
|
||||
cost: 2000,
|
||||
buildingBonus: { moonBloom: 2 },
|
||||
requirements: { moonBloom: 1 }
|
||||
},
|
||||
|
||||
bloomBoost2: {
|
||||
id: 'bloomBoost2',
|
||||
name: 'Full Moon Ritual',
|
||||
description: 'Moon Blooms produce x3',
|
||||
icon: '🌕',
|
||||
cost: 20000,
|
||||
buildingBonus: { moonBloom: 3 },
|
||||
requirements: { moonBloom: 25 }
|
||||
},
|
||||
|
||||
// Building upgrades - Solar Leaf
|
||||
leafBoost1: {
|
||||
id: 'leafBoost1',
|
||||
name: 'Photosynthesis Plus',
|
||||
description: 'Solar Leaves produce x2',
|
||||
icon: '☀️',
|
||||
cost: 10000,
|
||||
buildingBonus: { solarLeaf: 2 },
|
||||
requirements: { solarLeaf: 1 }
|
||||
},
|
||||
|
||||
leafBoost2: {
|
||||
id: 'leafBoost2',
|
||||
name: 'Solar Flare',
|
||||
description: 'Solar Leaves produce x3',
|
||||
icon: '💥',
|
||||
cost: 100000,
|
||||
buildingBonus: { solarLeaf: 3 },
|
||||
requirements: { solarLeaf: 25 }
|
||||
},
|
||||
|
||||
// Building upgrades - Nebula Vine
|
||||
vineBoost1: {
|
||||
id: 'vineBoost1',
|
||||
name: 'Cosmic Compost',
|
||||
description: 'Nebula Vines produce x2',
|
||||
icon: '🌿',
|
||||
cost: 50000,
|
||||
buildingBonus: { nebulaVine: 2 },
|
||||
requirements: { nebulaVine: 1 }
|
||||
},
|
||||
|
||||
vineBoost2: {
|
||||
id: 'vineBoost2',
|
||||
name: 'Dust Storm',
|
||||
description: 'Nebula Vines produce x3',
|
||||
icon: '🌪️',
|
||||
cost: 500000,
|
||||
buildingBonus: { nebulaVine: 3 },
|
||||
requirements: { nebulaVine: 25 }
|
||||
},
|
||||
|
||||
// Building upgrades - Comet Rose
|
||||
roseBoost1: {
|
||||
id: 'roseBoost1',
|
||||
name: 'Comet Trail',
|
||||
description: 'Comet Roses produce x2',
|
||||
icon: '🌹',
|
||||
cost: 250000,
|
||||
buildingBonus: { cometRose: 2 },
|
||||
requirements: { cometRose: 1 }
|
||||
},
|
||||
|
||||
roseBoost2: {
|
||||
id: 'roseBoost2',
|
||||
name: 'Perihelion Bloom',
|
||||
description: 'Comet Roses produce x3',
|
||||
icon: '☄️',
|
||||
cost: 2500000,
|
||||
buildingBonus: { cometRose: 3 },
|
||||
requirements: { cometRose: 25 }
|
||||
},
|
||||
|
||||
// Global upgrades
|
||||
cosmicGarden1: {
|
||||
id: 'cosmicGarden1',
|
||||
name: 'Cosmic Garden',
|
||||
description: 'All production x1.5',
|
||||
icon: '🌌',
|
||||
cost: 1000000,
|
||||
globalBonus: 1.5,
|
||||
requirements: { starSprout: 50, moonBloom: 25, solarLeaf: 10 }
|
||||
},
|
||||
|
||||
stellarNursery: {
|
||||
id: 'stellarNursery',
|
||||
name: 'Stellar Nursery',
|
||||
description: 'All production x2',
|
||||
icon: '🌟',
|
||||
cost: 10000000,
|
||||
globalBonus: 2,
|
||||
requirements: { nebulaVine: 25, cometRose: 10 }
|
||||
},
|
||||
|
||||
galacticBotanist: {
|
||||
id: 'galacticBotanist',
|
||||
name: 'Galactic Botanist',
|
||||
description: 'All production x3',
|
||||
icon: '👨🌾',
|
||||
cost: 100000000,
|
||||
globalBonus: 3,
|
||||
requirements: { pulsarPalm: 10, blackholeOrchid: 5 }
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user