🎮ArcadeLab

STAR STRIKE

by GoldenBuilder46
736 lines13.0 KB
▶ Play
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>STAR STRIKE</title>

<style>
*{box-sizing:border-box}

body{
    margin:0;
    overflow:hidden;
    background:#02030d;
    font-family:Arial,sans-serif;
}

canvas{
    display:block;
    background:linear-gradient(#02051b,#080018);
}

#hud{
    position:fixed;
    top:15px;
    left:15px;
    right:15px;
    display:flex;
    justify-content:space-between;
    color:white;
    font-size:18px;
    font-weight:bold;
    pointer-events:none;
}

#start,#over{
    position:fixed;
    inset:0;
    display:flex;
    align-items:center;
    justify-content:center;
    flex-direction:column;
    background:rgba(0,0,15,.82);
    color:white;
    text-align:center;
}

#over{display:none}

h1{
    font-size:60px;
    margin:0 0 10px;
    letter-spacing:5px;
}

p{font-size:20px}

button{
    border:0;
    border-radius:12px;
    padding:15px 35px;
    font-size:20px;
    font-weight:bold;
    cursor:pointer;
}
</style>
</head>

<body>

<canvas id="game"></canvas>

<div id="hud">
    <span>⭐ <span id="score">0</span></span>
    <span>🚀 LEVEL <span id="level">1</span></span>
    <span>❤️ <span id="lives">3</span></span>
</div>

<div id="start">
    <h1>STAR STRIKE</h1>
    <p>Destroy the invasion and defeat the bosses!</p>
    <p>← → / A D = Move &nbsp; | &nbsp; SPACE = Shoot</p>
    <button onclick="startGame()">START GAME</button>
</div>

<div id="over">
    <h1>GAME OVER</h1>
    <p>Score: <span id="finalScore">0</span></p>
    <button onclick="restart()">PLAY AGAIN</button>
</div>

<script>

const canvas=document.getElementById("game");
const ctx=canvas.getContext("2d");

function resize(){
    canvas.width=innerWidth;
    canvas.height=innerHeight;
}
resize();
addEventListener("resize",resize);

const keys={};

addEventListener("keydown",e=>{
    keys[e.code]=true;
    if(e.code==="Space") e.preventDefault();
});

addEventListener("keyup",e=>{
    keys[e.code]=false;
});

let score=0;
let lives=3;
let level=1;
let running=false;
let boss=null;
let spawnTimer=0;
let shotTimer=0;

const player={
    x:0,
    y:0,
    speed:8,
    size:22
};

let bullets=[];
let enemyBullets=[];
let enemies=[];
let particles=[];
let powerups=[];
let stars=[];

function resetPlayer(){
    player.x=canvas.width/2;
    player.y=canvas.height-90;
}

for(let i=0;i<160;i++){
    stars.push({
        x:Math.random()*innerWidth,
        y:Math.random()*innerHeight,
        s:Math.random()*2+0.5,
        speed:Math.random()*2+0.5
    });
}

function startGame(){
    document.getElementById("start").style.display="none";
    restart();
}

function restart(){
    score=0;
    lives=3;
    level=1;
    boss=null;
    bullets=[];
    enemyBullets=[];
    enemies=[];
    particles=[];
    powerups=[];
    spawnTimer=0;
    running=true;
    resetPlayer();
    document.getElementById("over").style.display="none";
}

function shoot(){

    if(shotTimer>0)return;

    bullets.push({
        x:player.x,
        y:player.y-25,
        speed:13,
        size:5
    });

    shotTimer=8;
}

function spawnEnemy(){

    enemies.push({
        x:Math.random()*(canvas.width-60)+30,
        y:-40,
        size:20+Math.random()*15,
        speed:2+level*.25,
        hp:1+Math.floor(level/3)
    });
}

function spawnBoss(){

    boss={
        x:canvas.width/2,
        y:100,
        size:70,
        hp:80+level*20,
        maxHp:80+level*20,
        direction:1,
        timer:0
    };
}

function explosion(x,y){

    for(let i=0;i<25;i++){

        particles.push({
            x:x,
            y:y,
            vx:(Math.random()-.5)*8,
            vy:(Math.random()-.5)*8,
            life:30+Math.random()*20,
            size:2+Math.random()*5
        });
    }
}

function dropPowerup(x,y){

    if(Math.random()<.12){

        powerups.push({
            x:x,
            y:y,
            size:12,
            speed:3
        });
    }
}

