> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://studio.sketchpad.cc/sp/pad/view/ro.HV8ZiiSv0MT/rev.49
 * 
 * authors: 
 *   GoToLoop

 * license (unless otherwise specified): 
 *   creative commons attribution-share alike 3.0 license.
 *   https://creativecommons.org/licenses/by-sa/3.0/ 
 */ 



/**
 * Asteroids Vs. Bullets Sim (v3.21)
 * by GoToLoop (2014/Mar)
 *
 * forum.processing.org/two/discussion/3980/check-asteroid-death-help
 * forum.processing.org/two/discussion/3890/collision-problem
 *
 * studio.processingtogether.com/sp/pad/export/ro.9K-kjhuONcJDZ/latest
 */

static final int ASTEROIDS = 30, BULLETS = 100, NUM = 10, FPS = 60;
static final color BG = 0;
static final boolean ONLINE = 1/2 == 1/2.;

final ArrayList<Asteroid> asteroids;
final ArrayList<Bullet>   bullets;
{
  asteroids = ONLINE? new ArrayList() : new ArrayList(ASTEROIDS);
  bullets   = ONLINE? new ArrayList() : new ArrayList(BULLETS);
}

int score;
boolean isPaused;

void setup() {
  size(900, 750, JAVA2D);
  frameRate(FPS);
  smooth(4);

  ellipseMode(CENTER);
  rectMode(Asteroid.RECT_MODE);

  strokeWeight(Bullet.DIAM);
  fill(Asteroid.COLOUR);

  instantiateSprites();
}

void draw() {
  background(BG);

  displaySprites();
  checkDeath();
  checkVictory();
}

void mousePressed() {
  keyPressed();
}

void keyPressed() {
  if (isPaused ^= true)  noLoop();
  else                   loop();
}

void restart() {
  instantiateSprites();
  score = 0;
}

void instantiateSprites() {
  asteroids.clear();
  //bullets.clear();

  for ( int i = 0; i++ != ASTEROIDS; asteroids.add(new Asteroid()) );

  for ( int i = 0; i++ != BULLETS; bullets.add(new Bullet(
  random(width), random(height), random(TWO_PI))) );
}

void displaySprites() {
  noStroke();
  for (int a = asteroids.size(), len = a; a-- != 0;)
    if (asteroids.get(a).script()) {
      asteroids.set(a, asteroids.get(--len));
      asteroids.remove(len);
      println("An asteroid has flown too far away!");
    }

  stroke(Bullet.COLOUR);
  for (int b = bullets.size(), len = b; b-- != 0;)
    if (bullets.get(b).script()) {
      bullets.set(b, bullets.get(--len));
      bullets.remove(len);
      println("A bullet has strayed too far from view!");
    }
}

void checkDeath() {
  for (int lenB = bullets.size(), b = lenB; b-- != 0;) {
    final Bullet u = bullets.get(b);

    for (int lenA = asteroids.size(), a = lenA; a-- != 0;)
      if (u.checkHit(asteroids.get(a))) {
        //java.util.Collections.swap(asteroids, a, --lenA);
        //java.util.Collections.swap(bullets, b, --lenB);

        asteroids.set(a, asteroids.get(--lenA));
        bullets.set(b, bullets.get(--lenB));

        asteroids.remove(lenA);
        bullets.remove(lenB);

        score += NUM;
        println("An asteroid has been pulverized!");

        break;
      }
  }
}

void checkVictory() {
  final int a = asteroids.size(), b = bullets.size();

  if (!ONLINE)  frame.setTitle("Score: #" + score
    + "\t\tFPS: #" + round(frameRate));

  if (b == 0) {
    println("\nAsteroids left: #" + a + "\tFinal score: #" + score);
    isPaused = true;
    noLoop();
    restart();
  }
}

class Asteroid {
  static final boolean IS_CIRCLE = false;
  static final int RECT_MODE = CORNER;  // Either CORNER or CENTER.

  static final color COLOUR = #00A0F0;
  static final float SPD = 1.5;

  static final int DIAM  = 25, RAD = DIAM>>1, RAD_SQ = RAD*RAD;

  static final int DIM_X = 20, RAD_X = DIM_X>>1;
  static final int DIM_Y = 30, RAD_Y = DIM_Y>>1;

  float x = IS_CIRCLE
    ? random(DIAM, width   - DIAM)
    : random(DIM_X, width  - DIM_X);

  float y = IS_CIRCLE
    ? random(DIAM, height  - DIAM)
    : random(DIM_Y, height - DIM_Y);

  final float vx, vy;
  Asteroid()  // Fix for JS
  {
    final float dir = random(TWO_PI);
    vx  = cos(dir)*SPD;
    vy  = sin(dir)*SPD;
  }

  boolean script() {
    update();
    display();
    return isOuttaBounds();
  }

  void update() {
    x += vx;
    y += vy;
  }

  void display() {
    if (IS_CIRCLE)  ellipse(x, y, DIAM, DIAM);
    else            rect(x, y, DIM_X, DIM_Y);
  }

  boolean isOuttaBounds() {
    return IS_CIRCLE
      ? x < -RAD | x >= width+RAD | y < -RAD | y >= height+RAD

      : RECT_MODE == CORNER
      ? x < 0 | x >= width+DIM_X | y < 0 | y >= height+DIM_Y
      
      : x < -RAD_X | x >= width+RAD_X | y < -RAD_Y | y >= height+RAD_Y;
  }
}

class Bullet {
  static final color COLOUR = #C86400;
  static final int DIAM = 4;
  static final float SPD = 4.0;

  float x, y;
  final float vx, vy;

  Bullet(float xx, float yy, float dir) {
    x = xx;
    y = yy;

    vx = cos(dir)*SPD;
    vy = sin(dir)*SPD;
  }

  boolean script() {
    update();
    display();
    return isOuttaBounds();
  }

  void update() {
    x += vx;
    y += vy;
  }

  void display() {
    point(x, y);
  }

  boolean isOuttaBounds() {
    return x < -DIAM | x > width+DIAM | y < -DIAM | y > height+DIAM;
  }

  boolean checkHit(Asteroid a) {
    return Asteroid.IS_CIRCLE
      ? sq(a.x - x) + sq(a.y - y) < Asteroid.RAD_SQ

      : Asteroid.RECT_MODE == CORNER
      ? x >= a.x & x < a.x+Asteroid.DIM_X
      & y >= a.y & y < a.y+Asteroid.DIM_Y

      : x >= a.x-Asteroid.RAD_X & x < a.x+Asteroid.RAD_X
      & y >= a.y-Asteroid.RAD_Y & y < a.y+Asteroid.RAD_Y;
  }
}