
//////////////////////////////////////////
///////   (C) EddieTheWild 2008    ///////
//////////////////////////////////////////

 
package seeker.postgrado;

import java.applet.Applet;
import java.lang.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;

import java.applet.AudioClip;



public class Main extends Applet implements KeyListener, Runnable {
     
  static Nave nave1, nave2;
  static Thread t;
  static int opcionJugadores=0; // 1 o 2 segun el numero de jugadores elegido
  static int jugadores=0;       // cuantos jugadores quedan activos en la partida
  static boolean music=true;
  
  KeyProcessor keyProcessor;   // para procesar pulsaciones multiples de teclas sin delay

// Para los sonidos y musica                 
  static AudioClip sMissile, sExplosion, sCrash, sMusicaMenu, sMusicaJuego, sMusicaFinPartida; 
                  
  public void init() {
     Setup.doblebuffer=createImage(Setup.width,Setup.Theight);
     Setup.CG=Setup.doblebuffer.getGraphics();
     sMissile=getAudioClip( getDocumentBase(), "missile.wav");     // Cargamos los sonidos y musica
     sExplosion=getAudioClip( getDocumentBase(), "explosion.wav");
     sCrash=getAudioClip( getDocumentBase(), "crash.wav");
     sMusicaMenu=getAudioClip( getDocumentBase(), "theLastStarfighter.mid");
     sMusicaJuego=getAudioClip( getDocumentBase(), "mortalKombat.mid");
     sMusicaFinPartida=getAudioClip( getDocumentBase(), "terminator2.mid");
     
     addKeyListener(this); // para procesar las teclas que no se pulsaran simultaneamente y les afecta el repeat delay
  
     // Procesador de pulsaciones de teclado para la teclas que requieren un mayor control
     // el timer se ejecuta cada 50 milisegundos
     // asi se permite control a los dos jugadores sin interferencia
     
     keyProcessor = new KeyProcessor(50,null);
     this.addKeyListener(new KeyAdapter() {
        public void keyPressed(KeyEvent e){
           keyProcessor.setKeystate(e.getKeyCode(),true);
        }
        public void keyReleased(KeyEvent e){
           keyProcessor.setKeystate(e.getKeyCode(),false);
        }
     });
     keyProcessor.start(); 
     Menu();                 
     t=new Thread(this);
     t.start();
  }
  
  
  // inner class porque accede a muchos objetos de la clase Main

  class KeyProcessor extends javax.swing.Timer {
 
    Map<Integer,Boolean> keystate = new Hashtable<Integer,Boolean>();
 