function update(){

    if(!running)return;

    // movement
    if(keys.ArrowLeft||keys.KeyA)
        player.x-=player.speed;

    if(keys.ArrowRight||keys.KeyD)
        player.x+=player.speed;

    player.x=Math.max(25,Math.min(canvas.width-25,player.x));

    // shooting
    if(keys.Space)shoot();

    if(shotTimer>0)shotTimer--;

    // stars
    stars.forEach(s=>{
        s.y+=s.speed;

        if(s.y>canvas.height){
            s.y=0;
            s.x=Math.random()*canvas.width;
        }
    });

    // spawn enemies
    spawnTimer--;

    if(!boss && spawnTimer<=0){

        spawnEnemy();

        spawnTimer=Math.max(
            12,
            50-level*3
        );
    }

    // bullets
    bullets.forEach(b=>{
        b.y-=b.speed;
    });

    bullets=bullets.filter(b=>b.y>-30);

    // enemies
    enemies.forEach(e=>{
        e.y+=e.speed;
    });

    // enemy bullets
    enemyBullets.forEach(b=>{
        b.y+=b.speed;
    });

    enemyBullets=enemyBullets.filter(
        b=>b.y<canvas.height+30
    );

    // enemy shooting
    enemies.forEach(e=>{

        if(Math.random()<.004+level*.0005){

            enemyBullets.push({
                x:e.x,
                y:e.y,
                speed:4+level*.2,
                size:5
            });
        }
    });

    // boss
    if(boss){

        boss.x+=boss.direction*2;

        if(
            boss.x<boss.size ||
            boss.x>canvas.width-boss.size
        ){
            boss.direction*=-1;
        }

        boss.timer++;

        if(boss.timer%45===0){

            enemyBullets.push({
                x:boss.x,
                y:boss.y+boss.size,
                speed:6,
                size:8
            });
        }
    }

    // bullet vs enemies
    for(let i=enemies.length-1;i>=0;i--){

        let e=enemies[i];

        for(let j=bullets.length-1;j>=0;j--){

            let b=bullets[j];

            if(
                Math.hypot(
                    b.x-e.x,
                    b.y-e.y
                )<e.size
            ){

                bullets.splice(j,1);
                e.hp--;

                explosion(b.x,b.y);

                if(e.hp<=0){

                    score+=10;
                    dropPowerup(e.x,e.y);
                    explosion(e.x,e.y);
                    enemies.splice(i,1);
                }

                break;
            }
        }
    }

    // bullets vs boss
    if(boss){

        for(let i=bullets.length-1;i>=0;i--){

            let b=bullets[i];

            if(
                Math.hypot(
                    b.x-boss.x,
                    b.y-boss.y
                )<boss.size
            ){

                bullets.splice(i,1);
                boss.hp--;

                explosion(b.x,b.y);

                if(boss.hp<=0){

                    score+=500;
                    explosion(boss.x,boss.y);
                    boss=null;
                    level++;
                }

                break;
            }
        }
    }

    // player vs enemy bullets
    for(let i=enemyBullets.length-1;i>=0;i--){

        let b=enemyBullets[i];

        if(
            Math.hypot(
                b.x-player.x,
                b.y-player.y
            )<player.size+8
        ){

            enemyBullets.splice(i,1);
            hitPlayer();
        }
    }

    // player vs enemies
    for(let i=enemies.length-1;i>=0;i--){

        let e=enemies[i];

        if(
            Math.hypot(
                e.x-player.x,
                e.y-player.y
            )<e.size+player.size
        ){

            enemies.splice(i,1);
            explosion(e.x,e.y);
            hitPlayer();
        }

        else if(e.y>canvas.height+50){

            enemies.splice(i,1);
            hitPlayer();
        }
    }

    // powerups
    powerups.forEach(p=>{
        p.y+=p.speed;
    });

    for(let i=powerups.length-1;i>=0;i--){

        let p=powerups[i];

        if(
            Math.hypot(
                p.x-player.x,
                p.y-player.y
            )<30
        ){

            score+=50;
            lives=Math.min(5,lives+1);
            powerups.splice(i,1);
        }

        else if(p.y>canvas.height){

            powerups.splice(i,1);
        }
    }

    // particles
    particles.forEach(p=>{
        p.x+=p.vx;
        p.y+=p.vy;
        p.life--;
    });

    particles=particles.filter(p=>p.life>0);

    // boss appears every 500 points
    if(
        score>0 &&
        score%500<10 &&
        !boss
    ){
        spawnBoss();
    }

    document.getElementById("score").textContent=score;
    document.getElementById("lives").textContent=lives;
    document.getElementById("level").textContent=level;
}

function hitPlayer(){

    lives--;
    explosion(player.x,player.y);

    if(lives<=0){
        gameOver();
    }
}

