/* 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;
  }
}