class Game { constructor() { this.canvas = document.getElementById('game-canvas'); this.ctx = this.canvas.getContext('2d'); this.audio = { bgMusic: document.getElementById('bg-music'), eat: new Audio('assets/sounds/eat.wav'), die: new Audio('assets/sounds/die.wav'), win: new Audio('assets/sounds/win.wav') }; this.resizeCanvas(); this.setupEventListeners(); // Настройки игры this.gameState = 'menu'; this.score = 0; this.highScore = localStorage.getItem('highScore') || 0; this.volume = 0.5; this.pixelSize = 4; this.showGrid = true; this.currentSkin = 'classic'; // Параметры червяка this.worm = { x: 100, y: 100, size: 16, speed: 2, direction: 'right', body: [], maxLength: 3 }; // Еда this.food = { x: 0, y: 0, size: 16, type: 'normal' }; // Достижения this.achievements = { novice: false, experienced: false }; this.spawnFood(); this.updateVolume(); } setupEventListeners() { // Главное меню document.getElementById('start-game').addEventListener('click', () => this.startGame()); document.getElementById('skins-btn').addEventListener('click', () => this.showMenu('skins')); document.getElementById('achievements-btn').addEventListener('click', () => this.showMenu('achievements')); document.getElementById('settings-btn').addEventListener('click', () => this.showMenu('settings')); // Меню скинов document.querySelectorAll('.skin-option').forEach(option => { option.addEventListener('click', () => this.selectSkin(option.dataset.skin)); }); document.getElementById('back-from-skins').addEventListener('click', () => this.showMenu('main')); // Меню достижений document.getElementById('back-from-achievements').addEventListener('click', () => this.showMenu('main')); // Меню настроек document.getElementById('volume-slider').addEventListener('input', (e) => { document.getElementById('volume-value').textContent = `${Math.round(e.target.value * 100)}%`; }); document.getElementById('save-settings').addEventListener('click', () => this.saveSettings()); document.getElementById('exit-game').addEventListener('click', () => window.close()); document.getElementById('back-from-settings').addEventListener('click', () => this.showMenu('main')); // Игровые события document.addEventListener('keydown', (e) => this.handleKeyInput(e)); document.getElementById('pause-btn').addEventListener('click', () => this.togglePause()); document.getElementById('resume-btn').addEventListener('click', () => this.togglePause()); document.getElementById('quit-to-menu').addEventListener('click', () => this.quitToMenu()); document.getElementById('restart-btn').addEventListener('click', () => this.startGame()); document.getElementById('main-menu-btn').addEventListener('click', () => this.quitToMenu()); // Обработка изменения размера окна window.addEventListener('resize', () => this.resizeCanvas()); } showMenu(menu) { const menus = ['main', 'skins', 'achievements', 'settings']; menus.forEach(m => { document.getElementById(`${m}-menu`).style.display = m === menu ? 'flex' : 'none'; }); if (menu === 'settings') { document.getElementById('volume-slider').value = this.volume; document.getElementById('volume-value').textContent = `${Math.round(this.volume * 100)}%`; document.getElementById('pixel-size').value = this.pixelSize; } } selectSkin(skin) { this.currentSkin = skin; document.querySelectorAll('.skin-option').forEach(option => { option.classList.toggle('selected', option.dataset.skin === skin); }); } startGame() { this.showMenu(null); document.getElementById('game-container').style.display = 'block'; document.getElementById('game-over-menu').style.display = 'none'; this.gameState = 'playing'; this.score = 0; this.worm = { x: 100, y: 100, size: 16, speed: 2, direction: 'right', body: [], maxLength: 3 }; document.getElementById('score').textContent = `ОЧКИ: ${this.score}`; document.getElementById('high-score').textContent = `РЕКОРД: ${this.highScore}`; this.audio.bgMusic.currentTime = 0; this.audio.bgMusic.play(); this.spawnFood(); this.gameLoop(); } togglePause() { if (this.gameState === 'playing') { this.gameState = 'paused'; document.getElementById('pause-menu').style.display = 'block'; this.audio.bgMusic.pause(); } else if (this.gameState === 'paused') { this.gameState = 'playing'; document.getElementById('pause-menu').style.display = 'none'; this.audio.bgMusic.play(); this.gameLoop(); } } quitToMenu() { this.gameState = 'menu'; document.getElementById('game-container').style.display = 'none'; document.getElementById('pause-menu').style.display = 'none'; document.getElementById('game-over-menu').style.display = 'none'; this.showMenu('main'); this.audio.bgMusic.pause(); } gameOver() { this.gameState = 'gameover'; document.getElementById('game-over-menu').style.display = 'block'; document.getElementById('final-score').textContent = this.score; if (this.score > this.highScore) { this.highScore = this.score; localStorage.setItem('highScore', this.highScore); } this.audio.bgMusic.pause(); this.audio.die.play(); } spawnFood() { this.food = { x: Math.floor(Math.random() * (this.canvas.width - 32) / 16) * 16 + 8, y: Math.floor(Math.random() * (this.canvas.height - 32) / 16) * 16 + 8, size: 16, type: Math.random() > 0.9 ? 'special' : 'normal' }; } checkCollision() { // Проверка столкновения с границами if (this.worm.x < 0 || this.worm.x >= this.canvas.width || this.worm.y < 0 || this.worm.y >= this.canvas.height) { this.gameOver(); return; } // Проверка столкновения с телом for (let i = 0; i < this.worm.body.length; i++) { if (this.worm.x === this.worm.body[i].x && this.worm.y === this.worm.body[i].y) { this.gameOver(); return; } } // Проверка столкновения с едой const distance = Math.sqrt( Math.pow(this.worm.x - this.food.x, 2) + Math.pow(this.worm.y - this.food.y, 2) ); if (distance < this.worm.size / 2 + this.food.size / 2) { this.eatFood(); } } eatFood() { this.score += this.food.type === 'special' ? 50 : 10; document.getElementById('score').textContent = `ОЧКИ: ${this.score}`; this.worm.maxLength += 3; this.audio.eat.play(); // Проверка достижений if (this.score >= 100 && !this.achievements.novice) { this.achievements.novice = true; this.audio.win.play(); } if (this.score >= 500 && !this.achievements.experienced) { this.achievements.experienced = true; this.audio.win.play(); } this.spawnFood(); } updateWorm() { // Добавляем новую голову this.worm.body.unshift({ x: this.worm.x, y: this.worm.y }); // Удаляем лишние сегменты if (this.worm.body.length > this.worm.maxLength) { this.worm.body.pop(); } // Двигаем голову switch (this.worm.direction) { case 'up': this.worm.y -= this.worm.speed; break; case 'down': this.worm.y += this.worm.speed; break; case 'left': this.worm.x -= this.worm.speed; break; case 'right': this.worm.x += this.worm.speed; break; } } handleKeyInput(e) { if (this.gameState !== 'playing') return; switch (e.key) { case 'ArrowUp': if (this.worm.direction !== 'down') this.worm.direction = 'up'; break; case 'ArrowDown': if (this.worm.direction !== 'up') this.worm.direction = 'down'; break; case 'ArrowLeft': if (this.worm.direction !== 'right') this.worm.direction = 'left'; break; case 'ArrowRight': if (this.worm.direction !== 'left') this.worm.direction = 'right'; break; } } drawPixel(x, y, size, color) { this.ctx.fillStyle = color; this.ctx.fillRect( Math.floor(x / this.pixelSize) * this.pixelSize, Math.floor(y / this.pixelSize) * this.pixelSize, Math.floor(size / this.pixelSize) * this.pixelSize, Math.floor(size / this.pixelSize) * this.pixelSize ); } drawWorm() { // Рисуем тело for (let i = 0; i < this.worm.body.length; i++) { const segment = this.worm.body[i]; let color; switch (this.currentSkin) { case 'fire': color = i === 0 ? '#ff5e5e' : `hsl(${20 + i * 2}, 100%, ${50 + i}%)`; break; case 'ice': color = i === 0 ? '#5ebdff' : `hsl(${180 + i * 2}, 100%, ${50 + i}%)`; break; default: // classic color = i === 0 ? '#5eff5e' : `hsl(${120 + i * 2}, 100%, ${50 + i}%)`; } this.drawPixel(segment.x, segment.y, this.worm.size, color); } } drawFood() { const color = this.food.type === 'special' ? '#ff55ff' : '#ff5555'; this.drawPixel(this.food.x, this.food.y, this.food.size, color); // Анимация специальной еды if (this.food.type === 'special') { this.ctx.strokeStyle = '#ffffff'; this.ctx.lineWidth = 2; const size = this.food.size + Math.sin(Date.now() / 200) * 4; this.ctx.strokeRect( Math.floor((this.food.x - size/2) / this.pixelSize) * this.pixelSize, Math.floor((this.food.y - size/2) / this.pixelSize) * this.pixelSize, Math.floor(size / this.pixelSize) * this.pixelSize, Math.floor(size / this.pixelSize) * this.pixelSize ); } } drawGrid() { if (!this.showGrid) return; this.ctx.strokeStyle = 'rgba(106, 58, 199, 0.2)'; this.ctx.lineWidth = 1; for (let x = 0; x < this.canvas.width; x += this.pixelSize * 8) { this.ctx.beginPath(); this.ctx.moveTo(x, 0); this.ctx.lineTo(x, this.canvas.height); this.ctx.stroke(); } for (let y = 0; y < this.canvas.height; y += this.pixelSize * 8) { this.ctx.beginPath(); this.ctx.moveTo(0, y); this.ctx.lineTo(this.canvas.width, y); this.ctx.stroke(); } } saveSettings() { this.volume = parseFloat(document.getElementById('volume-slider').value); this.pixelSize = parseInt(document.getElementById('pixel-size').value); this.updateVolume(); this.resizeCanvas(); this.showMenu('main'); } updateVolume() { Object.values(this.audio).forEach(sound => { if (sound) sound.volume = this.volume; }); } resizeCanvas() { this.canvas.width = Math.floor(window.innerWidth / this.pixelSize) * this.pixelSize; this.canvas.height = Math.floor(window.innerHeight / this.pixelSize) * this.pixelSize; } gameLoop() { if (this.gameState !== 'playing') return; // Очистка экрана this.ctx.fillStyle = '#0a0a1a'; this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); // Рисование this.drawGrid(); this.drawFood(); this.drawWorm(); // Обновление this.updateWorm(); this.checkCollision(); // Следующий кадр requestAnimationFrame(() => this.gameLoop()); } } // Инициализация игры при загрузке страницы window.addEventListener('load', () => { const game = new Game(); });