260 lines
8.6 KiB
JavaScript
260 lines
8.6 KiB
JavaScript
/**
|
|
* 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();
|
|
|
|
// Prestige
|
|
PrestigeUI.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;
|