towerdefense / index.html
adnannovus's picture
update
830dc9a verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tower Defense</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: #1a1a1a;
font-family: Arial, sans-serif;
color: white;
}
#gameContainer {
position: relative;
width: 800px;
height: 600px;
background: #2a2a2a;
border-radius: 10px;
overflow: hidden;
}
#gameCanvas {
position: absolute;
left: 0;
top: 0;
}
.ui-panel {
position: absolute;
right: 0;
top: 0;
width: 200px;
height: 100%;
background: rgba(0, 0, 0, 0.8);
padding: 10px;
}
.tower-btn {
display: block;
width: 100%;
padding: 10px;
margin: 5px 0;
background: #4a4a4a;
border: none;
border-radius: 5px;
color: white;
cursor: pointer;
transition: background 0.3s;
}
.tower-btn:hover {
background: #5a5a5a;
}
#stats {
position: absolute;
left: 10px;
top: 10px;
font-size: 16px;
text-shadow: 2px 2px 2px rgba(0,0,0,0.5);
z-index: 1;
}
#waveMessage {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
font-size: 24px;
color: white;
text-shadow: 2px 2px 4px rgba(0,0,0,0.7);
display: none;
z-index: 2;
}
@keyframes fadeInOut {
0% { opacity: 0; transform: translate(-50%, -50%) scale(0.5); }
10% { opacity: 1; transform: translate(-50%, -50%) scale(1.1); }
20% { transform: translate(-50%, -50%) scale(1); }
80% { opacity: 1; }
100% { opacity: 0; }
}
</style>
</head>
<body>
<div id="gameContainer">
<canvas id="gameCanvas"></canvas>
<div id="stats">
Lives: <span id="lives">20</span>
Gold: <span id="gold">200</span>
Wave: <span id="wave">1</span>
</div>
<div id="waveMessage"></div>
<div class="ui-panel">
<button class="tower-btn" data-type="basic">Basic Tower ($100)</button>
<button class="tower-btn" data-type="sniper">Sniper Tower ($200)</button>
<button class="tower-btn" data-type="splash">Splash Tower ($300)</button>
</div>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const waveMessage = document.getElementById('waveMessage');
canvas.width = 600;
canvas.height = 600;
let gameState = {
lives: 20,
gold: 200,
wave: 0,
towers: [],
enemies: [],
path: [],
projectiles: [],
selectedTower: null,
waveInProgress: false,
enemiesSpawned: 0,
totalEnemiesInWave: 10,
spawnInterval: null,
timeBetweenWaves: 5000
};
const towerTypes = {
basic: {
cost: 100,
range: 120,
damage: 30,
fireRate: 2,
color: '#4a90e2',
projectileColor: '#4a90e2',
projectileSize: 5
},
sniper: {
cost: 200,
range: 250,
damage: 100,
fireRate: 1,
color: '#e24a4a',
projectileColor: '#e24a4a',
projectileSize: 7
},
splash: {
cost: 300,
range: 100,
damage: 45,
fireRate: 3,
color: '#4ae24a',
projectileColor: '#4ae24a',
projectileSize: 6
}
};
function generatePath() {
const path = [];
let x = 0;
let y = Math.random() * (canvas.height - 200) + 100;
let direction = 1;
while (x < canvas.width - 50) {
path.push({ x, y });
x += 20;
y += Math.random() * 40 * direction;
direction = -direction;
y = Math.max(50, Math.min(canvas.height - 50, y));
}
return path;
}
function showWaveMessage(message, duration = 3000) {
waveMessage.textContent = message;
waveMessage.style.display = 'block';
waveMessage.style.animation = 'fadeInOut 3s ease-in-out';
setTimeout(() => {
waveMessage.style.display = 'none';
}, duration);
}
class Enemy {
constructor() {
this.pathIndex = 0;
this.x = gameState.path[0].x;
this.y = gameState.path[0].y;
this.health = 100;
this.maxHealth = 100;
this.speed = 1;
this.value = 20;
}
update() {
if (this.pathIndex < gameState.path.length - 1) {
const target = gameState.path[this.pathIndex + 1];
const dx = target.x - this.x;
const dy = target.y - this.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < this.speed) {
this.pathIndex++;
} else {
this.x += (dx / distance) * this.speed;
this.y += (dy / distance) * this.speed;
}
} else {
gameState.lives--;
return true;
}
if (this.health <= 0) {
gameState.gold += this.value;
return true;
}
return false;
}
draw() {
ctx.fillStyle = '#ff0000';
ctx.beginPath();
ctx.arc(this.x, this.y, 10, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#000000';
ctx.fillRect(this.x - 15, this.y - 20, 30, 5);
ctx.fillStyle = '#00ff00';
ctx.fillRect(this.x - 15, this.y - 20, (this.health / this.maxHealth) * 30, 5);
}
}
class Projectile {
constructor(x, y, target, damage, color, size, speed = 8) {
this.x = x;
this.y = y;
this.target = target;
this.damage = damage;
this.color = color;
this.size = size;
this.speed = speed;
}
update() {
if (!this.target) return true;
const dx = this.target.x - this.x;
const dy = this.target.y - this.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < this.speed) {
this.target.health -= this.damage;
return true;
}
this.x += (dx / distance) * this.speed;
this.y += (dy / distance) * this.speed;
return false;
}
draw() {
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
}
}
class Tower {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type;
this.lastShot = 0;
this.level = 1;
Object.assign(this, towerTypes[type]);
}
update(time) {
if (time - this.lastShot > 1000 / this.fireRate) {
for (let enemy of gameState.enemies) {
const dx = enemy.x - this.x;
const dy = enemy.y - this.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= this.range) {
gameState.projectiles.push(
new Projectile(
this.x,
this.y,
enemy,
this.damage * this.level,
this.projectileColor,
this.projectileSize
)
);
this.lastShot = time;
break;
}
}
}
}
draw() {
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, 20, 0, Math.PI * 2);
ctx.fill();
if (this === gameState.selectedTower) {
ctx.strokeStyle = 'rgba(255, 255, 255, 0.3)';
ctx.beginPath();
ctx.arc(this.x, this.y, this.range, 0, Math.PI * 2);
ctx.stroke();
}
}
}
function drawPath() {
ctx.strokeStyle = '#666666';
ctx.lineWidth = 30;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.beginPath();
ctx.moveTo(gameState.path[0].x, gameState.path[0].y);
for (let i = 1; i < gameState.path.length; i++) {
ctx.lineTo(gameState.path[i].x, gameState.path[i].y);
}
ctx.stroke();
}
function startNewWave() {
gameState.wave++;
gameState.waveInProgress = true;
gameState.enemiesSpawned = 0;
gameState.totalEnemiesInWave = Math.floor(10 + gameState.wave * 2);
gameState.path = generatePath();
gameState.towers = [];
gameState.projectiles = [];
gameState.enemies = [];
showWaveMessage(`Wave ${gameState.wave} Starting!`);
if(gameState.spawnInterval) clearInterval(gameState.spawnInterval);
setTimeout(() => {
gameState.spawnInterval = setInterval(() => {
if(gameState.enemiesSpawned < gameState.totalEnemiesInWave) {
const enemy = new Enemy();
enemy.health = 100 + (gameState.wave - 1) * 20;
enemy.maxHealth = enemy.health;
enemy.value = 20 + Math.floor(gameState.wave / 2) * 10;
gameState.enemies.push(enemy);
gameState.enemiesSpawned++;
} else {
clearInterval(gameState.spawnInterval);
}
}, 1000);
}, 2000);
}
function checkWaveStatus() {
if (!gameState.waveInProgress) return;
if (gameState.enemiesSpawned >= gameState.totalEnemiesInWave &&
gameState.enemies.length === 0) {
gameState.waveInProgress = false;
const bonus = 100 + gameState.wave * 20;
gameState.gold += bonus;
setTimeout(startNewWave, gameState.timeBetweenWaves);
}
}
function gameLoop(timestamp) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawPath();
gameState.enemies = gameState.enemies.filter(enemy => !enemy.update());
gameState.enemies.forEach(enemy => enemy.draw());
gameState.towers.forEach(tower => {
tower.update(timestamp);
tower.draw();
});
gameState.projectiles = gameState.projectiles.filter(proj => !proj.update());
gameState.projectiles.forEach(proj => proj.draw());
checkWaveStatus();
document.getElementById('lives').textContent = gameState.lives;
document.getElementById('gold').textContent = gameState.gold;
document.getElementById('wave').textContent = gameState.wave;
if (gameState.lives <= 0) {
alert('Game Over!');
return;
}
requestAnimationFrame(gameLoop);
}
canvas.addEventListener('click', (e) => {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
if (gameState.selectedTower) {
const tower = new Tower(x, y, gameState.selectedTower);
if (gameState.gold >= tower.cost) {
gameState.towers.push(tower);
gameState.gold -= tower.cost;
gameState.selectedTower = null;
}
}
});
document.querySelectorAll('.tower-btn').forEach(btn => {
btn.addEventListener('click', () => {
const type = btn.dataset.type;
if (gameState.gold >= towerTypes[type].cost) {
gameState.selectedTower = type;
}
});
});
startNewWave();
requestAnimationFrame(gameLoop);
</script>
</body>
</html>