Session 1: Particle effects system with click, building, and achievement visual feedback

This commit is contained in:
Hermes Agent
2026-08-19 13:26:07 +00:00
parent 1df8bd9dd6
commit 42adcdaa87
7 changed files with 257 additions and 0 deletions
+8
View File
@@ -164,6 +164,14 @@ const Achievements = {
if (achievement.condition(Game.state)) {
Game.state.achievements.push(id);
UI.showNotification(`Achievement: ${achievement.name}!`, 'achievement');
// Particle effect for achievement unlock
const countEl = document.querySelector('#achievements-tab h3');
if (countEl) {
const rect = countEl.getBoundingClientRect();
Particles.createAchievementEffect(rect.left + rect.width / 2, rect.top + rect.height / 2);
}
console.log(`🏆 Achievement unlocked: ${achievement.name}`);
}
}
+9
View File
@@ -101,6 +101,7 @@ const Game = {
// Visual feedback
UI.showFloatingText(event.clientX, event.clientY, `+${this.formatNumber(amount)}`);
Particles.createClickParticles(event.clientX, event.clientY, 8);
UI.update();
},
@@ -180,6 +181,14 @@ const Game = {
this.state.buildings[buildingId] = (this.state.buildings[buildingId] || 0) + 1;
UI.showNotification(`Bought ${BUILDINGS[buildingId].name}!`, 'success');
// Particle effect on building purchase
const card = document.getElementById(`building-${buildingId}`);
if (card) {
const rect = card.getBoundingClientRect();
Particles.createBuildingEffect(rect.left + rect.width / 2, rect.top + rect.height / 2);
}
UI.update();
return true;
}
+3
View File
@@ -5,6 +5,9 @@
document.addEventListener('DOMContentLoaded', () => {
Game.init();
// Create ambient background stars
Particles.createAmbientStars(15);
// Show last save time
const lastSave = SaveSystem.getLastSaveTime();
if (lastSave) {
+184
View File
@@ -0,0 +1,184 @@
/**
* Cosmic Garden - Particle System
* Creates visual particle effects for clicks and achievements
*/
const Particles = {
/**
* Create sparkle particles on click
*/
createClickParticles(x, y, count = 8) {
const colors = ['#ffd700', '#9b59b6', '#3498db', '#2ecc71', '#ffffff'];
for (let i = 0; i < count; i++) {
const particle = document.createElement('div');
particle.className = 'particle';
// Random properties
const angle = (Math.PI * 2 * i) / count + (Math.random() - 0.5);
const velocity = 50 + Math.random() * 50;
const size = 6 + Math.random() * 8;
const color = colors[Math.floor(Math.random() * colors.length)];
particle.style.cssText = `
left: ${x}px;
top: ${y}px;
width: ${size}px;
height: ${size}px;
background: ${color};
border-radius: 50%;
pointer-events: none;
position: fixed;
z-index: 998;
`;
document.body.appendChild(particle);
// Animate
const animation = particle.animate([
{
transform: 'translate(-50%, -50%) scale(1)',
opacity: 1
},
{
transform: `translate(calc(-50% + ${Math.cos(angle) * velocity}px), calc(-50% + ${Math.sin(angle) * velocity}px)) scale(0)`,
opacity: 0
}
], {
duration: 600 + Math.random() * 200,
easing: 'cubic-bezier(0, 0.9, 0.57, 1)'
});
animation.onfinish = () => particle.remove();
}
},
/**
* Create achievement unlock effect
*/
createAchievementEffect(x, y) {
const colors = ['#ffd700', '#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4'];
// Burst of particles
for (let i = 0; i < 20; i++) {
const particle = document.createElement('div');
particle.className = 'particle achievement-particle';
const angle = (Math.PI * 2 * i) / 20;
const velocity = 80 + Math.random() * 60;
const size = 8 + Math.random() * 12;
const color = colors[Math.floor(Math.random() * colors.length)];
particle.style.cssText = `
left: ${x}px;
top: ${y}px;
width: ${size}px;
height: ${size}px;
background: ${color};
border-radius: 50%;
pointer-events: none;
position: fixed;
z-index: 998;
box-shadow: 0 0 10px ${color};
`;
document.body.appendChild(particle);
const animation = particle.animate([
{
transform: 'translate(-50%, -50%) scale(1)',
opacity: 1
},
{
transform: `translate(calc(-50% + ${Math.cos(angle) * velocity}px), calc(-50% + ${Math.sin(angle) * velocity}px)) scale(0.5)`,
opacity: 0.8
},
{
transform: `translate(calc(-50% + ${Math.cos(angle) * velocity * 1.5}px), calc(-50% + ${Math.sin(angle) * velocity * 1.5}px)) scale(0)`,
opacity: 0
}
], {
duration: 1000,
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'
});
animation.onfinish = () => particle.remove();
}
},
/**
* Create building purchase effect
*/
createBuildingEffect(x, y) {
const colors = ['#2ecc71', '#27ae60', '#1abc9c'];
for (let i = 0; i < 12; i++) {
const particle = document.createElement('div');
particle.className = 'particle building-particle';
const angle = (Math.PI * 2 * i) / 12;
const velocity = 60 + Math.random() * 40;
const size = 5 + Math.random() * 6;
const color = colors[Math.floor(Math.random() * colors.length)];
particle.style.cssText = `
left: ${x}px;
top: ${y}px;
width: ${size}px;
height: ${size}px;
background: ${color};
border-radius: 2px;
pointer-events: none;
position: fixed;
z-index: 998;
`;
document.body.appendChild(particle);
const animation = particle.animate([
{
transform: 'translate(-50%, -50%) rotate(0deg) scale(1)',
opacity: 1
},
{
transform: `translate(calc(-50% + ${Math.cos(angle) * velocity}px), calc(-50% + ${Math.sin(angle) * velocity}px)) rotate(${360 + Math.random() * 180}deg) scale(0)`,
opacity: 0
}
], {
duration: 700,
easing: 'ease-out'
});
animation.onfinish = () => particle.remove();
}
},
/**
* Create ambient floating stars in the background
*/
createAmbientStars(count = 15) {
for (let i = 0; i < count; i++) {
const star = document.createElement('div');
star.className = 'ambient-star';
const size = 2 + Math.random() * 3;
const left = Math.random() * 100;
const delay = Math.random() * 5;
const duration = 3 + Math.random() * 2;
star.style.cssText = `
width: ${size}px;
height: ${size}px;
left: ${left}%;
top: ${Math.random() * 100}%;
animation-delay: ${delay}s;
animation-duration: ${duration}s;
`;
document.getElementById('game-container').appendChild(star);
}
}
};
// Make globally accessible
window.Particles = Particles;