Untitled

 avatar
unknown
plain_text
4 days ago
8.4 kB
1
Indexable
<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>Mini Browser Shooter — PWA</title>
  <link rel="manifest" href="manifest.json">
  <meta name="theme-color" content="#0b1220" />
  <style>
    html,body{height:100%;margin:0;font-family:Inter,system-ui,Segoe UI,Roboto,Arial}
    #game {display:flex;align-items:center;justify-content:center;height:100%;background:#0b1220}
    canvas{background:#0f1724;border-radius:8px;box-shadow:0 8px 30px rgba(2,6,23,.6)}
    .hud{position:fixed;left:16px;top:16px;color:#e6eef8;font-weight:600}
    .controls{position:fixed;right:16px;top:16px;color:#c8d7ee;font-size:13px}
    .footer{position:fixed;left:16px;bottom:16px;color:#94a3b8;font-size:13px}
  </style>
</head>
<body>
  <div id="game">
    <canvas id="c" width="900" height="600"></canvas>
  </div>
  <div class="hud">Score: <span id="score">0</span> | HP: <span id="hp">100</span></div>
  <div class="controls">WASD/Arrows: move<br>Mouse: aim, Click: shoot</div>
  <div class="footer">Installable PWA shooter. Works offline after first load.</div>  <script>
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('sw.js').then(() => {
        console.log('Service Worker registered');
      });
    }

    // ===== Shooter Game Code =====
    const canvas = document.getElementById('c');
    const ctx = canvas.getContext('2d');
    const W = canvas.width, H = canvas.height;
    const scoreEl = document.getElementById('score');
    const hpEl = document.getElementById('hp');

    let keys = {}, mouse = {x: W/2, y: H/2, down: false};
    const player = {x: W/2, y: H/2, r: 16, speed: 240, hp: 100, fireRate: 0.12, fireTimer: 0};
    let bullets = [], enemies = [], particles = [];
    let score = 0, lastTime = performance.now(), spawnTimer = 0, spawnInterval = 1.2;

    function clamp(v,a,b){return Math.max(a,Math.min(b,v));}
    function rand(min,max){return Math.random()*(max-min)+min}
    function distance(a,b){return Math.hypot(a.x-b.x,a.y-b.y);} 

    window.addEventListener('keydown', e => keys[e.key.toLowerCase()] = true);
    window.addEventListener('keyup', e => keys[e.key.toLowerCase()] = false);
    canvas.addEventListener('mousemove', e => {
      const rect = canvas.getBoundingClientRect();
      mouse.x = (e.clientX - rect.left) * (canvas.width / rect.width);
      mouse.y = (e.clientY - rect.top) * (canvas.height / rect.height);
    });
    canvas.addEventListener('mousedown', () => mouse.down = true);
    window.addEventListener('mouseup', () => mouse.down = false);

    function shoot() {
      const angle = Math.atan2(mouse.y - player.y, mouse.x - player.x);
      const speed = 600;
      bullets.push({x: player.x + Math.cos(angle)*(player.r+6), y: player.y + Math.sin(angle)*(player.r+6), vx: Math.cos(angle)*speed, vy: Math.sin(angle)*speed, r:4});
    }

    function spawnEnemy() {
      const side = Math.floor(rand(0,4));
      let x,y;
      if(side===0){ x = -20; y = rand(0,H); }
      else if(side===1){ x = W+20; y = rand(0,H); }
      else if(side===2){ x = rand(0,W); y = -20; }
      else { x = rand(0,W); y = H+20; }
      const spd = rand(40,110);
      enemies.push({x,y,r:14,spd, hp: 20});
    }

    function spawnParticles(x,y,color,count=8) {
      for(let i=0;i<count;i++){
        const angle = rand(0,Math.PI*2);
        const s = rand(40,240);
        particles.push({x,y,vx:Math.cos(angle)*s, vy:Math.sin(angle)*s, life:rand(0.4,0.9), r:rand(1,3), color});
      }
    }

    function update(dt){
      let dx = 0, dy = 0;
      if(keys['w']||keys['arrowup']) dy -= 1;
      if(keys['s']||keys['arrowdown']) dy += 1;
      if(keys['a']||keys['arrowleft']) dx -= 1;
      if(keys['d']||keys['arrowright']) dx += 1;
      if(dx!==0||dy!==0){
        const len = Math.hypot(dx,dy); dx/=len; dy/=len;
        player.x += dx * player.speed * dt;
        player.y += dy * player.speed * dt;
      }
      player.x = clamp(player.x, player.r, W-player.r);
      player.y = clamp(player.y, player.r, H-player.r);

      player.fireTimer -= dt;
      if(mouse.down && player.fireTimer <= 0){
        shoot();
        player.fireTimer = player.fireRate;
      }

      for(let i=bullets.length-1;i>=0;i--){
        const b = bullets[i];
        b.x += b.vx * dt; b.y += b.vy * dt;
        if(b.x < -50 || b.x > W+50 || b.y < -50 || b.y > H+50) bullets.splice(i,1);
      }

      spawnTimer -= dt;
      if(spawnTimer<=0){ spawnEnemy(); spawnTimer = Math.max(0.4, spawnInterval * (0.92 - Math.min(0.6, score/40)*0.5)); }

      for(let i=enemies.length-1;i>=0;i--){
        const e = enemies[i];
        const angle = Math.atan2(player.y - e.y, player.x - e.x);
        e.x += Math.cos(angle) * e.spd * dt;
        e.y += Math.sin(angle) * e.spd * dt;

        if(distance(e, player) < e.r + player.r){
          player.hp -= 8;
          spawnParticles(player.x, player.y, '255,120,90', 10);
          enemies.splice(i,1);
          if(player.hp<=0) player.hp=0;
          continue;
        }

        for(let j=bullets.length-1;j>=0;j--){
          const b = bullets[j];
          if(distance(e,b) < e.r + b.r){
            e.hp -= 15;
            bullets.splice(j,1);
            spawnParticles(b.x,b.y,'255,230,120',6);
            if(e.hp<=0){
              score += 5;
              scoreEl.textContent = score;
              spawnParticles(e.x,e.y,'180,255,160',14);
              enemies.splice(i,1);
            }
            break;
          }
        }
      }

      for(let i=particles.length-1;i>=0;i--){
        const p = particles[i];
        p.x += p.vx * dt; p.y += p.vy * dt;
        p.vx *= 0.98; p.vy *= 0.98;
        p.life -= dt;
        if(p.life <= 0) particles.splice(i,1);
      }

      hpEl.textContent = Math.max(0, Math.round(player.hp));
    }

    function draw(){
      ctx.clearRect(0,0,W,H);
      ctx.save();
      ctx.globalAlpha = 0.06;
      ctx.strokeStyle = '#6b7280';
      for(let gx=0; gx<W; gx+=40){ ctx.beginPath(); ctx.moveTo(gx,0); ctx.lineTo(gx,H); ctx.stroke(); }
      for(let gy=0; gy<H; gy+=40){ ctx.beginPath(); ctx.moveTo(0,gy); ctx.lineTo(W,gy); ctx.stroke(); }
      ctx.restore();

      for(const p of particles){
        ctx.beginPath(); ctx.arc(p.x,p.y,p.r,0,Math.PI*2);
        ctx.fillStyle = `rgba(${p.color}, ${Math.max(0, p.life/1)})`;
        ctx.fill();
      }

      for(const e of enemies){
        ctx.beginPath(); ctx.arc(e.x,e.y,e.r,0,Math.PI*2);
        ctx.fillStyle = '#ff6b6b'; ctx.fill();
        ctx.beginPath(); ctx.arc(e.x,e.y, e.r-5,0,Math.PI*2);
        ctx.fillStyle = '#7b2b2b'; ctx.fill();
      }

      for(const b of bullets){
        ctx.beginPath(); ctx.arc(b.x,b.y,b.r,0,Math.PI*2);
        ctx.fillStyle = '#ffd166'; ctx.fill();
      }

      ctx.save();
      const ang = Math.atan2(mouse.y - player.y, mouse.x - player.x);
      ctx.translate(player.x, player.y);
      ctx.rotate(ang);
      ctx.beginPath(); ctx.arc(0,0,player.r,0,Math.PI*2); ctx.fillStyle='#60a5fa'; ctx.fill();
      ctx.fillStyle = '#1e293b'; ctx.fillRect(0, -6, player.r+10, 12);
      ctx.beginPath(); ctx.arc(6,0,4,0,Math.PI*2); ctx.fillStyle='#c7ddff'; ctx.fill();
      ctx.restore();

      ctx.beginPath(); ctx.arc(mouse.x, mouse.y, 8,0,Math.PI*2); ctx.strokeStyle='rgba(200,220,255,0.6)'; ctx.stroke();
      ctx.beginPath(); ctx.moveTo(mouse.x-14,mouse.y); ctx.lineTo(mouse.x+14,mouse.y); ctx.moveTo(mouse.x,mouse.y-14); ctx.lineTo(mouse.x,mouse.y+14); ctx.stroke();
    }

    function loop(t){
      const dt = Math.min(0.05, (t - lastTime) / 1000);
      lastTime = t;
      update(dt);
      draw();
      if(player.hp <= 0){
        ctx.fillStyle = 'rgba(2,6,23,0.6)'; ctx.fillRect(0,0,W,H);
        ctx.fillStyle = '#fff'; ctx.font = '40px sans-serif'; ctx.textAlign = 'center';
        ctx.fillText('GAME OVER', W/2, H/2 - 20);
        ctx.font = '18px sans-serif'; ctx.fillText('Press R to restart', W/2, H/2 + 20);
        if(keys['r']) reset();
      } else {
        requestAnimationFrame(loop);
      }
    }

    function reset(){
      bullets = []; enemies = []; particles = []; score = 0; player.hp = 100; scoreEl.textContent = 0; hpEl.textContent = 100; lastTime = performance.now(); spawnTimer = 0; requestAnimationFrame(loop);
    }

    for(let i=0;i<3;i++) spawnEnemy();
    requestAnimationFrame(loop);
  </script></body>
</html>
Editor is loading...
Leave a Comment