ClaudeThis example demonstrates how mkarchi can generate a complex project structure with multiple files and complete source code. Simply copy the syntax below into a .txt file and run mkarchi apply.




Copy the syntax below into a file named game.txt and run:
Tip: Use a local server (like Live Server) for better experience.
mini-game/
├── index.html (begincontenu)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Space Defender - Mini Game</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<div class="game-container">
<div class="header">
<h1>Space Defender</h1>
<div class="score-board">
<span class="label">Score:</span>
<span id="score">0</span>
<span class="label">Lives:</span>
<span id="lives">3</span>
<span class="label">Level:</span>
<span id="level">1</span>
</div>
</div>
<canvas id="gameCanvas" width="800" height="600"></canvas>
<div class="controls">
<button id="startBtn" class="btn btn-primary">Start Game</button>
<button id="pauseBtn" class="btn btn-secondary" disabled>Pause</button>
<button id="restartBtn" class="btn btn-warning" disabled>Restart</button>
</div>
<div class="instructions">
<h3>How to Play</h3>
<ul>
<li>Use Arrow Keys or WASD to move your ship</li>
<li>Press SPACE to shoot</li>
<li>Destroy enemies to gain points</li>
<li>Avoid enemy bullets and collisions</li>
<li>Collect power-ups for special abilities</li>
</ul>
</div>
<div id="gameOverModal" class="modal hidden">
<div class="modal-content">
<h2>Game Over!</h2>
<p>Final Score: <span id="finalScore">0</span></p>
<p>Level Reached: <span id="finalLevel">1</span></p>
<button id="playAgainBtn" class="btn btn-primary">Play Again</button>
</div>
</div>
</div>
<script src="js/config.js"></script>
<script src="js/utils.js"></script>
<script src="js/entities.js"></script>
<script src="js/player.js"></script>
<script src="js/enemy.js"></script>
<script src="js/powerup.js"></script>
<script src="js/particle.js"></script>
<script src="js/collision.js"></script>
<script src="js/game.js"></script>
<script src="js/main.js"></script>
</body>
</html>
(endcontenu)
│
├── css/
│ └── styles.css (begincontenu)
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
.game-container {
background: rgba(255, 255, 255, 0.95);
border-radius: 15px;
padding: 30px;
box-shadow: 0 10px 50px rgba(0, 0, 0, 0.3);
max-width: 900px;
width: 100%;
}
.header {
text-align: center;
margin-bottom: 20px;
}
.header h1 {
color: #2c3e50;
font-size: 2.5em;
margin-bottom: 15px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
}
.score-board {
display: flex;
justify-content: center;
gap: 30px;
font-size: 1.2em;
color: #34495e;
font-weight: bold;
}
.score-board .label {
color: #7f8c8d;
font-weight: normal;
}
.score-board span:not(.label) {
color: #e74c3c;
font-size: 1.3em;
}
#gameCanvas {
display: block;
margin: 0 auto;
background: #000;
border: 3px solid #34495e;
border-radius: 10px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}
.controls {
display: flex;
justify-content: center;
gap: 15px;
margin: 20px 0;
}
.btn {
padding: 12px 30px;
font-size: 1.1em;
border: none;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: bold;
text-transform: uppercase;
}
.btn:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btn-primary {
background: #3498db;
color: white;
}
.btn-primary:hover:not(:disabled) {
background: #2980b9;
}
.btn-secondary {
background: #95a5a6;
color: white;
}
.btn-secondary:hover:not(:disabled) {
background: #7f8c8d;
}
.btn-warning {
background: #e67e22;
color: white;
}
.btn-warning:hover:not(:disabled) {
background: #d35400;
}
.instructions {
background: #ecf0f1;
padding: 20px;
border-radius: 10px;
margin-top: 20px;
}
.instructions h3 {
color: #2c3e50;
margin-bottom: 15px;
font-size: 1.3em;
}
.instructions ul {
list-style-position: inside;
color: #34495e;
line-height: 1.8;
}
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal.hidden {
display: none;
}
.modal-content {
background: white;
padding: 40px;
border-radius: 15px;
text-align: center;
box-shadow: 0 10px 50px rgba(0, 0, 0, 0.5);
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from {
transform: translateY(-50px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.modal-content h2 {
color: #e74c3c;
font-size: 2.5em;
margin-bottom: 20px;
}
.modal-content p {
color: #34495e;
font-size: 1.3em;
margin: 10px 0;
}
.modal-content span {
color: #e74c3c;
font-weight: bold;
font-size: 1.5em;
}
(endcontenu)
│
├── js/
│ ├── config.js (begincontenu)
const CONFIG = {
canvas: {
width: 800,
height: 600
},
player: {
width: 40,
height: 50,
speed: 5,
color: '#00ff00',
bulletSpeed: 7,
bulletWidth: 4,
bulletHeight: 15,
bulletColor: '#ffff00',
fireRate: 250
},
enemy: {
width: 35,
height: 35,
speed: 2,
color: '#ff0000',
spawnRate: 2000,
bulletSpeed: 4,
bulletWidth: 4,
bulletHeight: 12,
bulletColor: '#ff6600',
fireRate: 1500,
points: 10
},
powerup: {
width: 25,
height: 25,
speed: 2,
spawnRate: 15000,
types: {
RAPID_FIRE: {
color: '#ffff00',
duration: 5000
},
SHIELD: {
color: '#00ffff',
duration: 8000
},
MULTI_SHOT: {
color: '#ff00ff',
duration: 7000
}
}
},
particle: {
count: 15,
speed: 3,
lifetime: 30,
colors: ['#ff6b6b', '#feca57', '#48dbfb', '#ff9ff3', '#54a0ff']
},
game: {
initialLives: 3,
levelUpScore: 500,
difficultyIncrease: 0.15
}
};
(endcontenu)
│ │
│ ├── utils.js (begincontenu)
class Utils {
static randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
static randomFloat(min, max) {
return Math.random() * (max - min) + min;
}
static randomColor() {
const colors = CONFIG.particle.colors;
return colors[Math.floor(Math.random() * colors.length)];
}
static distance(x1, y1, x2, y2) {
const dx = x2 - x1;
const dy = y2 - y1;
return Math.sqrt(dx * dx + dy * dy);
}
static clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
static lerp(start, end, t) {
return start + (end - start) * t;
}
}
(endcontenu)
│ │
│ ├── entities.js (begincontenu)
class Entity {
constructor(x, y, width, height, color) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
this.alive = true;
}
draw(ctx) {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
update() {
// To be overridden by subclasses
}
isOffScreen(canvasWidth, canvasHeight) {
return this.x + this.width < 0 ||
this.x > canvasWidth ||
this.y + this.height < 0 ||
this.y > canvasHeight;
}
}
class Bullet extends Entity {
constructor(x, y, width, height, color, speed, direction = 'up') {
super(x, y, width, height, color);
this.speed = speed;
this.direction = direction;
}
update() {
if (this.direction === 'up') {
this.y -= this.speed;
} else {
this.y += this.speed;
}
}
draw(ctx) {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
ctx.shadowBlur = 10;
ctx.shadowColor = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
ctx.shadowBlur = 0;
}
}
(endcontenu)
│ │
│ ├── player.js (begincontenu)
class Player extends Entity {
constructor(x, y) {
super(x, y, CONFIG.player.width, CONFIG.player.height, CONFIG.player.color);
this.speed = CONFIG.player.speed;
this.bullets = [];
this.lastFireTime = 0;
this.fireRate = CONFIG.player.fireRate;
this.hasShield = false;
this.shieldEndTime = 0;
this.hasRapidFire = false;
this.rapidFireEndTime = 0;
this.hasMultiShot = false;
this.multiShotEndTime = 0;
}
move(direction, canvasWidth, canvasHeight) {
switch(direction) {
case 'left':
this.x = Math.max(0, this.x - this.speed);
break;
case 'right':
this.x = Math.min(canvasWidth - this.width, this.x + this.speed);
break;
case 'up':
this.y = Math.max(0, this.y - this.speed);
break;
case 'down':
this.y = Math.min(canvasHeight - this.height, this.y + this.speed);
break;
}
}
shoot(currentTime) {
const actualFireRate = this.hasRapidFire ? this.fireRate / 2 : this.fireRate;
if (currentTime - this.lastFireTime > actualFireRate) {
if (this.hasMultiShot) {
this.bullets.push(new Bullet(
this.x + this.width / 2 - CONFIG.player.bulletWidth / 2 - 15,
this.y,
CONFIG.player.bulletWidth,
CONFIG.player.bulletHeight,
CONFIG.player.bulletColor,
CONFIG.player.bulletSpeed,
'up'
));
this.bullets.push(new Bullet(
this.x + this.width / 2 - CONFIG.player.bulletWidth / 2,
this.y,
CONFIG.player.bulletWidth,
CONFIG.player.bulletHeight,
CONFIG.player.bulletColor,
CONFIG.player.bulletSpeed,
'up'
));
this.bullets.push(new Bullet(
this.x + this.width / 2 - CONFIG.player.bulletWidth / 2 + 15,
this.y,
CONFIG.player.bulletWidth,
CONFIG.player.bulletHeight,
CONFIG.player.bulletColor,
CONFIG.player.bulletSpeed,
'up'
));
} else {
this.bullets.push(new Bullet(
this.x + this.width / 2 - CONFIG.player.bulletWidth / 2,
this.y,
CONFIG.player.bulletWidth,
CONFIG.player.bulletHeight,
CONFIG.player.bulletColor,
CONFIG.player.bulletSpeed,
'up'
));
}
this.lastFireTime = currentTime;
}
}
updatePowerUps(currentTime) {
if (this.hasShield && currentTime > this.shieldEndTime) {
this.hasShield = false;
}
if (this.hasRapidFire && currentTime > this.rapidFireEndTime) {
this.hasRapidFire = false;
}
if (this.hasMultiShot && currentTime > this.multiShotEndTime) {
this.hasMultiShot = false;
}
}
update(canvasWidth, canvasHeight, currentTime) {
this.bullets = this.bullets.filter(bullet => {
bullet.update();
return !bullet.isOffScreen(canvasWidth, canvasHeight);
});
this.updatePowerUps(currentTime);
}
draw(ctx) {
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.moveTo(this.x + this.width / 2, this.y);
ctx.lineTo(this.x, this.y + this.height);
ctx.lineTo(this.x + this.width, this.y + this.height);
ctx.closePath();
ctx.fill();
if (this.hasShield) {
ctx.strokeStyle = '#00ffff';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc(this.x + this.width / 2, this.y + this.height / 2, 30, 0, Math.PI * 2);
ctx.stroke();
}
this.bullets.forEach(bullet => bullet.draw(ctx));
}
}
(endcontenu)
│ │
│ ├── enemy.js (begincontenu)
class Enemy extends Entity {
constructor(x, y) {
super(x, y, CONFIG.enemy.width, CONFIG.enemy.height, CONFIG.enemy.color);
this.speed = CONFIG.enemy.speed;
this.bullets = [];
this.lastFireTime = 0;
this.direction = 1;
this.verticalSpeed = 0.5;
}
shoot(currentTime) {
if (currentTime - this.lastFireTime > CONFIG.enemy.fireRate) {
this.bullets.push(new Bullet(
this.x + this.width / 2 - CONFIG.enemy.bulletWidth / 2,
this.y + this.height,
CONFIG.enemy.bulletWidth,
CONFIG.enemy.bulletHeight,
CONFIG.enemy.bulletColor,
CONFIG.enemy.bulletSpeed,
'down'
));
this.lastFireTime = currentTime;
}
}
update(canvasWidth, canvasHeight, currentTime) {
this.x += this.speed * this.direction;
this.y += this.verticalSpeed;
if (this.x <= 0 || this.x + this.width >= canvasWidth) {
this.direction *= -1;
}
if (Math.random() < 0.01) {
this.shoot(currentTime);
}
this.bullets = this.bullets.filter(bullet => {
bullet.update();
return !bullet.isOffScreen(canvasWidth, canvasHeight);
});
}
draw(ctx) {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
ctx.fillStyle = '#ff6666';
ctx.fillRect(this.x + 5, this.y + 10, 10, 10);
ctx.fillRect(this.x + 20, this.y + 10, 10, 10);
this.bullets.forEach(bullet => bullet.draw(ctx));
}
}
(endcontenu)
│ │
│ ├── powerup.js (begincontenu)
class PowerUp extends Entity {
constructor(x, y, type) {
const config = CONFIG.powerup.types[type];
super(x, y, CONFIG.powerup.width, CONFIG.powerup.height, config.color);
this.type = type;
this.speed = CONFIG.powerup.speed;
this.rotation = 0;
this.duration = config.duration;
}
update() {
this.y += this.speed;
this.rotation += 0.05;
}
draw(ctx) {
ctx.save();
ctx.translate(this.x + this.width / 2, this.y + this.height / 2);
ctx.rotate(this.rotation);
ctx.fillStyle = this.color;
ctx.fillRect(-this.width / 2, -this.height / 2, this.width, this.height);
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 2;
ctx.strokeRect(-this.width / 2, -this.height / 2, this.width, this.height);
ctx.restore();
ctx.shadowBlur = 15;
ctx.shadowColor = this.color;
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
ctx.shadowBlur = 0;
}
}
(endcontenu)
│ │
│ ├── particle.js (begincontenu)
class Particle {
constructor(x, y) {
this.x = x;
this.y = y;
this.vx = Utils.randomFloat(-CONFIG.particle.speed, CONFIG.particle.speed);
this.vy = Utils.randomFloat(-CONFIG.particle.speed, CONFIG.particle.speed);
this.life = CONFIG.particle.lifetime;
this.maxLife = CONFIG.particle.lifetime;
this.color = Utils.randomColor();
this.size = Utils.randomInt(2, 5);
}
update() {
this.x += this.vx;
this.y += this.vy;
this.life--;
this.vx *= 0.98;
this.vy *= 0.98;
}
draw(ctx) {
const alpha = this.life / this.maxLife;
ctx.fillStyle = this.color;
ctx.globalAlpha = alpha;
ctx.fillRect(this.x, this.y, this.size, this.size);
ctx.globalAlpha = 1;
}
isDead() {
return this.life <= 0;
}
}
class ParticleSystem {
constructor() {
this.particles = [];
}
createExplosion(x, y) {
for (let i = 0; i < CONFIG.particle.count; i++) {
this.particles.push(new Particle(x, y));
}
}
update() {
this.particles = this.particles.filter(particle => {
particle.update();
return !particle.isDead();
});
}
draw(ctx) {
this.particles.forEach(particle => particle.draw(ctx));
}
}
(endcontenu)
│ │
│ ├── collision.js (begincontenu)
class CollisionDetector {
static checkCollision(rect1, rect2) {
return rect1.x < rect2.x + rect2.width &&
rect1.x + rect1.width > rect2.x &&
rect1.y < rect2.y + rect2.height &&
rect1.y + rect1.height > rect2.y;
}
static checkBulletEnemyCollisions(player, enemies, particleSystem, score) {
let newScore = score;
player.bullets.forEach(bullet => {
enemies.forEach(enemy => {
if (bullet.alive && enemy.alive && this.checkCollision(bullet, enemy)) {
bullet.alive = false;
enemy.alive = false;
particleSystem.createExplosion(
enemy.x + enemy.width / 2,
enemy.y + enemy.height / 2
);
newScore += CONFIG.enemy.points;
}
});
});
return newScore;
}
static checkEnemyBulletPlayerCollisions(player, enemies) {
if (player.hasShield) {
enemies.forEach(enemy => {
enemy.bullets = enemy.bullets.filter(bullet => {
if (this.checkCollision(bullet, player)) {
return false;
}
return true;
});
});
return false;
}
for (let enemy of enemies) {
for (let bullet of enemy.bullets) {
if (bullet.alive && this.checkCollision(bullet, player)) {
bullet.alive = false;
return true;
}
}
}
return false;
}
static checkEnemyPlayerCollisions(player, enemies) {
if (player.hasShield) {
return false;
}
for (let enemy of enemies) {
if (enemy.alive && this.checkCollision(player, enemy)) {
return true;
}
}
return false;
}
static checkPowerUpPlayerCollisions(player, powerups, currentTime) {
powerups.forEach(powerup => {
if (powerup.alive && this.checkCollision(player, powerup)) {
powerup.alive = false;
switch(powerup.type) {
case 'RAPID_FIRE':
player.hasRapidFire = true;
player.rapidFireEndTime = currentTime + powerup.duration;
break;
case 'SHIELD':
player.hasShield = true;
player.shieldEndTime = currentTime + powerup.duration;
break;
case 'MULTI_SHOT':
player.hasMultiShot = true;
player.multiShotEndTime = currentTime + powerup.duration;
break;
}
}
});
}
}
(endcontenu)
│ │
│ ├── game.js (begincontenu)
class Game {
constructor(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.width = CONFIG.canvas.width;
this.height = CONFIG.canvas.height;
this.player = new Player(
this.width / 2 - CONFIG.player.width / 2,
this.height - 100
);
this.enemies = [];
this.powerups = [];
this.particleSystem = new ParticleSystem();
this.score = 0;
this.lives = CONFIG.game.initialLives;
this.level = 1;
this.gameOver = false;
this.paused = false;
this.lastEnemySpawn = 0;
this.lastPowerUpSpawn = 0;
this.keys = {};
this.setupEventListeners();
}
setupEventListeners() {
document.addEventListener('keydown', (e) => {
this.keys[e.key] = true;
if (e.key === ' ') {
e.preventDefault();
if (!this.paused && !this.gameOver) {
this.player.shoot(Date.now());
}
}
});
document.addEventListener('keyup', (e) => {
this.keys[e.key] = false;
});
}
spawnEnemy(currentTime) {
const spawnRate = CONFIG.enemy.spawnRate * Math.pow(0.95, this.level - 1);
if (currentTime - this.lastEnemySpawn > spawnRate) {
const x = Utils.randomInt(0, this.width - CONFIG.enemy.width);
this.enemies.push(new Enemy(x, -CONFIG.enemy.height));
this.lastEnemySpawn = currentTime;
}
}
spawnPowerUp(currentTime) {
if (currentTime - this.lastPowerUpSpawn > CONFIG.powerup.spawnRate) {
const x = Utils.randomInt(0, this.width - CONFIG.powerup.width);
const types = Object.keys(CONFIG.powerup.types);
const randomType = types[Utils.randomInt(0, types.length - 1)];
this.powerups.push(new PowerUp(x, -CONFIG.powerup.height, randomType));
this.lastPowerUpSpawn = currentTime;
}
}
handleInput() {
if (this.keys['ArrowLeft'] || this.keys['a'] || this.keys['A']) {
this.player.move('left', this.width, this.height);
}
if (this.keys['ArrowRight'] || this.keys['d'] || this.keys['D']) {
this.player.move('right', this.width, this.height);
}
if (this.keys['ArrowUp'] || this.keys['w'] || this.keys['W']) {
this.player.move('up', this.width, this.height);
}
if (this.keys['ArrowDown'] || this.keys['s'] || this.keys['S']) {
this.player.move('down', this.width, this.height);
}
}
updateLevel() {
const newLevel = Math.floor(this.score / CONFIG.game.levelUpScore) + 1;
if (newLevel > this.level) {
this.level = newLevel;
CONFIG.enemy.speed = 2 + (this.level * CONFIG.game.difficultyIncrease);
}
}
update() {
if (this.paused || this.gameOver) return;
const currentTime = Date.now();
this.handleInput();
this.player.update(this.width, this.height, currentTime);
this.spawnEnemy(currentTime);
this.spawnPowerUp(currentTime);
this.enemies.forEach(enemy => {
enemy.update(this.width, this.height, currentTime);
});
this.powerups.forEach(powerup => {
powerup.update();
});
this.enemies = this.enemies.filter(enemy => {
return enemy.alive && !enemy.isOffScreen(this.width, this.height);
});
this.powerups = this.powerups.filter(powerup => {
return powerup.alive && !powerup.isOffScreen(this.width, this.height);
});
this.player.bullets = this.player.bullets.filter(b => b.alive);
this.enemies.forEach(enemy => {
enemy.bullets = enemy.bullets.filter(b => b.alive);
});
this.score = CollisionDetector.checkBulletEnemyCollisions(this.player, this.enemies, this.particleSystem, this.score);
const hitByBullet = CollisionDetector.checkEnemyBulletPlayerCollisions(this.player, this.enemies);
const hitByEnemy = CollisionDetector.checkEnemyPlayerCollisions(this.player, this.enemies);
if (hitByBullet || hitByEnemy) {
this.handlePlayerHit();
}
CollisionDetector.checkPowerUpPlayerCollisions(this.player, this.powerups, currentTime);
this.particleSystem.update();
this.updateLevel();
}
handlePlayerHit() {
this.lives--;
this.particleSystem.createExplosion(
this.player.x + this.player.width / 2,
this.player.y + this.player.height / 2
);
this.player.x = this.width / 2 - CONFIG.player.width / 2;
this.player.y = this.height - 100;
this.enemies = [];
this.player.bullets = [];
if (this.lives <= 0) {
this.gameOver = true;
}
}
draw() {
this.ctx.fillStyle = '#000000';
this.ctx.fillRect(0, 0, this.width, this.height);
this.ctx.fillStyle = '#ffffff';
for(let i=0; i<20; i++) {
let x = (Date.now() / 50 * (i+1) + i*100) % this.width;
let y = (i * 40) % this.height;
this.ctx.fillRect(x, y, 2, 2);
}
this.player.draw(this.ctx);
this.enemies.forEach(enemy => enemy.draw(this.ctx));
this.powerups.forEach(powerup => powerup.draw(this.ctx));
this.particleSystem.draw(this.ctx);
if (this.paused) {
this.ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
this.ctx.fillRect(0, 0, this.width, this.height);
this.ctx.fillStyle = '#ffffff';
this.ctx.font = '30px Arial';
this.ctx.textAlign = 'center';
this.ctx.fillText('PAUSED', this.width / 2, this.height / 2);
}
}
}
(endcontenu)
│ │
│ ├── main.js (begincontenu)
document.addEventListener('DOMContentLoaded', () => {
const canvas = document.getElementById('gameCanvas');
const scoreEl = document.getElementById('score');
const livesEl = document.getElementById('lives');
const levelEl = document.getElementById('level');
const startBtn = document.getElementById('startBtn');
const pauseBtn = document.getElementById('pauseBtn');
const restartBtn = document.getElementById('restartBtn');
const gameOverModal = document.getElementById('gameOverModal');
const finalScoreEl = document.getElementById('finalScore');
const finalLevelEl = document.getElementById('finalLevel');
const playAgainBtn = document.getElementById('playAgainBtn');
let game = null;
let animationId = null;
let isGameRunning = false;
function initGame() {
game = new Game(canvas);
updateUI();
startBtn.disabled = true;
pauseBtn.disabled = false;
restartBtn.disabled = false;
gameOverModal.classList.add('hidden');
isGameRunning = true;
gameLoop();
}
function gameLoop() {
if (!isGameRunning) return;
game.update();
game.draw();
updateUI();
if (game.gameOver) {
handleGameOver();
} else {
animationId = requestAnimationFrame(gameLoop);
}
}
function updateUI() {
if (!game) return;
scoreEl.textContent = game.score;
livesEl.textContent = game.lives;
levelEl.textContent = game.level;
}
function handleGameOver() {
isGameRunning = false;
cancelAnimationFrame(animationId);
finalScoreEl.textContent = game.score;
finalLevelEl.textContent = game.level;
gameOverModal.classList.remove('hidden');
pauseBtn.disabled = true;
restartBtn.disabled = true;
}
startBtn.addEventListener('click', () => {
if (!isGameRunning) initGame();
});
pauseBtn.addEventListener('click', () => {
if (game && !game.gameOver) {
game.paused = !game.paused;
pauseBtn.textContent = game.paused ? 'Resume' : 'Pause';
}
});
restartBtn.addEventListener('click', () => {
if (animationId) cancelAnimationFrame(animationId);
initGame();
pauseBtn.textContent = 'Pause';
});
playAgainBtn.addEventListener('click', () => {
initGame();
pauseBtn.textContent = 'Pause';
});
});
(endcontenu)Total length: ~750 lines • Uses mkarchi 0.1.7 syntax tags