import processing.core.*; 
import processing.xml.*; 

import processing.opengl.*; 
import javax.media.opengl.*; 

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.*; 
import java.util.regex.*; 

public class RepairEarth extends PApplet {



//3d Friendly Game Engine Thingy.
//System.out.println("1");
//import java.awt.Button;
ArrayList gameState; //an array that will hold gamestates

long elapsedTime; //in miliseconds 
// Get current time
long start = 1;

int currentState = 0;


public void setup() {   
 frameRate(60) ;
 //cursor(HAND);
  
  Frame framer;
   gameState = new ArrayList();
   
   gameState.add(new Menu());
   gameState.add(new Play()); //play is a der. of GameState, and represents the actual game
   gameState.add(new GameOver());

   colorMode(RGB,100); //specifying colours is done between 0 and 100.
   rectMode(CENTER);   //mode for drawing rectangles
//   size(1024, 768, P3D); //screen size
 // size(1024, 768, OPENGL);
   noCursor();
   noStroke();
   fill(50);        //fill colour
   
   start =  System.currentTimeMillis();
 } 
 
 public void draw() {

   // Get elapsed time in milliseconds
    elapsedTime = System.currentTimeMillis()-start;
    start = System.currentTimeMillis();
    if (elapsedTime == 0)
      elapsedTime = 1;
  // System.out.println("framerate: " + 1000/elapsedTime + "  elapsedTime: " + elapsedTime);
    //System.out.println("elapsedTime " +System.currentTimeMillis());
   
   background(128,128,128); //background colour
  
   int stateChange =  ((GameState)gameState.get(currentState)).Update(elapsedTime);
                      ((GameState)gameState.get(currentState)).Draw(elapsedTime);


   CheckGameStateChange(stateChange); //check to see if the game state should be changed
                          //(ie. pop a state off and put it on top)
   
 }
 
boolean left=false;
boolean right=false;
boolean center=false;

public void mousePressed()
{
  ((GameState)gameState.get(currentState)).mousePressed();
}

public void mouseReleased()
{
  ((GameState)gameState.get(currentState)).mouseReleased();
} 

 public void CheckGameStateChange(int stateChange)
 {
    if (stateChange == 1)
    {
      
      currentState = 1;
    }
    if (stateChange ==2)
    {
      currentState = 2;
    }
    if (stateChange == 0)
    {
      for (int i = gameState.size() - 1; i >= 0; i--)
      {
        gameState.remove(i);
      }
      gameState.add(new Menu());
     gameState.add(new Play()); //play is a der. of GameState, and represents the actual game
     gameState.add(new GameOver());
     currentState = 0;
    }
 }

class AllHumans
{
  float growthDistance = 100;
  int chanceSpawnType = 25; //25 percent (out of 100) chance that new humans will spawn randomly somewhere
                            //opposed to spawning as a branch off an old human node.
  int humanSpawnCounter = 0;
  int humanSpawnTime = 1700; //1800 should be level 1;  make this baseed on how many humans there are
                           //more humans, the higher teh spawn time (300 being the max).
  
  ArrayList humanList = new ArrayList();
  int type1Dmg = 1;
  int type2Dmg = 3;
  int type3Dmg = 9;
  BoundingCircle[] landCircles;
  PImage landImg;
  PImage greenHumanImg;
  PImage redHumanImg;
  PImage terrorHumanImg;
  PImage entgreenHumanImg;
  PImage entredHumanImg;
  PImage entterrorHumanImg;
  PImage infectedImg;
  float landMaxWeight = 0;
  
  public AllHumans()
  {
    greenHumanImg = loadImage("greenHuman.bmp");
    redHumanImg = loadImage("redHuman.bmp");
    terrorHumanImg = loadImage("terrorHuman.bmp");
    entgreenHumanImg = loadImage("EgreenHuman.bmp");
    entredHumanImg = loadImage("EredHuman.bmp");
    entterrorHumanImg = loadImage("EterrorHuman.bmp");
    infectedImg = loadImage("infected.png");
    landImg = loadImage("purpRad.png");
    landCircles = new BoundingCircle[15];
    
    landCircles[0] = new BoundingCircle(new PVector(-100,600,0), 900, true, 0); //australia
    
    landCircles[1] = new BoundingCircle(new PVector(2500,-1800,0), 1800, true, 1); //canada
    
    landCircles[2] = new BoundingCircle(new PVector(1900,-2050,0), 1100, true, 2); //alaska
    
    landCircles[3] = new BoundingCircle(new PVector(2950,-1150,0), 1800, true, 3); //USA
    
     landCircles[4] = new BoundingCircle(new PVector(3750,150,0), 1300, true, 4 ); //SouthAmN
     
     landCircles[5] = new BoundingCircle(new PVector(3800,650,0), 1000, true, 5); //SouthAmS
     
     landCircles[6] = new BoundingCircle(new PVector(5300,-500,0), 1450, true, 6); //NAfrica
     
     landCircles[7] = new BoundingCircle(new PVector(5850,-150,0), 1500, true, 7); //CAfrica
     
     landCircles[8] = new BoundingCircle(new PVector(5775, 400,0), 1050, true, 8); //SAfrica
     
     landCircles[9] = new BoundingCircle(new PVector(5375, -1290,0), 570, true, 9); //france
      
     landCircles[10] = new BoundingCircle(new PVector(5725,-1315,0), 900, true, 10); //CEur
       
     landCircles[11] = new BoundingCircle(new PVector(7300,-1500,0), 3750, true, 11); //Asia
       
     landCircles[12] = new BoundingCircle(new PVector(6185,-1600,0), 1680, true, 12); //EastEur
     
     landCircles[13] = new BoundingCircle(new PVector(8360,-2060,0), 1400, true, 13); //NEAsia
  
     landCircles[14] = new BoundingCircle(new PVector(8900, -2100,0), 920, true, 14); //NEAsia2

      //humanList.add(new Human (new PVector(1900,-2050,0), 2, 1));
     AssignWeights(landCircles);
     AssignNeighbours(landCircles);
  }
  
  public void AssignWeights(BoundingCircle[] circleList)
  {
    landMaxWeight = 0;
    //first get the max weight
    for (int i = 0; i < circleList.length; i++)
        landMaxWeight += circleList[i].radius*2;
        
    //assign the circles a range of values, representing their 
    //weight within the list of circles based on radius
    for (int i = 0; i < circleList.length; i++)
    {
      if (i == 0)
      {
        circleList[i].weightMin = 0;
        circleList[i].weightMax = circleList[i].radius * 2;  
      }
      else
      {
        circleList[i].weightMin = circleList[i-1].weightMax;
        circleList[i].weightMax = circleList[i].weightMin + (circleList[i].radius * 2);  
      }
    }
  }
  public void AssignNeighbours(BoundingCircle[] circleList)
  {
    for (int i = 0; i < circleList.length; i++)
    {
      for (int j = 0; j < circleList.length; j++)
      {
        //if (circleList[i].pos != circleList[j].pos)
        //{
          if (circleList[i].CircleCollision(circleList[j].pos, circleList[j].radius))
          {
          circleList[i].neighbourList.add(circleList[j]);
          
          //System.out.println("circle " + i + " has " + j + " as a neighbour");
          }
        //}
      }
    }
  }
  
  public void Update(long elapsedTime, ArrayList entangles, Camera3D cameraa, float worldWidth)
  {    
    humanSpawnCounter += elapsedTime;
    
    //Spawn More Humans
    if (humanSpawnCounter > humanSpawnTime)
    {
      humanSpawnCounter = 0;
      
      if (random(100) < chanceSpawnType)
      {
          float rhor = random(landMaxWeight);
          for (int i = 0; i < landCircles.length; i++)
          {
            if (rhor >= landCircles[i].weightMin && rhor < landCircles[i].weightMax)
            {
              float randDir = random(359);
              PVector spawnPos = new PVector(landCircles[i].pos.x + ((rhor - landCircles[i].weightMin) - landCircles[i].radius), landCircles[i].pos.y, 0);
              spawnPos = RotateVector(landCircles[i].pos, randDir, spawnPos);  
               
               if (!HumanOnEntangle(spawnPos, 25/2, entangles, cameraa, worldWidth))
                   humanList.add(new Human(spawnPos, i, (int)random(3) + 1));
              
              break;
            }
          }
      }
      else
      {
        if (humanList.size() > 0)
        {
         
          int randHum = (int)(random(humanList.size()));
          float randDir = random(359);
          
          PVector spawnPos = new PVector();
          spawnPos.set(((Human)humanList.get(randHum)).quadd.center);
          spawnPos.x -= growthDistance;
          
          spawnPos = RotateVector(((Human)humanList.get(randHum)).quadd.center, randDir, spawnPos);  
          Human newHuman = new Human(spawnPos, -1, ((Human)humanList.get(randHum)).type);
          if (newHuman.type == 1)
          {
            if ( (int)random(4) > 2)
            {
              newHuman.type = 2;
            }
          }
          newHuman.currentCircle = CheckNewHumanOnLandCollision(randHum, newHuman.pos );
          
          if (newHuman.currentCircle == -1) //if it is outside the circle, spawn it randomly within the circle
          {
           
            PVector moveVector =PVector.sub( landCircles[((Human)humanList.get(randHum)).currentCircle].pos, newHuman.quadd.center);
            moveVector.normalize();
            moveVector.mult((int)random( landCircles[((Human)humanList.get(randHum)).currentCircle].radius ) + growthDistance);
            
            newHuman.Move( moveVector );
            newHuman.currentCircle = ((Human)humanList.get(randHum)).currentCircle;
           // println("outside of circle");
          }
          
         // println("spawn new human off of human num = " + randHum + " on circleID = " + newHuman.currentCircle);
        if (!HumanOnEntangle(newHuman.quadd.center, newHuman.radius, entangles, cameraa, worldWidth))
          humanList.add(newHuman);
        }    
        
      }
     
    }
    
  }
  
  
  public boolean HumanOnEntangle(PVector newHumanPos, float newHumanRadius, ArrayList entangles, Camera3D cameraa, float worldWidth)
  {
    if (entangles.size() >0)
    {
    for (int i = entangles.size() - 1; i >=0; i--)
    {
     if ( CircleCollisionNoOverlap(((BoundingCircle)entangles.get(i)).quadd.center, ((BoundingCircle)entangles.get(i)).radius, newHumanPos, newHumanRadius ))
      {
        return true;
      }
     else if (newHumanPos.x > cameraa.pos.x)
      {
        PVector virtualEntPos = new PVector();
        virtualEntPos.set(((BoundingCircle)entangles.get(i)).quadd.center);
        virtualEntPos.x += worldWidth;
        if ( CircleCollisionNoOverlap(virtualEntPos, ((BoundingCircle)entangles.get(i)).radius, newHumanPos, newHumanRadius ))
        {
          return true;
        }
      }
     else if (newHumanPos.x < cameraa.pos.x)
     {
       PVector virtualEntPos= new PVector();
       virtualEntPos.set(((BoundingCircle)entangles.get(i)).quadd.center);
        virtualEntPos.x -= worldWidth;
       if ( CircleCollisionNoOverlap(virtualEntPos, ((BoundingCircle)entangles.get(i)).radius, newHumanPos, newHumanRadius ))
        {
          return true;
        }
     }
    }
    
    }
    return false;
  }
  
  
// FOR DEBUGGING ONLY, will create problems otherwise
//  void CreateHumanAtPos(PVector ipos)
//  {
//    humanList.add(new Human(ipos, 0, 1));
//  }


