games / index.html
Davi111's picture
Add 2 files
2bc517b verified
Raw
History Blame Contribute Delete
36.6 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2D FPS Game</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
body {
font-family: 'Press Start 2P', cursive;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #111;
color: white;
}
#gameContainer {
position: relative;
width: 100vw;
height: 100vh;
overflow: hidden;
perspective: 1000px;
}
#gameCanvas {
position: absolute;
width: 100%;
height: 100%;
background-color: #222;
image-rendering: pixelated;
}
#crosshair {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 20px;
height: 20px;
pointer-events: none;
z-index: 100;
}
#crosshair::before, #crosshair::after {
content: '';
position: absolute;
background-color: rgba(255, 255, 255, 0.8);
}
#crosshair::before {
width: 2px;
height: 12px;
top: 4px;
left: 9px;
}
#crosshair::after {
width: 12px;
height: 2px;
top: 9px;
left: 4px;
}
#uiContainer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 50;
}
#healthBar {
position: absolute;
bottom: 20px;
left: 20px;
width: 200px;
height: 30px;
background-color: rgba(70, 0, 0, 0.7);
border: 3px solid #444;
border-radius: 5px;
overflow: hidden;
}
#healthFill {
height: 100%;
width: 100%;
background-color: #f00;
transition: width 0.3s;
}
#ammoCount {
position: absolute;
bottom: 20px;
right: 20px;
font-size: 24px;
color: white;
text-shadow: 2px 2px 0 #000;
}
#scoreDisplay {
position: absolute;
top: 20px;
left: 20px;
font-size: 24px;
color: white;
text-shadow: 2px 2px 0 #000;
}
#weaponDisplay {
position: absolute;
bottom: 70px;
right: 20px;
font-size: 24px;
color: white;
text-shadow: 2px 2px 0 #000;
}
#gameOverScreen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
display: none;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 200;
}
#startScreen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.9);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 150;
}
.btn {
background-color: #f00;
color: white;
border: none;
padding: 15px 30px;
font-size: 18px;
border-radius: 5px;
cursor: pointer;
margin-top: 30px;
font-family: 'Press Start 2P', cursive;
text-transform: uppercase;
letter-spacing: 2px;
box-shadow: 0 0 10px rgba(255, 0, 0, 0.5);
transition: all 0.3s;
}
.btn:hover {
background-color: #c00;
transform: translateY(-3px);
box-shadow: 0 0 20px rgba(255, 0, 0, 0.8);
}
#bloodEffect {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 0, 0, 0);
pointer-events: none;
z-index: 60;
transition: background-color 0.3s;
}
#reloadIndicator {
position: absolute;
bottom: 120px;
right: 20px;
width: 100px;
height: 10px;
background-color: rgba(255, 255, 255, 0.3);
border-radius: 5px;
overflow: hidden;
display: none;
}
#reloadFill {
height: 100%;
width: 0%;
background-color: #ff0;
transition: width 0.1s linear;
}
#enemyHitEffect {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0);
pointer-events: none;
z-index: 60;
transition: background-color 0.1s;
}
#mobileControls {
position: absolute;
bottom: 20px;
width: 100%;
display: none;
justify-content: space-between;
padding: 0 20px;
box-sizing: border-box;
}
.mobile-btn {
width: 70px;
height: 70px;
background-color: rgba(255, 255, 255, 0.2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24px;
pointer-events: auto;
user-select: none;
touch-action: manipulation;
}
#shootBtn {
background-color: rgba(255, 0, 0, 0.5);
}
#movePad {
position: relative;
width: 120px;
height: 120px;
background-color: rgba(255, 255, 255, 0.1);
border-radius: 50%;
}
#moveKnob {
position: absolute;
width: 50px;
height: 50px;
background-color: rgba(255, 255, 255, 0.3);
border-radius: 50%;
top: 35px;
left: 35px;
}
@media (max-width: 768px) {
#mobileControls {
display: flex;
}
}
</style>
</head>
<body>
<div id="gameContainer">
<canvas id="gameCanvas"></canvas>
<div id="crosshair"></div>
<div id="bloodEffect"></div>
<div id="enemyHitEffect"></div>
<div id="uiContainer">
<div id="healthBar">
<div id="healthFill"></div>
</div>
<div id="ammoCount">30/30</div>
<div id="scoreDisplay">SCORE: 0</div>
<div id="weaponDisplay">PISTOL</div>
<div id="reloadIndicator">
<div id="reloadFill"></div>
</div>
<div id="mobileControls">
<div id="movePad">
<div id="moveKnob"></div>
</div>
<div id="shootBtn" class="mobile-btn">
<i class="fas fa-fire"></i>
</div>
</div>
</div>
<div id="gameOverScreen">
<h1 class="text-5xl text-red-600 mb-8">GAME OVER</h1>
<p class="text-2xl mb-4">YOUR SCORE:</p>
<p class="text-4xl mb-12" id="finalScore">0</p>
<button id="restartBtn" class="btn">PLAY AGAIN</button>
</div>
<div id="startScreen">
<h1 class="text-6xl text-red-600 mb-8">2D FPS</h1>
<p class="text-xl mb-12">SURVIVE AS LONG AS YOU CAN</p>
<button id="startBtn" class="btn">START GAME</button>
<div class="mt-16 text-gray-400 text-center">
<p class="mb-4">CONTROLS:</p>
<p class="mb-2">WASD - MOVE</p>
<p class="mb-2">MOUSE - AIM</p>
<p class="mb-2">LEFT CLICK - SHOOT</p>
<p class="mb-2">R - RELOAD</p>
</div>
</div>
</div>
<script>
// Game variables
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const healthFill = document.getElementById('healthFill');
const ammoCount = document.getElementById('ammoCount');
const scoreDisplay = document.getElementById('scoreDisplay');
const weaponDisplay = document.getElementById('weaponDisplay');
const gameOverScreen = document.getElementById('gameOverScreen');
const finalScore = document.getElementById('finalScore');
const startScreen = document.getElementById('startScreen');
const startBtn = document.getElementById('startBtn');
const restartBtn = document.getElementById('restartBtn');
const bloodEffect = document.getElementById('bloodEffect');
const reloadIndicator = document.getElementById('reloadIndicator');
const reloadFill = document.getElementById('reloadFill');
const enemyHitEffect = document.getElementById('enemyHitEffect');
const shootBtn = document.getElementById('shootBtn');
const movePad = document.getElementById('movePad');
const moveKnob = document.getElementById('moveKnob');
// Game state
let gameWidth = window.innerWidth;
let gameHeight = window.innerHeight;
let playerX = 0;
let playerY = 0;
let playerAngle = 0;
let playerSpeed = 0.1;
let health = 100;
let score = 0;
let gameRunning = false;
let lastShotTime = 0;
let lastEnemySpawn = 0;
let enemySpawnRate = 2000;
let isReloading = false;
let movePadActive = false;
let moveDirection = { x: 0, y: 0 };
// Weapon data
const weapons = {
pistol: {
name: "PISTOL",
damage: 25,
ammo: 30,
maxAmmo: 30,
reloadTime: 1000,
fireRate: 300,
color: "#aaa"
},
rifle: {
name: "RIFLE",
damage: 15,
ammo: 90,
maxAmmo: 30,
reloadTime: 1500,
fireRate: 100,
color: "#5a5"
},
shotgun: {
name: "SHOTGUN",
damage: 40,
ammo: 24,
maxAmmo: 8,
reloadTime: 2000,
fireRate: 800,
color: "#a55"
}
};
let currentWeapon = "pistol";
let enemies = [];
let walls = [];
let particles = [];
// Initialize canvas
canvas.width = gameWidth;
canvas.height = gameHeight;
// Start screen
startBtn.addEventListener('click', startGame);
// Restart game
restartBtn.addEventListener('click', startGame);
// Keyboard controls
const keys = {
w: false,
a: false,
s: false,
d: false
};
document.addEventListener('keydown', (e) => {
if (e.key.toLowerCase() === 'w') keys.w = true;
if (e.key.toLowerCase() === 'a') keys.a = true;
if (e.key.toLowerCase() === 's') keys.s = true;
if (e.key.toLowerCase() === 'd') keys.d = true;
if (e.key === 'r' && gameRunning && !isReloading) reload();
});
document.addEventListener('keyup', (e) => {
if (e.key.toLowerCase() === 'w') keys.w = false;
if (e.key.toLowerCase() === 'a') keys.a = false;
if (e.key.toLowerCase() === 's') keys.s = false;
if (e.key.toLowerCase() === 'd') keys.d = false;
});
// Mouse controls
document.addEventListener('mousemove', (e) => {
if (!gameRunning) return;
// Calculate angle based on mouse position
const centerX = gameWidth / 2;
const centerY = gameHeight / 2;
const mouseX = e.clientX;
const mouseY = e.clientY;
playerAngle = Math.atan2(mouseY - centerY, mouseX - centerX);
});
document.addEventListener('mousedown', (e) => {
if (e.button === 0 && gameRunning) { // Left click
shoot();
}
});
// Touch controls for mobile
shootBtn.addEventListener('touchstart', (e) => {
e.preventDefault();
if (gameRunning) shoot();
});
movePad.addEventListener('touchstart', handleMoveStart);
document.addEventListener('touchmove', handleMoveMove);
document.addEventListener('touchend', handleMoveEnd);
function handleMoveStart(e) {
e.preventDefault();
movePadActive = true;
handleMoveMove(e);
}
function handleMoveMove(e) {
if (!movePadActive) return;
e.preventDefault();
const padRect = movePad.getBoundingClientRect();
const padCenterX = padRect.left + padRect.width / 2;
const padCenterY = padRect.top + padRect.height / 2;
let touchX, touchY;
if (e.touches) {
touchX = e.touches[0].clientX;
touchY = e.touches[0].clientY;
} else {
touchX = e.clientX;
touchY = e.clientY;
}
const dx = touchX - padCenterX;
const dy = touchY - padCenterY;
const distance = Math.min(50, Math.sqrt(dx * dx + dy * dy));
const angle = Math.atan2(dy, dx);
// Calculate direction vector
moveDirection = {
x: Math.cos(angle) * (distance / 50),
y: Math.sin(angle) * (distance / 50)
};
// Move knob
const knobX = Math.cos(angle) * distance;
const knobY = Math.sin(angle) * distance;
moveKnob.style.transform = `translate(${knobX}px, ${knobY}px)`;
}
function handleMoveEnd(e) {
e.preventDefault();
movePadActive = false;
moveDirection = { x: 0, y: 0 };
moveKnob.style.transform = 'translate(0, 0)';
}
function startGame() {
// Reset game state
health = 100;
score = 0;
playerX = 0;
playerY = 0;
playerAngle = 0;
enemies = [];
particles = [];
walls = [];
// Reset weapons
weapons.pistol.ammo = weapons.pistol.maxAmmo;
weapons.rifle.ammo = weapons.rifle.maxAmmo;
weapons.shotgun.ammo = weapons.shotgun.maxAmmo;
currentWeapon = "pistol";
// Update UI
updateHealth();
updateAmmo();
updateScore();
updateWeapon();
// Hide screens
gameOverScreen.style.display = 'none';
startScreen.style.display = 'none';
// Generate walls
generateWalls();
// Start game loop
gameRunning = true;
lastEnemySpawn = Date.now();
requestAnimationFrame(gameLoop);
}
function gameLoop() {
if (!gameRunning) return;
const now = Date.now();
// Clear canvas
ctx.clearRect(0, 0, gameWidth, gameHeight);
// Draw floor and ceiling
ctx.fillStyle = '#333';
ctx.fillRect(0, 0, gameWidth, gameHeight / 2);
ctx.fillStyle = '#555';
ctx.fillRect(0, gameHeight / 2, gameWidth, gameHeight / 2);
// Draw walls
drawWalls();
// Move player
let moveX = 0;
let moveY = 0;
if (keys.w || moveDirection.y < -0.5) moveY -= playerSpeed;
if (keys.s || moveDirection.y > 0.5) moveY += playerSpeed;
if (keys.a || moveDirection.x < -0.5) moveX -= playerSpeed;
if (keys.d || moveDirection.x > 0.5) moveX += playerSpeed;
// Normalize diagonal movement
if (moveX !== 0 && moveY !== 0) {
moveX *= 0.7071; // 1/sqrt(2)
moveY *= 0.7071;
}
// Check wall collisions
const newX = playerX + moveX * Math.cos(playerAngle) - moveY * Math.sin(playerAngle);
const newY = playerY + moveX * Math.sin(playerAngle) + moveY * Math.cos(playerAngle);
if (!checkWallCollision(newX, newY)) {
playerX = newX;
playerY = newY;
}
// Spawn enemies
if (now - lastEnemySpawn > enemySpawnRate) {
spawnEnemy();
lastEnemySpawn = now;
// Increase difficulty
if (score > 0 && score % 5 === 0) {
enemySpawnRate = Math.max(500, enemySpawnRate - 100);
}
}
// Update and draw enemies
updateEnemies();
// Update and draw particles
updateParticles();
// Draw weapon
drawWeapon();
// Check game over
if (health <= 0) {
gameOver();
return;
}
// Continue game loop
requestAnimationFrame(gameLoop);
}
function generateWalls() {
// Create a simple maze-like structure
for (let x = -10; x <= 10; x++) {
for (let y = -10; y <= 10; y++) {
// Border walls
if (x === -10 || x === 10 || y === -10 || y === 10) {
walls.push({ x, y });
}
// Random inner walls
else if (Math.abs(x) > 2 || Math.abs(y) > 2) {
if (Math.random() < 0.2) {
walls.push({ x, y });
}
}
}
}
}
function drawWalls() {
const fov = Math.PI / 3; // 60 degrees
const halfFov = fov / 2;
const numRays = gameWidth;
const rayAngleStep = fov / numRays;
for (let i = 0; i < numRays; i++) {
const rayAngle = playerAngle - halfFov + i * rayAngleStep;
// Cast ray
let rayX = playerX;
let rayY = playerY;
let rayDist = 0;
let hitWall = false;
let wallType = 0;
// Ray casting loop
while (!hitWall && rayDist < 20) {
rayDist += 0.05;
rayX = playerX + Math.cos(rayAngle) * rayDist;
rayY = playerY + Math.sin(rayAngle) * rayDist;
// Check if ray hit a wall
const mapX = Math.floor(rayX + 0.5);
const mapY = Math.floor(rayY + 0.5);
if (walls.some(wall => wall.x === mapX && wall.y === mapY)) {
hitWall = true;
wallType = (mapX + mapY) % 2;
}
}
// Fix fisheye effect
const correctedDist = rayDist * Math.cos(rayAngle - playerAngle);
// Calculate wall height
const wallHeight = Math.min(gameHeight, gameHeight / correctedDist * 2);
const wallTop = (gameHeight - wallHeight) / 2;
// Draw wall slice
const wallColor = wallType === 0 ? '#600' : '#333';
ctx.fillStyle = wallColor;
ctx.fillRect(i, wallTop, 1, wallHeight);
// Add shading based on distance
const shade = Math.min(1, correctedDist / 10);
ctx.fillStyle = `rgba(0, 0, 0, ${shade})`;
ctx.fillRect(i, wallTop, 1, wallHeight);
}
}
function spawnEnemy() {
// Find a position not too close to player
let angle = Math.random() * Math.PI * 2;
let distance = 5 + Math.random() * 5;
let x = playerX + Math.cos(angle) * distance;
let y = playerY + Math.sin(angle) * distance;
// Make sure it's not inside a wall
while (checkWallCollision(x, y)) {
angle = Math.random() * Math.PI * 2;
distance = 5 + Math.random() * 5;
x = playerX + Math.cos(angle) * distance;
y = playerY + Math.sin(angle) * distance;
}
enemies.push({
x,
y,
health: 100,
speed: 0.03 + Math.random() * 0.02,
lastAttack: 0,
attackCooldown: 1000,
damage: 10
});
}
function updateEnemies() {
for (let i = enemies.length - 1; i >= 0; i--) {
const enemy = enemies[i];
// Move toward player
const angleToPlayer = Math.atan2(playerY - enemy.y, playerX - enemy.x);
const moveX = Math.cos(angleToPlayer) * enemy.speed;
const moveY = Math.sin(angleToPlayer) * enemy.speed;
// Check wall collisions
if (!checkWallCollision(enemy.x + moveX, enemy.y + moveY)) {
enemy.x += moveX;
enemy.y += moveY;
}
// Check distance to player
const dx = playerX - enemy.x;
const dy = playerY - enemy.y;
const distance = Math.sqrt(dx * dx + dy * dy);
// Attack if close enough
const now = Date.now();
if (distance < 1 && now - enemy.lastAttack > enemy.attackCooldown) {
takeDamage(enemy.damage);
enemy.lastAttack = now;
// Blood effect
bloodEffect.style.backgroundColor = 'rgba(255, 0, 0, 0.5)';
setTimeout(() => {
bloodEffect.style.backgroundColor = 'rgba(255, 0, 0, 0)';
}, 200);
}
// Draw enemy
const enemyAngle = angleToPlayer - playerAngle;
const enemyDistance = distance;
// Only draw if in field of view
if (Math.abs(enemyAngle) < Math.PI / 3) {
const correctedDist = enemyDistance * Math.cos(enemyAngle);
const enemySize = Math.min(200, gameHeight / correctedDist);
const enemyX = gameWidth / 2 + Math.tan(enemyAngle) * gameWidth / 2;
const enemyY = gameHeight / 2;
// Simple enemy sprite
ctx.fillStyle = '#f00';
ctx.beginPath();
ctx.arc(enemyX, enemyY, enemySize / 2, 0, Math.PI * 2);
ctx.fill();
// Health bar
const healthBarWidth = enemySize;
const healthBarHeight = 5;
ctx.fillStyle = '#300';
ctx.fillRect(enemyX - healthBarWidth / 2, enemyY - enemySize / 2 - 10, healthBarWidth, healthBarHeight);
ctx.fillStyle = '#f00';
ctx.fillRect(enemyX - healthBarWidth / 2, enemyY - enemySize / 2 - 10, healthBarWidth * (enemy.health / 100), healthBarHeight);
}
// Remove dead enemies
if (enemy.health <= 0) {
enemies.splice(i, 1);
// Add blood particles
for (let j = 0; j < 10; j++) {
particles.push({
x: enemy.x,
y: enemy.y,
size: 0.1 + Math.random() * 0.1,
color: '#f00',
speed: 0.05 + Math.random() * 0.1,
angle: Math.random() * Math.PI * 2,
life: 30 + Math.random() * 30
});
}
// Increase score
score += 10;
updateScore();
// Random weapon drop
if (Math.random() < 0.2) {
const weaponTypes = ['pistol', 'rifle', 'shotgun'];
const randomWeapon = weaponTypes[Math.floor(Math.random() * weaponTypes.length)];
if (randomWeapon !== currentWeapon) {
weapons[randomWeapon].ammo = weapons[randomWeapon].maxAmmo;
currentWeapon = randomWeapon;
updateWeapon();
// Show weapon pickup effect
enemyHitEffect.style.backgroundColor = 'rgba(0, 255, 0, 0.3)';
setTimeout(() => {
enemyHitEffect.style.backgroundColor = 'rgba(255, 255, 255, 0)';
}, 200);
}
}
}
}
}
function updateParticles() {
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
// Update position
p.x += Math.cos(p.angle) * p.speed;
p.y += Math.sin(p.angle) * p.speed;
p.life--;
// Draw particle
const angle = Math.atan2(p.y - playerY, p.x - playerX) - playerAngle;
const distance = Math.sqrt(Math.pow(p.x - playerX, 2) + Math.pow(p.y - playerY, 2));
if (Math.abs(angle) < Math.PI / 3) {
const correctedDist = distance * Math.cos(angle);
const size = Math.min(20, p.size * gameHeight / correctedDist);
const x = gameWidth / 2 + Math.tan(angle) * gameWidth / 2;
const y = gameHeight / 2;
ctx.fillStyle = p.color;
ctx.beginPath();
ctx.arc(x, y, size, 0, Math.PI * 2);
ctx.fill();
}
// Remove dead particles
if (p.life <= 0) {
particles.splice(i, 1);
}
}
}
function drawWeapon() {
const weapon = weapons[currentWeapon];
const weaponHeight = gameHeight / 2;
const weaponWidth = weaponHeight * 0.5;
const weaponX = gameWidth / 2 - weaponWidth / 2;
const weaponY = gameHeight - weaponHeight;
// Simple weapon graphic
ctx.fillStyle = weapon.color;
ctx.fillRect(weaponX, weaponY, weaponWidth, weaponHeight);
// Add some details
ctx.fillStyle = '#333';
ctx.fillRect(weaponX + 10, weaponY + 20, weaponWidth - 20, 10);
// Muzzle flash when shooting
const now = Date.now();
if (now - lastShotTime < 100) {
ctx.fillStyle = '#ff0';
ctx.fillRect(weaponX + weaponWidth, weaponY + 20, 30, 10);
}
}
function shoot() {
const now = Date.now();
const weapon = weapons[currentWeapon];
if (now - lastShotTime < weapon.fireRate || isReloading) return;
if (weapon.ammo <= 0) {
reload();
return;
}
lastShotTime = now;
weapon.ammo--;
updateAmmo();
// Create muzzle flash particle
particles.push({
x: playerX + Math.cos(playerAngle) * 0.5,
y: playerY + Math.sin(playerAngle) * 0.5,
size: 0.2,
color: '#ff0',
speed: 0.2,
angle: playerAngle,
life: 5
});
// Check if hit an enemy
for (let i = 0; i < enemies.length; i++) {
const enemy = enemies[i];
// Calculate angle to enemy
const angleToEnemy = Math.atan2(enemy.y - playerY, enemy.x - playerX);
const angleDiff = Math.abs(normalizeAngle(angleToEnemy - playerAngle));
// Check if enemy is in front of player (within 5 degrees)
if (angleDiff < 0.087) { // ~5 degrees in radians
const distance = Math.sqrt(Math.pow(enemy.x - playerX, 2) + Math.pow(enemy.y - playerY, 2));
// Check line of sight (simple version)
let blocked = false;
const steps = 10;
for (let j = 1; j <= steps; j++) {
const checkX = playerX + (enemy.x - playerX) * (j / steps);
const checkY = playerY + (enemy.y - playerY) * (j / steps);
if (checkWallCollision(checkX, checkY)) {
blocked = true;
break;
}
}
if (!blocked) {
// Hit the enemy
enemy.health -= weapon.damage;
// Hit effect
enemyHitEffect.style.backgroundColor = 'rgba(255, 255, 255, 0.5)';
setTimeout(() => {
enemyHitEffect.style.backgroundColor = 'rgba(255, 255, 255, 0)';
}, 100);
// Blood particles
for (let j = 0; j < 5; j++) {
particles.push({
x: enemy.x,
y: enemy.y,
size: 0.1 + Math.random() * 0.1,
color: '#f00',
speed: 0.05 + Math.random() * 0.1,
angle: playerAngle + (Math.random() - 0.5) * 0.5,
life: 20 + Math.random() * 20
});
}
break; // Only hit one enemy per shot
}
}
}
// Auto reload if empty
if (weapon.ammo <= 0) {
reload();
}
}
function reload() {
if (isReloading || weapons[currentWeapon].ammo === weapons[currentWeapon].maxAmmo) return;
isReloading = true;
reloadIndicator.style.display = 'block';
reloadFill.style.width = '0%';
const reloadTime = weapons[currentWeapon].reloadTime;
const startTime = Date.now();
const reloadInterval = setInterval(() => {
const elapsed = Date.now() - startTime;
const progress = (elapsed / reloadTime) * 100;
reloadFill.style.width = `${progress}%`;
if (elapsed >= reloadTime) {
clearInterval(reloadInterval);
weapons[currentWeapon].ammo = weapons[currentWeapon].maxAmmo;
updateAmmo();
isReloading = false;
reloadIndicator.style.display = 'none';
}
}, 16);
}
function takeDamage(amount) {
health -= amount;
updateHealth();
if (health <= 0) {
health = 0;
updateHealth();
gameOver();
}
}
function updateHealth() {
healthFill.style.width = `${health}%`;
// Change color based on health
if (health > 60) {
healthFill.style.backgroundColor = '#0f0';
} else if (health > 30) {
healthFill.style.backgroundColor = '#ff0';
} else {
healthFill.style.backgroundColor = '#f00';
}
}
function updateAmmo() {
ammoCount.textContent = `${weapons[currentWeapon].ammo}/${weapons[currentWeapon].maxAmmo}`;
}
function updateScore() {
scoreDisplay.textContent = `SCORE: ${score}`;
}
function updateWeapon() {
weaponDisplay.textContent = weapons[currentWeapon].name;
}
function gameOver() {
gameRunning = false;
finalScore.textContent = score;
gameOverScreen.style.display = 'flex';
}
function checkWallCollision(x, y) {
const mapX = Math.floor(x + 0.5);
const mapY = Math.floor(y + 0.5);
return walls.some(wall => wall.x === mapX && wall.y === mapY);
}
function normalizeAngle(angle) {
while (angle < -Math.PI) angle += Math.PI * 2;
while (angle > Math.PI) angle -= Math.PI * 2;
return angle;
}
// Handle window resize
window.addEventListener('resize', () => {
gameWidth = window.innerWidth;
gameHeight = window.innerHeight;
canvas.width = gameWidth;
canvas.height = gameHeight;
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Davi111/games" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>