    public KeyProcessor(int delay, ActionListener listener) {
        super(delay, null);
           // teclas que queremos procesar sin delay, y teniendolas pulsadas simultaneamente
        keystate.put(KeyEvent.VK_LEFT,false); 
        keystate.put(KeyEvent.VK_RIGHT,false);
        keystate.put(KeyEvent.VK_Z,false);
        keystate.put(KeyEvent.VK_X,false);
                      
        addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
               if (keystate.get(KeyEvent.VK_LEFT)) nave1.angle=nave1.angle-nave1.giro;
                if (keystate.get(KeyEvent.VK_RIGHT)) nave1.angle=nave1.angle+nave1.giro;
                if (keystate.get(KeyEvent.VK_Z)) nave2.angle=nave2.angle-nave2.giro; 
                if (keystate.get(KeyEvent.VK_X)) nave2.angle=nave2.angle+nave2.giro;                                  
            }
        });
    }
 
    public void setKeystate(int keycode, boolean pressed){
        keystate.put(keycode, pressed);
    }
    
  } // end inner class KeyProcessor
  
  
  public void run() {
     while (true) {
       repaint();
    //   System.out.println(Thread.activeCount());  // muestra el numero de threads activos
       try { 
         t.sleep(5);
       } catch (InterruptedException e) {
       }
     }
  }

  public void update(Graphics g) {
    paint(g);
  }

  public void paint(Graphics g) {
    g.drawImage(Setup.doblebuffer,0,0,this);
  }


  public static void Menu() { 
     if (music) sMusicaMenu.loop(); // tocamos la musica de fondo
     opcionJugadores=0;
     Setup.CG.setColor(Color.black);
     Setup.CG.fillRect(0,0,Setup.width,Setup.Theight);
     Setup.CG.setColor(Color.orange);
     Font font = new java.awt.Font("Arial", Font.BOLD, 40);
     Setup.CG.setFont(font);                  
     int fila=Setup.Theight/2-120;
     int columna=Setup.width/2-50;
     Setup.CG.drawString("Seeker",columna,fila);
     font = new java.awt.Font("Arial", Font.PLAIN, 28);
                 Setup.CG.setFont(font);
     columna=columna-25;
     fila=fila+75;
     Setup.CG.drawString("1. One Player",columna,fila); fila=fila+50;
     Setup.CG.drawString("2. Two Players",columna,fila); fila=fila+50;
     Setup.CG.drawString("3. Help",columna,fila); fila=fila+75;
     Setup.CG.drawString("Choose your choice:",columna-25,fila);
  }

  
  public void IniciarPartida(){
        sMusicaMenu.stop(); // paramos la musica de menu
        if (music) sMusicaJuego.loop();
        Setup.CG.setColor(Color.black);
        Setup.CG.fillRect(0,0,Setup.width,Setup.Theight);
        Setup.CG.setColor(Color.gray);
        Setup.CG.fillRect(0,Setup.height,Setup.width,Setup.Theight-Setup.height);
        nave1=new Nave((int)Math.round(Math.random()*Setup.width),
                       30+(int)Math.round(Math.random()*(Setup.height-50)),
                       "Ship 1", Setup.width-300);
        Thread tnave1=new Thread(nave1);
        tnave1.start();
  }
        

  public static void finPartida() {
      int x,y,xa,ya; 
      sMusicaJuego.stop();
      sMusicaFinPartida.play();
      nave1.activo=false;   
      if (nave2!=null) nave2.activo=false;
      xa=100; ya=100; // cualquier posicion
      for (double i=(int)Math.round(2*Setup.dospi);i>0;i=i-.1) {
        x=(int)Math.round(Setup.width/2-100+i*20*Math.cos(i));
        y=(int)Math.round(Setup.height/2-40+i*10*Math.sin(i));
        Font font = new java.awt.Font("Arial", Font.BOLD, 22);
        synchronized (Setup.CG) {
            Setup.CG.setFont(font);  
            Setup.CG.setColor(Color.black);
            Setup.CG.fillRect(xa,ya,200,80);
            xa=x; ya=y;
            Setup.CG.setColor(Color.white);
            Setup.CG.fillRect(x,y,200,80);
            Setup.CG.setColor(Color.red); 
            Setup.CG.drawString("GAME OVER !!!",x+20,y+40);
        }
        try{
               Thread.sleep(20); // para que pueda continuar la ultima explosion
        } catch(InterruptedException e1){}   
      }    
      try{
         Thread.sleep(3000); // esperamos 1 segundo para que se lea "game over"
      } catch(InterruptedException e1){}   
      sCrash.stop(); sExplosion.stop(); sMissile.stop();
      sMusicaFinPartida.stop();
      Menu();
  }


  /////// Metodos de KeyListener ///////
  // aqui procesamos las pulsaciones que no queremos que se hagan concurrentemente

  public void keyPressed(KeyEvent e) {
  
      int tecla=e.getKeyCode();     
      switch (tecla) {
          case KeyEvent.VK_1: if (opcionJugadores==0){
                      opcionJugadores=1;
                      jugadores=1;
                      IniciarPartida();
                  }
                  break;
          case KeyEvent.VK_2: if (opcionJugadores==0){
                      opcionJugadores=2;
                      jugadores=2;
                      IniciarPartida();
                      nave2=new Nave((int)Math.round(Math.random()*Setup.width),
                                     30+(int)Math.round(Math.random()*(Setup.height-50)),
                                     "Ship 2",100);                
                      Thread tnave2=new Thread(nave2);
                      tnave2.start();
                  }      
                  break;
          case KeyEvent.VK_3: if (opcionJugadores==0){
                      Setup.CG.setColor(Color.black);
                      Setup.CG.fillRect(0,0,Setup.width,Setup.Theight);
                      Font font1 = new java.awt.Font("Arial", Font.BOLD, 22);
                      Setup.CG.setFont(font1);
                      Setup.CG.setColor(Color.orange);
                      int fila=Setup.Theight/2-150;
                      int columna=Setup.width/2-50;
                      Setup.CG.drawString("Controls:",columna,fila); fila=fila+40;
                      font1 = new java.awt.Font("Arial", Font.PLAIN, 18);
                      Setup.CG.setFont(font1);
                      Setup.CG.setColor(Color.blue);
                      Setup.CG.drawString("Ship 1:",columna-25,fila); fila=fila+20;
                      Setup.CG.setColor(Color.lightGray);
                      Setup.CG.drawString("Left: Turn left",columna,fila); fila=fila+20;
                      Setup.CG.drawString("Right: Turn right",columna,fila); fila=fila+20;
                      Setup.CG.drawString("J: Fire AAM",columna,fila); fila=fila+20;
                      Setup.CG.drawString("K: Fast/slow speed",columna,fila); fila=fila+20;
                      Setup.CG.drawString("L: Shield on/off",columna,fila); fila=fila+30;
                      Setup.CG.setColor(Color.blue);
                      Setup.CG.drawString("Ship 2:",columna-25,fila); fila=fila+20;
                      Setup.CG.setColor(Color.lightGray);
                      Setup.CG.drawString("Z: Turn left",columna,fila); fila=fila+20;
                      Setup.CG.drawString("X: Turn right",columna,fila); fila=fila+20;
                      Setup.CG.drawString("S: Fire AAM",columna,fila); fila=fila+20;
                      Setup.CG.drawString("D: Fast/slow speed",columna,fila); fila=fila+20;
                      Setup.CG.drawString("F: Shield on/off",columna,fila);  fila=fila+30;                    
                      Setup.CG.drawString("Enter: Return to menu",columna-25,fila); fila=fila+20;
                      Setup.CG.drawString("F1: Toggle music on/off",columna-25,fila); fila=fila+20;
                      Setup.CG.drawString("ESC: Quit match",columna-25,fila);
                   }                               
                   break;
            case KeyEvent.VK_ENTER: if (opcionJugadores==0) Menu(); break;
            case KeyEvent.VK_F1: if (music) {
                                        if (opcionJugadores==0) sMusicaMenu.stop();
                                           else if (jugadores>0) sMusicaJuego.stop(); 
                                        music=false;   
                                 } else {
                                         if (opcionJugadores==0) sMusicaMenu.loop();
                                           else if (jugadores>0) sMusicaJuego.loop();       
                                         music=true;
                                 }  
                                 break;
            case KeyEvent.VK_ESCAPE: if (opcionJugadores!=0) {
                                    nave1.hull=0;
                                    if (nave2!=null) nave2.hull=0;
                                 }
                                 break;
           case KeyEvent.VK_J: if (nave1!=null && nave1.hull>0) // si existe y no ha sido derribado
                                 if (opcionJugadores==1){                                                              
                                     Sam misil=new Sam((int)Math.round(Math.random()*Setup.width),
                                                       40+(int)Math.round(Math.random()*Setup.height-40),
                                                       0,nave1); // el misil seguira a la nave1
                                     Thread t=new Thread(misil);
                                     t.start();
                                 } else if (opcionJugadores==2) {  
                                     Sam misil=new Sam(nave1.X,nave1.Y,nave1.angle,nave2); // el misil seguira a la nave2
                                     Thread t=new Thread(misil);
                                     t.start();
                                 }
                                 break;
             case KeyEvent.VK_S: if (nave2!=null && nave2.hull>0) { // si existe y no ha sido derribado
                                  //  sMissile.play();   // Sonido de lanzamiento misil
                                    Sam misil1=new Sam(nave2.X,nave2.Y,nave2.angle,nave1); // el misil seguira a la nave1
                                    Thread t=new Thread(misil1);
                                    t.start();
                                 }
                                 break;                                 
             case KeyEvent.VK_K: nave1.speed=Math.IEEEremainder(nave1.speed,2)+1; break; // pasa de 1 a 2 y viceversa
             case KeyEvent.VK_D: nave2.speed=Math.IEEEremainder(nave2.speed,2)+1; break;
             case KeyEvent.VK_F: if (nave2.escudo>0) nave2.hayEscudo=!nave2.hayEscudo; break;
             case KeyEvent.VK_L: if (nave1.escudo>0) nave1.hayEscudo=!nave1.hayEscudo;
       }
  }

  public void keyTyped(KeyEvent e) {
  }

  public void keyReleased(KeyEvent e) {
  }


} // end class Main