  //check to see if the new human being spawned is going to be on land.
  public int CheckNewHumanOnLandCollision(int humanNum, PVector newHumanPos)
  {
    for (int i = 0; i < landCircles[(((Human)humanList.get(humanNum)).currentCircle)].neighbourList.size(); i++)
    {
      if (((BoundingCircle)landCircles[(((Human)humanList.get(humanNum)).currentCircle)].neighbourList.get(i)).CircleCollision( newHumanPos, 
                                           ((Human)humanList.get(humanNum)).radius) )
       {
         
         return ((BoundingCircle)landCircles[(((Human)humanList.get(humanNum)).currentCircle)].neighbourList.get(i)).id;
       }
    }
    return -1;
  }
  
  
  public void Draw(float worldWidth, Camera3D cameraa)
  {
    //System.out.println("camera x y = " + cameraa.pos.x + " " + cameraa.pos.y);
    for (int i = 0; i < landCircles.length; i++)
    {
       if (!ShouldDraw(   landCircles[i].quadd.points[0].x, landCircles[i].quadd.points[2].x, cameraa))
       {
          float changeAmount = SetQuadX(cameraa, landCircles[i].quadd, worldWidth);
          if (changeAmount != 0) //if the quad was moved, also move all the humans that are on that circle.
          {
            for (int j = 0; j < humanList.size(); j++)
            {
              //System.out.println("humans currentcircle = " + ((Human)humanList.get(j)).currentCircle);
              
              if ( ((Human)humanList.get(j)).currentCircle == i)
              {
                ((Human)humanList.get(j)).MoveX(changeAmount);
              }
            }
          }
          landCircles[i].pos.set(landCircles[i].quadd.center);
       }
       else
       {
         //landCircles[i].Draw(landImg);//,  (float)worldMapImg.width/numOfCols * (i),    0,     (float)worldMapImg.width/numOfCols * (i+1),     (float)worldMapImg.height);
       }
       
    }
    
    //draw humans
    if (humanList.size() > 0)
    {
      for (int i = 0; i < humanList.size(); i++)
      {
        switch(((Human)humanList.get(i)).type)
        {
          case 1:
        ((Human)humanList.get(i)).Draw(greenHumanImg);
          break;
          case 2:
        ((Human)humanList.get(i)).Draw(redHumanImg);
          break;
          case 3:
        ((Human)humanList.get(i)).Draw(terrorHumanImg);
          break;
           case 4:
        ((Human)humanList.get(i)).Draw(entgreenHumanImg);
          break;
          case 5:
        ((Human)humanList.get(i)).Draw(entredHumanImg);
          break;
          case 6:
        ((Human)humanList.get(i)).Draw(entterrorHumanImg);
          break;
          default:
          ((Human)humanList.get(i)).Draw(terrorHumanImg);
          break;
        }
        if (((Human)humanList.get(i)).infected > -1)
            ((Human)humanList.get(i)).Draw(infectedImg);
      }
    }
  }
  
  
  //returns the amount that was translated
  public float SetQuadX(Camera3D cameraa, Quad2D iQuad, float worldWidth)
  {
    if (cameraa.pos.x < iQuad.center.x)
    {  
      if (Math.abs(iQuad.center.x - cameraa.pos.x) > (worldWidth - Math.abs(iQuad.center.x - cameraa.pos.x)))
      {
             TranslateQuadX(iQuad, -worldWidth);
             return -worldWidth;
      }
    }
    else 
    { 
      if (Math.abs(cameraa.pos.x - iQuad.center.x) > (worldWidth - Math.abs(cameraa.pos.x - iQuad.center.x)))
      {
              TranslateQuadX(iQuad, worldWidth);
              return worldWidth;
      }
    }
    return 0;
  }
  public void TranslateQuadX(Quad2D iQuad, float changeX)
  {
     iQuad.points[0].x += changeX;
     iQuad.points[1].x += changeX;
     iQuad.points[2].x += changeX;
     iQuad.points[3].x += changeX;
     
     iQuad.center.x += changeX;
  }
   public void TranslatePointX(PVector ivec, float changeX)
  {
     ivec.x += changeX;
  }
  
  public boolean ShouldDraw(float minA, float maxA, Camera3D cameraa)
  {
    if (cameraa.pos.x- (cameraa.cwidth) > maxA)
        return false;
      if (minA > cameraa.pos.x + (cameraa.cwidth))
        return false;
       
       return true;
  }
  
  public PVector  RotateVector(PVector rotAxis, float rotAmount, PVector rotVec)
  {
    PVector rotateVec = new PVector();
    rotateVec.set(rotVec);
    rotateVec.sub(rotAxis);
    PVector returnVec = new PVector();
    
    returnVec.x = (rotateVec.x *(cos(rotAmount))) - (rotateVec.y *(sin(rotAmount)));
    returnVec.y = (rotateVec.x *(sin(rotAmount))) + (rotateVec.y *(cos(rotAmount)));
    returnVec.add(rotAxis);
    return returnVec;
  }
  
  public boolean CircleCollisionNoOverlap(PVector aPos, float aRadius, PVector bPos, float bRadius)
  {
    if ((PVector.sub(aPos, bPos)).mag() < (aRadius + bRadius))
      return true;
    else
      return false;
  }
  
 
  
}

class BoundingCircle extends Entity
{
  int lifeTime = 0;
  float radius; 
  boolean isLand;
  Quad2D quadd;
  int id;
  ArrayList neighbourList = new ArrayList();
  
  float weightMin, weightMax;
  
  public BoundingCircle(PVector ipos, float iradius, boolean isLand, int iid)
  {
    id = iid;
    pos = ipos;
    radius = iradius/4;
    quadd = new Quad2D( ipos, radius*2, radius*2);  
  }
  
  public void Draw(PImage img)
  {
    quadd.Draw(img);
  }
  
  public boolean CircleCollision(PVector iPos, float iradius)
  {
    if ((PVector.sub(pos, iPos)).mag() < (iradius + radius))
    {
      return true;
    }
    
    return false;
  }
   
  
}
class Camera3D extends Entity
{
  float cwidth, cheight;
  
  PVector up, look;
  
  Camera3D()
  {
    cwidth = width ; cheight = height;
    
    look = new PVector(0,0,0);
    up = new PVector(0,1,0);
    pos = new PVector(cwidth/2, cheight/2, 0);
  }
  
  Camera3D(PVector ipos)
  {
    cwidth = width*1; cheight = height*1;
    
    look = new PVector(0,0,0);
    up = new PVector(0,1,0);
    pos = new PVector(ipos.x, ipos.y, 0);
  }
  
  public void Update()
  {
  }
  
  public void Draw()
  {
  }
  
}

class Effect
{
  int Etype;
  Quad2D beamQuadd;
  Quad2D quadd;
  PImage img;
  PImage beamImg =loadImage("beam.png");
  int lifeCounter = 0;
  int lifeTime;  
  public Effect(int type, PVector pos)
  {
    switch(type)
    {
      //smite effect
      case 1:
      quadd = new Quad2D(pos, 110, 110);
       lifeTime = 140;
       img = loadImage("smiteEffect.png");
      break;
      //finger of god
      case 2:
      quadd = new Quad2D(pos, 1000, 1000);
      beamQuadd = new Quad2D(new PVector(pos.x, pos.y - 1000, 0), 50, 2000);
       lifeTime = 150;
       img = loadImage("fingEffect.png");
       Etype = 2;
      break;
    }
   
    
  }
  
  public boolean Update(long elapsedTime)
  {
    lifeCounter += elapsedTime;
    if (lifeCounter > lifeTime)
      return true;  
    else
      return false;
  }
  
  public void Draw()
  {
    quadd.Draw(img);
    if (Etype == 2)
    {
      beamQuadd.Draw(beamImg);
    }
  }
}

//import com.sun.opengl.util.*;
//import processing.opengl.*;
//
////add gui after i actually make the switch from p3d to OPENGL
String Mode = OPENGL;
String RMode = OPENGL;  // Rendering mode, mean do we use processing function to display or GL context functions
//String RMode = P3D;  // Rendering mode, mean do we use processing function to display or GL context functions
PFont fontA;
boolean bFlush = false;
//
class GUI
{
  Camera3D cameraa;
  World earth;
  Player p1;
  Quad2D ISmiteQ;
  Quad2D IFingGodQ;
  Quad2D IEntangleQ;
  Quad2D ISaucerQ;
  Quad2D IVirusQ;
  Quad2D IActiveQ;
  