function draw(){

    ctx.clearRect(
        0,0,
        canvas.width,
        canvas.height
    );

    // stars
    ctx.fillStyle="white";

    stars.forEach(s=>{
        ctx.globalAlpha=.4+Math.random()*.6;
        ctx.fillRect(
            s.x,s.y,
            s.s,s.s
        );
    });

    ctx.globalAlpha=1;

    // player
    ctx.save();

    ctx.translate(
        player.x,
        player.y
    );

    ctx.shadowBlur=25;
    ctx.shadowColor="#00ffff";

    ctx.fillStyle="#00ffff";

    ctx.beginPath();
    ctx.moveTo(0,-30);
    ctx.lineTo(-25,25);
    ctx.lineTo(0,15);
    ctx.lineTo(25,25);
    ctx.closePath();
    ctx.fill();

    ctx.fillStyle="white";

    ctx.beginPath();
    ctx.arc(0,-5,7,0,Math.PI*2);
    ctx.fill();

    ctx.restore();

    // player bullets
    ctx.fillStyle="#00ffcc";
    ctx.shadowBlur=15;
    ctx.shadowColor="#00ffcc";

    bullets.forEach(b=>{
        ctx.fillRect(
            b.x-b.size/2,
            b.y,
            b.size,
            18
        );
    });

    // enemies
    enemies.forEach(e=>{

        ctx.save();
        ctx.translate(e.x,e.y);

        ctx.shadowBlur=20;
        ctx.shadowColor="#ff1744";

        ctx.fillStyle="#ff1744";

        ctx.beginPath();
        ctx.arc(
            0,0,
            e.size,
            0,
            Math.PI*2
        );
        ctx.fill();

        ctx.fillStyle="#111";

        ctx.beginPath();
        ctx.arc(-7,-4,4,0,Math.PI*2);
        ctx.arc(7,-4,4,0,Math.PI*2);
        ctx.fill();

        ctx.restore();
    });

    // boss
    if(boss){

        ctx.save();

        ctx.translate(
            boss.x,
            boss.y
        );

        ctx.shadowBlur=30;
        ctx.shadowColor="#a020f0";

        ctx.fillStyle="#8e44ad";

        ctx.beginPath();
        ctx.arc(
            0,0,
            boss.size,
            0,
            Math.PI*2
        );
        ctx.fill();

        ctx.fillStyle="#ff4444";

        ctx.beginPath();
        ctx.arc(-25,-10,10,0,Math.PI*2);
        ctx.arc(25,-10,10,0,Math.PI*2);
        ctx.fill();

        ctx.restore();

        // boss health bar
        ctx.fillStyle="#333";

        ctx.fillRect(
            canvas.width/2-150,
            25,
            300,
            12
        );

        ctx.fillStyle="#ff3366";

        ctx.fillRect(
            canvas.width/2-150,
            25,
            300*(boss.hp/boss.maxHp),
            12
        );
    }

    // enemy bullets
    ctx.fillStyle="#ff5555";

    enemyBullets.forEach(b=>{
        ctx.beginPath();
        ctx.arc(
            b.x,
            b.y,
            b.size,
            0,
            Math.PI*2
        );
        ctx.fill();
    });

    // powerups
    powerups.forEach(p=>{

        ctx.fillStyle="#ffd32a";
        ctx.shadowBlur=20;
        ctx.shadowColor="#ffd32a";

        ctx.beginPath();
        ctx.arc(
            p.x,
            p.y,
            p.size,
            0,
            Math.PI*2
        );
        ctx.fill();

        ctx.fillStyle="#111";
        ctx.font="bold 14px Arial";
        ctx.textAlign="center";
        ctx.fillText("+",p.x,p.y+5);
    });

    // explosions
    particles.forEach(p=>{

        ctx.globalAlpha=p.life/50;
        ctx.fillStyle="#ffcc00";

        ctx.beginPath();
        ctx.arc(
            p.x,
            p.y,
            p.size,
            0,
            Math.PI*2
        );
        ctx.fill();
    });

    ctx.globalAlpha=1;
    ctx.shadowBlur=0;
}

function gameOver(){

    running=false;

    document.getElementById("finalScore")
        .textContent=score;

    document.getElementById("over")
        .style.display="flex";
}

function loop(){

    update();
    draw();

    requestAnimationFrame(loop);
}

resetPlayer();
loop();

</script>
</body>
</html>

Game Source: STAR STRIKE

Creator: GoldenBuilder46

Libraries: none

Complexity: complex (736 lines, 13.0 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: star-strike-goldenbuilder46" to link back to the original. Then publish at arcadelab.ai/publish.