/** Clase que define lo que deberia tener cualquier objeto que puede ser
  * blanco de un misil
  */

class Target extends Observable { // los blancos seran observados por sus perseguidores
  double x,y;    // coordenada de dibujo
  double xx,yy;  // coordenada anterior
  double dx;     // velocidad horizontal
  double dy;     // velocidad vertical
  int X, Y, XX, YY;  // int(x), int(y), int(xx), int(yy)
  boolean activo;
  double angle;
  double speed;
  double fuel;
  double giro = 0.2;
  String nombre;

  public Target(double xi, double yi){
      x=xi; y=yi;
      activo=true;
  }       

   public void run() {
       while (activo) {
          control();
          dibuja();
          try {
            Thread.sleep(10);
          }
          catch (InterruptedException e) {}
       }
     //  if (nombre!=null) System.out.println("\nTarget '"+nombre+" finished.");
    }

    void dibuja() {
       X=(int)Math.round(x);
       Y=(int)Math.round(y); 
       XX=(int)Math.round(xx);
       YY=(int)Math.round(yy);
    }

   void control () {
     x=x+dx;
     y=y+dy;
   }
}



/* Nave */

class Nave extends Target implements Runnable { 
   
   int hull;
   int MAXHULL=10;
   double escudo;
   int MAXESCUDO=10;
   int MAXFUEL=12000;
   boolean hayEscudo;
   int marcadorX;
   int marcadorY=Setup.height+30;    
   
