Retro Zombie Survival Arcade
by TurboMaker33285 lines9.7 KB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Retro Zombie Survival Arcade</title>
<style>
* {
box-sizing: border-box;
user-select: none;
}
body {
margin: 0;
padding: 0;
background: #111;
color: #fff;
font-family: 'Courier New', Courier, monospace;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
overflow: hidden;
}
#game-container {
position: relative;
width: 800px;
height: 400px;
background: linear-gradient(to bottom, #1a0b2e, #11051c, #05010a);
border: 4px solid #333;
box-shadow: 0 0 20px rgba(255, 0, 0, 0.3);
overflow: hidden;
}
canvas {
display: block;
width: 100%;
height: 100%;
}
.ui-layer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 15px;
}
.hud-top {
display: flex;
justify-content: space-between;
font-size: 18px;
font-weight: bold;
text-shadow: 2px 2px #000;
}
#health-bar-container {
width: 150px;
height: 15px;
background: #441111;
border: 2px solid #ff3333;
margin-top: 5px;
}
#health-bar {
width: 100%;
height: 100%;
background: #ff3333;
transition: width 0.1s ease;
}
.score-color { color: #00ffcc; }
.wave-color { color: #ffcc00; }
.ammo-color { color: #ff9900; }
.screen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(10, 5, 15, 0.85);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
backdrop-filter: blur(4px);
z-index: 10;
text-align: center;
}
.hidden {
display: none !important;
}
h1 {
font-size: 42px;
margin: 0 0 10px 0;
color: #ff3333;
text-shadow: 3px 3px #000, 0 0 10px rgba(255,51,51,0.5);
letter-spacing: 2px;
}
p {
font-size: 14px;
margin: 5px 0 20px 0;
color: #bbb;
}
button {
background: #ff3333;
color: white;
border: none;
padding: 12px 30px;
font-size: 18px;
font-family: inherit;
font-weight: bold;
cursor: pointer;
box-shadow: 0 4px #990000;
transition: background 0.1s;
pointer-events: auto;
}
button:hover {
background: #ff5555;
}
button:active {
transform: translateY(4px);
box-shadow: 0 0 #990000;
}
.controls-hint {
margin-top: 25px;
font-size: 12px;
color: #888;
border-top: 1px solid #333;
padding-top: 15px;
width: 80%;
}
.controls-grid {
display: flex;
justify-content: space-around;
margin-top: 10px;
}
.control-item {
background: #222;
padding: 5px 10px;
border-radius: 4px;
border: 1px solid #444;
}
</style>
</head>
<body>
<div id="game-container">
<!-- Canvas for rendering physics/graphics -->
<canvas id="gameCanvas" width="800" height="400"></canvas>
<!-- Heads Up Display Layer -->
<div class="ui-layer">
<div class="hud-top">
<div>
<div>VITAL SIGNS</div>
<div id="health-bar-container"><div id="health-bar"></div></div>
</div>
<div>
<div>SCORE</div>
<div id="score-display" class="score-color">000000</div>
</div>
<div>
<div>WAVE</div>
<div id="wave-display" class="wave-color" style="text-align: right;">1</div>
</div>
<div>
<div>MUNITIONS</div>
<div id="ammo-display" class="ammo-color" style="text-align: right;">30 / ∞</div>
</div>
</div>
<div></div> <!-- Spacer -->
</div>
<!-- Start/Menu Screen -->
<div id="start-screen" class="screen">
<h1>OUTBREAK: ARCADE</h1>
<p>Slay the endless dynamic horde. Survive the tactical decay.</p>
<button id="start-btn">ENGAGE PROTOCOL</button>
<div class="controls-hint">
<div class="controls-grid">
<div class="control-item">← / A : Move Left</div>
<div class="control-item">→ / D : Move Right</div>
<div class="control-item">SPACE / W : Jump</div>
<div class="control-item">CLICK : Fire Weapon</div>
</div>
</div>
</div>
<!-- Game Over Screen -->
<div id="gameover-screen" class="screen hidden">
<h1>OUTPOST OVERRUN</h1>
<p id="final-stats">You survived 0 waves with a score of 0.</p>
<button id="restart-btn">REDEPLOY</button>
</div>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Target UI Elements
const startScreen = document.getElementById('start-screen');
const gameoverScreen = document.getElementById('gameover-screen');
const startBtn = document.getElementById('start-btn');
const restartBtn = document.getElementById('restart-btn');
const healthBar = document.getElementById('health-bar');
const scoreDisplay = document.getElementById('score-display');
const waveDisplay = document.getElementById('wave-display');
const ammoDisplay = document.getElementById('ammo-display');
// Game State Core Variables
let gameState = 'MENU'; // MENU, PLAYING, GAMEOVER
let score = 0;
let wave = 1;
let keys = {};
let mouse = { x: 0, y: 0, isDown: false };
// Entity Lists
let player;
let zombies = [];
let bullets = [];
let particles = [];
let drops = [];
// Environment Variables
const groundY = 340;
const gravity = 0.6;
// Wave Management Systems
let waveTimer = 0;
let zombiesSpawnedThisWave = 0;
let totalZombiesForWave = 5;
let spawnInterval = 120; // Frames between spawns
// Audio Synth Helper Function (Web Audio API Retro Sound Effects Engine)
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
function playLaserSound(type) {
if (!audioCtx) return;
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.connect(gain);
gain.connect(audioCtx.destination);
if (type === 'shoot') {
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(400, audioCtx.currentTime);
osc.frequency.exponentialRampToValueAtTime(80, audioCtx.currentTime + 0.15);
gain.gain.setValueAtTime(0.1, audioCtx.currentTime);
gain.gain.linearRampToValueAtTime(0, audioCtx.currentTime + 0.15);
osc.start(); osc.stop(audioCtx.currentTime + 0.15);
} else if (type === 'hit') {
osc.type = 'triangle';
osc.frequency.setValueAtTime(120, audioCtx.currentTime);
osc.frequency.linearRampToValueAtTime(30, audioCtx.currentTime + 0.1);
gain.gain.setValueAtTime(0.2, audioCtx.currentTime);
gain.gain.linearRampToValueAtTime(0, audioCtx.currentTime + 0.1);
osc.start(); osc.stop(audioCtx.currentTime + 0.1);
} else if (type === 'death') {
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(100, audioCtx.currentTime);
osc.frequency.linearRampToValueAtTime(10, audioCtx.currentTime + 0.4);
gain.gain.setValueAtTime(0.3, audioCtx.currentTime);
gain.gain.linearRampToValueAtTime(0, audioCtx.currentTime + 0.4);
osc.start(); osc.stop(audioCtx.currentTime + 0.4);
} else if (type === 'powerup') {
osc.type = 'sine';
osc.frequency.setValueAtTime(200, audioCtx.currentTime);
osc.frequency.linearRampToValueAtTime(600, audioCtx.currentTime + 0.2);
gain.gain.setValueAtTime(0.15, audioCtx.currentTime);
gain.gain.linearRampToValueAtTime(0, audioCtx.currentTime + 0.2);
osc.start(); osc.stop(audioCtx.currentTime + 0.2);
}
}
// Entity Classes Definitions
class Player {
constructor() {
this.x = 400;
this.y = groundY - 40;
this.width = 24;
this.height = 40;
this.vx = 0;
this.vy = 0;
this.speed = 4.5;Game Source: Retro Zombie Survival Arcade
Creator: TurboMaker33
Libraries: none
Complexity: complex (285 lines, 9.7 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: retro-zombie-survival-arcade-turbomaker33" to link back to the original. Then publish at arcadelab.ai/publish.