CoolFace
Apppublic

Avtor2/Space_battle

sourceHugging Faceupdated 2y agoView on Hugging Face
1likes
code728 linesDownload Raw Back to root
1<html><head><base href="https://cosmic-rhythm-battle.com/"><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Космическая Ритмичная Битва</title><style>2    body, html {3        margin: 0;4        padding: 0;5        overflow: hidden;6        background: #000;7        font-family: 'Orbitron', sans-serif;8        color: #fff;9        cursor: crosshair; /* Custom cursor */10    }11    #gameCanvas {12        position: absolute;13        top: 0;14        left: 0;15    }16    #ui {17        position: absolute;18        top: 10px;19        left: 10px;20        z-index: 10;21    }22    #modeSelection {23        position: absolute;24        top: 50%;25        left: 50%;26        transform: translate(-50%, -50%);27        text-align: center;28        background: rgba(0, 0, 0, 0.7);29        padding: 20px;30        border-radius: 10px;31        display: none;32    }33    #modeSelection h2 {34        margin-bottom: 20px;35    }36    #modeSelection button {37        padding: 10px 20px;38        font-size: 18px;39        margin: 0 10px;40        background: #4CAF50;41        color: white;42        border: none;43        cursor: pointer;44        border-radius: 5px;45        text-transform: uppercase;46        letter-spacing: 2px;47        transition: all 0.3s ease;48    }49    #modeSelection button:hover {50        background: #45a049;51        box-shadow: 0 0 10px #4CAF50;52    }53    #languageSelection {54        position: absolute;55        top: 50%;56        left: 50%;57        transform: translate(-50%, -50%);58        text-align: center;59        background: rgba(0, 0, 0, 0.7);60        padding: 20px;61        border-radius: 10px;62        display: none;63    }64    #languageSelection h2 {65        margin-bottom: 20px;66    }67    #languageSelection button {68        padding: 10px 20px;69        font-size: 18px;70        margin: 0 10px;71        background: #4CAF50;72        color: white;73        border: none;74        cursor: pointer;75        border-radius: 5px;76        text-transform: uppercase;77        letter-spacing: 2px;78        transition: all 0.3s ease;79    }80    #languageSelection button:hover {81        background: #45a049;82        box-shadow: 0 0 10px #4CAF50;83    }84    #startButton {85        padding: 10px 20px;86        font-size: 18px;87        background: #4CAF50;88        color: white;89        border: none;90        cursor: pointer;91        display: none;92        border-radius: 5px;93        text-transform: uppercase;94        letter-spacing: 2px;95        transition: all 0.3s ease;96    }97    #startButton:hover {98        background: #45a049;99        box-shadow: 0 0 10px #4CAF50;100    }101    #scoreDisplay {102        font-size: 24px;103        margin-top: 10px;104        text-shadow: 0 0 5px #00ffff;105    }106    #audioInput {107        margin-bottom: 10px;108    }109    #healthBar {110        width: 200px;111        height: 20px;112        background: #333;113        border: 2px solid #fff;114        margin-top: 10px;115    }116    #healthFill {117        width: 100%;118        height: 100%;119        background: #00ff00;120        transition: width 0.3s ease;121    }122</style>123<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap" rel="stylesheet">124</head>125<body>126<div id="ui">127    <div id="languageSelection" style="display: none;">128        <h2>Select Language / Выберите язык</h2>129        <button id="englishBtn">English</button>130        <button id="russianBtn">Русский</button>131    </div>132    <div id="modeSelection">133        <h2>Выберите режим игры</h2>134        <button id="classicMode">Классика</button>135        <button id="bossMode">С боссом</button>136        <button id="insanityMode">Безумие</button>137    </div>138    <input type="file" id="audioInput" accept="audio/*">139    <button id="startButton">Запустить корабль</button>140    <div id="scoreDisplay">Очки: 0</div>141    <div id="healthBar">142        <div id="healthFill"></div>143    </div>144</div>145<canvas id="gameCanvas"></canvas>146 147<script>148const canvas = document.getElementById('gameCanvas');149const ctx = canvas.getContext('2d');150const audioInput = document.getElementById('audioInput');151const startButton = document.getElementById('startButton');152const scoreDisplay = document.getElementById('scoreDisplay');153let audioContext, audioBuffer, analyser, dataArray;154let player, enemies = [], bullets = [], stars = [], planets = [];155let isGameRunning = false;156let score = 0;157let difficulty = 1;158let gameMode = '';159let currentLanguage = 'en';160let isPaused = false;161let audioSource;162let pausedAt = 0;163let startTime = 0;164const translations = {165    en: {166        selectMode: "Select game mode",167        classic: "Classic",168        boss: "Boss",169        insanity: "Insanity",170        start: "Launch ship",171        score: "Score",172        gameOver: "GAME OVER",173        finalScore: "Final score",174        playAgain: "Play again",175        paused: "PAUSED"176    },177    ru: {178        selectMode: "Выберите режим игры",179        classic: "Классика",180        boss: "С боссом",181        insanity: "Безумие",182        start: "Запустить корабль",183        score: "Очки",184        gameOver: "ИГРА ОКОНЧЕНА",185        finalScore: "Финальный счет",186        playAgain: "Играть снова",187        paused: "ПАУЗА"188    }189};190canvas.width = window.innerWidth;191canvas.height = window.innerHeight;192class GameObject {193    constructor(x, y, radius, color) {194        this.x = x;195        this.y = y;196        this.radius = radius;197        this.color = color;198    }199    draw() {200        ctx.beginPath();201        ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);202        ctx.fillStyle = this.color;203        ctx.fill();204        ctx.closePath();205    }206}207class Player extends GameObject {208    constructor(weakArmor = false) {209        super(canvas.width / 2, canvas.height / 2, 20, '#00ffff');210        this.speed = 5;211        this.health = weakArmor ? 50 : 100;212        this.weakArmor = weakArmor;213        this.invincible = false;214    }215    draw() {216        ctx.save();217        ctx.translate(this.x, this.y);218        ctx.rotate(Math.atan2(mouseY - this.y, mouseX - this.x));219        220        ctx.beginPath();221        ctx.moveTo(20, 0);222        ctx.lineTo(-10, -10);223        ctx.lineTo(-5, 0);224        ctx.lineTo(-10, 10);225        ctx.closePath();226        ctx.fillStyle = this.color;227        ctx.fill();228        ctx.shadowColor = '#00ffff';229        ctx.shadowBlur = 10;230        ctx.strokeStyle = '#ffffff';231        ctx.lineWidth = 2;232        ctx.stroke();233        ctx.restore();234    }235    move(keys) {236        // WASD, ЦФЫВ (Russian layout), Arrow keys237        if ((keys.w || keys.ц || keys.ArrowUp) && this.y > this.radius) this.y -= this.speed;238        if ((keys.s || keys.ы || keys.ArrowDown) && this.y < canvas.height - this.radius) this.y += this.speed;239        if ((keys.a || keys.ф || keys.ArrowLeft) && this.x > this.radius) this.x -= this.speed;240        if ((keys.d || keys.в || keys.ArrowRight) && this.x < canvas.width - this.radius) this.x += this.speed;241    }242}243class Enemy extends GameObject {244    constructor() {245        const side = Math.floor(Math.random() * 4);246        let x, y;247        switch(side) {248            case 0: x = Math.random() * canvas.width; y = 0; break;249            case 1: x = canvas.width; y = Math.random() * canvas.height; break;250            case 2: x = Math.random() * canvas.width; y = canvas.height; break;251            case 3: x = 0; y = Math.random() * canvas.height; break;252        }253        super(x, y, 10, '#ff0000'); 254        this.speed = 2 * difficulty;255    }256    draw() {257        ctx.save();258        ctx.translate(this.x, this.y);259        ctx.beginPath();260        for (let i = 0; i < 5; i++) {261            ctx.rotate(Math.PI * 2 / 5);262            ctx.lineTo(0, -this.radius);263            ctx.lineTo(0, -this.radius * 0.5);264        }265        ctx.fillStyle = this.color;266        ctx.fill();267        ctx.closePath();268        ctx.restore();269    }270    move(playerX, playerY) {271        const angle = Math.atan2(playerY - this.y, playerX - this.x);272        this.x += Math.cos(angle) * this.speed;273        this.y += Math.sin(angle) * this.speed;274    }275}276class Boss extends Enemy {277    constructor() {278        super();279        this.radius = 50; 280        this.color = '#ff00ff';281        this.health = 1000; 282        this.speed = 0.8 * difficulty; 283        this.shootInterval = 1500; 284        this.lastShot = 0;285    }286    draw() {287        ctx.save();288        ctx.translate(this.x, this.y);289        ctx.beginPath();290        for (let i = 0; i < 8; i++) {291            ctx.rotate(Math.PI * 2 / 8);292            ctx.lineTo(0, -this.radius);293            ctx.lineTo(0, -this.radius * 0.7);294        }295        ctx.fillStyle = this.color;296        ctx.fill();297        ctx.closePath();298        ctx.restore();299        ctx.fillStyle = 'red';300        ctx.fillRect(this.x - 20, this.y - this.radius - 10, 40, 5);301        ctx.fillStyle = 'green';302        ctx.fillRect(this.x - 20, this.y - this.radius - 10, 40 * (this.health / 1000), 5);303    }304    shoot(playerX, playerY) {305        const now = Date.now();306        if (now - this.lastShot > this.shootInterval) {307            const angle = Math.atan2(playerY - this.y, playerX - this.x);308            bullets.push(new Bullet(this.x, this.y, this.x + Math.cos(angle) * 1000, this.y + Math.sin(angle) * 1000, true));309            this.lastShot = now;310        }311    }312}313class Bullet extends GameObject {314    constructor(x, y, targetX, targetY, isBossBullet = false) {315        super(x, y, 5, isBossBullet ? '#ff00ff' : '#ffff00');316        const angle = Math.atan2(targetY - y, targetX - x);317        this.speed = 10;318        this.dx = Math.cos(angle) * this.speed;319        this.dy = Math.sin(angle) * this.speed;320        this.isBossBullet = isBossBullet;321    }322    move() {323        this.x += this.dx;324        this.y += this.dy;325    }326    draw() {327        ctx.beginPath();328        ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);329        ctx.fillStyle = this.color;330        ctx.fill();331        ctx.closePath();332        ctx.shadowColor = '#ffff00';333        ctx.shadowBlur = 10;334        ctx.beginPath();335        ctx.arc(this.x, this.y, this.radius + 2, 0, Math.PI * 2);336        ctx.strokeStyle = '#ffffff';337        ctx.lineWidth = 2;338        ctx.stroke();339    }340}341class Star {342    constructor() {343        this.x = Math.random() * canvas.width;344        this.y = Math.random() * canvas.height;345        this.size = Math.random() * 2;346        this.speed = Math.random() * 0.5;347    }348    draw() {349        ctx.fillStyle = '#ffffff';350        ctx.fillRect(this.x, this.y, this.size, this.size);351    }352    move() {353        this.y += this.speed;354        if (this.y > canvas.height) {355            this.y = 0;356            this.x = Math.random() * canvas.width;357        }358    }359}360class Planet {361    constructor() {362        this.x = Math.random() * canvas.width;363        this.y = Math.random() * canvas.height;364        this.radius = Math.random() * 30 + 10;365        this.color = `hsl(${Math.random() * 360}, 50%, 50%)`;366    }367    draw() {368        ctx.beginPath();369        ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);370        ctx.fillStyle = this.color;371        ctx.fill();372        ctx.closePath();373        for (let i = 0; i < 5; i++) {374            const craterRadius = Math.random() * (this.radius / 4);375            const craterX = this.x + (Math.random() - 0.5) * this.radius;376            const craterY = this.y + (Math.random() - 0.5) * this.radius;377            ctx.beginPath();378            ctx.arc(craterX, craterY, craterRadius, 0, Math.PI * 2);379            ctx.fillStyle = `rgba(0, 0, 0, 0.3)`;380            ctx.fill();381            ctx.closePath();382        }383    }384}385function applyScreenShake(intensity) {386    const shakeAmount = intensity * 10; 387    ctx.save();388    ctx.translate(389        (Math.random() - 0.5) * shakeAmount,390        (Math.random() - 0.5) * shakeAmount391    );392}393function showLanguageSelection() {394    document.getElementById('languageSelection').style.display = 'block';395}396function hideLanguageSelection() {397    document.getElementById('languageSelection').style.display = 'none';398}399function selectLanguage(lang) {400    currentLanguage = lang;401    hideLanguageSelection();402    updateUILanguage();403    showModeSelection();404}405function updateUILanguage() {406    document.getElementById('modeSelection').querySelector('h2').textContent = translations[currentLanguage].selectMode;407    document.getElementById('classicMode').textContent = translations[currentLanguage].classic;408    document.getElementById('bossMode').textContent = translations[currentLanguage].boss;409    document.getElementById('insanityMode').textContent = translations[currentLanguage].insanity;410    startButton.textContent = translations[currentLanguage].start;411}412function showModeSelection() {413    document.getElementById('modeSelection').style.display = 'block';414}415function hideModeSelection() {416    document.getElementById('modeSelection').style.display = 'none';417}418function selectMode(mode) {419    gameMode = mode;420    hideModeSelection();421    startButton.style.display = 'block';422    if (mode === 'insanity') {423        player = new Player(true);424    }425}426function resetGame() {427    player = new Player(gameMode === 'insanity');428    enemies = [];429    bullets = [];430    stars = Array(200).fill().map(() => new Star());431    planets = Array(5).fill().map(() => new Planet());432    score = 0;433    difficulty = 1;434    435    if (gameMode === 'insanity') {436        enemies.push(new Boss(), new Boss());437    }438}439function initAudio(file) {440    const reader = new FileReader();441    reader.onload = function(e) {442        audioContext.decodeAudioData(e.target.result, function(buffer) {443            audioBuffer = buffer;444            analyser = audioContext.createAnalyser();445            dataArray = new Uint8Array(analyser.frequencyBinCount);446            showLanguageSelection();447        });448    };449    reader.readAsArrayBuffer(file);450}451function startGame() {452    if (isGameRunning) return;453    454    isGameRunning = true;455    isPaused = false;456    resetGame();457    458    if (!audioContext || audioContext.state === 'closed') {459        audioContext = new (window.AudioContext || window.webkitAudioContext)();460        analyser = audioContext.createAnalyser();461        dataArray = new Uint8Array(analyser.frequencyBinCount);462    }463    464    audioSource = audioContext.createBufferSource();465    audioSource.buffer = audioBuffer;466    audioSource.connect(analyser);467    analyser.connect(audioContext.destination);468    audioSource.start(0);469    startTime = audioContext.currentTime;470    audioSource.onended = () => {471        isGameRunning = false;472        showGameOver();473    };474    475    animate();476}477function showPauseScreen() {478    ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';479    ctx.fillRect(0, 0, canvas.width, canvas.height);480    ctx.fillStyle = '#ffffff';481    ctx.font = '48px Orbitron';482    ctx.textAlign = 'center';483    ctx.fillText(translations[currentLanguage].paused, canvas.width / 2, canvas.height / 2);484}485function hidePauseScreen() {486}487function togglePause() {488    isPaused = !isPaused;489    if (isPaused) {490        audioSource.stop();491        pausedAt = audioContext.currentTime - startTime;492        showPauseScreen();493    } else {494        startTime = audioContext.currentTime - pausedAt;495        audioSource = audioContext.createBufferSource();496        audioSource.buffer = audioBuffer;497        audioSource.connect(analyser);498        audioSource.start(0, pausedAt);499        hidePauseScreen();500        requestAnimationFrame(animate);501    }502}503function animate() {504    if (!isGameRunning || isPaused) return;505    analyser.getByteFrequencyData(dataArray);506    let sum = dataArray.reduce((a, b) => a + b, 0);507    let average = sum / dataArray.length;508    difficulty = 1 + (average / 128) * 0.5; 509    ctx.clearRect(0, 0, canvas.width, canvas.height);510    511    applyScreenShake(average / 255); 512    spawnEnemy();513    updateGameObjects();514    drawGameObjects();515    ctx.fillStyle = `rgba(255, 255, 255, ${average / 512})`;516    ctx.fillRect(0, 0, canvas.width, canvas.height);517    ctx.restore(); 518    scoreDisplay.textContent = `${translations[currentLanguage].score}: ${score}`;519    requestAnimationFrame(animate);520}521const keys = {};522window.addEventListener('keydown', e => {523    keys[e.key.toLowerCase()] = true;524    if (e.code === 'Space' && isGameRunning) {525        e.preventDefault();526        togglePause();527    }528});529window.addEventListener('keyup', e => keys[e.key.toLowerCase()] = false);530canvas.addEventListener('mousemove', e => {531    mouseX = e.clientX;532    mouseY = e.clientY;533});534canvas.addEventListener('click', e => {535    if (isGameRunning && !isPaused) {536        bullets.push(new Bullet(player.x, player.y, e.clientX, e.clientY));537    }538});539document.getElementById('englishBtn').addEventListener('click', () => selectLanguage('en'));540document.getElementById('russianBtn').addEventListener('click', () => selectLanguage('ru'));541document.getElementById('classicMode').addEventListener('click', () => selectMode('classic'));542document.getElementById('bossMode').addEventListener('click', () => selectMode('boss'));543document.getElementById('insanityMode').addEventListener('click', () => selectMode('insanity'));544audioInput.addEventListener('change', e => {545    const file = e.target.files[0];546    if (file) {547        audioContext = new (window.AudioContext || window.webkitAudioContext)();548        initAudio(file);549    }550});551startButton.addEventListener('click', startGame);552window.addEventListener('resize', () => {553    canvas.width = window.innerWidth;554    canvas.height = window.innerHeight;555});556function spawnEnemy() {557    const spawnRate = gameMode === 'insanity' ? 0.04 : 0.02;558    if (Math.random() < spawnRate * difficulty) {559        enemies.push(new Enemy());560    }561    if (gameMode === 'boss' && enemies.every(e => !(e instanceof Boss))) {562        enemies.push(new Boss());563    }564    if (gameMode === 'insanity' && enemies.filter(e => e instanceof Boss).length < 2) {565        enemies.push(new Boss());566    }567}568function updateGameObjects() {569    player.move(keys);570    571    enemies.forEach((enemy, index) => {572        enemy.move(player.x, player.y);573        if (enemy instanceof Boss) {574            enemy.shoot(player.x, player.y);575        }576        if (Math.hypot(enemy.x - player.x, enemy.y - player.y) < player.radius + enemy.radius) {577            if (!player.invincible) {578                player.health -= enemy instanceof Boss ? 20 : 10; 579            }580            if (enemy instanceof Boss) {581                enemy.health -= 5; 582                if (enemy.health <= 0) {583                    createBossExplosion(enemy.x, enemy.y);584                    enemies.splice(index, 1);585                    score += 2000; 586                }587            } else {588                enemies.splice(index, 1);589            }590            if (player.health <= 0) {591                isGameRunning = false;592                showGameOver();593            }594        }595    });596    const healthFill = document.getElementById('healthFill');597    healthFill.style.width = `${player.health}%`;598    bullets = bullets.filter(bullet => {599        bullet.move();600        if (bullet.isBossBullet) {601            if (Math.hypot(bullet.x - player.x, bullet.y - player.y) < bullet.radius + player.radius) {602                if (!player.invincible) {603                    player.health -= 5;604                }605                return false;606            }607        } else {608            let hitEnemy = false;609            enemies = enemies.filter(enemy => {610                if (Math.hypot(bullet.x - enemy.x, bullet.y - enemy.y) < bullet.radius + enemy.radius) {611                    hitEnemy = true;612                    if (enemy instanceof Boss) {613                        enemy.health -= 2; 614                        if (enemy.health <= 0) {615                            score += 2000;616                            return false;617                        }618                        return true;619                    } else {620                        score += 10;621                        return false;622                    }623                }624                return true;625            });626            if (hitEnemy) return false;627        }628        return bullet.x > 0 && bullet.x < canvas.width && bullet.y > 0 && bullet.y < canvas.height;629    });630    stars.forEach(star => star.move());631}632function drawBackground() {633    ctx.fillStyle = '#000033';634    ctx.fillRect(0, 0, canvas.width, canvas.height);635    stars.forEach(star => star.draw());636    planets.forEach(planet => planet.draw());637}638function drawGameObjects() {639    drawBackground();640    player.draw();641    enemies.forEach(enemy => enemy.draw());642    bullets.forEach(bullet => bullet.draw());643}644function showGameOver() {645    if (audioContext && audioContext.state === 'running') {646        audioContext.close();647    }648    ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';649    ctx.fillRect(0, 0, canvas.width, canvas.height);650    651    ctx.fillStyle = '#ffffff';652    ctx.font = '48px Orbitron';653    ctx.textAlign = 'center';654    ctx.fillText(translations[currentLanguage].gameOver, canvas.width / 2, canvas.height / 2 - 50);655    656    ctx.font = '24px Orbitron';657    ctx.fillText(`${translations[currentLanguage].finalScore}: ${score}`, canvas.width / 2, canvas.height / 2 + 50);658    659    const restartButton = document.createElement('button');660    restartButton.textContent = translations[currentLanguage].playAgain;661    restartButton.style.position = 'absolute';662    restartButton.style.left = '50%';663    restartButton.style.top = '60%';664    restartButton.style.transform = 'translate(-50%, -50%)';665    restartButton.style.padding = '10px 20px';666    restartButton.style.fontSize = '18px';667    restartButton.style.backgroundColor = '#4CAF50';668    restartButton.style.color = 'white';669    restartButton.style.border = 'none';670    restartButton.style.borderRadius = '5px';671    restartButton.style.cursor = 'pointer';672    673    restartButton.addEventListener('click', () => {674        document.body.removeChild(restartButton);675        restartGame();676    });677    678    document.body.appendChild(restartButton);679}680function restartGame() {681    resetGame();682    startGame();683}684function createBossExplosion(x, y) {685    const explosionParticles = [];686    for (let i = 0; i < 100; i++) {687        explosionParticles.push({688            x: x,689            y: y,690            radius: Math.random() * 3 + 1,691            color: `hsl(${Math.random() * 60 + 300}, 100%, 50%)`,692            velocity: {693                x: (Math.random() - 0.5) * 8,694                y: (Math.random() - 0.5) * 8695            },696            alpha: 1697        });698    }699    700    function animateExplosion() {701        ctx.save();702        ctx.globalAlpha = 0.7;703        explosionParticles.forEach((particle, index) => {704            particle.x += particle.velocity.x;705            particle.y += particle.velocity.y;706            particle.alpha -= 0.01;707            708            if (particle.alpha <= 0) {709                explosionParticles.splice(index, 1);710            } else {711                ctx.beginPath();712                ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2);713                ctx.fillStyle = particle.color;714                ctx.globalAlpha = particle.alpha;715                ctx.fill();716            }717        });718        ctx.restore();719        720        if (explosionParticles.length > 0) {721            requestAnimationFrame(animateExplosion);722        }723    }724    725    animateExplosion();726}727</script>728</body></html>