  PImage ISmite;
  PImage IFingGod;
  PImage IEntangle;
  PImage ISaucer;
  PImage IVirus; 
  PImage IActive; 
  PImage notReady;
  
GUI(Camera3D icamera, World iearth, Player ip1) 
{ 
  ISmiteQ = new Quad2D(new PVector(), 60, 60);
  IFingGodQ= new Quad2D(new PVector(), 60,60);
  IEntangleQ= new Quad2D(new PVector(), 60, 60);
  ISaucerQ= new Quad2D(new PVector(), 60, 60);
  IVirusQ= new Quad2D(new PVector(), 60, 60);
  IActiveQ= new Quad2D(new PVector(), 90, 90);
  
  ISmite = loadImage("ISmite.bmp");
  IFingGod = loadImage("IFingGod.bmp");
  IEntangle = loadImage("IEntangle.bmp");
  ISaucer = loadImage("ISaucer.bmp");
  IVirus = loadImage("IVirus.bmp");
  IActive = loadImage("pukecircle.png");
  notReady = loadImage("55alphaBlack.png");
  
  cameraa = icamera;
  earth = iearth;
  p1 = ip1;
  if ( RMode == OPENGL )
    Mode = OPENGL;
  size(800, 600, Mode);

  if ( Mode == OPENGL )
  {
    // enable VSync to avoid framerate problems ?
    PGraphicsOpenGL pgl;
    GL gl;
    pgl = (PGraphicsOpenGL)g; //processing graphics object
    gl = pgl.beginGL(); //begin opengl
    gl.setSwapInterval(1); //set vertical sync on, screen refresh is 60 Hz so framerate will be 60
    pgl.endGL(); //end opengl
    
  }
  else
  {
    frameRate(60); // a little more than 60 because 60hz screen and vsync activated ?
  }

  background(100);
  fontA = loadFont("ComicSansMS-48.vlw");
  

}

// ____________________
public void Draw(long elapsedTime) 
{
  //print kills next to where Saucers are:
  
  
  
  //background(10);

  // display framerate
  int FillColor = color(100,100,100, 100);
  fill(FillColor);
  textFont(fontA, 24);
  text("fps    "+ (int)frameRate,                  cameraa.pos.x - cameraa.cwidth + 35, cameraa.pos.y - cameraa.cheight + 155);
  
  if (earth.levelUpDisplayTimeCounter > 0)
  {
    earth.levelUpDisplayTimeCounter -= elapsedTime;
    textFont(fontA, 65);
    
    if (earth.levelUpDisplayTimeCounter > (earth.levelUpDisplayTime - earth.levelUpDisplayFadeTime)) //fade in
    {
      float alphaa = ( ((float)earth.levelUpDisplayTime - (float)earth.levelUpDisplayTimeCounter)/ (float)earth.levelUpDisplayFadeTime) *100;
      FillColor = color(100,100,100, alphaa  );
      fill(FillColor);
    }
    else if (earth.levelUpDisplayTimeCounter < earth.levelUpDisplayFadeTime) //fade out
    {
      float alphaa = (1 - (((float)earth.levelUpDisplayFadeTime - (float)earth.levelUpDisplayTimeCounter) / (float)earth.levelUpDisplayFadeTime) )*100;
      FillColor = color(100,100,100, alphaa  );
      fill(FillColor);
    }
    
    text("Wave "+ earth.level,   cameraa.pos.x - 35, cameraa.pos.y);
  }
  
  FillColor = color(100,100,100, 100);
  fill(FillColor);
  
  textFont(fontA, 18);
  for (int i = 0; i < earth.saucers.size(); i++)
  {
    text("Kills " + ((Saucer)earth.saucers.get(i)).kills,  ((Saucer)earth.saucers.get(i)).quadd.center.x - 70,  ((Saucer)earth.saucers.get(i)).quadd.center.y + 80);
  }
  
  textFont(fontA, 24);
  text("1. Smite: \nReq: " + p1.smiteNatureCost +" Nature \nCooldown " + p1.smiteInterval/1000 + " s",  cameraa.pos.x - cameraa.cwidth + 60,  cameraa.pos.y + cameraa.cheight - 75);
  text("2. Virus: \nReq: "+p1.virusNatureCost+" Nature, "+p1.virusRageCost+" Rage\nCooldown " + p1.virusInterval/1000 + " s",  cameraa.pos.x - cameraa.cwidth + 60 + (310*1),  cameraa.pos.y + cameraa.cheight - 75);
  text("3. Entangle: \nReq: "+p1.entNatureCost+" Nature\nCooldown " + p1.entInterval/1000 + " s",  cameraa.pos.x - cameraa.cwidth + 60 + (320*2),  cameraa.pos.y + cameraa.cheight - 75);
  text("4. Saucer: \nReq: "+p1.saucerLuckCost+" Luck, "+p1.saucerEtherCost+" Ether\nCooldown " + p1.saucerInterval/1000 + " s",  cameraa.pos.x - cameraa.cwidth + 60 + (320*3),  cameraa.pos.y + cameraa.cheight - 75);
  text("5. Finger Of God: \nReq: "+ p1.fingRageCost +" Rage, "+p1.fingEtherCost+" Ether\nCooldown " + p1.fingInterval/1000 + " s",  cameraa.pos.x - cameraa.cwidth + 60 + (320*4),  cameraa.pos.y + cameraa.cheight - 75);
  
  textFont(fontA, 35);
  
  text("  Humans Colonies " + (earth.humans.humanList.size()),     cameraa.pos.x - cameraa.cwidth + 15, cameraa.pos.y - cameraa.cheight + 105);
  text("  Nature " + p1.nature, cameraa.pos.x - cameraa.cwidth + 430, cameraa.pos.y - cameraa.cheight + 55);
  text("Rage " + p1.rage,       cameraa.pos.x - cameraa.cwidth + 530 + (90*3), cameraa.pos.y - cameraa.cheight + 55);
  text("Luck " + p1.luck,       cameraa.pos.x - cameraa.cwidth + 530 + (90*6), cameraa.pos.y - cameraa.cheight + 55);
  text("Ether " + p1.ether,     cameraa.pos.x - cameraa.cwidth + 530 + (90*9), cameraa.pos.y - cameraa.cheight + 55);
  
  textFont(fontA, 30);
  if (p1.natureDiff >= 0)
     text("+" + p1.natureDiff,   cameraa.pos.x - cameraa.cwidth + 500, cameraa.pos.y - cameraa.cheight + 105);
  else
     text(p1.natureDiff,   cameraa.pos.x - cameraa.cwidth + 500, cameraa.pos.y - cameraa.cheight + 80);
  
  FillColor = color(20,80,50, 100);
  fill(FillColor);
  textFont(fontA, 35);
   text("Earth Health " + p1.planetHealth,     cameraa.pos.x - cameraa.cwidth + 35, cameraa.pos.y - cameraa.cheight + 55);
  
  ISmiteQ.MoveTo(new PVector(cameraa.pos.x - cameraa.cwidth + 110,  cameraa.pos.y + cameraa.cheight - 140, 0) );
  IVirusQ.MoveTo(new PVector(cameraa.pos.x - cameraa.cwidth + 110 + (310*1),  cameraa.pos.y + cameraa.cheight - 140, 0) );
  IEntangleQ.MoveTo(new PVector(cameraa.pos.x - cameraa.cwidth + 110 + (320*2),  cameraa.pos.y + cameraa.cheight - 140, 0) );
  ISaucerQ.MoveTo(new PVector(cameraa.pos.x - cameraa.cwidth + 110 + (320*3),  cameraa.pos.y + cameraa.cheight - 140, 0) );
  IFingGodQ.MoveTo(new PVector(cameraa.pos.x - cameraa.cwidth + 110 + (320*4),  cameraa.pos.y + cameraa.cheight - 140, 0) );
  
  
  switch(p1.activeA)
  {
    case 1:
    IActiveQ.MoveTo(ISmiteQ.center);
    break;
    case 2:
    IActiveQ.MoveTo(IFingGodQ.center);
    break;
    case 3:
    IActiveQ.MoveTo(IEntangleQ.center);
    break;
    case 4:
    IActiveQ.MoveTo(ISaucerQ.center);
    break;
    case 5:
    IActiveQ.MoveTo(IVirusQ.center);
    break;
  }
  
 
  IActiveQ.Draw(IActive);
  
   if (p1.smiteReady)
  ISmiteQ.Draw(ISmite);
   else
   {
   ISmiteQ.Draw(ISmite);
   ISmiteQ.Draw(notReady);
   }
  if(p1.fingReady)
  IFingGodQ.Draw(IFingGod);
  else
  {
   IFingGodQ.Draw(IFingGod);
   IFingGodQ.Draw(notReady);
  }
  if(p1.entReady)
  IEntangleQ.Draw(IEntangle);
  else
  {
   IEntangleQ.Draw(IEntangle);
   IEntangleQ.Draw(notReady);
  }
  if(p1.saucerReady)
  ISaucerQ.Draw(ISaucer );
  else
  {
   ISaucerQ.Draw(ISaucer );
   ISaucerQ.Draw(notReady);
  }
  if(p1.virusReady)
  IVirusQ.Draw(IVirus);
  else
  {
   IVirusQ.Draw(IVirus);
   IVirusQ.Draw(notReady);
  }
  //text("Instruction Mode (R) = " + RMode, cameraa.pos.x - cameraa.cwidth + 15, cameraa.pos.y - cameraa.cheight + 105);
  //text("force flush (F) ? " + bFlush,     cameraa.pos.x - cameraa.cwidth + 15, cameraa.pos.y - cameraa.cheight + 140);
  
  
}

 public void Testdraw()
 {
   int FillColor = color(100,100,100, 100);
  fill(FillColor);
  textFont(fontA, 24);
  text("TESTING IMNPUT", cameraa.pos.x , cameraa.pos.y);
  
 }
// ____________________
//void Update()
//{
//  if (keyPressed)
//  {
//  if ( key == 'f' || key == 'F' )
//    bFlush = !bFlush;
//
//  if ( key == 'r' || key == 'R' )
//  {
//    if ( Mode == OPENGL )
//    {
//	if (RMode == OPENGL )
//	{
//	  RMode = P3D;
//	  println("going to P3D Rmode");
//	}
//	else if ( RMode == P3D )
//	{
//	  RMode = OPENGL;
//	  println("going to OPENGL Rmode");
//	}
//    }
//    else
//	  println("Can't switch to OpenGL instructions in P3D render mode");
//  }
//  }
//}
}

class GameOver extends GameState
{
  int liveTime = 3000;
  int inputCounter = 0;
  int inputMinTime = 120;

  PImage backgroundImg = loadImage("gameover.jpg");
  Camera3D cameraa;

  Quad2D backgroundQuad;

  GameOver()
  {
    cameraa = new Camera3D(new PVector(0,0,0) );
    
    backgroundQuad = new Quad2D( cameraa.pos, cameraa.cwidth, cameraa.cwidth);
  }



  public int Update(long elapsedTime)
  { 
    liveTime -= elapsedTime;
    if (liveTime <=0)
    {
        if(keyPressed) {
          return 0;
          }
          
          
         if(checkKey("enter") || checkKey("s") || checkKey("space"))
        {
          println("inputtt");
          return 1;
        }

    }
    return -1;
  }

