about summary refs log tree commit diff stats
path: root/html/plains/game.js
diff options
context:
space:
mode:
authorelioat <elioat@tilde.institute>2024-12-18 15:52:05 -0500
committerelioat <elioat@tilde.institute>2024-12-18 15:52:05 -0500
commitee38675b78dc7f6a49af2a7004822565086df476 (patch)
tree0c33c4a3d675a5dda7d54e2943a6c76b622bc399 /html/plains/game.js
parentf66730b7aeb9494c266260df7eeba8a8e26beb4e (diff)
downloadtour-ee38675b78dc7f6a49af2a7004822565086df476.tar.gz
*
Diffstat (limited to 'html/plains/game.js')
-rw-r--r--html/plains/game.js193
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