weapon-pac / index.html
ILLERRAPS's picture
Add 3 files
adf846c verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Weapon-Pac: Battle Maze</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>
@keyframes ghost-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
@keyframes powerup-glow {
0%, 100% { box-shadow: 0 0 5px 2px #ff0; }
50% { box-shadow: 0 0 15px 5px #ff0; }
}
.ghost {
animation: ghost-pulse 2s infinite;
}
.powerup {
animation: powerup-glow 1.5s infinite;
}
.bullet {
transition: all 0.1s linear;
}
#game-container {
background-color: #111;
position: relative;
overflow: hidden;
}
.wall {
background-color: #1a3d8a;
box-shadow: inset 0 0 10px #000;
}
.path {
background-color: #000;
}
#player {
transition: transform 0.2s ease;
z-index: 10;
}
.weapon-icon {
transition: all 0.3s ease;
}
.weapon-icon:hover {
transform: scale(1.2);
filter: drop-shadow(0 0 5px #fff);
}
#game-overlay {
background-color: rgba(0, 0, 0, 0.8);
z-index: 100;
}
</style>
</head>
<body class="bg-gray-900 text-white font-mono flex flex-col items-center justify-center min-h-screen p-4">
<div class="max-w-4xl w-full">
<header class="flex justify-between items-center mb-4">
<h1 class="text-3xl font-bold text-yellow-400 flex items-center">
<i class="fas fa-ghost mr-2"></i> WEAPON-PAC
</h1>
<div class="flex items-center space-x-6">
<div class="bg-gray-800 px-4 py-2 rounded-lg flex items-center">
<span class="text-yellow-400 mr-2">SCORE:</span>
<span id="score" class="text-xl font-bold">0</span>
</div>
<div class="bg-gray-800 px-4 py-2 rounded-lg flex items-center">
<span class="text-red-400 mr-2">LIVES:</span>
<span id="lives" class="text-xl font-bold">3</span>
</div>
<div class="bg-gray-800 px-4 py-2 rounded-lg flex items-center">
<span class="text-blue-400 mr-2">WEAPON:</span>
<span id="current-weapon" class="text-xl font-bold">PISTOL</span>
</div>
</div>
</header>
<div class="relative">
<div id="game-container" class="w-full h-96 border-4 border-blue-800 rounded-lg relative">
<!-- Game elements will be generated by JavaScript -->
</div>
<div id="game-overlay" class="absolute inset-0 flex flex-col items-center justify-center hidden">
<div class="bg-gray-900 border-2 border-yellow-400 rounded-lg p-8 max-w-md text-center">
<h2 id="overlay-title" class="text-3xl font-bold mb-4 text-yellow-400">GAME OVER</h2>
<p id="overlay-message" class="text-xl mb-6">You scored <span id="final-score" class="text-yellow-400">0</span> points!</p>
<button id="restart-btn" class="bg-yellow-500 hover:bg-yellow-600 text-black font-bold py-3 px-6 rounded-lg text-lg transition">
PLAY AGAIN
</button>
</div>
</div>
</div>
<div class="mt-6 bg-gray-800 rounded-lg p-4">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold text-yellow-400">WEAPON SELECTOR</h2>
<div class="flex items-center">
<span class="mr-2">AMMO:</span>
<span id="ammo-count" class="font-bold"></span>
</div>
</div>
<div class="grid grid-cols-5 gap-4">
<div class="weapon-icon bg-gray-700 p-3 rounded-lg cursor-pointer border-2 border-yellow-500 flex flex-col items-center" data-weapon="pistol">
<i class="fas fa-gun text-2xl mb-1"></i>
<span class="text-sm">PISTOL</span>
</div>
<div class="weapon-icon bg-gray-700 p-3 rounded-lg cursor-pointer border-2 border-gray-500 flex flex-col items-center" data-weapon="shotgun">
<i class="fas fa-gun text-2xl mb-1"></i>
<span class="text-sm">SHOTGUN</span>
</div>
<div class="weapon-icon bg-gray-700 p-3 rounded-lg cursor-pointer border-2 border-gray-500 flex flex-col items-center" data-weapon="laser">
<i class="fas fa-bolt text-2xl mb-1"></i>
<span class="text-sm">LASER</span>
</div>
<div class="weapon-icon bg-gray-700 p-3 rounded-lg cursor-pointer border-2 border-gray-500 flex flex-col items-center" data-weapon="rocket">
<i class="fas fa-rocket text-2xl mb-1"></i>
<span class="text-sm">ROCKET</span>
</div>
<div class="weapon-icon bg-gray-700 p-3 rounded-lg cursor-pointer border-2 border-gray-500 flex flex-col items-center" data-weapon="mine">
<i class="fas fa-land-mine-on text-2xl mb-1"></i>
<span class="text-sm">MINE</span>
</div>
</div>
</div>
<div class="mt-6 bg-gray-800 rounded-lg p-4">
<h2 class="text-xl font-bold text-yellow-400 mb-2">CONTROLS</h2>
<div class="grid grid-cols-2 gap-4">
<div>
<p class="mb-2"><span class="font-bold">WASD</span> or <span class="font-bold">Arrow Keys</span> to move</p>
<p><span class="font-bold">SPACE</span> to shoot</p>
</div>
<div>
<p class="mb-2"><span class="font-bold">1-5</span> to switch weapons</p>
<p>Click weapon icons to select</p>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
// Game constants
const GRID_SIZE = 19;
const CELL_SIZE = 20;
const PLAYER_SPEED = 150;
const GHOST_SPEED = 180;
const BULLET_SPEED = 8;
// Game state
let score = 0;
let lives = 3;
let gameRunning = true;
let playerDirection = 'right';
let nextDirection = 'right';
let playerPosition = { x: 1, y: 1 };
let ghosts = [];
let bullets = [];
let powerups = [];
let walls = [];
let pellets = [];
let mines = [];
let currentWeapon = 'pistol';
let ammo = {
pistol: Infinity,
shotgun: 20,
laser: 30,
rocket: 5,
mine: 3
};
// DOM elements
const gameContainer = document.getElementById('game-container');
const scoreDisplay = document.getElementById('score');
const livesDisplay = document.getElementById('lives');
const weaponDisplay = document.getElementById('current-weapon');
const ammoDisplay = document.getElementById('ammo-count');
const gameOverlay = document.getElementById('game-overlay');
const overlayTitle = document.getElementById('overlay-title');
const overlayMessage = document.getElementById('overlay-message');
const finalScore = document.getElementById('final-score');
const restartBtn = document.getElementById('restart-btn');
const weaponIcons = document.querySelectorAll('.weapon-icon');
// Initialize game
initGame();
// Event listeners
document.addEventListener('keydown', handleKeyPress);
restartBtn.addEventListener('click', resetGame);
weaponIcons.forEach(icon => {
icon.addEventListener('click', () => {
const weapon = icon.dataset.weapon;
if (ammo[weapon] > 0 || weapon === 'pistol') {
selectWeapon(weapon);
}
});
});
// Game loop
let lastTime = 0;
function gameLoop(timestamp) {
if (!gameRunning) return;
const deltaTime = timestamp - lastTime;
lastTime = timestamp;
updatePlayer(deltaTime);
updateGhosts(deltaTime);
updateBullets();
updateMines();
checkCollisions();
checkWinCondition();
requestAnimationFrame(gameLoop);
}
requestAnimationFrame(gameLoop);
// Game functions
function initGame() {
// Set up game container
gameContainer.style.width = `${GRID_SIZE * CELL_SIZE}px`;
gameContainer.style.height = `${GRID_SIZE * CELL_SIZE}px`;
// Generate maze
generateMaze();
// Create player
createPlayer();
// Create initial ghosts
createGhosts(3);
// Create initial pellets
createPellets();
// Create powerups
createPowerups();
// Update displays
updateDisplays();
}
function generateMaze() {
// Clear previous elements
gameContainer.innerHTML = '';
walls = [];
// Create border walls
for (let y = 0; y < GRID_SIZE; y++) {
for (let x = 0; x < GRID_SIZE; x++) {
if (x === 0 || y === 0 || x === GRID_SIZE - 1 || y === GRID_SIZE - 1) {
createWall(x, y);
} else if (Math.random() < 0.15 && !(x < 3 && y < 3)) {
createWall(x, y);
}
}
}
// Ensure some paths
for (let x = 1; x < GRID_SIZE - 1; x++) {
if (!walls.some(w => w.x === x && w.y === 1)) {
createPath(x, 1);
}
if (!walls.some(w => w.x === x && w.y === GRID_SIZE - 2)) {
createPath(x, GRID_SIZE - 2);
}
}
for (let y = 1; y < GRID_SIZE - 1; y++) {
if (!walls.some(w => w.x === 1 && w.y === y)) {
createPath(1, y);
}
if (!walls.some(w => w.x === GRID_SIZE - 2 && w.y === y)) {
createPath(GRID_SIZE - 2, y);
}
}
}
function createWall(x, y) {
const wall = document.createElement('div');
wall.className = 'wall absolute';
wall.style.width = `${CELL_SIZE}px`;
wall.style.height = `${CELL_SIZE}px`;
wall.style.left = `${x * CELL_SIZE}px`;
wall.style.top = `${y * CELL_SIZE}px`;
gameContainer.appendChild(wall);
walls.push({ x, y });
}
function createPath(x, y) {
const path = document.createElement('div');
path.className = 'path absolute';
path.style.width = `${CELL_SIZE}px`;
path.style.height = `${CELL_SIZE}px`;
path.style.left = `${x * CELL_SIZE}px`;
path.style.top = `${y * CELL_SIZE}px`;
gameContainer.appendChild(path);
}
function createPlayer() {
const player = document.createElement('div');
player.id = 'player';
player.className = 'absolute bg-yellow-400 rounded-full';
player.style.width = `${CELL_SIZE - 4}px`;
player.style.height = `${CELL_SIZE - 4}px`;
player.style.left = `${playerPosition.x * CELL_SIZE + 2}px`;
player.style.top = `${playerPosition.y * CELL_SIZE + 2}px`;
gameContainer.appendChild(player);
// Add mouth effect based on direction
updatePlayerMouth();
}
function updatePlayerMouth() {
const player = document.getElementById('player');
player.innerHTML = '';
const mouth = document.createElement('div');
mouth.className = 'absolute bg-black rounded-full';
switch(playerDirection) {
case 'right':
mouth.style.width = `${CELL_SIZE / 3}px`;
mouth.style.height = `${CELL_SIZE / 3}px`;
mouth.style.right = '0';
mouth.style.top = '50%';
mouth.style.transform = 'translateY(-50%)';
break;
case 'left':
mouth.style.width = `${CELL_SIZE / 3}px`;
mouth.style.height = `${CELL_SIZE / 3}px`;
mouth.style.left = '0';
mouth.style.top = '50%';
mouth.style.transform = 'translateY(-50%)';
break;
case 'up':
mouth.style.width = `${CELL_SIZE / 3}px`;
mouth.style.height = `${CELL_SIZE / 3}px`;
mouth.style.top = '0';
mouth.style.left = '50%';
mouth.style.transform = 'translateX(-50%)';
break;
case 'down':
mouth.style.width = `${CELL_SIZE / 3}px`;
mouth.style.height = `${CELL_SIZE / 3}px`;
mouth.style.bottom = '0';
mouth.style.left = '50%';
mouth.style.transform = 'translateX(-50%)';
break;
}
player.appendChild(mouth);
}
function createGhosts(count) {
ghosts = [];
for (let i = 0; i < count; i++) {
let x, y;
do {
x = Math.floor(Math.random() * (GRID_SIZE - 4)) + 2;
y = Math.floor(Math.random() * (GRID_SIZE - 4)) + 2;
} while (walls.some(w => w.x === x && w.y === y) ||
(x === playerPosition.x && y === playerPosition.y));
const ghost = {
x,
y,
id: `ghost-${i}`,
direction: ['up', 'down', 'left', 'right'][Math.floor(Math.random() * 4)],
speed: GHOST_SPEED + Math.random() * 50,
color: ['red', 'pink', 'cyan', 'orange'][i % 4]
};
ghosts.push(ghost);
const ghostElement = document.createElement('div');
ghostElement.id = ghost.id;
ghostElement.className = `ghost absolute rounded-full`;
ghostElement.style.backgroundColor = ghost.color;
ghostElement.style.width = `${CELL_SIZE - 4}px`;
ghostElement.style.height = `${CELL_SIZE - 4}px`;
ghostElement.style.left = `${ghost.x * CELL_SIZE + 2}px`;
ghostElement.style.top = `${ghost.y * CELL_SIZE + 2}px`;
// Add eyes
const leftEye = document.createElement('div');
leftEye.className = 'absolute bg-white rounded-full';
leftEye.style.width = `${CELL_SIZE / 4}px`;
leftEye.style.height = `${CELL_SIZE / 4}px`;
leftEye.style.left = `${CELL_SIZE / 4}px`;
leftEye.style.top = `${CELL_SIZE / 4}px`;
const rightEye = leftEye.cloneNode();
rightEye.style.left = `${CELL_SIZE / 2}px`;
ghostElement.appendChild(leftEye);
ghostElement.appendChild(rightEye);
gameContainer.appendChild(ghostElement);
}
}
function createPellets() {
pellets = [];
for (let y = 1; y < GRID_SIZE - 1; y++) {
for (let x = 1; x < GRID_SIZE - 1; x++) {
if (!walls.some(w => w.x === x && w.y === y) &&
!(x === playerPosition.x && y === playerPosition.y) &&
!ghosts.some(g => g.x === x && g.y === y)) {
// Skip some cells to not overcrowd
if (Math.random() > 0.3) {
pellets.push({ x, y });
const pellet = document.createElement('div');
pellet.className = 'absolute bg-white rounded-full';
pellet.style.width = `${CELL_SIZE / 4}px`;
pellet.style.height = `${CELL_SIZE / 4}px`;
pellet.style.left = `${x * CELL_SIZE + CELL_SIZE / 2 - CELL_SIZE / 8}px`;
pellet.style.top = `${y * CELL_SIZE + CELL_SIZE / 2 - CELL_SIZE / 8}px`;
pellet.dataset.x = x;
pellet.dataset.y = y;
gameContainer.appendChild(pellet);
}
}
}
}
}
function createPowerups() {
powerups = [];
// Create 2 powerups in random positions
for (let i = 0; i < 2; i++) {
let x, y;
do {
x = Math.floor(Math.random() * (GRID_SIZE - 4)) + 2;
y = Math.floor(Math.random() * (GRID_SIZE - 4)) + 2;
} while (walls.some(w => w.x === x && w.y === y) ||
(x === playerPosition.x && y === playerPosition.y) ||
ghosts.some(g => g.x === x && g.y === y));
powerups.push({ x, y });
const powerup = document.createElement('div');
powerup.className = 'powerup absolute bg-yellow-400 rounded-full';
powerup.style.width = `${CELL_SIZE / 2}px`;
powerup.style.height = `${CELL_SIZE / 2}px`;
powerup.style.left = `${x * CELL_SIZE + CELL_SIZE / 4}px`;
powerup.style.top = `${y * CELL_SIZE + CELL_SIZE / 4}px`;
powerup.dataset.x = x;
powerup.dataset.y = y;
gameContainer.appendChild(powerup);
}
}
function handleKeyPress(e) {
if (!gameRunning) return;
switch(e.key) {
case 'ArrowUp':
case 'w':
case 'W':
nextDirection = 'up';
break;
case 'ArrowDown':
case 's':
case 'S':
nextDirection = 'down';
break;
case 'ArrowLeft':
case 'a':
case 'A':
nextDirection = 'left';
break;
case 'ArrowRight':
case 'd':
case 'D':
nextDirection = 'right';
break;
case ' ':
shoot();
break;
case '1':
selectWeapon('pistol');
break;
case '2':
selectWeapon('shotgun');
break;
case '3':
selectWeapon('laser');
break;
case '4':
selectWeapon('rocket');
break;
case '5':
selectWeapon('mine');
break;
}
}
function selectWeapon(weapon) {
if (ammo[weapon] <= 0 && weapon !== 'pistol') return;
currentWeapon = weapon;
weaponDisplay.textContent = weapon.toUpperCase();
// Update weapon icons
weaponIcons.forEach(icon => {
if (icon.dataset.weapon === weapon) {
icon.classList.add('border-yellow-500');
icon.classList.remove('border-gray-500');
} else {
icon.classList.add('border-gray-500');
icon.classList.remove('border-yellow-500');
}
});
updateDisplays();
}
function shoot() {
if (ammo[currentWeapon] <= 0 && currentWeapon !== 'pistol') return;
if (currentWeapon !== 'pistol') {
ammo[currentWeapon]--;
updateDisplays();
}
switch(currentWeapon) {
case 'pistol':
createBullet(playerPosition.x, playerPosition.y, playerDirection);
break;
case 'shotgun':
// Create 3 bullets in a spread
createBullet(playerPosition.x, playerPosition.y, playerDirection);
createBullet(playerPosition.x, playerPosition.y, rotateDirection(playerDirection, -15));
createBullet(playerPosition.x, playerPosition.y, rotateDirection(playerDirection, 15));
break;
case 'laser':
createLaser(playerPosition.x, playerPosition.y, playerDirection);
break;
case 'rocket':
createRocket(playerPosition.x, playerPosition.y, playerDirection);
break;
case 'mine':
placeMine(playerPosition.x, playerPosition.y);
break;
}
}
function rotateDirection(direction, degrees) {
const directions = ['up', 'right', 'down', 'left'];
let index = directions.indexOf(direction);
const steps = Math.round(degrees / 90);
index = (index + steps + directions.length) % directions.length;
return directions[index];
}
function createBullet(x, y, direction) {
const bullet = {
x: x * CELL_SIZE + CELL_SIZE / 2,
y: y * CELL_SIZE + CELL_SIZE / 2,
direction,
speed: BULLET_SPEED,
id: `bullet-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,
type: 'bullet'
};
bullets.push(bullet);
const bulletElement = document.createElement('div');
bulletElement.id = bullet.id;
bulletElement.className = 'bullet absolute bg-yellow-400 rounded-full';
bulletElement.style.width = `${CELL_SIZE / 4}px`;
bulletElement.style.height = `${CELL_SIZE / 4}px`;
bulletElement.style.left = `${bullet.x - CELL_SIZE / 8}px`;
bulletElement.style.top = `${bullet.y - CELL_SIZE / 8}px`;
gameContainer.appendChild(bulletElement);
}
function createLaser(x, y, direction) {
const laser = {
x: x * CELL_SIZE + CELL_SIZE / 2,
y: y * CELL_SIZE + CELL_SIZE / 2,
direction,
distance: 0,
maxDistance: CELL_SIZE * 10,
id: `laser-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,
type: 'laser'
};
bullets.push(laser);
const laserElement = document.createElement('div');
laserElement.id = laser.id;
laserElement.className = 'bullet absolute bg-red-500';
laserElement.style.width = direction === 'left' || direction === 'right' ? '2px' : `${CELL_SIZE / 2}px`;
laserElement.style.height = direction === 'up' || direction === 'down' ? '2px' : `${CELL_SIZE / 2}px`;
updateLaserPosition(laser, laserElement);
gameContainer.appendChild(laserElement);
// Laser only lasts for a short time
setTimeout(() => {
if (laserElement.parentNode) {
gameContainer.removeChild(laserElement);
bullets = bullets.filter(b => b.id !== laser.id);
}
}, 300);
}
function createRocket(x, y, direction) {
const rocket = {
x: x * CELL_SIZE + CELL_SIZE / 2,
y: y * CELL_SIZE + CELL_SIZE / 2,
direction,
speed: BULLET_SPEED * 0.8,
id: `rocket-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,
type: 'rocket'
};
bullets.push(rocket);
const rocketElement = document.createElement('div');
rocketElement.id = rocket.id;
rocketElement.className = 'bullet absolute';
rocketElement.innerHTML = '<i class="fas fa-rocket text-orange-500"></i>';
rocketElement.style.fontSize = `${CELL_SIZE / 2}px`;
rocketElement.style.transform = `rotate(${direction === 'right' ? 0 : direction === 'left' ? 180 : direction === 'up' ? -90 : 90}deg)`;
rocketElement.style.left = `${rocket.x - CELL_SIZE / 4}px`;
rocketElement.style.top = `${rocket.y - CELL_SIZE / 4}px`;
gameContainer.appendChild(rocketElement);
}
function placeMine(x, y) {
// Check if there's already a mine here
if (mines.some(m => m.x === x && m.y === y)) return;
const mine = {
x,
y,
id: `mine-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`
};
mines.push(mine);
const mineElement = document.createElement('div');
mineElement.id = mine.id;
mineElement.className = 'absolute';
mineElement.innerHTML = '<i class="fas fa-land-mine-on text-gray-400"></i>';
mineElement.style.fontSize = `${CELL_SIZE / 2}px`;
mineElement.style.left = `${x * CELL_SIZE + CELL_SIZE / 4}px`;
mineElement.style.top = `${y * CELL_SIZE + CELL_SIZE / 4}px`;
gameContainer.appendChild(mineElement);
}
function updatePlayer(deltaTime) {
// Check if we can change direction
const nextX = playerPosition.x + (nextDirection === 'left' ? -1 : nextDirection === 'right' ? 1 : 0);
const nextY = playerPosition.y + (nextDirection === 'up' ? -1 : nextDirection === 'down' ? 1 : 0);
if (!walls.some(w => w.x === nextX && w.y === nextY)) {
playerDirection = nextDirection;
updatePlayerMouth();
}
// Move in current direction
const moveX = playerDirection === 'left' ? -1 : playerDirection === 'right' ? 1 : 0;
const moveY = playerDirection === 'up' ? -1 : playerDirection === 'down' ? 1 : 0;
const newX = playerPosition.x + moveX * (deltaTime / PLAYER_SPEED);
const newY = playerPosition.y + moveY * (deltaTime / PLAYER_SPEED);
// Check wall collisions
const cellX = Math.round(newX);
const cellY = Math.round(newY);
if (!walls.some(w => w.x === cellX && w.y === cellY)) {
playerPosition.x = newX;
playerPosition.y = newY;
// Wrap around edges
if (playerPosition.x < 0) playerPosition.x = GRID_SIZE - 1;
if (playerPosition.x >= GRID_SIZE) playerPosition.x = 0;
if (playerPosition.y < 0) playerPosition.y = GRID_SIZE - 1;
if (playerPosition.y >= GRID_SIZE) playerPosition.y = 0;
// Update player element
const player = document.getElementById('player');
player.style.left = `${playerPosition.x * CELL_SIZE + 2}px`;
player.style.top = `${playerPosition.y * CELL_SIZE + 2}px`;
}
}
function updateGhosts(deltaTime) {
ghosts.forEach(ghost => {
// Change direction randomly or when hitting a wall
if (Math.random() < 0.02 ||
walls.some(w =>
w.x === ghost.x + (ghost.direction === 'left' ? -1 : ghost.direction === 'right' ? 1 : 0) &&
w.y === ghost.y + (ghost.direction === 'up' ? -1 : ghost.direction === 'down' ? 1 : 0))) {
const directions = ['up', 'down', 'left', 'right'];
ghost.direction = directions[Math.floor(Math.random() * directions.length)];
}
// Move ghost
const moveX = ghost.direction === 'left' ? -1 : ghost.direction === 'right' ? 1 : 0;
const moveY = ghost.direction === 'up' ? -1 : ghost.direction === 'down' ? 1 : 0;
const newX = ghost.x + moveX * (deltaTime / ghost.speed);
const newY = ghost.y + moveY * (deltaTime / ghost.speed);
// Check wall collisions
const cellX = Math.round(newX);
const cellY = Math.round(newY);
if (!walls.some(w => w.x === cellX && w.y === cellY)) {
ghost.x = newX;
ghost.y = newY;
// Update ghost element
const ghostElement = document.getElementById(ghost.id);
if (ghostElement) {
ghostElement.style.left = `${ghost.x * CELL_SIZE + 2}px`;
ghostElement.style.top = `${ghost.y * CELL_SIZE + 2}px`;
// Update eyes direction
const eyes = ghostElement.querySelectorAll('div');
eyes.forEach(eye => {
eye.style.left = ghost.direction === 'left' ? `${CELL_SIZE / 4}px` :
ghost.direction === 'right' ? `${CELL_SIZE / 2}px` :
`${CELL_SIZE / 3}px`;
eye.style.top = ghost.direction === 'up' ? `${CELL_SIZE / 4}px` :
ghost.direction === 'down' ? `${CELL_SIZE / 2}px` :
`${CELL_SIZE / 3}px`;
});
}
}
});
}
function updateBullets() {
bullets.forEach(bullet => {
if (bullet.type === 'laser') {
// Laser grows until max distance
bullet.distance += BULLET_SPEED * 2;
if (bullet.distance > bullet.maxDistance) {
bullet.distance = bullet.maxDistance;
}
const laserElement = document.getElementById(bullet.id);
if (laserElement) {
updateLaserPosition(bullet, laserElement);
}
} else {
// Move bullet
const moveX = bullet.direction === 'left' ? -1 : bullet.direction === 'right' ? 1 : 0;
const moveY = bullet.direction === 'up' ? -1 : bullet.direction === 'down' ? 1 : 0;
bullet.x += moveX * bullet.speed;
bullet.y += moveY * bullet.speed;
// Update bullet element
const bulletElement = document.getElementById(bullet.id);
if (bulletElement) {
if (bullet.type === 'rocket') {
bulletElement.style.left = `${bullet.x - CELL_SIZE / 4}px`;
bulletElement.style.top = `${bullet.y - CELL_SIZE / 4}px`;
} else {
bulletElement.style.left = `${bullet.x - CELL_SIZE / 8}px`;
bulletElement.style.top = `${bullet.y - CELL_SIZE / 8}px`;
}
}
}
// Check if bullet is out of bounds
if (bullet.x < 0 || bullet.x > GRID_SIZE * CELL_SIZE ||
bullet.y < 0 || bullet.y > GRID_SIZE * CELL_SIZE) {
removeBullet(bullet.id);
}
});
}
function updateLaserPosition(laser, laserElement) {
const centerX = laser.x;
const centerY = laser.y;
if (laser.direction === 'left' || laser.direction === 'right') {
const length = laser.distance;
const left = laser.direction === 'left' ? centerX - length : centerX;
laserElement.style.left = `${left}px`;
laserElement.style.top = `${centerY - 1}px`;
laserElement.style.width = `${length}px`;
} else {
const length = laser.distance;
const top = laser.direction === 'up' ? centerY - length : centerY;
laserElement.style.left = `${centerX - 1}px`;
laserElement.style.top = `${top}px`;
laserElement.style.height = `${length}px`;
}
}
function updateMines() {
// Mines don't move, just check for explosions
}
function removeBullet(id) {
const bulletElement = document.getElementById(id);
if (bulletElement && bulletElement.parentNode) {
gameContainer.removeChild(bulletElement);
}
bullets = bullets.filter(b => b.id !== id);
}
function checkCollisions() {
// Check pellet collection
const playerCellX = Math.round(playerPosition.x);
const playerCellY = Math.round(playerPosition.y);
// Check pellets
const pelletIndex = pellets.findIndex(p => p.x === playerCellX && p.y === playerCellY);
if (pelletIndex !== -1) {
pellets.splice(pelletIndex, 1);
score += 10;
// Remove pellet element
const pelletElements = document.querySelectorAll(`[data-x="${playerCellX}"][data-y="${playerCellY}"]`);
pelletElements.forEach(el => {
if (el.parentNode) gameContainer.removeChild(el);
});
updateDisplays();
}
// Check powerups
const powerupIndex = powerups.findIndex(p => p.x === playerCellX && p.y === playerCellY);
if (powerupIndex !== -1) {
powerups.splice(powerupIndex, 1);
score += 50;
// Refill all ammo
Object.keys(ammo).forEach(key => {
if (key !== 'pistol') ammo[key] += 10;
});
// Remove powerup element
const powerupElements = document.querySelectorAll(`[data-x="${playerCellX}"][data-y="${playerCellY}"]`);
powerupElements.forEach(el => {
if (el.parentNode) gameContainer.removeChild(el);
});
updateDisplays();
}
// Check ghost collisions
ghosts.forEach(ghost => {
const ghostCellX = Math.round(ghost.x);
const ghostCellY = Math.round(ghost.y);
if (ghostCellX === playerCellX && ghostCellY === playerCellY) {
loseLife();
}
});
// Check bullet collisions with ghosts
bullets.forEach(bullet => {
const bulletCellX = Math.round(bullet.x / CELL_SIZE);
const bulletCellY = Math.round(bullet.y / CELL_SIZE);
if (bullet.type === 'laser') {
// Laser hits everything in its path
const hitGhosts = ghosts.filter(ghost => {
const ghostCellX = Math.round(ghost.x);
const ghostCellY = Math.round(ghost.y);
if (bullet.direction === 'left' || bullet.direction === 'right') {
return ghostCellY === bulletCellY &&
((bullet.direction === 'left' && ghostCellX <= bulletCellX) ||
(bullet.direction === 'right' && ghostCellX >= bulletCellX));
} else {
return ghostCellX === bulletCellX &&
((bullet.direction === 'up' && ghostCellY <= bulletCellY) ||
(bullet.direction === 'down' && ghostCellY >= bulletCellY));
}
});
hitGhosts.forEach(ghost => {
killGhost(ghost);
});
if (hitGhosts.length > 0) {
removeBullet(bullet.id);
}
} else if (bullet.type === 'rocket') {
// Rocket explodes when hitting a ghost or wall
const hitGhost = ghosts.find(ghost => {
const ghostCellX = Math.round(ghost.x);
const ghostCellY = Math.round(ghost.y);
return ghostCellX === bulletCellX && ghostCellY === bulletCellY;
});
const hitWall = walls.some(w => w.x === bulletCellX && w.y === bulletCellY);
if (hitGhost || hitWall) {
// Explosion effect
createExplosion(bulletCellX, bulletCellY);
removeBullet(bullet.id);
// Kill ghosts in 3x3 area
for (let y = bulletCellY - 1; y <= bulletCellY + 1; y++) {
for (let x = bulletCellX - 1; x <= bulletCellX + 1; x++) {
const ghostIndex = ghosts.findIndex(g =>
Math.round(g.x) === x && Math.round(g.y) === y);
if (ghostIndex !== -1) {
killGhost(ghosts[ghostIndex]);
}
}
}
}
} else {
// Regular bullet hits single ghost
const ghostIndex = ghosts.findIndex(ghost => {
const ghostCellX = Math.round(ghost.x);
const ghostCellY = Math.round(ghost.y);
return ghostCellX === bulletCellX && ghostCellY === bulletCellY;
});
if (ghostIndex !== -1) {
killGhost(ghosts[ghostIndex]);
removeBullet(bullet.id);
}
}
// Check bullet collisions with walls
if (walls.some(w => w.x === bulletCellX && w.y === bulletCellY)) {
removeBullet(bullet.id);
}
});
// Check mine explosions
mines.forEach(mine => {
const mineCellX = mine.x;
const mineCellY = mine.y;
// Check if player stepped on mine
if (playerCellX === mineCellX && playerCellY === mineCellY) {
createExplosion(mineCellX, mineCellY);
removeMine(mine.id);
loseLife();
}
// Check if ghost stepped on mine
const ghostIndex = ghosts.findIndex(ghost =>
Math.round(ghost.x) === mineCellX && Math.round(ghost.y) === mineCellY);
if (ghostIndex !== -1) {
createExplosion(mineCellX, mineCellY);
removeMine(mine.id);
killGhost(ghosts[ghostIndex]);
}
});
}
function createExplosion(x, y) {
const explosion = document.createElement('div');
explosion.className = 'absolute bg-orange-500 rounded-full';
explosion.style.width = `${CELL_SIZE * 3}px`;
explosion.style.height = `${CELL_SIZE * 3}px`;
explosion.style.left = `${x * CELL_SIZE - CELL_SIZE}px`;
explosion.style.top = `${y * CELL_SIZE - CELL_SIZE}px`;
explosion.style.opacity = '0.7';
explosion.style.transform = 'scale(0)';
explosion.style.transition = 'transform 0.3s ease-out, opacity 0.3s ease-out';
gameContainer.appendChild(explosion);
// Animate explosion
setTimeout(() => {
explosion.style.transform = 'scale(1)';
explosion.style.opacity = '0';
}, 10);
// Remove after animation
setTimeout(() => {
if (explosion.parentNode) gameContainer.removeChild(explosion);
}, 500);
}
function removeMine(id) {
const mineElement = document.getElementById(id);
if (mineElement && mineElement.parentNode) {
gameContainer.removeChild(mineElement);
}
mines = mines.filter(m => m.id !== id);
}
function killGhost(ghost) {
score += 100;
// Remove ghost element
const ghostElement = document.getElementById(ghost.id);
if (ghostElement && ghostElement.parentNode) {
// Death animation
ghostElement.style.transform = 'scale(1.5)';
ghostElement.style.opacity = '0';
ghostElement.style.transition = 'all 0.3s ease-out';
setTimeout(() => {
if (ghostElement.parentNode) gameContainer.removeChild(ghostElement);
}, 300);
}
// Remove from array
ghosts = ghosts.filter(g => g.id !== ghost.id);
updateDisplays();
}
function loseLife() {
lives--;
updateDisplays();
if (lives <= 0) {
gameOver(false);
} else {
// Reset player position
playerPosition = { x: 1, y: 1 };
const player = document.getElementById('player');
player.style.left = `${playerPosition.x * CELL_SIZE + 2}px`;
player.style.top = `${playerPosition.y * CELL_SIZE + 2}px`;
// Brief invulnerability
player.style.opacity = '0.5';
setTimeout(() => {
player.style.opacity = '1';
}, 1000);
}
}
function checkWinCondition() {
if (pellets.length === 0 && powerups.length === 0) {
gameOver(true);
}
}
function gameOver(won) {
gameRunning = false;
overlayTitle.textContent = won ? 'VICTORY!' : 'GAME OVER';
overlayTitle.className = won ? 'text-3xl font-bold mb-4 text-green-400' : 'text-3xl font-bold mb-4 text-red-400';
finalScore.textContent = score;
if (won) {
overlayMessage.innerHTML = `You cleared the maze with <span class="text-green-400">${lives}</span> lives remaining!`;
} else {
overlayMessage.innerHTML = 'You were defeated by the ghosts!';
}
gameOverlay.classList.remove('hidden');
}
function updateDisplays() {
scoreDisplay.textContent = score;
livesDisplay.textContent = lives;
weaponDisplay.textContent = currentWeapon.toUpperCase();
ammoDisplay.textContent = ammo[currentWeapon] === Infinity ? '∞' : ammo[currentWeapon];
// Update weapon icons to show ammo
weaponIcons.forEach(icon => {
const weapon = icon.dataset.weapon;
if (weapon !== 'pistol') {
const ammoText = document.createElement('span');
ammoText.className = 'text-xs mt-1';
ammoText.textContent = ammo[weapon];
// Remove existing ammo display if any
const existingAmmo = icon.querySelector('.text-xs');
if (existingAmmo) icon.removeChild(existingAmmo);
icon.appendChild(ammoText);
}
});
}
function resetGame() {
// Reset game state
score = 0;
lives = 3;
gameRunning = true;
playerDirection = 'right';
nextDirection = 'right';
playerPosition = { x: 1, y: 1 };
ghosts = [];
bullets = [];
powerups = [];
pellets = [];
mines = [];
currentWeapon = 'pistol';
ammo = {
pistol: Infinity,
shotgun: 20,
laser: 30,
rocket: 5,
mine: 3
};
// Hide overlay
gameOverlay.classList.add('hidden');
// Reinitialize game
initGame();
// Restart game loop
lastTime = 0;
requestAnimationFrame(gameLoop);
}
});
</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=ILLERRAPS/weapon-pac" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>