   Nave(double xi, double yi, String label, int xPos) {
     super(xi,yi);
     xx=x; yy=y;
     angle=0;
     speed=1;
     hull=MAXHULL;
     escudo=MAXESCUDO;
     fuel=MAXFUEL;
     marcadorX=xPos;  // columna para pintar las barras de estado
     hayEscudo=false;
     nombre=label;
     // Dibujamos las etiquetas de las barras de estado
     synchronized(Setup.CG) {
        Setup.CG.setColor(Color.cyan);
        Font font = new java.awt.Font("Arial", Font.PLAIN, 16);
        Setup.CG.setFont(font);
        Setup.CG.drawString(nombre+":",marcadorX-50,10+marcadorY);
        Setup.CG.drawString("Shield:",marcadorX-50,10+marcadorY+20);
        Setup.CG.drawString("  Fuel:",marcadorX-50,10+marcadorY+40);
     }
   }


   void control () {
      if (escudo<=0) hayEscudo=false;
            
      fuel=fuel-speed;
      if (fuel>0) {
          angle=Setup.arregla(angle);
          dx=speed*Math.cos(angle);
          dy=speed*Math.sin(angle);
         }
        else { // No hay fuel
           dy=dy+Setup.gravedad;
           hayEscudo=false;
      }
      super.control();
      if (x<0) x=Setup.width;  // cruza los bordes automaticamente
      if (x>Setup.width) x=0;
      if (y<0) y=0;           // Evita que salga por la parte superior
      if (activo && y+2.5+dy>Setup.height) {  // ha chocado con el suelo 
                            Main.sCrash.play();  // Sonido de choque
                            Explosion.boom(x,y,dx/2,dy/2,40);
                            activo=false;      // Acabara la ejecucion del Thread
                            hull=0;
                            escudo=0;
                            hayEscudo=false;
                            fuel=0;
                            Main.jugadores--;
                            if (Main.jugadores==0) {
                                dibuja();
                                Main.finPartida();
                            }
                          }
      if (activo & hull<=0) { // le han derribado
               Explosion.boom(x,y,dx/2,-dy/2,5);
               fuel=0; // Caera explotando
               escudo=0;
      }
      if (activo && !hayEscudo && escudo<MAXESCUDO) escudo=escudo+.01; // el escudo se va recuperando

   }


   void dibuja() {
         setChanged();
         notifyObservers();       
         int XX=(int)Math.round(this.xx);
         int YY=(int)Math.round(this.yy);
         super.dibuja();
         synchronized(Setup.CG) {
               Setup.CG.setFont(new java.awt.Font("Arial", Font.ITALIC, 12));  // para el nombre de las naves
               Setup.CG.setColor(Color.black);
               Setup.CG.fillRect(XX-2,YY-2,5,5); 
               Setup.CG.drawString(nombre,XX-12,YY-5);
               Setup.CG.drawOval(XX-8,YY-8,15,15); 
               if (activo) {
                 if (hayEscudo){
                   Setup.CG.setColor(Color.cyan);
                   Setup.CG.drawOval(X-8,Y-8,15,15);
                 }
                 else Setup.CG.setColor(Color.white);
                 Setup.CG.fillRect(X-2,Y-2,5,5);        // la x,y sera el centro del cuadrado
                 Setup.CG.setColor(Color.yellow);
                 Setup.CG.drawString(nombre,X-12,Y-5);
                 dibujaNivel(hull,MAXHULL,Color.red,0);        // esta aqui para no pintar en menu
                 dibujaNivel(escudo,MAXESCUDO,Color.blue,20);
                 dibujaNivel(fuel,MAXFUEL,Color.green,40);
               }
          }
          if (activo && Math.random()>0.7 && fuel>0) {
                Debris afterburner=new Debris(XX,YY,0,0,30,2);
                afterburner.start();
         }
      xx=x; 
      yy=y; 
  }

  
// Para pintar las barras de hull, escudo y fuel de la nave. 
// debe ser invocado desde un bloque synchronized(Setup.CG)