  public void Draw(long elapsedTime)
  {    
    //noCursor(); //hides the cursor
    background(0); //background colour

    ortho( - cameraa.cwidth/2, 
    cameraa.cwidth/2, 
    - cameraa.cheight/1.5f, 
    cameraa.cheight/1.5f, -100, 100);

    camera(cameraa.pos.x, cameraa.pos.y, 10, // eyeX, eyeY, eyeZ
    cameraa.pos.x, cameraa.pos.y, 0.0f, // lookX, lookY, lookZ
    0.0f, 1.0f, 0.0f); // upX, upY, upZ

    //draw scene here
    backgroundQuad.Draw(backgroundImg);
    
  }


  public void PrintVector(PVector vec)
  {
    System.out.println("vec x y z: " + vec.x + " " + vec.y + " " + vec.z  );
  }




  boolean[] keys = new boolean[526];
  public boolean checkKey(String k)
  {
    for(int i = 0; i < keys.length; i++)
      if(KeyEvent.getKeyText(i).toLowerCase().equals(k.toLowerCase())) return keys[i];  
    return false;
  }

  public void keyPressed()
  { 
    keys[keyCode] = true;
  }

  public void keyReleased()
  { 
    keys[keyCode] = false; 
  }




}
























class GameState
{
  boolean isActive;
  
  GameState()
  {

  }

  public int TakeInput(long elapsedTime)
  {
    
    
    return -1;
  }
  
  public int Update(long elapsedTime)
  {
    return -1;
  }

  public void Draw(long elapsedTime)
  {
  }

public void mousePressed()
{
 
}

public void mouseReleased()
{
 
} 

}

class Human extends Entity
{
  boolean isSomeonesClosest = false;
  int infected = -1;
  int type;
  Quad2D quadd;
  float radius = 25/2;
  int currentCircle;
  public Human(PVector ipos, int iCircle, int itype)
  {
    type = itype;
    quadd = new Quad2D( ipos, 25 , 25);
    currentCircle = iCircle;
    pos.set(ipos);
  }
  
  public void Move(PVector amount)
  {
    quadd.Translate(amount);
    pos.add(amount);
  }
  public void MoveX(float changeAmount)
  {
    TranslateQuadX(quadd, changeAmount);
    pos.x += (changeAmount);
  }
  
    public void TranslateQuadX(Quad2D iQuad, float changeX)
  {
     iQuad.points[0].x += changeX;
     iQuad.points[1].x += changeX;
     iQuad.points[2].x += changeX;
     iQuad.points[3].x += changeX;
     
     iQuad.center.x += changeX;
  }
  public void Draw(PImage img)
  {
    quadd.Draw(img);
  }
}

class Menu extends GameState
{
  int inputCounter = 0;
  int inputMinTime = 120;

  PImage backgroundImg = loadImage("mainMenu.jpg");
  Camera3D cameraa;

  Quad2D backgroundQuad;

  Menu()
  {
    cameraa = new Camera3D(new PVector(0,0,0) );
    
    backgroundQuad = new Quad2D( cameraa.pos, cameraa.cwidth, cameraa.cwidth);
  }



  public int Update(long elapsedTime)
  { 
    if(keyPressed) {
      return 1;
      }
      
      
     if(checkKey("enter") || checkKey("s") || checkKey("space"))
    {
      println("inputtt");
      return 1;
    }


    return -1;
  }

  public void Draw(long elapsedTime)
  {    
    //noCursor(); //hides the cursor
    background(0); //background colour

    ortho( - cameraa.cwidth/2, 
    cameraa.cwidth/2, 
    - cameraa.cheight/1.5f, 
    cameraa.cheight/1.5f, -100, 100);

    camera(cameraa.pos.x, cameraa.pos.y, 10, // eyeX, eyeY, eyeZ
    cameraa.pos.x, cameraa.pos.y, 0.0f, // lookX, lookY, lookZ
    0.0f, 1.0f, 0.0f); // upX, upY, upZ

    //draw scene here
    backgroundQuad.Draw(backgroundImg);
    
  }


  public void PrintVector(PVector vec)
  {
    System.out.println("vec x y z: " + vec.x + " " + vec.y + " " + vec.z  );
  }




  boolean[] keys = new boolean[526];
  public boolean checkKey(String k)
  {
    for(int i = 0; i < keys.length; i++)
      if(KeyEvent.getKeyText(i).toLowerCase().equals(k.toLowerCase())) return keys[i];  
    return false;
  }

  public void keyPressed()
  { 
    keys[keyCode] = true;
  }

  public void keyReleased()
  { 
    keys[keyCode] = false; 
  }




}
























//import java.awt.AWTException; 
//import java.awt.Robot; 
//import java.awt.event.KeyEvent;
//import java.awt.Frame;

class Play extends GameState
{
  int clockTime = 0;
  
  int levelUpInterval = 40000;
  int nextLevelUp = levelUpInterval;
  
  ArrayList effects = new ArrayList(); 
  
  Quad2D crosshair;
  PImage crosshairImg;
  
  GUI gui;
  boolean drawWorld = true;
  float mouseInputThreshold = 1;
  float speedMult = 0.1f; //0.05 is perfect
    
  World earth;
    
  int centerPosX, centerPosY; //where the mouse needs to reset to.
  boolean mouseLocked = true;
  Camera3D cameraa;
  Player p1; 
  
  
  Play()
  {
    
    earth = new World();
    cameraa = new Camera3D(new PVector(earth.worldWidth/2,0,0));
    earth.star.cameraa = cameraa;
    p1 = new Player(new PVector(earth.worldWidth/2, 0, 0), 32, 32);
    
    crosshairImg = loadImage("crosshair.png");
    crosshair = new Quad2D(cameraa.pos, 32, 32);
    
    gui = new GUI(cameraa, earth, p1);
    //ResetMouse();
    
   // println( -360%170);
   
  }
  
  public int TakeInput(long elapsedTime)
  {
    if(keyPressed)
    { 
      switch(key)
      {
        case '1':
        p1.activeA = 1;
        break;
        case '5':
        p1.activeA = 2;        
        break;
        case '3':
        p1.activeA = 3;    
        break;
        case '4':
        p1.activeA = 4;        
        break;
        case '2':
        p1.activeA = 5;      
        break;
        
      }
      //check to see if the player changes the game state (ie. pushes q to quit)
    }

  
    return -1;
  }
  
  public void LevelUp()
  {
    if (clockTime > nextLevelUp)
    {
      nextLevelUp += levelUpInterval;
      earth.level ++;
      earth.levelUpDisplayTimeCounter = earth.levelUpDisplayTime;
      
      earth.humans.humanSpawnTime -= 60;
      if (earth.humans.humanSpawnTime < 140)
      {
        earth.humans.humanSpawnTime = 140;
        
      }
    }
  }
  
  public int Update(long elapsedTime)
  { 
    if (effects.size() > 0)
    {
    for (int i = effects.size() - 1; i >=0; i--)
    {
      boolean destroy = ((Effect)effects.get(i)).Update(elapsedTime);
      if (destroy == true)
      {
        effects.remove(i);
      }
    }
    }
    

          if (p1.planetHealth <=0)
          {
            return 2;
          }

      clockTime += elapsedTime;
      LevelUp();
      int stateID = TakeInput(elapsedTime); //if stateID != -1, then change to the correct state.
      
      //TAKE MOUSE INPUT AND MOVE THE CAMERA
      
      p1.mouseInput = new PVector((((float)mouseX + 3) - (float)width/2), 
                                  (((float)mouseY + 25) - (float)height/2), 0); //the +3 and + 25 are something fucked, but it makes it work
      
      crosshair.MoveTo(new PVector(cameraa.pos.x - cameraa.cwidth + mouseX*2 , cameraa.pos.y - cameraa.cheight + mouseY*2 , 0));
      
      float mSpeed = p1.mouseInput.mag();
      mSpeed -= mouseInputThreshold;
      if (mSpeed < 0)
        mSpeed = 0; //create the threshold for mouse input based on mouseInputThreshold
      
      
      PVector mInput = new PVector();
      mInput.set( p1.mouseInput ); //set to mouseInput because operating directly on p1.mouseInput will fuck it all up.

      mInput.normalize();
      mInput.mult(mSpeed);
     
      //p1.velocity = PVector.mult(mInput, 0.05 * ( (float)elapsedTime / 17  ) );
      p1.velocity = PVector.mult(mInput, speedMult);
      if (p1.velocity.mag() > p1.maxVel)
      {
        p1.velocity.normalize();
        p1.velocity.mult(p1.maxVel);
      }
      p1.pos.add(p1.velocity);
    //Translate camera;
   // cameraa.pos.add(  PVector.sub(p1.pos, cameraa.pos) ); //0.05
     cameraa.pos.add(  p1.velocity ); //0.05

      //keep the camera within some up and down boundaries.
     if (cameraa.pos.y - cameraa.cheight < earth.worldMapQuads[0].points[0].y)
     {
       cameraa.pos.y -= (cameraa.pos.y - cameraa.cheight) - earth.worldMapQuads[0].points[0].y;
       p1.pos.y -= (cameraa.pos.y - cameraa.cheight) - earth.worldMapQuads[0].points[0].y;
     }
     else if (cameraa.pos.y + cameraa.cheight > earth.worldMapQuads[0].points[2].y)
     {
       cameraa.pos.y -= (cameraa.pos.y + cameraa.cheight) - earth.worldMapQuads[0].points[2].y;
       p1.pos.y -= (cameraa.pos.y + cameraa.cheight) -earth.worldMapQuads[0].points[2].y;
     }
     
     ProcessAbilityReady();
     ProcessMouseClicks();
     
     ProcessEntangle(elapsedTime);
     ProcessSaucers();
     ProcessVirus();
     
     ProcessNature();
     ProcessRage();
     
     earth.Update(elapsedTime, cameraa);

     return -1;
  }
  
  public void ProcessAbilityReady()
  {
    if (clockTime > p1.nextSmiteInterval && p1.nature >= p1.smiteNatureCost)
    {
      p1.smiteReady = true;
    }
    else
      p1.smiteReady = false;
      
    if (clockTime > p1.nextFingInterval && p1.ether >= p1.fingEtherCost && p1.rage >= p1.fingRageCost)
    {
      p1.fingReady = true;
    }
    else
      p1.fingReady = false;
      
    if (clockTime > p1.nextEntInterval && p1.nature >= p1.entNatureCost)
    {
      p1.entReady = true;
    }
    else
      p1.entReady = false;
      
    if (clockTime > p1.nextSaucerInterval && p1.ether >= p1.saucerEtherCost && p1.luck >= p1.saucerLuckCost)
    {
      p1.saucerReady = true;
    }
    else
      p1.saucerReady = false;
      
    if (clockTime > p1.nextVirusInterval && p1.nature >= p1.virusNatureCost && p1.rage >= p1.virusRageCost)
    {
      p1.virusReady = true;
    }
    else
      p1.virusReady = false;
  }

