| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Chainsaw Massacre: 8-Bit Horror</title>WWWWWWWWWW |
| <script src="https://cdn.tailwindcss.com"></script>WWWWW |
| <style> |
| body { |
| margin: 0; |
| overflow: hidden; |
| background-color: #111; |
| font-family: 'Press Start 2P', cursive; |
| } |
| canvas { |
| display: block; |
| } |
| #ui { |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| color: white; |
| text-shadow: 2px 2px 0 #000; |
| pointer-events: none; |
| } |
| #health-bar { |
| width: 200px; |
| height: 20px; |
| border: 3px solid white; |
| margin: 10px; |
| } |
| #health-fill { |
| height: 100%; |
| width: 100%; |
| background-color: red; |
| transition: width 0.3s; |
| } |
| #game-over { |
| display: none; |
| position: absolute; |
| top: 50%; |
| left: 50%; |
| transform: translate(-50%, -50%); |
| background-color: rgba(0, 0, 0, 0.8); |
| padding: 20px; |
| border: 5px solid red; |
| text-align: center; |
| } |
| #start-screen { |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| background-color: #000; |
| display: flex; |
| flex-direction: column; |
| justify-content: center; |
| align-items: center; |
| color: white; |
| z-index: 100; |
| } |
| @font-face { |
| font-family: 'Press Start 2P'; |
| src: url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap'); |
| } |
| .pixel-text { |
| font-family: 'Press Start 2P', cursive; |
| } |
| .blood-particle { |
| position: absolute; |
| width: 5px; |
| height: 5px; |
| background-color: red; |
| border-radius: 50%; |
| pointer-events: none; |
| } |
| </style> |
| </head> |
| <body> |
| <div id="start-screen" class="pixel-text"> |
| <h1 class="text-4xl mb-8 text-red-600">CHAINSAW MASSACRE</h1> |
| <p class="mb-4 text-yellow-400">8-BIT HORROR</p> |
| <p class="mb-8 text-green-400">Find and eliminate all targets</p> |
| <button id="start-btn" class="bg-red-600 hover:bg-red-700 text-white px-8 py-4 rounded-lg text-xl transition-all hover:scale-110"> |
| START GAME |
| </button> |
| <div class="mt-12 text-xs text-gray-400"> |
| <p>WASD to move | SPACE to attack | SHIFT to run</p> |
| <p>Left click to chainsaw | Right click to hide</p> |
| </div> |
| </div> |
|
|
| <div id="ui" class="pixel-text"> |
| <div class="flex justify-between items-center p-4"> |
| <div> |
| <div id="health-bar"> |
| <div id="health-fill"></div> |
| </div> |
| <div id="score" class="mt-2">KILLS: 0</div> |
| </div> |
| <div id="phase" class="text-xl">PHASE 1: HUNT KIDS</div> |
| <div id="timer" class="text-xl">TIME: 00:00</div> |
| </div> |
| </div> |
|
|
| <div id="game-over" class="pixel-text"> |
| <h1 class="text-4xl text-red-600 mb-4">GAME OVER</h1> |
| <p id="final-score" class="text-2xl mb-8">You killed 0 victims</p> |
| <button id="restart-btn" class="bg-red-600 hover:bg-red-700 text-white px-8 py-4 rounded-lg text-xl"> |
| PLAY AGAIN |
| </button> |
| </div> |
|
|
| <canvas id="gameCanvas"></canvas> |
|
|
| <script> |
| |
| const canvas = document.getElementById('gameCanvas'); |
| const ctx = canvas.getContext('2d'); |
| const startScreen = document.getElementById('start-screen'); |
| const startBtn = document.getElementById('start-btn'); |
| const gameOverScreen = document.getElementById('game-over'); |
| const restartBtn = document.getElementById('restart-btn'); |
| const healthFill = document.getElementById('health-fill'); |
| const scoreDisplay = document.getElementById('score'); |
| const phaseDisplay = document.getElementById('phase'); |
| const timerDisplay = document.getElementById('timer'); |
| const finalScoreDisplay = document.getElementById('final-score'); |
| |
| |
| canvas.width = window.innerWidth; |
| canvas.height = window.innerHeight; |
| |
| |
| let gameRunning = false; |
| let score = 0; |
| let health = 100; |
| let gameTime = 0; |
| let gamePhase = 1; |
| let timerInterval; |
| |
| |
| const player = { |
| x: canvas.width / 2, |
| y: canvas.height / 2, |
| size: 30, |
| speed: 3, |
| isAttacking: false, |
| isHiding: false, |
| direction: 0, |
| chainsawSound: new Audio('data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YU...'), |
| }; |
| |
| |
| let kids = []; |
| const KID_COUNT = 8; |
| |
| |
| let police = []; |
| const POLICE_COUNT = 5; |
| |
| |
| const house = { |
| walls: [ |
| {x: 200, y: 200, width: 400, height: 300, color: '#8B4513'}, |
| {x: 300, y: 150, width: 200, height: 50, color: '#A52A2A'}, |
| {x: 600, y: 300, width: 100, height: 200, color: '#8B4513'}, |
| {x: 200, y: 500, width: 200, height: 50, color: '#228B22'}, |
| {x: 400, y: 500, width: 300, height: 50, color: '#228B22'}, |
| ], |
| doors: [ |
| {x: 350, y: 500, width: 50, height: 30, color: '#654321'} |
| ], |
| windows: [ |
| {x: 250, y: 250, width: 50, height: 50, color: '#87CEEB'}, |
| {x: 450, y: 250, width: 50, height: 50, color: '#87CEEB'}, |
| {x: 620, y: 350, width: 50, height: 50, color: '#87CEEB'} |
| ] |
| }; |
| |
| |
| const trees = [ |
| {x: 100, y: 400, trunkWidth: 20, trunkHeight: 60, leavesWidth: 80, leavesHeight: 100}, |
| {x: 700, y: 400, trunkWidth: 20, trunkHeight: 60, leavesWidth: 80, leavesHeight: 100}, |
| {x: 150, y: 600, trunkWidth: 20, trunkHeight: 60, leavesWidth: 80, leavesHeight: 100}, |
| {x: 650, y: 600, trunkWidth: 20, trunkHeight: 60, leavesWidth: 80, leavesHeight: 100} |
| ]; |
| |
| |
| function initGame() { |
| |
| gameRunning = true; |
| score = 0; |
| health = 100; |
| gameTime = 0; |
| gamePhase = 1; |
| kids = []; |
| police = []; |
| |
| |
| for (let i = 0; i < KID_COUNT; i++) { |
| kids.push({ |
| x: Math.random() * (canvas.width - 100) + 50, |
| y: Math.random() * (canvas.height - 100) + 50, |
| size: 20, |
| speed: 1 + Math.random() * 1, |
| direction: Math.random() * Math.PI * 2, |
| changeDirectionTimer: 0, |
| isAlive: true, |
| screamSound: new Audio('data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YU...') |
| }); |
| } |
| |
| |
| clearInterval(timerInterval); |
| timerInterval = setInterval(updateTimer, 1000); |
| |
| |
| startScreen.style.display = 'none'; |
| gameOverScreen.style.display = 'none'; |
| |
| |
| requestAnimationFrame(gameLoop); |
| } |
| |
| |
| function gameLoop() { |
| if (!gameRunning) return; |
| |
| |
| ctx.clearRect(0, 0, canvas.width, canvas.height); |
| |
| |
| updatePlayer(); |
| updateKids(); |
| if (gamePhase >= 3) updatePolice(); |
| |
| |
| drawWorld(); |
| |
| |
| drawPlayer(); |
| |
| |
| drawKids(); |
| |
| |
| if (gamePhase >= 3) drawPolice(); |
| |
| |
| checkPhaseTransitions(); |
| |
| |
| if (health <= 0) { |
| gameOver(); |
| return; |
| } |
| |
| |
| requestAnimationFrame(gameLoop); |
| } |
| |
| |
| function updatePlayer() { |
| |
| const moveX = (keys['d'] ? 1 : 0) - (keys['a'] ? 1 : 0); |
| const moveY = (keys['s'] ? 1 : 0) - (keys['w'] ? 1 : 0); |
| |
| if (moveX !== 0 || moveY !== 0) { |
| player.direction = Math.atan2(moveY, moveX); |
| const speed = keys['Shift'] ? player.speed * 1.5 : player.speed; |
| player.x += Math.cos(player.direction) * speed; |
| player.y += Math.sin(player.direction) * speed; |
| |
| |
| player.x = Math.max(player.size, Math.min(canvas.width - player.size, player.x)); |
| player.y = Math.max(player.size, Math.min(canvas.height - player.size, player.y)); |
| } |
| |
| |
| if (mouse.isAttacking && !player.isAttacking) { |
| player.isAttacking = true; |
| player.chainsawSound.play(); |
| |
| |
| const targets = gamePhase >= 3 ? [...kids, ...police] : kids; |
| for (let target of targets) { |
| if (!target.isAlive) continue; |
| |
| const dist = Math.sqrt( |
| Math.pow(target.x - player.x, 2) + |
| Math.pow(target.y - player.y, 2) |
| ); |
| |
| if (dist < player.size + target.size) { |
| |
| target.isAlive = false; |
| score++; |
| scoreDisplay.textContent = `KILLS: ${score}`; |
| |
| |
| createBlood(target.x, target.y); |
| |
| |
| if (target.screamSound) target.screamSound.play(); |
| } |
| } |
| |
| setTimeout(() => { |
| player.isAttacking = false; |
| }, 500); |
| } |
| |
| |
| player.isHiding = mouse.isHiding; |
| } |
| |
| |
| function updateKids() { |
| for (let kid of kids) { |
| if (!kid.isAlive) continue; |
| |
| |
| kid.changeDirectionTimer--; |
| if (kid.changeDirectionTimer <= 0) { |
| kid.direction = Math.random() * Math.PI * 2; |
| kid.changeDirectionTimer = 30 + Math.random() * 60; |
| } |
| |
| |
| kid.x += Math.cos(kid.direction) * kid.speed; |
| kid.y += Math.sin(kid.direction) * kid.speed; |
| |
| |
| kid.x = Math.max(kid.size, Math.min(canvas.width - kid.size, kid.x)); |
| kid.y = Math.max(kid.size, Math.min(canvas.height - kid.size, kid.y)); |
| |
| |
| const distToPlayer = Math.sqrt( |
| Math.pow(kid.x - player.x, 2) + |
| Math.pow(kid.y - player.y, 2) |
| ); |
| |
| if (distToPlayer < 200) { |
| |
| const angleAway = Math.atan2(kid.y - player.y, kid.x - player.x); |
| kid.x += Math.cos(angleAway) * kid.speed * 2; |
| kid.y += Math.sin(angleAway) * kid.speed * 2; |
| } |
| } |
| } |
| |
| |
| function updatePolice() { |
| if (police.length === 0) { |
| |
| for (let i = 0; i < POLICE_COUNT; i++) { |
| police.push({ |
| x: Math.random() * (canvas.width - 100) + 50, |
| y: Math.random() * (canvas.height - 100) + 50, |
| size: 25, |
| speed: 2 + Math.random() * 1, |
| direction: Math.random() * Math.PI * 2, |
| changeDirectionTimer: 0, |
| isAlive: true, |
| shootTimer: 0 |
| }); |
| } |
| } |
| |
| for (let cop of police) { |
| if (!cop.isAlive) continue; |
| |
| |
| cop.direction = Math.atan2(player.y - cop.y, player.x - cop.x); |
| cop.x += Math.cos(cop.direction) * cop.speed; |
| cop.y += Math.sin(cop.direction) * cop.speed; |
| |
| |
| cop.shootTimer--; |
| if (cop.shootTimer <= 0) { |
| cop.shootTimer = 60 + Math.random() * 60; |
| |
| |
| const distToPlayer = Math.sqrt( |
| Math.pow(player.x - cop.x, 2) + |
| Math.pow(player.y - cop.y, 2) |
| ); |
| |
| if (distToPlayer < 300 && !player.isHiding) { |
| |
| health -= 10; |
| healthFill.style.width = `${health}%`; |
| |
| |
| createBlood(player.x, player.y); |
| } |
| } |
| } |
| } |
| |
| |
| function drawWorld() { |
| |
| ctx.fillStyle = '#228B22'; |
| ctx.fillRect(0, 0, canvas.width, canvas.height); |
| |
| |
| for (let wall of house.walls) { |
| ctx.fillStyle = wall.color; |
| ctx.fillRect(wall.x, wall.y, wall.width, wall.height); |
| } |
| |
| |
| for (let door of house.doors) { |
| ctx.fillStyle = door.color; |
| ctx.fillRect(door.x, door.y, door.width, door.height); |
| } |
| |
| |
| for (let window of house.windows) { |
| ctx.fillStyle = window.color; |
| ctx.fillRect(window.x, window.y, window.width, window.height); |
| } |
| |
| |
| for (let tree of trees) { |
| |
| ctx.fillStyle = '#8B4513'; |
| ctx.fillRect( |
| tree.x - tree.trunkWidth/2, |
| tree.y - tree.trunkHeight/2, |
| tree.trunkWidth, |
| tree.trunkHeight |
| ); |
| |
| |
| ctx.fillStyle = '#006400'; |
| ctx.beginPath(); |
| ctx.ellipse( |
| tree.x, |
| tree.y - tree.leavesHeight/2, |
| tree.leavesWidth/2, |
| tree.leavesHeight/2, |
| 0, 0, Math.PI * 2 |
| ); |
| ctx.fill(); |
| } |
| } |
| |
| |
| function drawPlayer() { |
| |
| ctx.fillStyle = player.isHiding ? '#555' : '#333'; |
| ctx.beginPath(); |
| ctx.arc(player.x, player.y, player.size, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| |
| ctx.fillStyle = '#FFF'; |
| ctx.beginPath(); |
| ctx.arc( |
| player.x + Math.cos(player.direction) * player.size/2, |
| player.y + Math.sin(player.direction) * player.size/2, |
| player.size/4, 0, Math.PI * 2 |
| ); |
| ctx.fill(); |
| |
| |
| if (player.isAttacking) { |
| ctx.fillStyle = '#FF0000'; |
| ctx.beginPath(); |
| ctx.arc( |
| player.x + Math.cos(player.direction) * player.size * 1.5, |
| player.y + Math.sin(player.direction) * player.size * 1.5, |
| player.size/2, 0, Math.PI * 2 |
| ); |
| ctx.fill(); |
| |
| |
| ctx.fillStyle = 'rgba(255, 0, 0, 0.5)'; |
| ctx.beginPath(); |
| ctx.moveTo(player.x, player.y); |
| ctx.lineTo( |
| player.x + Math.cos(player.direction) * player.size * 3, |
| player.y + Math.sin(player.direction) * player.size * 3 |
| ); |
| ctx.lineWidth = 10; |
| ctx.stroke(); |
| } |
| } |
| |
| |
| function drawKids() { |
| for (let kid of kids) { |
| if (!kid.isAlive) continue; |
| |
| |
| ctx.fillStyle = '#FF69B4'; |
| ctx.beginPath(); |
| ctx.arc(kid.x, kid.y, kid.size, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| |
| ctx.fillStyle = '#FFF'; |
| ctx.beginPath(); |
| ctx.arc(kid.x, kid.y - kid.size/3, kid.size/3, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| |
| const distToPlayer = Math.sqrt( |
| Math.pow(kid.x - player.x, 2) + |
| Math.pow(kid.y - player.y, 2) |
| ); |
| |
| if (distToPlayer < 200) { |
| ctx.fillStyle = '#FF0000'; |
| ctx.beginPath(); |
| ctx.arc(kid.x, kid.y - kid.size/3, kid.size/5, 0, Math.PI * 2); |
| ctx.fill(); |
| } |
| } |
| } |
| |
| |
| function drawPolice() { |
| for (let cop of police) { |
| if (!cop.isAlive) continue; |
| |
| |
| ctx.fillStyle = '#0000FF'; |
| ctx.beginPath(); |
| ctx.arc(cop.x, cop.y, cop.size, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| |
| ctx.fillStyle = '#FFF'; |
| ctx.beginPath(); |
| ctx.arc(cop.x, cop.y - cop.size/3, cop.size/3, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| |
| ctx.fillStyle = '#FFD700'; |
| ctx.beginPath(); |
| ctx.arc(cop.x, cop.y, cop.size/3, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| |
| if (cop.shootTimer > 50) { |
| ctx.strokeStyle = '#000'; |
| ctx.lineWidth = 3; |
| ctx.beginPath(); |
| ctx.moveTo(cop.x, cop.y); |
| ctx.lineTo( |
| cop.x + Math.cos(cop.direction) * cop.size * 2, |
| cop.y + Math.sin(cop.direction) * cop.size * 2 |
| ); |
| ctx.stroke(); |
| } |
| } |
| } |
| |
| |
| function createBlood(x, y) { |
| for (let i = 0; i < 10; i++) { |
| const particle = document.createElement('div'); |
| particle.className = 'blood-particle'; |
| particle.style.left = `${x + (Math.random() * 20 - 10)}px`; |
| particle.style.top = `${y + (Math.random() * 20 - 10)}px`; |
| document.body.appendChild(particle); |
| |
| |
| const angle = Math.random() * Math.PI * 2; |
| const distance = 10 + Math.random() * 40; |
| const duration = 300 + Math.random() * 700; |
| |
| particle.animate([ |
| { |
| transform: `translate(0, 0)`, |
| opacity: 1 |
| }, |
| { |
| transform: `translate(${Math.cos(angle) * distance}px, ${Math.sin(angle) * distance}px)`, |
| opacity: 0 |
| } |
| ], { |
| duration: duration, |
| easing: 'cubic-bezier(0.1, 0.8, 0.2, 1)' |
| }); |
| |
| |
| setTimeout(() => { |
| particle.remove(); |
| }, duration); |
| } |
| } |
| |
| |
| function checkPhaseTransitions() { |
| |
| if (gamePhase === 1) { |
| const aliveKids = kids.filter(kid => kid.isAlive).length; |
| if (aliveKids === 0) { |
| gamePhase = 2; |
| phaseDisplay.textContent = 'PHASE 2: HIDE AND WAIT'; |
| |
| |
| setTimeout(() => { |
| gamePhase = 3; |
| phaseDisplay.textContent = 'PHASE 3: FIGHT POLICE'; |
| }, 10000); |
| } |
| } |
| } |
| |
| |
| function updateTimer() { |
| gameTime++; |
| const minutes = Math.floor(gameTime / 60).toString().padStart(2, '0'); |
| const seconds = (gameTime % 60).toString().padStart(2, '0'); |
| timerDisplay.textContent = `TIME: ${minutes}:${seconds}`; |
| } |
| |
| |
| function gameOver() { |
| gameRunning = false; |
| clearInterval(timerInterval); |
| |
| |
| finalScoreDisplay.textContent = `You killed ${score} victims`; |
| gameOverScreen.style.display = 'block'; |
| } |
| |
| |
| const keys = {}; |
| const mouse = { |
| x: 0, |
| y: 0, |
| isAttacking: false, |
| isHiding: false |
| }; |
| |
| window.addEventListener('keydown', (e) => { |
| keys[e.key.toLowerCase()] = true; |
| }); |
| |
| window.addEventListener('keyup', (e) => { |
| keys[e.key.toLowerCase()] = false; |
| }); |
| |
| canvas.addEventListener('mousemove', (e) => { |
| mouse.x = e.clientX; |
| mouse.y = e.clientY; |
| |
| |
| player.direction = Math.atan2( |
| e.clientY - player.y, |
| e.clientX - player.x |
| ); |
| }); |
| |
| canvas.addEventListener('mousedown', (e) => { |
| if (e.button === 0) { |
| mouse.isAttacking = true; |
| } else if (e.button === 2) { |
| mouse.isHiding = true; |
| } |
| }); |
| |
| canvas.addEventListener('mouseup', (e) => { |
| if (e.button === 0) { |
| mouse.isAttacking = false; |
| } else if (e.button === 2) { |
| mouse.isHiding = false; |
| } |
| }); |
| |
| canvas.addEventListener('contextmenu', (e) => { |
| e.preventDefault(); |
| }); |
| |
| |
| window.addEventListener('resize', () => { |
| canvas.width = window.innerWidth; |
| canvas.height = window.innerHeight; |
| }); |
| |
| |
| startBtn.addEventListener('click', initGame); |
| |
| |
| restartBtn.addEventListener('click', initGame); |
| </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=MaxHeadspace/chainsaw-massacre-2d" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |