diff options
author | elioat <elioat@tilde.institute> | 2025-04-09 22:57:16 -0400 |
---|---|---|
committer | elioat <elioat@tilde.institute> | 2025-04-09 22:57:16 -0400 |
commit | 8214be9251fc65cfbcd6edd81b68b627c3f1f27e (patch) | |
tree | 10e0ad8cc195a0b2ca000a51d78b98e77235739f /html/fps/game.js | |
parent | a96cbb4bdec31d07d53c618d4f8e824dddb06538 (diff) | |
download | tour-8214be9251fc65cfbcd6edd81b68b627c3f1f27e.tar.gz |
*
Diffstat (limited to 'html/fps/game.js')
-rw-r--r-- | html/fps/game.js | 188 |
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(); |