  boolean testDraw = false;
  public void ProcessMouseClicks()
  {
    if (right)
    {
     
    }
    if (left)
    {
      testDraw = true;
      switch(p1.activeA)
      {
        case 1: //smite
          Smite();
        break;
        
        case 2: //finger of god
          FingerOfGod();
        break;
        
        case 3: //entangle
          Entangle();
        break;
        
        case 4: //saucer
          Saucer();
        break;
        
        case 5: //virus
          Virus();
        break;
      }
    }
    else
      testDraw = false;
  }
  
public void ProcessEntangle(long elapsedTime)
{
  if (earth.entangles.size() > 0)
  {
    for (int i = earth.entangles.size() - 1; i >=0; i--)
    {
      ((BoundingCircle)earth.entangles.get(i)).lifeTime -= elapsedTime;
      if (((BoundingCircle)earth.entangles.get(i)).lifeTime <=0)
      {
        for (int j = earth.humans.humanList.size() - 1; j >= 0; j--)
          {
              if ( ((Human)earth.humans.humanList.get(j)).type ==4)
                  ((Human)earth.humans.humanList.get(j)).type =1;
              else if ( ((Human)earth.humans.humanList.get(j)).type ==5)
                  ((Human)earth.humans.humanList.get(j)).type =2;
              else if ( ((Human)earth.humans.humanList.get(j)).type ==6)
                  ((Human)earth.humans.humanList.get(j)).type =3;

          }
        earth.entangles.remove(i);
      }
    }
  }
}

public void ProcessSaucers()
{
  if (earth.saucers.size() > 0)
  {
  for (int i = earth.saucers.size() - 1; i >=0; i--)
    {
        for (int j = earth.humans.humanList.size() - 1; j >= 0; j--)
          {
            if ( CircleCollisionNoOverlap( ((Saucer)earth.saucers.get(i)).quadd.center, ((Saucer)earth.saucers.get(i)).attackRadius, ((Human)earth.humans.humanList.get(j)).quadd.center, ((Human)earth.humans.humanList.get(j)).radius))
            {
               KillHuman(j);
              ((Saucer)earth.saucers.get(i)).kills ++;
            }
              
          }
    }
  }
}

public void ProcessVirus()
{
  //println("p1.virus exists " + p1.virusExist);
  if (p1.virusExist)
  {
    if (clockTime > p1.nextVirusProcess)
    {
      p1.nextVirusProcess = clockTime + p1.processVirusTime;
      p1.virusExist = false;
      for (int i = earth.humans.humanList.size() - 1; i >= 0; i--)
      {
          if (((Human)earth.humans.humanList.get(i)).infected > -1 && ((Human)earth.humans.humanList.get(i)).infected < clockTime )
          {
            for (int j = earth.humans.humanList.size() - 1; j >= 0; j--)
            {
              if ( CircleCollisionNoOverlap( ((Human)earth.humans.humanList.get(i)).quadd.center, p1.virusInfectRange, ((Human)earth.humans.humanList.get(j)).quadd.center, 25/2   ) )
              {
                if ( ((Human)earth.humans.humanList.get(j)).infected == -1)
                  ((Human)earth.humans.humanList.get(j)).infected = clockTime;
                
                p1.virusExist = true;
              }
            }
            KillHuman(i);
          }
    
      }
    }
  }
}


// System.out.println("but am getting here " + clockTime + " crosshair x y = " + crosshair.center.x + " " + crosshair.center.y);
         //System.out.println("BE4 COLL human num: " + i + " at pos x y " + ((Human)earth.humans.humanList.get(i)).pos.x + " " + ((Human)earth.humans.humanList.get(i)).pos.y +" at circle: " + ((Human)earth.humans.humanList.get(i)).currentCircle );
         
           //System.out.println("COLL human num: " + i + " at pos x y " + ((Human)earth.humans.humanList.get(i)).pos.x + " " + ((Human)earth.humans.humanList.get(i)).pos.y +" at circle: " + ((Human)earth.humans.humanList.get(i)).currentCircle );
           
           
  public void Smite()
  {
    if (p1.smiteReady)
    {
      if (p1.nature >= p1.smiteNatureCost)
      {
        effects.add(new Effect(1, crosshair.center));
        p1.smiteReady = false;
        
        p1.nextSmiteInterval = clockTime + p1.smiteInterval;
        p1.nature -= p1.smiteNatureCost;
        
        
        if (earth.humans.humanList.size() > 0 )
        {
          for (int i = earth.humans.humanList.size() - 1; i >= 0; i--)
          {
           if ( CircleCollisionNoOverlap(crosshair.center, p1.smiteRadius, ((Human)earth.humans.humanList.get(i)).quadd.center, ((Human)earth.humans.humanList.get(i)).radius) )
               KillHuman(i);
          }
        }
        
      }
    }
  }

  

  public void FingerOfGod()
  {
    if (p1.fingReady)
    {
      if (p1.ether >= p1.fingEtherCost && p1.rage >= p1.fingRageCost)
      {
        effects.add(new Effect(2, crosshair.center));
        p1.nextFingInterval = clockTime + p1.fingInterval;
        p1.ether -= p1.fingEtherCost;
        p1.rage -=p1.fingRageCost;
        p1.fingReady = false;
        
        if (earth.humans.humanList.size() > 0 )
        {
          for (int i = earth.humans.humanList.size() - 1; i >= 0; i--)
          {
           if ( CircleCollisionNoOverlap(crosshair.center, p1.fingRadius, ((Human)earth.humans.humanList.get(i)).quadd.center, ((Human)earth.humans.humanList.get(i)).radius) )
               KillHuman(i);
          }
        }
        
      }
    }
  }
  
  
  
  public void Entangle()
  {
    if (p1.entReady)
    {
      if (p1.nature >= p1.entNatureCost)
      {
        p1.entReady = false;
        
        p1.nextEntInterval = clockTime + p1.entInterval;
        p1.nature -= p1.entNatureCost;
        
        earth.entangles.add(new BoundingCircle(crosshair.center, p1.entRadius*4, false, 0));
        ((BoundingCircle)earth.entangles.get(earth.entangles.size()-1)).lifeTime = p1.entLifeTime;
        
        //if the entangle collides with a star, create an ETHER.
        if ( CircleCollisionNoOverlap( earth.star.quadd.center, (earth.star.sizee/2), crosshair.center, p1.entRadius  ) )
        {
          if (earth.star.active)
            earth.star.GoToEtherMode();
        }
        
        //println("earth.entangles.size() " + earth.entangles.size());
        if (earth.humans.humanList.size() > 0 )
        {
          for (int i = earth.humans.humanList.size() - 1; i >= 0; i--)
          {
           if ( CircleCollisionNoOverlap(crosshair.center, p1.entRadius, ((Human)earth.humans.humanList.get(i)).quadd.center, ((Human)earth.humans.humanList.get(i)).radius) )
           { 
             if (((Human)earth.humans.humanList.get(i)).type == 1)  
               ((Human)earth.humans.humanList.get(i)).type = 4;
             else if (((Human)earth.humans.humanList.get(i)).type == 2)  
               ((Human)earth.humans.humanList.get(i)).type = 5;
             else if (((Human)earth.humans.humanList.get(i)).type == 3)  
               ((Human)earth.humans.humanList.get(i)).type = 6;
           }
          }
        }
        
      }
    }
  }
  
  
  
  public void Saucer()
  {
    if (p1.saucerReady)
    {
      if (p1.luck >= p1.saucerLuckCost && p1.ether >= p1.saucerEtherCost)
      {
        p1.saucerReady = false;
        
        p1.nextSaucerInterval = clockTime + p1.saucerInterval;
        p1.luck -= p1.saucerLuckCost;
        p1.ether -= p1.saucerEtherCost;
        
        earth.saucers.add(new Saucer(crosshair.center, p1.saucerRadius));

      }
    }
  }
  
  
  
  public void Virus()
  {
    if (p1.virusReady)
    {
      if (p1.rage >= p1.virusRageCost && p1.nature >= p1.virusNatureCost)
      {
        p1.virusReady = false;
        
        p1.nextVirusInterval = clockTime + p1.virusInterval;
        p1.rage -= p1.virusRageCost;
        p1.nature -= p1.virusNatureCost;
        
        for (int i = earth.humans.humanList.size() - 1; i >= 0; i--)
          {
           if ( CircleCollisionNoOverlap(crosshair.center, p1.virusRadius, ((Human)earth.humans.humanList.get(i)).quadd.center, ((Human)earth.humans.humanList.get(i)).radius) )
           { 
             ((Human)earth.humans.humanList.get(i)).infected = clockTime;
             p1.virusExist = true;
             
             p1.nextVirusProcess = clockTime + p1.processVirusTime;
           }
          }

      }
    }
  }

  public void KillHuman(int humanNum)
  {
    earth.humans.humanList.remove(humanNum);
    p1.rage +=1;
  }
  
  public void ProcessNature()
  {
    if (clockTime > p1.nextNatureInterval)
    {
        p1.nextNatureInterval += p1.processNatureInterval;
        p1.natureDiff = 0;
        
        if (earth.humans.humanList.size() > 0)
        {
            
            for (int i = 0; i < earth.humans.humanList.size(); i++)
            {
              switch (((Human)earth.humans.humanList.get(i)).type)
              {
                case 1:
                p1.natureDiff -= earth.humans.type1Dmg;
                break;
                case 2:
                p1.natureDiff -= earth.humans.type2Dmg;
                break;
                case 3:
                p1.natureDiff -= earth.humans.type3Dmg;
                break;
              }
            }
            
        }
        p1.natureDiff = (int)(p1.natureDiff * 0.5f);
        p1.natureDiff += 100;

        //for balancing purposes
        if (p1.natureDiff < 0)
        {
          println("naturediff before = "+ p1.natureDiff);
          p1.natureDiff *= 0.25f;
          println("naturediff after = "+ p1.natureDiff);
        }
        
        p1.nature += p1.natureDiff;
        if (p1.nature > 1000)
          p1.nature = 1000;
        else if (p1.nature < 0)
        p1.nature = 0;
        
      if (p1.nature <= 0)
      {
          p1.planetHealth -=5;
      }
      
    }
  }
  