  void dibujaNivel(double value, int maxValue, Color c, int dy) {

       int longMax=200; // La longitud y anchura maxima de la barra es de 200 pixeles
       int anchMax=10;

       int nivel=(int)((value/maxValue)*longMax); // Normalizamos 
       if (nivel<0) nivel=0;
       Setup.CG.setColor(c);  // marcamos hasta el nivel indicado
       Setup.CG.fillRect(marcadorX,marcadorY+dy,nivel,anchMax);  
       Setup.CG.setColor(Color.gray); // el resto en color de fondo
       Setup.CG.fillRect(marcadorX+nivel,marcadorY+dy,longMax-nivel,anchMax);  
       Setup.CG.setColor(Color.cyan); // borde
       Setup.CG.drawRect(marcadorX-1,marcadorY+dy-1,longMax+1,anchMax+1); 
  }

} // end class Nave




/** SAM: Surface Air Missile 
  */

class Sam extends Target implements Observer, Runnable { // son observadores de sus blancos. A su vez pueden
                                                         // ser blancos de otros misiles
    private Target target;
    double targetX, targetY; // las coordenadas del blanco. Actualizadas a traves de observer-observable
    int DXX,DYY; // valores anteriores porque puede cambiar la velocidad
    int aceleracion;
    Color color;
    int length=5;


    public Sam(double xi, double yi, double ang, Target t) {
       super(xi,yi);
       AudioClip lanzamiento=Main.sMissile;
       lanzamiento.play(); // Sonido de lanzamiento misil    
       this.target=t;
       targetX=t.x;
       targetY=t.y;
       dx=0; 
       dy=0; 
       xx=(int)Math.round(x+2+5*Math.cos(ang));
       yy=(int)Math.round(y+2+5*Math.sin(ang));
       DXX=0;
       DYY=0;
       angle=ang;
       fuel=2000;
       speed=0;       // velocidad inicial
       aceleracion=3; // aceleracion inicial
       giro=0.03; // gira mas despacio que las naves
       color=Color.green;
       t.addObserver(this); // se convierte en un observador del blanco
    }


   

    /** Devuelve el angulo que parte de x1,y1 y llega a x2,y2
      */


    void control() {
       if (fuel>0) {
          fuel=fuel-speed;
          double dif=angle-Setup.angulo(x,y,targetX+2,targetY+2); // apunta al centro de la nave target
          if (Math.round(dif)==0) { speed=4; } // Mas rapido si no hay que girar 
                 else { speed=2; }
          if (Math.abs(dif)>Math.PI) { angle=angle+giro*Setup.sgn(dif); }
                 else { angle=angle-giro*Setup.sgn(dif); }
          angle=Setup.arregla(angle);
          dx=speed*Math.cos(angle);
          dy=speed*Math.sin(angle);
         }
        else { // No hay fuel
           angle=Setup.arregla(Setup.angulo(x,y,xx,yy));
           dy=dy+Setup.gravedad;
       }

       x=x+dx;
       y=y+dy;
       boolean targetAlcanzado=(Math.abs(x-targetX)<3 && Math.abs(y-targetY)<3);
       if (targetAlcanzado || (y+dy>Setup.height)){
                      Explosion.boom(x,y,dx/2,dy/2,20);                     
                      activo=false;                // Acabara la ejecucion del Thread
                      target.deleteObserver(this); // Se elimina como observer
                      if (targetAlcanzado) {
                           Nave nave=(Nave)target;
                           if (nave.hayEscudo) nave.escudo--;  
                                     else nave.hull--; 
                      }  
                      this.color=Color.black;      // Para que se borre el misil
       } 
    }

