import java.applet.*; 
import java.awt.*; 
import java.awt.image.*; 
import java.awt.event.*; 
import java.io.*; 
import java.net.*; 
import java.text.*; 
import java.util.*; 
import java.util.zip.*; 

public class burly extends BApplet {
// Burly Brawl
// by Naz (http://www.obect404.com)
// inspired by the "Burly Brawl" scene from Matrix Reloaded

int population = 10000;
int startpop = 1;
int counter = 0;
float easing = 20;
int i=0, j=0, k=0;
float a;
int[][] grid = new int[100][100];
int gridx=100, gridy=100;

chosen Neo = new chosen(random(300), random(300), random(TWO_PI), PI/6, random(TWO_PI), 0, PI/3, 1+(int)(random(4)), 1, 5);
agent[] Smith = new agent[population];

void setup() {
  size(300,300);
  background(0xffffffff);
  framerate(30);
  rectMode(CENTER_DIAMETER);
  ellipseMode(CENTER_DIAMETER);
  for (k=0; k<population; k++) {
    Smith[k] = new agent(k, random(300), (int)(random(2))*300, random(TWO_PI), PI/6, random(TWO_PI), 0, PI/3, 1+random(4), 1, 5, Neo);
  }
  for (i=0; i<gridx; i++) {
    for (j=0; j<gridy; j++) {
      grid[i][j] = -1;
    }
  }
}

void loop() {
  // Neo routine
  if (mousePressed == true) {
    Neo.xpos += (mouseX - Neo.xpos)/easing;
    Neo.ypos += (mouseY - Neo.ypos)/easing;
  }
  push();
  Neo.move();
  Neo.spin();
  Neo.bounce();
  Neo.turn();
  Neo.chspeed();
  translate(Neo.xpos, Neo.ypos);
  rotate(Neo.spin);
  rect(0, 0, 15, 1);
  ellipse(0, 0, 5, 5);
  pop();
  
  // Smith[0] routine
  for (k=0; k<startpop; k++) {
    push();
    Smith[k].chase();
    translate(Smith[k].xpos, Smith[k].ypos);
    rotate(Smith[k].anglediff);
    rect(10,0,3,5);
    pop();
  }
  counter++;
  if (counter % 100 == 0 && startpop < population) {
    startpop++;
  }
}

// Chosen one class
class chosen {
  float xpos, ypos, dir, turnrate, spin, spinrate, maxspin, speed, minspeed, maxspeed;
  chosen (float x, float y, float d, float trate, float sp, float sprate, float maxsp, float s, float mins, float maxs) {
    xpos = x;
    ypos = y;
    dir = d;
    turnrate = trate;
    spin = sp;
    spinrate = sprate;
    maxspin = maxsp;
    speed = s;
    minspeed = mins;
    maxspeed = maxs;
  }
  void move() {
    xpos += speed*cos(dir);
    ypos += speed*sin(dir);
  }
  void bounce() {
    if (xpos < 20) {
      xpos = 20;
    } else if (xpos > width - 20) {
      xpos = width - 20;
    }
    if (ypos < 20) {
      ypos = 20;
    } else if (ypos > height - 20) {
      ypos = height - 20;
    }
  }
  void turn() {
    dir += random(2*turnrate) - turnrate;
    if (dir < 0) {
      dir = TWO_PI + dir;
    } else if (dir > TWO_PI) {
      dir = dir - TWO_PI;
    }
  }
  void chspeed() {
    speed += random(2) - 1;
    if (speed < minspeed) {
      speed = minspeed;
    } else if (speed > maxspeed) {
      speed = maxspeed;
    }
  }
  void spin() {
    spinrate += random(2) - 1;
    if (spinrate < -maxspin) {
      spinrate = -maxspin;
    } else if (spinrate > maxspin) {
      spinrate = maxspin;
    }
    spin += spinrate;
    if (spin < 0) {
      spin = TWO_PI - spin;
    } else if (spin > TWO_PI) {
      spin = spin - TWO_PI;
    }
  }
}

// Agent class
class agent {
  int id;
  float gas=-200, recovery=random(80)+20, xpos, ypos, dir, turnrate, spin, spinrate, maxspin, speed, minspeed, maxspeed, anglediff, distance2;
  chosen target;
  agent (int a_id, float x, float y, float d, float trate, float sp, float sprate, float maxsp, float s, float mins, float maxs, chosen t) {
    id = a_id;
    xpos = x;
    ypos = y;
    dir = d;
    turnrate = trate;
    spin = sp;
    spinrate = sprate;
    maxspin = maxsp;
    speed = s;
    minspeed = mins;
    maxspeed = maxs;
    target = t;
  }
  void chase() {
     distance2 = (sq(target.xpos-xpos)+sq(target.ypos-ypos));
      //impact
      if (distance2 <= 49 && target.spinrate > 0.4f) {
        spin = random(TWO_PI);
        gas = target.spinrate*20;
      } 
      if (gas > -recovery) { // knockback
        if (gas > 0) {
          xpos += gas*cos(spin);
          ypos += gas*sin(spin);
          if (xpos < 1) {
            xpos = 1;
          } else if (xpos > width-2) {
            xpos = width-2;
          }
          if (ypos < 1) {
            ypos = 1;
          } else if (ypos > height-2) {
            ypos = height-2;
          }
        }
        gas --;
      } else {
        //chase
        anglediff = atan2(target.ypos-ypos, target.xpos-xpos);
        xpos += speed*cos(anglediff);
        ypos += speed*sin(anglediff);
      }
    }
  void move() {
    xpos += speed*cos(dir);
    ypos += speed*sin(dir);
  }
  void bounce() {
    if (xpos < 20) {
      xpos = 20;
    } else if (xpos > width - 20) {
      xpos = width - 20;
    }
    if (ypos < 20) {
      ypos = 20;
    } else if (ypos > height - 20) {
      ypos = height - 20;
    }
  }
  void turn() {
    dir += random(2*turnrate) - turnrate;
    if (dir < 0) {
      dir = TWO_PI + dir;
    } else if (dir > TWO_PI) {
      dir = dir - TWO_PI;
    }
  }
  void chspeed() {
    speed += random(2) - 1;
    if (speed < minspeed) {
      speed = minspeed;
    } else if (speed > maxspeed) {
      speed = maxspeed;
    }
  }
  void spin() {
    spinrate += random(2) - 1;
    if (spinrate < -maxspin) {
      spinrate = -maxspin;
    } else if (spinrate > maxspin) {
      spinrate = maxspin;
    }
    spin += spinrate;
    if (spin < 0) {
      spin = TWO_PI - spin;
    } else if (spin > TWO_PI) {
      spin = spin - TWO_PI;
    }
  }
}

}