  public void ProcessRage()
  {
    if (clockTime > p1.nextRageInterval)
    {
      p1.nextRageInterval += p1.processRageInterval;
      
      p1.rage -= p1.rageLossPerInterval;
      if (p1.rage < 0)
        p1.rage = 0;
      else if (p1.rage > 100)
       p1.rage = 100;
    }
  }
  
  public void ProcessStar()
  {
    if (right)
    {
    if (CircleCollisionNoOverlap( crosshair.center, 20, earth.star.quadd.center, (earth.star.sizee/2)))
    {
      if (earth.star.active)
      {
        if (earth.star.etherMode)
        {
          earth.star.GoToStarMode();
          p1.ether +=1;
        }
        else
          p1.luck += 1;
          
          earth.star.active = false;
      }
    }
    }
  }
  
  public boolean CircleCollisionNoOverlap(PVector aPos, float aRadius, PVector bPos, float bRadius)
  {
    if ((PVector.sub(aPos, bPos)).mag() < (aRadius + bRadius))
      return true;
    else
      return false;
  }
  
  public PVector CircleCollision(PVector aPos, float aRadius, PVector bPos, float bRadius)
  {
    if ((PVector.sub(aPos, bPos)).mag() < (aRadius + bRadius))
    {
      PVector distance = (PVector.sub(aPos, bPos));

      float overlap = (aRadius + bRadius) - distance.mag();

      distance.normalize();
      if (distance.mag() ==0)
        distance.x = 0.1f;

      return PVector.mult(distance, overlap);

    }
    else
      return new PVector(0,0,0);
  }

  
  
  public void Draw(long elapsedTime)
  {    
    //noCursor(); //hides the cursor

      background(0); //background colour

    ortho( - cameraa.cwidth , 
    cameraa.cwidth , 
    - cameraa.cheight, 
    cameraa.cheight , -100, 100);

    camera(cameraa.pos.x, cameraa.pos.y, 10, // eyeX, eyeY, eyeZ
    cameraa.pos.x, cameraa.pos.y, 0.0f, // lookX, lookY, lookZ
    0.0f, 1.0f, 0.0f); // upX, upY, upZ

    earth.Draw(elapsedTime, cameraa, drawWorld);
    
    for (int i = effects.size()-1; i >= 0; i--)
    {
      ((Effect)effects.get(i)).Draw();
    }
    
    p1.Draw(crosshair.center);
    
    crosshair.Draw(crosshairImg);
    
    gui.Draw(elapsedTime); 
//    if (testDraw == true)
//    gui.Testdraw();
  }


  public void PrintVector(PVector vec)
  {
    System.out.println("vec x y z: " + vec.x + " " + vec.y + " " + vec.z  );
  }
  



boolean left=false;
boolean right=false;
boolean center=false;
public void mousePressed()
{
  if(mouseButton==LEFT)
    left=true;
  if(mouseButton==RIGHT)
    right=true;
  if(mouseButton==CENTER)
    center=true;
}

public void mouseReleased()
{
  ProcessStar();
  
  if(mouseButton==LEFT)
    left=false;
  if(mouseButton==RIGHT)
    right=false;
  if(mouseButton==CENTER)
    center=false;
    
  
} 


}













class Player extends Entity
{
  int planetHealth = 100;

  int nature = 1000;
  int rage = 25; //25 good
  int luck = 2;
  int ether = 3;
  
  int processNatureInterval = 1000;
  int nextNatureInterval = processNatureInterval;
  
  int processRageInterval = 1000;
  int nextRageInterval = processRageInterval;
  int rageLossPerInterval = 1;
  
  //stuff for smite
  boolean smiteReady = true;
  int smiteInterval = 850;
  int nextSmiteInterval = 0;
  float smiteRadius = 50;
  Quad2D smiteRadiusQ;
  PImage smiteRadiusImg;
  
  int smiteNatureCost = 20;
  
  //stuff for finger of god
  boolean fingReady = true;
  int fingInterval = 6000; // 10 000
  int nextFingInterval = 0;
  float fingRadius = 550;
  Quad2D fingRadiusQ;
  PImage fingRadiusImg;
  
  int fingEtherCost = 1; //1
  int fingRageCost = 20; //20
  
  //stuff for entangle
  boolean entReady = true;
  int entInterval = 15000;
  int nextEntInterval = 0;
  float entRadius = 300;
  Quad2D entRadiusQ;
  PImage entRadiusImg;
  
  int entNatureCost = 75;
  int entLifeTime = 13000;
    
  //stuff for flying saucer
  boolean saucerReady = true;
  int saucerInterval = 10000;// 10 is perfect
  int nextSaucerInterval = 0;
  float saucerRadius = 75;
  Quad2D saucerRadiusQ;
  PImage saucerRadiusImg;
  
  int saucerLuckCost = 1;
  int saucerEtherCost = 1;
  
  //stuff for virus
  boolean virusReady = true;
  int virusInterval = 4000; //10
  int nextVirusInterval = 0;
  float virusRadius = 75;
  Quad2D virusRadiusQ;
  PImage virusRadiusImg;
   
  int virusRageCost = 10; //20
  int virusNatureCost = 10; //50
  
  int processVirusTime = 1250;
  int nextVirusProcess = 0; //next time virus will be processed
  int virusInfectRange = 160;
  boolean virusExist = false;
  
  int natureDiff = 0;
  
  int  activeA = 1; 
  boolean dirLeft = true;

  PVector velocity = new PVector(0,0,0);
  float maxVel = 50; //25 is perfect

  PVector mouseInput = new PVector(0,0,0); //used to store the current mouse input
  PVector prevMouseInput = new PVector(0,0,0);

  PVector down = new PVector(0,0,0);


  PImage img;

  float radius; 


  Player(PVector iPos, float iWidth, float iHeight)
  {
    radius = iHeight/2;      
    pos = iPos;

  //stuff for smite
  smiteRadiusQ = new Quad2D(new PVector(0,0,0), smiteRadius*2, smiteRadius*2);
  smiteRadiusImg = loadImage("yel10Rad.png");
  
  //stuff for finger of god
  fingRadiusQ = new Quad2D(new PVector(0,0,0), fingRadius*2, fingRadius*2);
  fingRadiusImg = loadImage("purpRad.png");
  
  //stuff for entangle
  entRadiusQ = new Quad2D(new PVector(0,0,0), entRadius*2, entRadius*2);
  entRadiusImg = loadImage("greRad.png");
  
  //stuff for flying saucer
  saucerRadiusQ = new Quad2D(new PVector(0,0,0), saucerRadius*2, saucerRadius*2);
  saucerRadiusImg = loadImage("yelRad.png");

  //stuff for virus
  virusRadiusQ = new Quad2D(new PVector(0,0,0), virusRadius*2, virusRadius*2);
  virusRadiusImg = loadImage("redRad.png");
  }

  //  void Move(int hor, int ver)
  //  {
  //    
  //    pos = new PVector(hor, ver, pos.z);
  //    quadd.Translate(new PVector(hor, ver, 0));
  //    swordQuad.Translate(new PVector(hor, ver, 0));
  //  }

  public void Update()
  {
  }

  public void Translate(PVector iVelocity)
  {
    //iVelocity.mult(0.15);

    pos.add(iVelocity);

  }

  public float FindAngle2D(PVector v1, PVector v2)
  {
    //float angle = -(180/PI)*atan2(v1.x*v2.y - v1.y*v2.x, v1.x*v2.x+v1.y*v2.y);
    float angle = -atan2(v1.x*v2.y - v1.y*v2.x, v1.x*v2.x+v1.y*v2.y);

    return angle;
  }

  public PVector ReturnNormal(PVector v1, boolean clockwise)
  {
    PVector normalV1 = new PVector();
    normalV1.set(v1);

    if (clockwise)
    {
      float y = normalV1.y;
      normalV1.y = -normalV1.x;
      normalV1.x = y;
    }
    else
    {
      float y = normalV1.y;
      normalV1.y = normalV1.x;
      normalV1.x = -y;
    }

    return normalV1;
  }

  
   public PVector  RotateVector(PVector rotAxis, float rotAmount, PVector rotVec)
  {
    PVector rotateVec = new PVector();
    rotateVec.set(rotVec);
    rotateVec.sub(rotAxis);
    PVector returnVec = new PVector();
    
    returnVec.x = (rotateVec.x *(cos(rotAmount))) - (rotateVec.y *(sin(rotAmount)));
    returnVec.y = (rotateVec.x *(sin(rotAmount))) + (rotateVec.y *(cos(rotAmount)));
    returnVec.add(rotAxis);
    return returnVec;
  }
  
   public PVector MirrorVector2D(PVector v1, PVector axisA, PVector axisB)
  {
    PVector axis = PVector.sub(axisA, axisB);
    
    PVector axisNormal = ReturnNormal(axis, true);
    
    PVector mirrorVec = new PVector();
    
    mirrorVec.set(v1);
    mirrorVec.sub(axisB);
    mirrorVec = PVector.sub(mirrorVec, PVector.div( PVector.mult( axisNormal, 2*mirrorVec.dot(axisNormal) ), pow(axisNormal.mag(),2))     );
    mirrorVec.add(axisB);
      
     return mirrorVec;
  }
  
  public void Draw(PVector crosshair)
  {
    switch(activeA)
    {
      case 1:
      if (smiteReady){
      smiteRadiusQ.MoveTo(crosshair);
      smiteRadiusQ.Draw(smiteRadiusImg);
      }
      break;
      case 2:
      if (fingReady){
      fingRadiusQ.MoveTo(crosshair);
      fingRadiusQ.Draw(fingRadiusImg);
      }
      break;
      case 3:
      if (entReady){
      entRadiusQ.MoveTo(crosshair);
      entRadiusQ.Draw(entRadiusImg);
      }
      break;
      case 4:
      if (saucerReady){
      saucerRadiusQ.MoveTo(crosshair);
      saucerRadiusQ.Draw(saucerRadiusImg);
      }
      break;
      case 5:
      if (virusReady){
      virusRadiusQ.MoveTo(crosshair);
      virusRadiusQ.Draw(virusRadiusImg);
      }
      break;
    }
  }

  
}



class Quad2D
{