    void dibuja() {
       try {
         super.dibuja(); // actualiza redondea las coordenadas actuales y anteriores y las deja en X,Y,XX,YY
         int DX=(int)Math.round(length*Math.cos(angle));
         int DY=(int)Math.round(length*Math.sin(angle));    
         synchronized(Setup.CG) {
              Setup.CG.setColor(Color.black);
              Setup.CG.drawLine(XX,YY,XX-DXX,YY-DYY);
              Setup.CG.setColor(this.color);
              Setup.CG.drawLine(X,Y,X-DX,Y-DY);
         }
         if ((Math.random()>0.7)&&(fuel>0)) {
                Debris afterburner=new Debris(X-DX,Y-DY,0,0,30,2);
                afterburner.start();
         }
         xx=X; yy=Y; DXX=DX; DYY=DY;
       } catch (Exception e) {}
    }



    /** Metodo que se disparara cada vez que cambien las coordenadas del blanco
      */

    public void update(Observable t, Object args) {
       if (fuel>0) {
                targetX=((Target)t).x; 
                targetY=((Target)t).y;
               }
         else t.deleteObserver(this); // Sin fuel ya no seguira al blanco
    }

}


/* EXPLOSION */

class Explosion {

  static double inercia=1;

  static void boom (double x, double y, double dx, double dy, int puntos) {
      double dif=Math.PI/5;
      double ang=0;
      Main.sExplosion.play(); // Sonido de explosion
      for (double i=puntos; i>0; i--) {
          ang=ang+dif;
          double dxx=dx+inercia*Math.sin(ang)+Math.random()*4-2;
          double dyy=dy+inercia*Math.cos(ang)+Math.random()*4-2;
          int temp=80+(int)Math.round(Math.random()*40-20);
          Debris chispa=new Debris(x,y,dxx,dyy,temp,0.5);
          chispa.start(); 
      }
  }

}


/* DEBRIS */

class Debris extends Thread {

  private double x;
  private double y;
  private double xx;
  private double yy;
  private double dx;
  private double dy;
  private double caosX;
  private int temp;   // temperatura 0-100
  private boolean activo;
  private Color color;

  static int enfriamiento=3;


  /** Crea un nuevo punto partiendo en la coordenada x,y con el desplazamiento 
    * inicial de dx y dy, con la temperatura inicial temp. CaosX indica la variabilidad
    * que sufrira en dx. Dy siempre cambiara segun la gravedad
    */

  public Debris(double x, double y, double dx, double dy, int temp, double caosX) {
     this.x=x;
     this.y=y;
     this.xx=x;
     this.yy=y;
     this.dx=dx;
     this.dy=dy;
     this.temp=temp;
     this.caosX=caosX;
     activo=true;
   }

   public void run() {
     while (activo) {
       temp=temp-enfriamiento;
       dx=dx+(caosX*Math.random()-caosX/2);
       dy=dy+Setup.gravedad;
       if (y+dy>Setup.height) dy=-dy;  // Choque contra el suelo
       xx=x;
       yy=y;
       x=x+dx;
       y=y+dy; 
       if (temp>60) color=Color.white;
         else if (temp>30) color=Color.red;
                    else if (temp>0) color=Color.yellow;
                               else color=Color.black;
       dibujaDebris(); 
       if (temp<=0) activo=false;
       try {
         sleep(10);
       } catch (InterruptedException e) {
       }
     }
   }

   void dibujaDebris() {
       int X=(int)Math.round(x);
       int Y=(int)Math.round(y);
       int XX=(int)Math.round(xx);
       int YY=(int)Math.round(yy);     
       synchronized(Setup.CG) {
            Setup.CG.setColor(Color.black);
            Setup.CG.fillRect(XX,YY,2,2); 
            Setup.CG.setColor(this.color);
            Setup.CG.fillRect(X,Y,2,2); 
       }
   }


}




/** Clase con definiciones y metodos comunes
  */

class Setup {
  static final int width=1000;
  static final int height=500;
  static final int Theight=600;

  static Image doblebuffer;
  static Graphics CG; 

  static double dospi=Math.PI*2;
  static final double gravedad=0.05;

  static double angulo(double x1, double y1, double x2, double y2) {
          double alfa=Math.atan(Math.abs(y2-y1)/
                                Math.abs(x2-x1));
          if (x2-x1<0) { alfa=Math.PI-sgn(y2-y1)*alfa; }
                   else { alfa=sgn(y2-y1)*alfa; };
          return arregla(alfa);
    }

   static double arregla(double a) {
          if (a>dospi) { a=a-dospi; }
            else { if (a<0) { a=dospi+a; }   
                 }
          return a;
    }
 
    static int sgn(double n) {
       if (n<0) return(-1);
           else if (n>0) return(1);
                     else return(0);
    }
  
}

