/**
* 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 = `
${building.icon} ${building.name}
${building.description}
+${Game.formatNumber(building.baseProduction)}/s each
${Game.formatNumber(cost)} ✨
${count}
`;
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 = `Requires: ${reqParts.join(', ')}
`;
}
card.classList.toggle('purchased', purchased);
card.classList.toggle('locked', !requirementsMet && !purchased);
card.innerHTML = `
${upgrade.icon} ${upgrade.name}
${upgrade.description}
${reqText}
${purchased ? '✓ Owned' : `${Game.formatNumber(upgrade.cost)} ✨`}
`;
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 = `
${unlocked ? achievement.icon : '🔒'}
${unlocked ? achievement.name : '???'}
`;
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;