  PVector[] points = new PVector[4];
  
  PVector center;
  
  Quad2D()
  {
     points[0] = new PVector(0,0,0);
     points[1] = new PVector(100,0,0);
     points[2] = new PVector(100,100,0);
     points[3] = new PVector(0,100,0);
     
      SetCenter();
  }
  
  Quad2D(PVector p1, PVector p2, PVector p3, PVector p4)
  {
     points[0] = p1;
     points[1] = p2;
     points[2] = p3;
     points[3] = p4;    
     
     SetCenter();
  }
  
  Quad2D(PVector iPos, float iWidth, float iHeight)
  {
     points[0] = new PVector(iPos.x - iWidth/2, iPos.y - iHeight/2, iPos.z );
     points[1] = new PVector(iPos.x + iWidth/2, iPos.y - iHeight/2, iPos.z );
     points[2] = new PVector(iPos.x + iWidth/2, iPos.y + iHeight/2, iPos.z );
     points[3] = new PVector(iPos.x - iWidth/2, iPos.y + iHeight/2, iPos.z );
     
     center = new PVector();
     center.set(iPos);
  }
  
  public void set(Quad2D inQuad)
  {
    points[0].set(inQuad.points[0]);
    points[1].set(inQuad.points[1]);
    points[2].set(inQuad.points[2]);
    points[3].set(inQuad.points[3]);
    center.set(inQuad.center);
  }
  
  public void SetCenter() //positions the center at the average of all points
  {
    PVector avg = new PVector(0,0,0);
    
    for (int i = 0; i < 4; i++)
            avg.add(points[i]);
            
    avg.div(4);
    
    center = avg;
  }
  
  public void Translate(PVector change)
  {
     points[0].add(change);
     points[1].add(change);
     points[2].add(change);
     points[3].add(change);
     
     center.add(change);
  }
  
  public void MoveTo(PVector dest)
  {
    PVector change = PVector.sub(dest, center);
    
    Translate(change);
    
//    float iWidth = points[1].x - points[0].x;
//    float iHeight = points[2].y - points[1].y;
//    
//    points[0] = new PVector(dest.x - iWidth/2, dest.y - iHeight/2, dest.z );
//    points[1] = new PVector(dest.x + iWidth/2, dest.y - iHeight/2, dest.z );
//    points[2] = new PVector(dest.x + iWidth/2, dest.y + iHeight/2, dest.z );
//    points[3] = new PVector(dest.x - iWidth/2, dest.y + iHeight/2, dest.z );
//     
//    center.set(dest);
  }
  
  public void Draw(PImage img)
  {
    beginShape(QUADS);
    texture(img);
    
    vertex(points[0].x, points[0].y, points[0].z, 0, 0);
    vertex(points[1].x, points[1].y, points[1].z, img.width, 0);
    vertex(points[2].x, points[2].y, points[2].z, img.width, img.height);
    vertex(points[3].x, points[3].y, points[3].z, 0, img.height);
    
    endShape();
  }
  
  public void Draw(PImage img, float ix, float iy, float toX, float toY)
  {
    beginShape(QUADS);
    texture(img);
    
    vertex(points[0].x, points[0].y, points[0].z, ix, iy);
    vertex(points[1].x, points[1].y, points[1].z, toX, iy);
    vertex(points[2].x, points[2].y, points[2].z, toX, toY);
    vertex(points[3].x, points[3].y, points[3].z, ix, toY);
    
    endShape();
  }
  
  public Quad2D RotateQuad(PVector rotAxis, float rotAmount)
  {
    Quad2D returnQuad = new Quad2D();
    returnQuad.points[0] = RotateVector(rotAxis, rotAmount, points[0]);
    returnQuad.points[1] = RotateVector(rotAxis, rotAmount, points[1]);
    returnQuad.points[2] = RotateVector(rotAxis, rotAmount, points[2]);
    returnQuad.points[3] = RotateVector(rotAxis, rotAmount, points[3]);
    
    returnQuad.center = RotateVector(rotAxis, rotAmount, center);
    
    return returnQuad;
  }
  
  public PVector  RotateVector(PVector rotAxis, float rotAmount, PVector rotVec)
  {
    PVector rotateVec = new PVector();
    rotateVec.set(rotVec);
    rotateVec.sub(rotAxis);
    PVector returnVec = new PVector();
    
    returnVec.x = (rotateVec.x *(cos(rotAmount))) - (rotateVec.y *(sin(rotAmount)));
    returnVec.y = (rotateVec.x *(sin(rotAmount))) + (rotateVec.y *(cos(rotAmount)));
    returnVec.add(rotAxis);
    return returnVec;
  }
  
  public void MirrorQuad(PVector axisA, PVector axisB)
  {
    points[0] = MirrorVector2D( points[0], axisA, axisB );
    points[1] = MirrorVector2D( points[1], axisA, axisB );
    points[2] = MirrorVector2D( points[2], axisA, axisB );
    points[3] = MirrorVector2D( points[3], axisA, axisB );
    
    center = MirrorVector2D( center, axisA, axisB );
  }
  
   public PVector MirrorVector2D(PVector v1, PVector axisA, PVector axisB)
  {
    PVector axis = PVector.sub(axisA, axisB);
    
    PVector axisNormal = ReturnNormal(axis, true);
    
    PVector mirrorVec = new PVector();
    
    mirrorVec.set(v1);
    mirrorVec.sub(axisB);
    mirrorVec = PVector.sub(mirrorVec, PVector.div( PVector.mult( axisNormal, 2*mirrorVec.dot(axisNormal) ), pow(axisNormal.mag(),2))     );
    mirrorVec.add(axisB);
      
     return mirrorVec;
  }
  
  public PVector ReturnNormal(PVector v1, boolean clockwise)
  {
    PVector normalV1 = new PVector();
    normalV1.set(v1);

    if (clockwise)
    {
      float y = normalV1.y;
      normalV1.y = -normalV1.x;
      normalV1.x = y;
    }
    else
    {
      float y = normalV1.y;
      normalV1.y = normalV1.x;
      normalV1.x = -y;
    }

    return normalV1;
  }

}

class Saucer
{
  Quad2D quadd;
  
  float attackRadius;
  
  float rangeToThink = 350; //if there are no humans within this range, then mvoe towards the closest human;
  PVector closestHuman = new PVector(50000, 50000,0); //holds the closest human in the case that there are no humans within the "rangetothink", this is also stored as a dir vector
  boolean humanInRange = true;
  
  PVector vel;
  float speed = 0.12f;
  int changeDirCounter = 0;
  int changeDirMax = 2500;
  int kills = 0;
  
  Saucer(PVector ipos, float radius)
  {   
    quadd = new Quad2D(ipos, radius*2, radius*2);
    attackRadius = radius * 0.75f;
    
    float rotAmount = (int)random(360);
    vel = new PVector(1,0,0);
    vel = RotateVector(new PVector (0,0,0), rotAmount, vel);
    vel.mult(speed*17);
  }
  int counter = 0;
  public void Update(long elapsedTime, float worldHeight, ArrayList humanList)
  {
    changeDirCounter += elapsedTime;
    if (changeDirCounter > changeDirMax)
    {
      PVector closestHuman = new PVector(50000, 50000,0); 
     //  humanInRange = false;
      for (int j = 0; j < humanList.size(); j++)
      {
      //  if (!humanInRange)
      //  {

              //if the closest human is closer than the current closest human
               if( ( closestHuman.mag() ) > (PVector.sub(((Human)humanList.get(j)).quadd.center, quadd.center)).mag())
               {
                 //set closest human = to this human
                 closestHuman = PVector.sub(((Human)humanList.get(j)).quadd.center, quadd.center);
               }
       // }
//        if (CircleCollisionNoOverlap( quadd.center, rangeToThink, ((Human)humanList.get(j)).quadd.center, ((Human)humanList.get(j)).radius ))
//        {
//          humanInRange = true;
//        }
      }
       
            
      if ( (int)random(8) >0 )
      {
      changeDirCounter = 0;
      
      float rotAmount = (int)random(360);
      
      vel.x = 1;
      vel.y = 0;
      vel = RotateVector(new PVector (0,0,0), rotAmount, vel);
      vel.mult(speed*elapsedTime);
      }
      else
      {
        changeDirCounter = 0;
        vel.set(closestHuman);
        vel.normalize();
        vel.mult(speed*elapsedTime);
        counter++;
      }
      
    }
    
    if(quadd.center.y > worldHeight/2)
    {
      changeDirCounter = -changeDirMax*2;
      vel.y = -1;
      vel.x = 0;
      vel.mult (speed*2*elapsedTime);
    }
    else if (quadd.center.y < -worldHeight/2)
    {
      changeDirCounter = -changeDirMax*2;
      vel.y = 1;
      vel.x = 0;
      vel.mult (speed*2*elapsedTime);
    }
    
    quadd.Translate(vel);
  }
  
  
    public boolean CircleCollisionNoOverlap(PVector aPos, float aRadius, PVector bPos, float bRadius)
  {
    if ((PVector.sub(aPos, bPos)).mag() < (aRadius + bRadius))
      return true;
    else
      return false;
  }
  
  public PVector  RotateVector(PVector rotAxis, float rotAmount, PVector rotVec)
  {
    PVector rotateVec = new PVector();
    rotateVec.set(rotVec);
    rotateVec.sub(rotAxis);
    PVector returnVec = new PVector();
    
    returnVec.x = (rotateVec.x *(cos(rotAmount))) - (rotateVec.y *(sin(rotAmount)));
    returnVec.y = (rotateVec.x *(sin(rotAmount))) + (rotateVec.y *(cos(rotAmount)));
    returnVec.add(rotAxis);
    return returnVec;
  }
  
  public void Draw(PImage img)
  {
    quadd.Draw(img);

  }
}

class Star
{
  Camera3D cameraa;
  float worldWidth;
  float worldHeight;
  Quad2D quadd;
  PImage img;
  PImage etherImg;
  
  PVector vel;
  float speed = 10;
  int sizee = 130;
  int spawnTimeCounter = 0;
  int spawnTime = 10000;
  int spawnTimeMin = 10000;
  int spawnTimeMax = 25000;
  int aliveTime = 0; //counter for how long the star is live.
  int maxAliveTime = 9000;
  
  int timeInEther = 0;
  int maxTimeInEther = 10000;
  
  
  boolean active = false;
  boolean etherMode = false;
  
