| |
| class MagicPot { |
| constructor(x, y) { |
| this.x = x; |
| this.y = y; |
| this.width = 120; |
| this.height = 100; |
| this.state = 'idle'; |
| |
| |
| this.animationTime = 0; |
| this.glowIntensity = 0; |
| this.shakeAmount = 0; |
| this.lidOffset = 0; |
| this.steamParticles = []; |
| |
| |
| this.colors = { |
| pot: '#8B4513', |
| potHighlight: '#CD853F', |
| potShadow: '#654321', |
| lid: '#A0522D', |
| lidHighlight: '#DEB887', |
| glow: '#FFD700', |
| steam: '#FFFFFF', |
| magic: '#FF69B4' |
| }; |
| |
| |
| this.foodImage = null; |
| this.animalImage = null; |
| this.currentFood = null; |
| |
| |
| this.potImage = null; |
| this.imageLoaded = false; |
| this.loadPotImage(); |
| } |
| |
| update(deltaTime) { |
| this.animationTime += deltaTime * 0.001; |
| |
| |
| switch (this.state) { |
| case 'idle': |
| this.updateIdleAnimation(); |
| break; |
| case 'activated': |
| this.updateActivatedAnimation(); |
| break; |
| case 'cooking': |
| this.updateCookingAnimation(); |
| break; |
| case 'overflowing': |
| this.updateOverflowingAnimation(); |
| break; |
| } |
| |
| |
| this.updateSteamParticles(deltaTime); |
| } |
| |
| updateIdleAnimation() { |
| |
| this.glowIntensity = Math.sin(this.animationTime * 0.5) * 0.1 + 0.1; |
| this.shakeAmount = 0; |
| this.lidOffset = 0; |
| } |
| |
| updateActivatedAnimation() { |
| |
| this.glowIntensity = Math.sin(this.animationTime * 2) * 0.3 + 0.5; |
| this.shakeAmount = 0; |
| this.lidOffset = 0; |
| |
| |
| if (Math.random() < 0.1) { |
| this.createMagicParticle(); |
| } |
| } |
| |
| updateCookingAnimation() { |
| |
| this.glowIntensity = Math.sin(this.animationTime * 3) * 0.4 + 0.6; |
| this.shakeAmount = Math.sin(this.animationTime * 8) * 2; |
| this.lidOffset = Math.sin(this.animationTime * 4) * 3; |
| |
| |
| if (Math.random() < 0.3) { |
| this.createSteamParticle(); |
| } |
| |
| |
| if (Math.random() < 0.2) { |
| this.createMagicParticle(); |
| } |
| } |
| |
| updateOverflowingAnimation() { |
| |
| this.glowIntensity = 1; |
| this.shakeAmount = Math.sin(this.animationTime * 15) * 5; |
| this.lidOffset = -20 + Math.sin(this.animationTime * 10) * 5; |
| |
| |
| if (Math.random() < 0.8) { |
| this.createSteamParticle(); |
| } |
| |
| |
| if (Math.random() < 0.5) { |
| this.createMagicParticle(); |
| } |
| } |
| |
| createSteamParticle() { |
| const particle = { |
| x: this.x + (Math.random() - 0.5) * this.width * 0.8, |
| y: this.y - this.height * 0.3, |
| vx: (Math.random() - 0.5) * 20, |
| vy: -Math.random() * 50 - 30, |
| size: Math.random() * 8 + 4, |
| opacity: 0.8, |
| life: 1, |
| maxLife: Math.random() * 2 + 1 |
| }; |
| |
| this.steamParticles.push(particle); |
| } |
| |
| createMagicParticle() { |
| const angle = Math.random() * Math.PI * 2; |
| const radius = this.width * 0.6; |
| |
| const particle = { |
| x: this.x + Math.cos(angle) * radius, |
| y: this.y + Math.sin(angle) * radius * 0.5, |
| vx: Math.cos(angle) * 30, |
| vy: Math.sin(angle) * 30 - 20, |
| size: Math.random() * 4 + 2, |
| opacity: 1, |
| life: 1, |
| maxLife: Math.random() * 1.5 + 0.5, |
| color: Math.random() < 0.5 ? '#FFD700' : '#FF69B4' |
| }; |
| |
| this.steamParticles.push(particle); |
| } |
| |
| updateSteamParticles(deltaTime) { |
| for (let i = this.steamParticles.length - 1; i >= 0; i--) { |
| const particle = this.steamParticles[i]; |
| |
| |
| particle.x += particle.vx * deltaTime * 0.001; |
| particle.y += particle.vy * deltaTime * 0.001; |
| |
| |
| particle.life -= deltaTime * 0.001 / particle.maxLife; |
| particle.opacity = particle.life; |
| |
| |
| if (!particle.color) { |
| particle.size += deltaTime * 0.002; |
| } |
| |
| |
| if (particle.life <= 0) { |
| this.steamParticles.splice(i, 1); |
| } |
| } |
| } |
| |
| render(ctx) { |
| const currentX = this.x + (Math.random() - 0.5) * this.shakeAmount; |
| const currentY = this.y + (Math.random() - 0.5) * this.shakeAmount; |
| |
| |
| if (this.glowIntensity > 0) { |
| this.renderGlow(ctx, currentX, currentY); |
| } |
| |
| |
| if (this.imageLoaded && this.potImage) { |
| this.renderPotImage(ctx, currentX, currentY); |
| } else { |
| |
| this.renderPotBody(ctx, currentX, currentY); |
| this.renderPotLid(ctx, currentX, currentY + this.lidOffset); |
| } |
| |
| |
| this.renderParticles(ctx); |
| |
| |
| this.renderDecorations(ctx, currentX, currentY); |
| } |
| |
| renderPotImage(ctx, x, y) { |
| ctx.save(); |
| |
| |
| if (this.glowIntensity > 0) { |
| ctx.shadowBlur = this.glowIntensity * 30; |
| ctx.shadowColor = '#FFD700'; |
| } |
| |
| |
| const imageWidth = this.width * 1.5; |
| const imageHeight = this.height * 1.5; |
| const imageX = x - imageWidth / 2; |
| const imageY = y - imageHeight / 2 + this.lidOffset * 0.3; |
| |
| |
| ctx.drawImage( |
| this.potImage, |
| imageX, |
| imageY, |
| imageWidth, |
| imageHeight |
| ); |
| |
| ctx.restore(); |
| } |
| |
| renderGlow(ctx, x, y) { |
| const glowRadius = this.width * 0.8 * this.glowIntensity; |
| const gradient = ctx.createRadialGradient(x, y, 0, x, y, glowRadius); |
| |
| gradient.addColorStop(0, `rgba(255, 215, 0, ${this.glowIntensity * 0.3})`); |
| gradient.addColorStop(0.5, `rgba(255, 105, 180, ${this.glowIntensity * 0.2})`); |
| gradient.addColorStop(1, 'rgba(255, 215, 0, 0)'); |
| |
| ctx.fillStyle = gradient; |
| ctx.beginPath(); |
| ctx.arc(x, y, glowRadius, 0, Math.PI * 2); |
| ctx.fill(); |
| } |
| |
| renderPotBody(ctx, x, y) { |
| const potWidth = this.width; |
| const potHeight = this.height; |
| |
| |
| ctx.fillStyle = this.colors.pot; |
| ctx.beginPath(); |
| ctx.ellipse(x, y + potHeight * 0.3, potWidth * 0.5, potHeight * 0.4, 0, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| |
| ctx.fillStyle = this.colors.potShadow; |
| ctx.fillRect(x - potWidth * 0.5, y - potHeight * 0.2, potWidth, potHeight * 0.5); |
| |
| |
| ctx.fillStyle = this.colors.potHighlight; |
| ctx.beginPath(); |
| ctx.ellipse(x - potWidth * 0.2, y - potHeight * 0.1, potWidth * 0.15, potHeight * 0.1, 0, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| |
| this.renderHandles(ctx, x, y); |
| } |
| |
| renderHandles(ctx, x, y) { |
| ctx.strokeStyle = this.colors.potShadow; |
| ctx.lineWidth = 8; |
| ctx.lineCap = 'round'; |
| |
| |
| ctx.beginPath(); |
| ctx.arc(x - this.width * 0.6, y, this.width * 0.15, -Math.PI * 0.3, Math.PI * 0.3); |
| ctx.stroke(); |
| |
| |
| ctx.beginPath(); |
| ctx.arc(x + this.width * 0.6, y, this.width * 0.15, Math.PI * 0.7, Math.PI * 1.3); |
| ctx.stroke(); |
| } |
| |
| renderPotLid(ctx, x, y) { |
| const lidWidth = this.width * 0.9; |
| const lidHeight = this.height * 0.2; |
| |
| |
| ctx.fillStyle = this.colors.lid; |
| ctx.beginPath(); |
| ctx.ellipse(x, y - this.height * 0.3, lidWidth * 0.5, lidHeight * 0.5, 0, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| |
| ctx.fillStyle = this.colors.lidHighlight; |
| ctx.beginPath(); |
| ctx.ellipse(x - lidWidth * 0.1, y - this.height * 0.35, lidWidth * 0.3, lidHeight * 0.3, 0, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| |
| ctx.fillStyle = this.colors.potShadow; |
| ctx.beginPath(); |
| ctx.ellipse(x, y - this.height * 0.45, 8, 12, 0, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| ctx.fillStyle = this.colors.lidHighlight; |
| ctx.beginPath(); |
| ctx.ellipse(x - 2, y - this.height * 0.48, 4, 6, 0, 0, Math.PI * 2); |
| ctx.fill(); |
| } |
| |
| renderParticles(ctx) { |
| this.steamParticles.forEach(particle => { |
| ctx.save(); |
| ctx.globalAlpha = particle.opacity; |
| |
| if (particle.color) { |
| |
| ctx.fillStyle = particle.color; |
| ctx.shadowBlur = 10; |
| ctx.shadowColor = particle.color; |
| } else { |
| |
| ctx.fillStyle = this.colors.steam; |
| } |
| |
| ctx.beginPath(); |
| ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| ctx.restore(); |
| }); |
| } |
| |
| renderDecorations(ctx, x, y) { |
| |
| if (this.state !== 'idle') { |
| this.renderMagicRunes(ctx, x, y); |
| } |
| |
| |
| if (this.state === 'cooking' && this.currentFood) { |
| this.renderFoodIcon(ctx, x, y); |
| } |
| } |
| |
| renderMagicRunes(ctx, x, y) { |
| const runeRadius = this.width * 0.8; |
| const runeCount = 6; |
| const time = this.animationTime * 2; |
| |
| ctx.strokeStyle = this.colors.magic; |
| ctx.lineWidth = 2; |
| ctx.globalAlpha = this.glowIntensity * 0.5; |
| |
| for (let i = 0; i < runeCount; i++) { |
| const angle = (i / runeCount) * Math.PI * 2 + time; |
| const runeX = x + Math.cos(angle) * runeRadius; |
| const runeY = y + Math.sin(angle) * runeRadius * 0.5; |
| |
| |
| ctx.beginPath(); |
| ctx.moveTo(runeX - 5, runeY - 5); |
| ctx.lineTo(runeX + 5, runeY + 5); |
| ctx.moveTo(runeX + 5, runeY - 5); |
| ctx.lineTo(runeX - 5, runeY + 5); |
| ctx.stroke(); |
| } |
| |
| ctx.globalAlpha = 1; |
| } |
| |
| renderFoodIcon(ctx, x, y) { |
| |
| const iconY = y - this.height * 0.8; |
| |
| ctx.fillStyle = '#FFD700'; |
| ctx.font = '24px Arial'; |
| ctx.textAlign = 'center'; |
| ctx.fillText(this.getFoodEmoji(this.currentFood), x, iconY); |
| } |
| |
| getFoodEmoji(foodName) { |
| const emojiMap = { |
| 'apple': '🍎', |
| 'banana': '🍌', |
| 'orange': '🍊', |
| 'strawberry': '🍓', |
| 'watermelon': '🍉', |
| 'grape': '🍇', |
| 'pizza': '🍕', |
| 'burger': '🍔', |
| 'cake': '🍰', |
| 'cookie': '🍪', |
| 'bread': '🍞', |
| 'cheese': '🧀', |
| 'fish': '🐟', |
| 'chicken': '🍗', |
| 'rice': '🍚', |
| 'noodles': '🍜' |
| }; |
| |
| return emojiMap[foodName] || '🍽️'; |
| } |
| |
| |
| activate() { |
| this.state = 'activated'; |
| this.animationTime = 0; |
| } |
| |
| startCooking(foodName) { |
| this.state = 'cooking'; |
| this.currentFood = foodName; |
| this.animationTime = 0; |
| } |
| |
| startOverflowing() { |
| this.state = 'overflowing'; |
| this.animationTime = 0; |
| } |
| |
| stopOverflowing() { |
| this.state = 'activated'; |
| this.lidOffset = 0; |
| this.shakeAmount = 0; |
| } |
| |
| reset() { |
| this.state = 'idle'; |
| this.currentFood = null; |
| this.animationTime = 0; |
| this.glowIntensity = 0; |
| this.shakeAmount = 0; |
| this.lidOffset = 0; |
| this.steamParticles = []; |
| } |
| |
| setFoodAssets(foodImage, animalImage) { |
| this.foodImage = foodImage; |
| this.animalImage = animalImage; |
| } |
| |
| |
| contains(x, y) { |
| const dx = x - this.x; |
| const dy = y - this.y; |
| const distance = Math.sqrt(dx * dx + dy * dy); |
| return distance < this.width * 0.5; |
| } |
| |
| |
| getBounds() { |
| return { |
| left: this.x - this.width * 0.5, |
| right: this.x + this.width * 0.5, |
| top: this.y - this.height * 0.5, |
| bottom: this.y + this.height * 0.5 |
| }; |
| } |
| |
| |
| loadPotImage() { |
| this.potImage = new Image(); |
| this.potImage.onload = () => { |
| this.imageLoaded = true; |
| console.log('魔法锅图片加载成功'); |
| }; |
| this.potImage.onerror = () => { |
| console.warn('魔法锅图片加载失败,使用默认绘制'); |
| this.imageLoaded = false; |
| }; |
| this.potImage.src = 'assets/images/magic_pot.png'; |
| } |
| } |
|
|