ASTEROIDS


// Animation
function animation1() {
  const f = [],
        i = [],
        stars = [];
  let t = 0;

  class S {
    constructor() {
      this.s = 20; // Size of the trail
      this.x = m.random() * (c.width / 2 - 100) + 50;
      this.y = m.random() * (c.height / 2 - 100) + 50;
      this.a = m.random() * m.PI * 2; // Angle of movement
      this.c = `hsl(${m.random() * 360},100%,50%)`; // Random color
      this.m = 2 + m.random() * 1; // Speed
      this.ac = 0.1; // Acceleration adjustment
      this.t = 0.06; // Turning rate
      this.av = 0.05; // Angular velocity
      this.e = 50; // Edge tolerance
      this.n = 20 + m.random() * 10; // Energy level
      this.z = 0.5; // Scale factor
      this.b = 0.05; // Energy drain
      this.vx = m.cos(this.a) * this.m; // Initial velocity x
      this.vy = m.sin(this.a) * this.m; // Initial velocity y
      this.p = []; // Trail points
      for (let j = 0; j < this.s; j++) {
        this.p.push({
          x: this.x - this.vx * j * 0.25,
          y: this.y - this.vy * j * 0.25
        });
      }
      this.g = null;
    }

    u(asteroids, spaceships) {
      const energyDrain = this.b * m.pow(this.z, 2);
      this.n -= energyDrain;
      if (this.n < 0) this.n = 0;
      this.z = 0.25 + (this.n / 100) * 0.75;

      let target = null,
        maxInfluence = -Infinity;

      // Determine influence of nearby asteroids
      for let asteroid of asteroids {
        const dx = asteroid.x - this.x;
        const dy = asteroid.y - this.y;
        const distance = m.hypot(dx, dy);
        const influence = asteroid.n / distance;
        if (influence > maxInfluence) {
          maxInfluence = influence;
          target = asteroid;
        }
      }

      let targetAngle = this.a;

      if (target) {
        this.g = target;
        targetAngle = m.atan2(target.y - this.y, target.x - this.x);
      }

      const boundaryAvoidanceAngle = this.ga();
      if (boundaryAvoidanceAngle !== null) {
        targetAngle = targetAngle * 0.2 + boundaryAvoidanceAngle * 0.8;
      }

      const alignmentAngle = this.aa(spaceships);
      if (alignmentAngle !== null) {
        targetAngle = targetAngle * 0.8 + alignmentAngle * 0.2;
      }

      let angularDifference = targetAngle - this.a;
      angularDifference = ((angularDifference + m.PI) % (m*2) - m.PI);
      this.a += angularDifference * this.t;

      const targetVx = m.cos(this.a) * this.m * this.z;
      const targetVy = m.sin(this.a) * this.m * this.z;
      this.vx += (targetVx - this.vx) * this.ac;
      this.vy += (targetVy - this.vy) * this.ac;

      this.x += this.vx;
      this.y += this.vy;

      if (
        this.x < 0 ||
        this.x > c.width / 2 ||
        this.y < 0 ||
        this.y > c.height / 2
      ) {
        this.n -= 10;
        if (this.n < 0) this.n = 0;
        this.x = m.max(0, m.min(this.x, c.width / 2));
        this.y = m.max(0, m.min(this.y, c.height / 2));
      }

      for (let j = 0; j < this.s; j++) {
        if (j === 0) {
          this.p[j] = { x: this.x, y: this.y };
        } else {
          const previousPoint = this.p[j-1];
          const currentPoint = this.p[j];
          const dx = previousPoint.x - currentPoint.x;
          const dy = previousPoint.y - currentPoint.y;
          const distance = m.hypot(dx, dy);
          const trailDistance = 10 * this.z;
          if (distance > trailDistance) {
            currentPoint.x += dx * 0.2;
            currentPoint.y += dy * 0.2;
          }
        }
      }

      if (this.g) {
        const dx = this.g.x - this.x;
        const dy = this.g.y - this.y;
        const distance = m.hypot(dx, dy);
        if (distance < 15 * this.z) {
          this.n += this.g.n;
          if (this.n > 200) this.n = 200;
          return this.g;
        }
      }

      return null;
    }

    ga() {
      let dx = 0,
          dy = 0;
      if (this.x < this.e) dx = 1;
      else if (this.x > c.width / 2 - this.e) dx = -1;
      if (this.y < this.e) dy = 1;
      else if (this.y > c.height / 2 - this.e) dy = -1;
      return dx !== 0 || dy !== 0 ? m.atan2(dy, dx) : null;
    }

    aa(spaceships) {
      let dx = 0,
          dy = 0;
      for (let ship of spaceships) {
        if (ship === this) continue;
        const deltaX = this.x - ship.x;
        const deltaY = this.y - ship.y;
        const distance = m.hypot(deltaX, deltaY);
        if (distance < 50) {
          dx += deltaX / distance;
          dy += deltaY / distance;
        }
      }
      return dx !== 0 || dy !== 0 ? m.atan2(dy, dx) : null;
    }

    d() {
      x.save();
      x.translate(this.x, this.y);
      x.rotate(this.a);

      // Spaceship body
      x.beginPath();
      x.moveTo(30 * this.z, 0);
      x.lineTo(-30 * this.z, 15 * this.z);
      x.lineTo(-30 * this.z, -15 * this.z);
      x.closePath();
      x.fillStyle = this.c;
      x.fill();
      x.restore();
    }
  }

  class A {
    constructor() {
      this.x = (m.random() * c.width) / 2;
      this.y = (m.random() * c.height) / 2;
      this.z = 3 + m.random() * 5;
      this.vx = (m.random() - 0.5) * 2;
      this.vy = (m.random() - 0.5) * 2;
      this.c = `rgb(${100 + m.random() * 155},${100 + m.random() * 155},${100 + m.random() * 155})`;
      this.n = this.z * 10;
    }

    u() {
      this.x += this.vx;
      this.y += this.vy;

      if (this.x < 0 || this.x > c.width / 2) this.vx *= -1;
      if (this.y < 0 || this.y > c.height / 2) this.vy *= -1;
    }

    d() {
      x.save();
      x.translate(this.x, this.y);
      x.beginPath();
      x.arc(0, 0, this.z, 0, 2 * m.PI);
      x.fill();
      x.restore();
    }
  }

  class Star {
    constructor() {
      this.x = (m.random() * c.width) / 2;
      this.y = (m.random() * c.height) / 2;
      this.size = 0.5 + m.random() * 2;
      this.color = `rgba(255, 255, 255, 0.7)`;
    }

    update() {
      this.y += 0.5;
      if (this.y > c.height / 2) {
        this.y = 0;
        this.x = m.random() * c.width;
      }
    }

    draw() {
      x.fillStyle = this.color;
      x.beginPath();
      x.arc(this.x, this.y, this.size, 0, 2 * m.PI);
      x.fill();
    }
  }

  function init() {
    resizeCanvas();
    for (let j = 0; j < 20; j++) {
      f.push(new S());
    }
    for (let j = 0; j < 50; j++) {
      i.push(new A());
    }
    for (let j = 0; j < 200; j++) {
      stars.push(new Star());
    }
  }

  function a() {
    x.clearRect(0, 0, c.width, c.height);

    stars.forEach((star) => {
      star.update();
      star.draw();
    });

    f.forEach((ship) => {
      let g = ship.u(i, f);
      if (g) {
        let j = i.indexOf(g);
        if (j > -1) i.splice(j, 1);
      }
      ship.d();
    });

    i.forEach((asteroid) => {
      asteroid.u();
      asteroid.d();
    });

    animationFrame = requestAnimationFrame(a());
  }

  init();
  a();
}