  Star(float iworldHeight, float iworldWidth)
  { 
    img = loadImage("shootingstar.png");
    etherImg = loadImage("ether.png");
    
    worldWidth = iworldHeight;
    worldHeight =  iworldWidth;
    
    spawnTime = (int)random(spawnTimeMax-spawnTimeMin) + spawnTimeMin;
    
    quadd = new Quad2D(new PVector(0,  0, 0), sizee, sizee);
    vel = new PVector(0,0,0);
  }
  
  public void Update(long elapsedTime)
  {  
    if (!etherMode)
   { 
        spawnTimeCounter += elapsedTime;
        if(spawnTimeCounter > spawnTime)
        {
          spawnTimeCounter = 0;
          spawnTime = (int)random(spawnTimeMax-spawnTimeMin) + spawnTimeMin;
          active = true;
          aliveTime = 0;
          
          
          float spawnX = cameraa.pos.x + (int)random(cameraa.cwidth) - cameraa.cwidth/2;
          
          float spawnY = (int)random(2);
          if (spawnY == 0)
          {
            spawnY = cameraa.pos.y + cameraa.cheight + 100;
            vel.y = -100;
          }
          else
          {
            spawnY = cameraa.pos.y - cameraa.cheight - 100;
            vel.y = 100;
          }
            
          quadd = new Quad2D(new PVector(spawnX,  spawnY, 0), sizee, sizee);
          
          vel.x = (int)random(50);
          if (spawnX > cameraa.pos.x)
            vel.x *= -1;
            
          vel.normalize();
          vel.mult(speed);
          
          quadd = quadd.RotateQuad(quadd.center, FindAngle2D(vel, new PVector(0,1,0)));
        }
        
        if (active)
        {
          aliveTime += elapsedTime;
          if (aliveTime > maxAliveTime)
          {
            active = false;  vel.x = 0; vel.y = 0;
          }
          quadd.Translate(vel);
        }
    
   }
   else
   {
     timeInEther += elapsedTime;
     if (timeInEther > maxTimeInEther)
       GoToStarMode();
       
     quadd.Translate(vel);
   }
  }
  
  
  public void GoToStarMode()
  {
    speed = 10;
    etherMode = false;
    active = false;
  }
  
  public void GoToEtherMode()
  {
    speed = 1.5f;
    etherMode = true;
    vel.normalize();
    vel.mult(speed);
    timeInEther = 0;
  }
  
  public PVector  RotateVector(PVector rotAxis, float rotAmount, PVector rotVec)
  {
    PVector rotateVec = new PVector();
    rotateVec.set(rotVec);
    rotateVec.sub(rotAxis);
    PVector returnVec = new PVector();
    
    returnVec.x = (rotateVec.x *(cos(rotAmount))) - (rotateVec.y *(sin(rotAmount)));
    returnVec.y = (rotateVec.x *(sin(rotAmount))) + (rotateVec.y *(cos(rotAmount)));
    returnVec.add(rotAxis);
    return returnVec;
  }
  
  public void Draw()
  {
    if (etherMode == false)
    {
    if (active)
    quadd.Draw(img);
    }
    else
    quadd.Draw(etherImg);

  }
  
  public float FindAngle2D(PVector v1, PVector v2)
  {
    //float angle = -(180/PI)*atan2(v1.x*v2.y - v1.y*v2.x, v1.x*v2.x+v1.y*v2.y);
    float angle = -atan2(v1.x*v2.y - v1.y*v2.x, v1.x*v2.x+v1.y*v2.y);

    return angle;
  }
}
//import java.awt.AWTException; 
//import java.awt.Robot; 
//import java.awt.event.KeyEvent;
//import java.awt.Frame;

class World
{
    int  level = 1;
    int levelUpDisplayTime = 5500;
    int levelUpDisplayTimeCounter = levelUpDisplayTime;
    int levelUpDisplayFadeTime = 2000;
    
    int wildLife = 1000;
    int maxWildLife = 1000;
    
    AllHumans humans;
    
    PImage worldMapImg = loadImage("WorldMap.jpg");
    
    PImage saucerImg = loadImage("saucer.png");
    ArrayList saucers = new ArrayList();
    
    PImage entangled = loadImage("entangled.png");
    ArrayList entangles = new ArrayList();
    
    int numOfCols = 10;
    float colWidth = 0;
    Quad2D[] worldMapQuads;
    Quad2D worldMapQuad;
    float worldWidth = (float)worldMapImg.width*2.5f;
    float worldHeight = (float)worldMapImg.height*2.5f;
    Star star;
    
  World()
  {        
    humans = new AllHumans();
    
     worldMapQuad = new Quad2D(new PVector(0, 0, 0), worldWidth, worldHeight);
     worldMapQuads = new Quad2D[numOfCols];
     
     
     colWidth = worldWidth/numOfCols;
     
     for (int i = 0; i < worldMapQuads.length; i++)
     {
       worldMapQuads[i] = new Quad2D(new PVector( (colWidth* i)  ,0,0), colWidth , worldHeight );
     }
     
     star = new Star(worldHeight, worldWidth);
  }

  
  public void Update(long elapsedTime, Camera3D cameraa)
  { 
      humans.Update(elapsedTime, entangles, cameraa, worldWidth);
      
      //set all humans issomeonesclosest to false
//      for (int i = 0; i < humans.humanList.size(); i++)
//      {
//        ((Human)humans.humanList.get(i)).isSomeonesClosest = false;
//      }
      for (int i = 0; i < saucers.size(); i++)
      {
        ((Saucer)saucers.get(i)).Update(elapsedTime, worldHeight, humans.humanList);
      }
     star.Update(elapsedTime);
  }

  public void Draw(long elapsedTime, Camera3D cameraa, boolean drawWorld)
  {    
     
     DrawWorldCols(cameraa, drawWorld);
     
     humans.Draw(worldWidth, cameraa);
     
    DrawEntangles(cameraa);
    DrawSaucers(cameraa);
      
     DrawStar(cameraa); 
    
  }


  public void DrawWorldCols(Camera3D cameraa, boolean drawWorld)
  {
    for (int i = 0; i < worldMapQuads.length; i++)
     {
       //only draw the column of the Earth if it is infront of the camera
       if (!ShouldDraw(   worldMapQuads[i].points[0].x, worldMapQuads[i].points[2].x, cameraa))
       {
        SetQuadX(cameraa, worldMapQuads[i]);
           //   TranslateQuadX(worldMapQuads[i], worldWidth );
              
       // if (ShouldDraw(  worldMapQuads[i].points[0].x, worldMapQuads[i].points[2].x, cameraa))
         //{
           //  worldMapQuads[i].Draw(worldMapImg,  (float)worldMapImg.width/numOfCols * (i),    0,     (float)worldMapImg.width/numOfCols * (i+1),     (float)worldMapImg.height);
         //}  
       }
       else
       {
         if (drawWorld)
           worldMapQuads[i].Draw(worldMapImg,  (float)worldMapImg.width/numOfCols * (i),    0,     (float)worldMapImg.width/numOfCols * (i+1),     (float)worldMapImg.height);
       }
     }
  }
  
  public void DrawEntangles(Camera3D cameraa)
  {
    if (entangles.size() > 0)
    {
    for(int i = 0; i < entangles.size(); i++)
    {
      if (!ShouldDraw(((BoundingCircle)entangles.get(i)).quadd.points[0].x, ((BoundingCircle)entangles.get(i)).quadd.points[2].x, cameraa))
      {
        SetQuadX(cameraa, ((BoundingCircle)entangles.get(i)).quadd);
      }
      else
        ((BoundingCircle)entangles.get(i)).Draw(entangled);
    }
    }
  }
  
  public void DrawSaucers(Camera3D cameraa)
  {
    for (int i = 0; i < saucers.size(); i++)
      {
       if (!ShouldDraw(((Saucer)saucers.get(i)).quadd.points[0].x, ((Saucer)saucers.get(i)).quadd.points[2].x, cameraa))
      {
        SetQuadX(cameraa, ((Saucer)saucers.get(i)).quadd);
      }
      else
        ((Saucer)saucers.get(i)).Draw(saucerImg);
      }
  }
  
  public void DrawStar(Camera3D cameraa)
  {
    if (!ShouldDraw( star.quadd.points[0].x, star.quadd.points[2].x, cameraa) )
        SetQuadX(cameraa, star.quadd);
    //else
        star.Draw();
  }
  public void SetQuadX(Camera3D cameraa, Quad2D iQuad)
  {
    if (cameraa.pos.x < iQuad.center.x)
    {  
      if (Math.abs(iQuad.center.x - cameraa.pos.x) > (worldWidth - Math.abs(iQuad.center.x - cameraa.pos.x)))
             TranslateQuadX(iQuad, -worldWidth );
    }
    else 
    { 
      if (Math.abs(cameraa.pos.x - iQuad.center.x) > (worldWidth - Math.abs(cameraa.pos.x - iQuad.center.x)))
              TranslateQuadX(iQuad, worldWidth );
    }
  }
  public void TranslateQuadX(Quad2D iQuad, float changeX)
  {
     iQuad.points[0].x += changeX;
     iQuad.points[1].x += changeX;
     iQuad.points[2].x += changeX;
     iQuad.points[3].x += changeX;
     
     iQuad.center.x += changeX;
  }
   public void TranslatePointX(PVector ivec, float changeX)
  {
     ivec.x += changeX;
  }
  
  public void SetObjects()
  {
  }
  public boolean ShouldDraw(float minA, float maxA, Camera3D cameraa)
  {
    if (cameraa.pos.x- (cameraa.cwidth) > maxA)
        return false;
      if (minA > cameraa.pos.x + (cameraa.cwidth))
        return false;
       
       return true;
  }
  public boolean LinearColl(float minA, float maxA,float minB, float maxB)
  {
      if (minA > maxB)
        return false;
      if (minB > maxA)
        return false;
       
       return true;
  }
  
  public void PrintVector(PVector vec)
  {
    System.out.println("vec x y z: " + vec.x + " " + vec.y + " " + vec.z  );
  }
  

}







class Entity
{
  //Entity parent = null;
  
  PVector pos;
  //int layer = 0;
  Entity()
  {
    pos = new PVector(0,0,0);
  }
  
  public void Update()
  {
  }
  
  public void Draw()
  {
  }
  
}

  static public void main(String args[]) {
    PApplet.main(new String[] { "--bgcolor=#F0F0F0", "RepairEarth" });
  }
}
