diff options
Diffstat (limited to 'html/plains/game.js')
-rw-r--r-- | html/plains/game.js | 193 |
1 files changed, 157 insertions, 36 deletions
diff --git a/html/plains/game.js b/html/plains/game.js index 5923b16..d25daaf 100644 --- a/html/plains/game.js +++ b/html/plains/game.js @@ -366,7 +366,9 @@ const createInitialState = () => ({ maxHp: 15, isInvulnerable: false, invulnerableUntil: 0, - isDead: false + isDead: false, + diamonds: 0, // Track number of collected diamonds + lastRenderedCircles: 4, // Add this line }, particles: [], footprints: [], @@ -381,7 +383,8 @@ const createInitialState = () => ({ villagers: [], gameComplete: false, gameCompleteMessageShown: false, - enemies: [] + enemies: [], + diamonds: [] // Array to hold diamond objects }); let state = createInitialState(); @@ -1080,46 +1083,101 @@ const renderPlayerHUD = (ctx) => { // Switch to screen coordinates ctx.setTransform(1, 0, 0, 1, 0, 0); - // HP Bar container - const barWidth = 200; - const barHeight = 20; - const barX = 20; - const barY = 20; - const borderWidth = 2; - - // Draw border - ctx.fillStyle = 'rgba(0, 0, 0, 0.8)'; - ctx.fillRect( - barX - borderWidth, - barY - borderWidth, - barWidth + borderWidth * 2, - barHeight + borderWidth * 2 - ); + const circleRadius = 15; + const circleSpacing = 40; + const startX = 30; + const startY = 30; + const totalCircles = 4; + const hpPerCircle = state.player.maxHp / totalCircles; + + // Track if we need to create explosion particles + const currentFilledCircles = Math.ceil(state.player.hp / hpPerCircle); + if (currentFilledCircles < state.player.lastRenderedCircles) { + // Create explosion particles for the depleted circle + const circleIndex = currentFilledCircles; + const particleX = startX + circleIndex * circleSpacing; + createHealthCircleExplosion(particleX, startY); + } + state.player.lastRenderedCircles = currentFilledCircles; - // Background - ctx.fillStyle = 'rgba(50, 50, 50, 0.8)'; - ctx.fillRect(barX, barY, barWidth, barHeight); - - // HP - const hpRatio = state.player.hp / state.player.maxHp; - ctx.fillStyle = `rgb(${Math.floor(255 * (1 - hpRatio))}, ${Math.floor(255 * hpRatio)}, 0)`; - ctx.fillRect(barX, barY, barWidth * hpRatio, barHeight); - - // HP Text - ctx.fillStyle = 'white'; - ctx.font = '14px Arial'; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.fillText( - `HP: ${state.player.hp}/${state.player.maxHp}`, - barX + barWidth/2, - barY + barHeight/2 - ); + // Draw circles + for (let i = 0; i < totalCircles; i++) { + const circleX = startX + i * circleSpacing; + const circleY = startY; + + // Calculate how full this circle should be + const circleStartHp = i * hpPerCircle; + const circleEndHp = (i + 1) * hpPerCircle; + let fillAmount = 1; + + if (state.player.hp <= circleStartHp) { + fillAmount = 0; + } else if (state.player.hp < circleEndHp) { + fillAmount = (state.player.hp - circleStartHp) / hpPerCircle; + } + + // Draw outer circle (border) + ctx.beginPath(); + ctx.arc(circleX, circleY, circleRadius, 0, Math.PI * 2); + ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)'; + ctx.lineWidth = 2; + ctx.stroke(); + + // Draw fill + if (fillAmount > 0) { + // Create gradient for filled portion + const gradient = ctx.createRadialGradient( + circleX, circleY, 0, + circleX, circleY, circleRadius + ); + gradient.addColorStop(0, 'rgba(255, 0, 0, 0.8)'); + gradient.addColorStop(0.7, 'rgba(200, 0, 0, 0.6)'); + gradient.addColorStop(1, 'rgba(150, 0, 0, 0.4)'); + + ctx.beginPath(); + ctx.arc(circleX, circleY, circleRadius * 0.9, 0, Math.PI * 2 * fillAmount); + ctx.lineTo(circleX, circleY); + ctx.fillStyle = gradient; + ctx.fill(); + } + } // Restore the context state ctx.restore(); }; +// Add this new function to create the explosion effect +const createHealthCircleExplosion = (screenX, screenY) => { + const numParticles = 20; + const colors = [ + 'rgba(255, 0, 0, 0.8)', + 'rgba(200, 0, 0, 0.6)', + 'rgba(150, 0, 0, 0.4)' + ]; + + // Convert screen coordinates to world coordinates + const worldX = screenX - state.camera.x; + const worldY = screenY - state.camera.y; + + for (let i = 0; i < numParticles; i++) { + const angle = (i / numParticles) * Math.PI * 2; + const speed = 2 + Math.random() * 3; + const size = 2 + Math.random() * 3; + const color = colors[Math.floor(Math.random() * colors.length)]; + + state.particles.push({ + x: worldX, + y: worldY, + dx: Math.cos(angle) * speed, + dy: Math.sin(angle) * speed, + size: size, + color: color, + lifetime: 1000, + createdAt: animationTime + }); + } +}; + const render = () => { ctx.clearRect(0, 0, GAME_WIDTH, GAME_HEIGHT); @@ -1489,6 +1547,9 @@ const render = () => { state.player.isInvulnerable = false; } + // Render diamonds + renderDiamonds(); + ctx.restore(); }; @@ -1598,6 +1659,7 @@ const gameLoop = (currentTime) => { updatePlayer(); state.enemies = enemySystem.updateEnemies(state.enemies, deltaTime); + collectDiamonds(); // Add this line to check for diamond collection render(); lastFrameTime = currentTime; @@ -1851,4 +1913,63 @@ const showGameOver = () => { <p>Press Space to restart</p> `; document.body.appendChild(gameOverDiv); +}; + +// Update the player's HP when collecting diamonds +const collectDiamonds = () => { + state.diamonds.forEach(diamond => { + const dx = state.player.x - diamond.x; + const dy = state.player.y - diamond.y; + const distance = Math.sqrt(dx * dx + dy * dy); + + if (distance < (CONFIG.player.size / 2 + diamond.size / 2) && !diamond.collected) { + diamond.collected = true; // Mark diamond as collected + state.player.hp = Math.min(state.player.hp + 1, state.player.maxHp); // Restore 1 HP, max out at maxHp + } + }); + + // Remove collected diamonds from the state + state.diamonds = state.diamonds.filter(diamond => !diamond.collected); +}; + +const renderDiamonds = () => { + state.diamonds.forEach(diamond => { + ctx.save(); + + // Add pulsing glow effect + const pulseIntensity = 0.5 + Math.sin(animationTime * 0.005) * 0.3; // Pulsing between 0.2 and 0.8 + + // Outer glow + const gradient = ctx.createRadialGradient( + diamond.x, diamond.y, diamond.size * 0.2, + diamond.x, diamond.y, diamond.size * 1.5 + ); + gradient.addColorStop(0, `rgba(255, 215, 0, ${pulseIntensity})`); // Gold center + gradient.addColorStop(0.6, 'rgba(255, 215, 0, 0.2)'); // Fading gold + gradient.addColorStop(1, 'rgba(255, 215, 0, 0)'); // Transparent edge + + ctx.beginPath(); + ctx.arc(diamond.x, diamond.y, diamond.size * 1.5, 0, Math.PI * 2); + ctx.fillStyle = gradient; + ctx.fill(); + + // Inner diamond + ctx.beginPath(); + ctx.arc(diamond.x, diamond.y, diamond.size * 0.4, 0, Math.PI * 2); // Make core smaller + ctx.fillStyle = `rgba(255, 223, 0, ${0.8 + pulseIntensity * 0.2})`; // Brighter gold + ctx.fill(); + + // Shine effect + ctx.beginPath(); + ctx.arc( + diamond.x - diamond.size * 0.2, + diamond.y - diamond.size * 0.2, + diamond.size * 0.15, + 0, Math.PI * 2 + ); + ctx.fillStyle = `rgba(255, 255, 255, ${0.6 + pulseIntensity * 0.4})`; + ctx.fill(); + + ctx.restore(); + }); }; \ No newline at end of file |