about summary refs log tree commit diff stats
path: root/html/fps/game.js
diff options
context:
space:
mode:
authorelioat <elioat@tilde.institute>2025-04-09 22:57:16 -0400
committerelioat <elioat@tilde.institute>2025-04-09 22:57:16 -0400
commit8214be9251fc65cfbcd6edd81b68b627c3f1f27e (patch)
tree10e0ad8cc195a0b2ca000a51d78b98e77235739f /html/fps/game.js
parenta96cbb4bdec31d07d53c618d4f8e824dddb06538 (diff)
downloadtour-8214be9251fc65cfbcd6edd81b68b627c3f1f27e.tar.gz
*
Diffstat (limited to 'html/fps/game.js')
-rw-r--r--html/fps/game.js188
1 files changed, 141 insertions, 47 deletions
diff --git a/html/fps/game.js b/html/fps/game.js
index 67cebc8..1dd0384 100644
--- a/html/fps/game.js
+++ b/html/fps/game.js
@@ -27,7 +27,8 @@ const GameState = {
     shots: [],
     isStarted: false,
     gradients: {}, // Cache for wall gradients
-    lastGradientUpdate: 0
+    lastGradientUpdate: 0,
+    particles: []
 };
 
 // Level generation using a simple maze algorithm
@@ -532,59 +533,84 @@ const render = (ctx) => {
     
     // Draw sprites with proper wall occlusion
     sprites.forEach(sprite => {
-        const spriteHeight = height / (sprite.distance * Math.cos(sprite.angle - GameState.player.angle));
-        const spriteWidth = spriteHeight;
-        const spriteX = sprite.x - spriteWidth/2;
-        const spriteY = halfHeight - spriteHeight/2;
+        // Calculate sprite size based on distance
+        const baseSize = 0.5; // Base size in world units
+        const screenSize = (baseSize / sprite.distance) * height; // Scale based on distance
+        const spriteX = sprite.x - screenSize/2;
+        const spriteY = halfHeight - screenSize/2;
         
         // Only draw if not occluded by a wall
         if (sprite.distance < rayResults[sprite.x].distance) {
-            ctx.save();
-            ctx.translate(spriteX + spriteWidth/2, spriteY + spriteHeight/2);
-            
-            // Enable anti-aliasing for clean edges
-            ctx.imageSmoothingEnabled = true;
-            
             if (sprite.type === 'enemy') {
                 // Draw enemy as red square
                 ctx.fillStyle = '#f00';
-                ctx.fillRect(-spriteWidth/2, -spriteHeight/2, spriteWidth, spriteHeight);
+                ctx.fillRect(spriteX, spriteY, screenSize, screenSize);
             } else if (sprite.data.type === 'health') {
-                // Draw health as golden square
-                ctx.fillStyle = '#ffd700';
-                ctx.fillRect(-spriteWidth/2, -spriteHeight/2, spriteWidth, spriteHeight);
+                // Draw health as green square
+                ctx.fillStyle = '#0f0';
+                ctx.fillRect(spriteX, spriteY, screenSize, screenSize);
             } else if (sprite.data.type === 'ammo') {
-                // Draw ammo as teal square
+                // Draw ammo as cyan square
                 ctx.fillStyle = '#0ff';
-                ctx.fillRect(-spriteWidth/2, -spriteHeight/2, spriteWidth, spriteHeight);
+                ctx.fillRect(spriteX, spriteY, screenSize, screenSize);
             } else if (sprite.data.type === 'flag') {
-                // Draw flag as blue diamond
-                ctx.fillStyle = '#00f';
-                ctx.beginPath();
-                ctx.moveTo(0, -spriteHeight/2);
-                ctx.lineTo(spriteWidth/2, 0);
-                ctx.lineTo(0, spriteHeight/2);
-                ctx.lineTo(-spriteWidth/2, 0);
-                ctx.closePath();
-                ctx.fill();
+                // Draw flag as yellow square
+                ctx.fillStyle = '#ff0';
+                ctx.fillRect(spriteX, spriteY, screenSize, screenSize);
             }
+        }
+    });
+    
+    // Draw particles
+    GameState.particles.forEach(particle => {
+        const dx = particle.x - GameState.player.x;
+        const dy = particle.y - GameState.player.y;
+        const distance = Math.sqrt(dx * dx + dy * dy);
+        const angle = Math.atan2(dx, dy) - GameState.player.angle;
+        
+        // Convert world position to screen position
+        const screenX = (angle / (Math.PI / 3) + 0.5) * width;
+        const screenY = height / 2;
+        
+        // Ensure size is always positive and within reasonable bounds
+        const size = Math.max(1, Math.min(particle.size, (1 - distance / 20) * particle.size));
+        
+        if (screenX >= 0 && screenX < width && size > 0) {
+            ctx.save();
+            ctx.translate(screenX, screenY);
+            ctx.rotate(particle.rotation);
+            
+            ctx.fillStyle = particle.color;
+            ctx.globalAlpha = particle.life;
+            
+            // Draw a more interesting particle shape
+            ctx.beginPath();
+            ctx.moveTo(0, -size);
+            ctx.lineTo(size, size);
+            ctx.lineTo(-size, size);
+            ctx.closePath();
+            ctx.fill();
             
             ctx.restore();
+            ctx.globalAlpha = 1;
         }
     });
     
     // Draw mini-map
-    const miniMapSize = 200; // Increased from 150
+    const miniMapSize = 200;
     const miniMapX = width - miniMapSize - 10;
     const miniMapY = 10;
     const cellSize = miniMapSize / GameState.level.width;
     
-    // Draw mini-map background
+    // Draw mini-map background with semi-transparent border
     ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
     ctx.fillRect(miniMapX, miniMapY, miniMapSize, miniMapSize);
+    ctx.strokeStyle = 'rgba(255, 255, 255, 0.3)';
+    ctx.lineWidth = 2;
+    ctx.strokeRect(miniMapX, miniMapY, miniMapSize, miniMapSize);
     
     // Draw walls
-    ctx.fillStyle = '#fff';
+    ctx.fillStyle = '#666';
     for (let y = 0; y < GameState.level.height; y++) {
         for (let x = 0; x < GameState.level.width; x++) {
             if (GameState.level.map[y][x] === 1) {
@@ -628,10 +654,24 @@ const render = (ctx) => {
         ctx.fill();
     });
     
-    // Draw player with enhanced visibility
+    // Draw enemies
+    ctx.fillStyle = '#f00';
+    GameState.enemies.forEach(enemy => {
+        ctx.beginPath();
+        ctx.arc(
+            miniMapX + enemy.x * cellSize + cellSize/2,
+            miniMapY + enemy.y * cellSize + cellSize/2,
+            cellSize/2,
+            0,
+            Math.PI * 2
+        );
+        ctx.fill();
+    });
+    
+    // Draw player with enhanced direction indicator
     const playerX = miniMapX + GameState.player.x * cellSize;
     const playerY = miniMapY + GameState.player.y * cellSize;
-    const size = cellSize * 1.5; // Increased player size
+    const size = cellSize * 1.5;
     
     // Draw player outline
     ctx.strokeStyle = '#fff';
@@ -640,32 +680,35 @@ const render = (ctx) => {
     ctx.arc(playerX + cellSize/2, playerY + cellSize/2, size/2 + 2, 0, Math.PI * 2);
     ctx.stroke();
     
-    // Draw player triangle
+    // Draw player triangle with direction indicator
     ctx.fillStyle = '#00f';
     ctx.save();
     ctx.translate(playerX + cellSize/2, playerY + cellSize/2);
     ctx.rotate(GameState.player.angle);
+    
+    // Draw main triangle
     ctx.beginPath();
     ctx.moveTo(0, -size/2);
     ctx.lineTo(size/2, size/2);
     ctx.lineTo(-size/2, size/2);
     ctx.closePath();
     ctx.fill();
-    ctx.restore();
     
-    // Draw enemies
-    ctx.fillStyle = '#f00';
-    GameState.enemies.forEach(enemy => {
-        ctx.beginPath();
-        ctx.arc(
-            miniMapX + enemy.x * cellSize + cellSize/2,
-            miniMapY + enemy.y * cellSize + cellSize/2,
-            cellSize/2,
-            0,
-            Math.PI * 2
-        );
-        ctx.fill();
-    });
+    // Draw direction indicator line
+    ctx.strokeStyle = '#fff';
+    ctx.lineWidth = 1;
+    ctx.beginPath();
+    ctx.moveTo(0, -size/2);
+    ctx.lineTo(0, -size);
+    ctx.stroke();
+    
+    // Draw small circle at the end of the direction line
+    ctx.fillStyle = '#fff';
+    ctx.beginPath();
+    ctx.arc(0, -size, size/4, 0, Math.PI * 2);
+    ctx.fill();
+    
+    ctx.restore();
 };
 
 // Game loop
@@ -685,7 +728,7 @@ const gameLoop = (ctx) => {
     
     // Check for firing input
     if ((keys[' '] || keys.e) && GameState.player.ammo > 0 && 
-        Date.now() - GameState.gun.lastShot > 200) { // 200ms cooldown
+        Date.now() - GameState.gun.lastShot > 200) {
         GameState.player.ammo--;
         GameState.gun.recoil = 1;
         GameState.gun.muzzleFlash = 1;
@@ -721,6 +764,17 @@ const gameLoop = (ctx) => {
         
         if (hitEnemy) {
             hitEnemy.health--;
+            if (hitEnemy.health <= 0) {
+                // Create more particles with more colors
+                const colors = [
+                    '#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff',
+                    '#ff8800', '#88ff00', '#00ff88', '#0088ff', '#8800ff', '#ff0088'
+                ];
+                for (let i = 0; i < 50; i++) { // More particles
+                    const color = colors[Math.floor(Math.random() * colors.length)];
+                    GameState.particles.push(new Particle(hitEnemy.x, hitEnemy.y, color));
+                }
+            }
         }
     }
     
@@ -744,6 +798,9 @@ const gameLoop = (ctx) => {
         GameState.gun.tilt = Math.max(0, GameState.gun.tilt - 0.1);
     }
     
+    // Update particles
+    GameState.particles = GameState.particles.filter(particle => particle.update());
+    
     handlePlayerMovement(keys);
     updateEnemies();
     checkCollisions();
@@ -844,6 +901,17 @@ document.addEventListener('click', (e) => {
         
         if (hitEnemy) {
             hitEnemy.health--;
+            if (hitEnemy.health <= 0) {
+                // Create more particles with more colors
+                const colors = [
+                    '#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff',
+                    '#ff8800', '#88ff00', '#00ff88', '#0088ff', '#8800ff', '#ff0088'
+                ];
+                for (let i = 0; i < 50; i++) { // More particles
+                    const color = colors[Math.floor(Math.random() * colors.length)];
+                    GameState.particles.push(new Particle(hitEnemy.x, hitEnemy.y, color));
+                }
+            }
         }
     }
 });
@@ -860,4 +928,30 @@ const init = () => {
     gameLoop(ctx);
 };
 
+// Add particle class
+class Particle {
+    constructor(x, y, color) {
+        this.x = x;
+        this.y = y;
+        this.color = color;
+        this.size = Math.random() * 4 + 2; // Larger size range
+        this.speedX = (Math.random() - 0.5) * 12; // Faster spread
+        this.speedY = (Math.random() - 0.5) * 12; // Faster spread
+        this.life = 1.0;
+        this.decay = Math.random() * 0.005 + 0.002; // Slower decay
+        this.rotation = Math.random() * Math.PI * 2; // Random starting rotation
+        this.rotationSpeed = (Math.random() - 0.5) * 0.2; // Random rotation speed
+        this.gravity = Math.random() * 0.15 + 0.05; // Random gravity effect
+    }
+
+    update() {
+        this.x += this.speedX;
+        this.y += this.speedY;
+        this.speedY += this.gravity; // Variable gravity
+        this.life -= this.decay;
+        this.rotation += this.rotationSpeed;
+        return this.life > 0;
+    }
+}
+
 init();