import java.applet.AudioClip;
import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Font;
import java.awt.Color;
import java.awt.Event;
import java.awt.Cursor;
import java.awt.Image;
import java.net.URL;

public class DropGame extends Applet implements Runnable
{
 Thread    genThread;
 AudioClip restartSound;
 AudioClip dieSound;
 AudioClip explodeSound;
 AudioClip thrustSound;
 AudioClip rainSound;
 AudioClip lightningSound;
 Font      defFont      = new Font("Serif", Font.PLAIN, 11);
 Font      msgFont      = new Font("Sans-Serif", Font.BOLD, 24);
 Cursor    normalCursor = new Cursor(Cursor.DEFAULT_CURSOR);
 Cursor    linkCursor   = new Cursor(Cursor.HAND_CURSOR);
 Color     rainColor0   = new Color(0x0000BB);
 Color     rainColor1   = new Color(0x0000FF);
 Color     rainColor2   = new Color(0x4444FF);
 Color     rainColor3   = new Color(0x7777FF);
 Color     rainColor4   = new Color(0xAAAAFF);
 Color     BgColor      = Color.lightGray;
 Color     FgColor      = Color.black;
 boolean   gameBegun    = false;
 boolean   writeInfo    = false;
 boolean   paused       = false;
 boolean   playSounds   = false;
 int       gameSpeed    = 40;
  // Highscore, eget är 1192 poäng
 long      maxPoints    = 0;

  // Vilka knappar som tryckts ner
 boolean keyUp    = false;
 boolean keyDown  = false;
 boolean keyRight = false;
 boolean keyLeft  = false;
 int     keyPressCounter;

  // Spelar-rektangels variabler
 long    PlPoints; //------/ Omgångens poäng
 int     PlHundredsPassed;
 int     PlX; //-----------/ Vänsterkanten
 int     PlY; //-----------/ Överkanten
 Color   PlColor; //-------/ Spelar-rektangelns inner-färg
 int     PlHeight; //------/ Spelar-rektangelns höjd
 int     PlWidth; //------/  och bredd
 int     PlSpeed; //-------/ Farten i höger-vänster
 int     PlJumpS; //-------/ Farten i ett hopp
 int     PlLives; //-------/ Antal liv kvar
 int     PlThrusts; //-----/ Antalet superfarts-enheter kvar
 boolean PlGoingDown; //---/ Närapå onödig variabel
 boolean PlIsJumping; //---/ Om spelar-rektangeln hoppar
 boolean PlAlive; //-------/ Om något spel är igång
 boolean PlImmortal; //----/ Om spelaren är tillfälligt odödlig
 int     PlImmortalityCounter;

  // Eldklotets variabler
 Color   ThColor;
 boolean ThAlive;
 int     ThX;
 int     ThY;
 int     ThNormY;
 int     ThDiameter;
 int     ThCrushStadium;

  // Regnets variabler
 Color[]   RaColor    = new Color[100];
 int[]     RaX        = new int[100];
 int[]     RaY        = new int[100];
 double[]  RaLastMove = new double[100];
 int[]     RaSpeed    = new int[100];
 boolean[] RaOnScreen = new boolean[100];

  // Bombernas variabler
 Color     BoColor    = new Color(0xCC, 0, 0);
 int       BoWidth    = 10;
 int       BoHeight   = 10;
 int[]     BoX            = new int[10];
 int[]     BoY            = new int[10];
 int[]     BoCrushStadium = new int[10];
 boolean[] BoOnScreen     = new boolean[10];

  // Blixtens koordinarer
 int[]    LiTailX     = new int[2000]; //--/ Alla prickars koordinater
 int[]    LiTailY     = new int[2000]; //-/
 int[]    LiHeadX     = new int[3]; //-----/ 'Spetsarnas' koordinater
 int[]    LiHeadY     = new int[3]; //----/
 double[] LiDirection = new double[3]; //--/ 'Spetsarnas' hastighet åt höger
 int      LiSpeed; //----------------------/ Hastigheten neråt
 int      LiTails; //----------------------/ Antalet prickar på skärmen
 int      LiHeads; //----------------------/ Antalet 'spetsar'
 boolean  LiOnScreen; //-------------------/ Om det finns någon blixt

 public void init()
 {
  String param;
  param = getParameter("GameSpeed");
  if(param != null)
  {
   switch(Integer.parseInt(param))
   {
    case 1:  gameSpeed = 50; break;
    case 2:  gameSpeed = 40; break;
    case 3:  gameSpeed = 35; break;
    case 4:  gameSpeed = 27; break;
    case 5:  gameSpeed = 20; break;
    default: gameSpeed = 40;
   }
  }
  colors:
  {
   param = getParameter("BgColor");
   if(param != null)
   {
    try
    {
     param = param.substring(1, 7);
     BgColor = new Color(Integer.parseInt(param, 16));
    }
    catch(Exception e)
    {
     break colors;
    }
   }
   else
   {
    break colors;
   }
   param = getParameter("FgColor");
   if(param != null)
   {
    try
    {
     param = param.substring(1, 7);
     FgColor = new Color(Integer.parseInt(param, 16));
    }
    catch(Exception e)
    {
     break colors;
    }
   }
   else
   {
    FgColor = invert(BgColor);
   }
  }
  initStart();
 }

 void initStart()
 {
  keyPressCounter = 0;

  PlPoints  = 0;
  PlHundredsPassed = 0;
  PlHeight  = 20;
  PlWidth   = 50;
  PlX       = size().width - PlWidth - 6;
  PlY       = size().height - PlHeight - 5;
  PlSpeed   = 0;
  PlColor   = new Color(0x33, 0x33, 0xFF);
  PlLives   = 3;
  PlThrusts = 0;
  PlIsJumping = false;
  PlAlive     = true;
  PlGoingDown = false;
  PlImmortal  = false;
  PlImmortalityCounter = 0;

  ThColor        = new Color(0xFF, 0x99, 0x00);
  ThAlive        = false;
  ThDiameter     = 19;
  ThCrushStadium = 0;
  ThNormY        = size().height - 5 - ThDiameter;

  for(byte i = 0; i < 100; i++)
  {
   RaColor[i]    = Color.white;
   RaX[i]        = -1;
   RaY[i]        = -1;
   RaLastMove[i] = 0.0;
   RaSpeed[i]    =  1;
   RaOnScreen[i] = false;
  }

  for(byte i = 0; i < 10; i++)
  {
   BoX[i]            = -1;
   BoY[i]            = -1;
   BoCrushStadium[i] = 10;
   BoOnScreen[i]     = false;
  }

  for(int i = 0; i < 2000; i++)
  {
   LiTailX[i] = -1;
   LiTailY[i] = -1;
  }
  for(int i = 0; i < 3; i++)
  {
   LiHeadX[i]     = -1;
   LiHeadY[i]     = -1;
   LiDirection[i] = 0;
  }
  LiSpeed    = 0;
  LiOnScreen = false;
  setBackground(BgColor);
 }

 public void begin()
 {
  genThread = new Thread(this);
  genThread.start();
 }

 public void run()
 {
  while(!genThread.isInterrupted() && genThread != null)
  {
   if(gameBegun)
   {
    if((!ThAlive)&&(PlAlive)&&((PlPoints > 175)||(Math.random() < 0.03)))
    {ThStart();}

    for(byte i = 0; i < 100; i++)
    {
     if(!RaOnScreen[i])
     {
      if((PlPoints > 200)||(Math.random() < 0.3))
      {
       RaStart(i);
      }
      break;
     }
    }

    if(PlAlive)
    {
     for(byte i = 0; i < 10; i++)
     {
      if(!BoOnScreen[i] && BoCrushStadium[i] >= 9)
      {
       if((PlPoints > 200)&&(Math.random() < 0.04))
       {
        BoStart(i);
       }
       break;
      }
      if(i > PlPoints / 100 - 3)
      {
       break;
      }
     }
    }
    if(!LiOnScreen && PlAlive
     && PlPoints > 600 && PlPoints % 50 < PlPoints/50 - 4)
    {LiStart();}
   }

   if(keyPressCounter == 3)
   {
    if(keyUp)
    {PlJump();}
    if(keyDown)
    {PlSlow();}
    if(keyLeft)
    {PlLeft();}
    if(keyRight)
    {PlRight();}
    keyPressCounter = 0;
   }
   else
   {keyPressCounter += 1;}

   repaint();

   try
   {
    Thread.sleep(gameSpeed);
   }
   catch(InterruptedException e)
   {
    return;
   }
  }
 }

//---------------------------------------------------------= = =----//

 public boolean keyDown(Event evt, int key)
 {
  if(key == Event.TAB || key == (int)('0'))
  {
   if(keyLeft || PlSpeed < 0)
   {
    PlThrustLeft();
   }
   else
   {
    PlThrustRight();
   }
  }
  if(key == Event.UP || key == (int)('8'))
  {
   keyDown = false;
   keyUp   = true;
   PlJump();
   return true;
  }
  if(key == Event.DOWN || key == (int)('5') || key == (int)('2'))
  {
   keyDown = true;
   keyUp   = false;
   PlSlow();
   return true;
  }
  if(key == Event.LEFT || key == (int)('4'))
  {
   keyLeft  = true;
   keyRight = false;
   PlLeft();
   return true;
  }
  if(key == Event.RIGHT || key == (int)('6'))
  {
   keyLeft  = false;
   keyRight = true;
   PlRight();
   return true;
  }
  if(key == (int)('7'))
  {
   keyDown = false;
   PlThrustLeft();
   return true;
  }
  if(key == (int)('9'))
  {
   keyDown = false;
   PlThrustRight();
   return true;
  }
  if(key == Event.F1)
  {
   writeInfo = !writeInfo;
   if((!gameBegun)||(paused))
   {repaint();}
   return true;
  }
  if(key == Event.F2)
  {
   restartPl();
   if(paused)
   {
    begin();
    paused = false;
   }
   return true;
  }
  if(key == (int)('p') || key == (int)(' '))
  {
   pauseGame();
   return true;
  }
  if((gameBegun)&&(key == (int)('m') || key == (int)('s')))
  {
   playSounds = !playSounds;
   return true;
  }
  return false;
 }

 public boolean keyUp(Event evt, int key)
 {
  if(key == Event.UP || key == (int)('8'))
  {
   keyUp = false;
   return true;
  }
  if(key == Event.DOWN || key == (int)('5') || key == (int)('2'))
  {
   keyDown = false;
   return true;
  }
  if(key == Event.LEFT || key == (int)('4'))
  {
   keyLeft = false;
   return true;
  }
  if(key == Event.RIGHT || key == (int)('6'))
  {
   keyRight = false;
   return true;
  }
  return false;
 }

 public boolean mouseDown(Event evt, int x, int y)
 {
  if((y <= 20)||(writeInfo))
  {
   writeInfo = !writeInfo;
   if((!gameBegun)||(paused))
   {repaint();}
  }
  else
  {
   if(!gameBegun)
   {
    gameBegun = true;
    if(playSounds && restartSound != null)
    {restartSound.play();}
    begin();
   }
   else if(!PlAlive)
   {
    restartPl();
   }
   else if(y < size().height / 3)
   {PlJump();}
   else if(y > size().height - 25)
   {PlSlow();}
   else if(x < PlX + (PlWidth / 2))
   {PlLeft();}
   else
   {PlRight();}
  }
  return true;
 }

 public boolean mouseMove(Event evt, int x, int y)
 {
  if(y <= 20)
  {
   setCursor(linkCursor);
  }
  else
  {
   setCursor(normalCursor);
  }
  return false;
 }

//---------------------------------------------------------= = =----//

 public void restartPl()
 {
  if(playSounds && restartSound != null)
  {restartSound.play();}
  initStart();
 }

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

 public void paint(Graphics g)
 {
  Image bgImage; //------------------/ Bild som innehåller det som ska målas på skärmen
  Graphics bg;   //------------------/ Grafikvariabeln för dubbelbuffringen, skyddar mot flimmer
  bgImage = createImage(size().width, size().height);
  bg      = bgImage.getGraphics();

  RaDraw(bg);   // Måla ut regn
  if(LiOnScreen)
  {LiDraw(bg);}
  BoDraw(bg);   // Måla ut bomber
  PlDraw(bg);   // Måla ut spelar-rektangeln
  if(ThAlive)
  {ThDraw(bg);} // Måla ut eldklotet

  bg.setColor(Color.lightGray);
  bg.fillRect(0, 0, size().width, 20);
  bg.setColor(Color.blue);
  for(byte i = 1; i <= PlThrusts && i <= 5; i++)
  {bg.fillRect(size().width-2-6*i, 3, 5, 15);}
  bg.setFont(defFont);
  bg.setColor(Color.black);
  bg.drawString("Score: " + PlPoints, 5, 15);
  bg.drawString("Lives: " + PlLives, size().width / 4, 15);
  bg.drawString("High Score: " + maxPoints, size().width/2, 15);
  bg.drawString("Thrusts: " + PlThrusts, size().width*3/4, 15);
  bg.drawLine(0, 20, size().width, 20);
  bg.drawRect(0, 0, size().width - 1, size().height - 1);
  bg.setColor(FgColor);
  if(writeInfo)
  {seeInfo(bg);}
  else
  {
   bg.setFont(msgFont);
   if(!gameBegun)
   {
    bg.drawString("Click to begin", 20, size().height / 2);
   }
   else
   {
    if(!PlAlive)
    {bg.drawString("Game Over", 20, size().height / 2);}
   }
  }

  if(paused)
  {
   bg.setFont(defFont);
   bg.setColor(Color.darkGray);
   bg.drawString("Game Paused", size().width - 75, 33);
  }
  else
  {
   if(!playSounds && gameBegun)
   {
    bg.setFont(defFont);
    bg.setColor(Color.darkGray);
    bg.drawString("Mute", size().width - 75, 33);
   }
  }
  g.drawImage(bgImage, 0, 0, this); // ska måla ut allt
  if(!playSounds && !gameBegun)
  {
   URL    tempURL;
   playSounds = true;
   try
   {
    g.setColor(FgColor);
    g.drawRect(size().width/2-32, size().height/4-1, 62, 11);
    g.setColor(Color.gray);
    showStatus("Loading sound 1 of 6");
    tempURL        = new URL(getCodeBase(), "Restart.au");
    restartSound   = getAudioClip(tempURL);
    g.fillRect(size().width/2-31, size().height/4, 11, 10);
    showStatus("Loading sound 2 of 6");
    tempURL        = new URL(getCodeBase(), "Death.au");
    dieSound       = getAudioClip(tempURL);
    g.fillRect(size().width/2-20, size().height/4, 10, 10);
    showStatus("Loading sound 3 of 6");
    tempURL        = new URL(getCodeBase(), "Explosion.au");
    explodeSound   = getAudioClip(tempURL);
    g.fillRect(size().width/2-10, size().height/4, 10, 10);
    showStatus("Loading sound 4 of 6");
    tempURL        = new URL(getCodeBase(), "Thrust.au");
    thrustSound    = getAudioClip(tempURL);
    g.fillRect(size().width/2, size().height/4, 10, 10);
    showStatus("Loading sound 5 of 6");
    tempURL        = new URL(getCodeBase(), "Rain.au");
    rainSound      = getAudioClip(tempURL);
    g.fillRect(size().width/2+10, size().height/4, 10, 10);
    showStatus("Loading sound 6 of 6");
    tempURL        = new URL(getCodeBase(), "Thunder.au");
    lightningSound = getAudioClip(tempURL);
    g.fillRect(size().width/2+20, size().height/4, 10, 10);
    showStatus("Applet: DropGame -- Click infobar for detailed information");
    g.setColor(Color.lightGray);
    g.setFont(new Font("Serif", Font.PLAIN, 9));
    g.drawString("Sounds Loaded", size().width/2-30, size().height/4+9);
   }
   catch(Exception e)
   {
    showStatus("Loading sound failed!");
    g.setColor(Color.red);
    g.setFont(new Font("Serif", Font.PLAIN, 9));
    g.drawString("Failure!", size().width/2-22, size().height/4+9);
    playSounds = false;
   }
  }
  setBackground(BgColor);
 }

//-------------------------------------------------------==------==-//

 void PlDraw(Graphics g)
 {
  if(!paused)
  {
   if(!(PlAlive)||((PlX >= 5)&&(PlX + PlWidth <= size().width - 6)))
   {PlX += PlSpeed;}
   else
   {
    if(PlX < 5)
    {
     if(PlSpeed == -25)
     {
      PlX     = 7;
      PlSpeed = 3;
     }
     else
     {
      PlX += 1;
      PlSpeed = 0;
     }
    }
    else
    {
     if(PlX + PlWidth > size().width - 4)
     {
      PlX = size().width - PlWidth - 4;
     }
     PlX -= 1;
     PlSpeed = 0;
    }
   }

   PlY -= PlJumpS;

   if((PlY < (size().height - PlHeight - 5))||(PlGoingDown))
   {PlJumpS -= 1;}
   else
   {
    PlJumpS = 0;
    PlIsJumping = false;
    PlY = size().height - PlHeight - 5;
   }

   if(PlY < size().height + 20)
   {PlGoingDown = false;}
  }

  if(PlSpeed != 25 && PlSpeed != -25)
  {
   if(PlImmortalityCounter > 0)
   {
    PlImmortalityCounter -= 1;
   }
   else
   {
    PlImmortal = false;
   }
  }

  g.setColor(PlColor);
  g.fillRect(PlX, PlY, PlWidth, PlHeight);
  g.setColor(FgColor);
  g.drawRect(PlX, PlY, PlWidth - 1, PlHeight - 1);
 }

 void PlLeft()
 {
  if((PlAlive)&&(!paused))
  {
   if(PlSpeed >= -10)
   {
    PlSpeed -= 1;
   }
   if(PlSpeed == 0)
   {PlSpeed -= 1;}
   if(PlSpeed > 0)
   {PlSpeed = (int)(Math.round(PlSpeed / 1.5));}
   else if(PlIsJumping)
   {PlSpeed = (int)(Math.round(PlSpeed / 1.2));}
  }
 }

 void PlRight()
 {
  if((PlAlive)&&(!paused))
  {
   if(PlSpeed <= 10)
   {
    PlSpeed += 1;
   }
   if(PlSpeed == 0)
   {PlSpeed += 1;}
   if(PlSpeed < 0)
   {PlSpeed = (int)(Math.round(PlSpeed / 1.5));}
   else if(PlIsJumping)
   {PlSpeed = (int)(Math.round(PlSpeed / 1.2));}
  }
 }

 void PlSlow()
 {
  if((PlAlive)&&(!paused))
  {
   PlSpeed = (int)(Math.round(PlSpeed / 2.5));
   if(PlY < (size().height - PlHeight - 5))
   {
    PlJumpS = -15;
   }
  }
 }

 void PlJump()
 {
  if((PlAlive)&&(!paused))
  {
   if((!PlIsJumping)&&(PlY <= (size().height - PlHeight)))
   {
    PlJumpS = 10;
    PlSpeed = (int)(Math.round(PlSpeed / 2.1));
    PlIsJumping = true;
   }
  }
 }

 void PlThrustLeft()
 {
  if((PlAlive)&&(!paused))
  {
   if((PlY <= (size().height - PlHeight))&&(PlThrusts > 0)
     &&(PlX >= 5)&&(PlX + PlWidth <= size().width - 6))
   {
    PlSpeed    = -25;
    PlThrusts -= 1;
    PlImmortal = true;
    PlImmortalityCounter = 15;
    if(playSounds && thrustSound != null)
    {thrustSound.play();}
   }
  }
 }

 void PlThrustRight()
 {
  if((PlAlive)&&(!paused))
  {
   if((PlY <= (size().height - PlHeight))&&(PlThrusts > 0)
     &&(PlX >= 5)&&(PlX + PlWidth <= size().width - 6))
   {
    PlSpeed    = 25;
    PlThrusts -= 1;
    PlImmortal = true;
    PlImmortalityCounter = 15;
    if(playSounds && thrustSound != null)
    {thrustSound.play();}
   }
  }
 }

 public void PlDie()
 {
  if(PlAlive && !PlImmortal)
  {
   if(playSounds && dieSound != null)
   {dieSound.play();}
   PlLives -= 1;
   switch(PlLives)
   {
    case 2:
     PlColor = new Color(0x99, 0, 0x66);
     PlImmortal = true;
     PlImmortalityCounter = 20;
     break;
    case 1:
     PlColor = new Color(0, 0x66, 0);
     PlImmortal = true;
     PlImmortalityCounter = 25;
     break;
    default:
     PlColor = Color.darkGray;
     PlGoingDown = true;
     if(PlSpeed == 0)
     {PlSpeed = 1;}
   }
   if(PlThrusts > 0)
   {
    incPoints(10*(PlThrusts - 1));
    if(!PlGoingDown)
    {
     PlThrusts = 1;
    }
    else
    {
     incPoints(10);
     PlThrusts = 0;
     PlAlive   = false;
    }
   }
   else
   {
    PlThrusts = 0;
    if(PlGoingDown)
    {PlAlive = false;}
   }
  }
 }

 boolean inPl(int Ox, int Oy, int Ow, int Oh)
 {
  if(Oy + Oh >= PlY && Ox + Ow > PlX
   && Ox < PlX + PlWidth && Oy <= PlY + PlHeight)
  {
   return true;
  }
  return false;
 }

 void incPoints(long points)
 {
  if(PlAlive && gameBegun)
  {
   PlPoints += points;
   if(PlPoints > maxPoints)
   {maxPoints = PlPoints;}
   if(PlPoints > PlHundredsPassed * 100 + 100)
   {
    PlHundredsPassed = (int)((PlPoints - PlPoints % 100) / 100);
    if(PlHundredsPassed % 3 == 1)
    {PlThrusts += 1;}
   }
  }
 }

//---------------------------------------------------------==---==-//

 void ThDraw(Graphics g)
 {
  if(!paused)
  {
   if(PlPoints < 75)
   {ThX += 5;}
   else if(PlPoints < 125)
   {ThX += 7;}
   else if(PlPoints < 185)
   {ThX += 10;}
   else if(PlPoints < 225)
   {ThX += 15;}
   else if(PlPoints < 1300)
   {ThX += 9;}
   else if(PlPoints < 1400)
   {ThX += 12;}
   else
   {ThX += 16;}

   ThY = (int)(Math.round((Math.random()-0.5)*1.9)+ThNormY);

   if((ThX - ThDiameter * 3 > size().width)&&(ThCrushStadium == 0))
   {
    ThDie();
    incPoints(5);
   }

   if(ThCrushStadium > 0)
   {
    if(ThCrushStadium < 9)
    {
     ThCrushing(ThCrushStadium);
     ThCrushStadium += 1;
    }
    else
    {
     ThAlive = false;
    }
   }
   else
   {
    if(inPl(ThX, ThY, ThDiameter, 1))
    {
     PlDie();
     ThDie();
     if(PlSpeed != 25 && PlSpeed != -25 && PlImmortalityCounter < 10)
     {
      PlImmortal = false;
     }
    }
   }
  }

  g.setColor(new Color(0xEE, 0x77, 0x33));
  g.drawOval(ThX, ThY, ThDiameter, ThDiameter);
  g.drawOval(ThX + 1, ThY, ThDiameter, ThDiameter);
  g.setColor(ThColor);
  g.fillOval(ThX-ThDiameter/6, ThNormY*2-ThY+ThDiameter/4, ThDiameter/2, ThDiameter/2);
  g.fillOval(ThX, ThY, ThDiameter, ThDiameter);
 }

 void ThDie()
 {
  ThCrushStadium = 1;
 }

 void ThStart()
 {
  ThAlive    = true;
  ThDiameter = 19;
  ThX        = 0-ThDiameter;
  ThY        = ThNormY;
  ThCrushStadium = 0;
  ThDiameter     = PlHeight;
 }

 void ThCrushing(int stadium)
 {
  ThDiameter = (int)(ThDiameter / 1.15);
  if((stadium & 1) == 0)
  {ThY += 1;}
 }

//---------------------------------------------------------==---==-//

 void RaDraw(Graphics g)
 {
  for(byte i = 0; i < 100; i++)
  {
   if(RaOnScreen[i])
   {
    if(!paused)
    {
     RaLastMove[i] = RaLastMove[i]*2.0/3.0+(Math.random()-0.5);
     RaX[i] += (int)(Math.round(RaLastMove[i]));
     RaY[i] += RaSpeed[i];
     if(RaY[i] > size().height || RaX[i] < 0 || RaX[i] > size().width)
     {RaOnScreen[i] = false;}

     if(inPl(RaX[i], RaY[i], 1, 3))
     {
      incPoints(2);
      RaOnScreen[i] = false;
      if(playSounds && rainSound != null)
      {rainSound.play();}
     }
    }

    g.setColor(RaColor[i]);
    g.drawLine(RaX[i], RaY[i] - 3, RaX[i], RaY[i]);
   }
  }
 }

 void RaStart(int index)
 {
  byte bColor = (byte)(Math.round(Math.random() * 4));
  switch(bColor)
  {
   case  0: RaColor[index] = rainColor0; break;
   case  1: RaColor[index] = rainColor1; break;
   case  2: RaColor[index] = rainColor2; break;
   case  3: RaColor[index] = rainColor3; break;
   default: RaColor[index] = rainColor4;
  }
  RaY[index] = 24;
  if(index != 0 && PlPoints < 100 && Math.random() < 0.3)
  {
   RaX[index] = RaX[index-1] - 5;
  }
  else if(index != 0 && PlPoints < 300 && PlPoints > 100 && Math.random() < 0.6)
  {
   RaX[index] = RaX[index-1] - 5;
  }
  else if(index != 0 && PlPoints > 300 && Math.random() < 0.9)
  {
   RaX[index] = RaX[index-1] - 5;
  }
  else
  {
   RaX[index] = (int)(Math.random() * size().width);
  }
  RaLastMove[index] = 0.0;
  RaSpeed[index]    = Math.max((int)((PlPoints / 50 + 1)+(Math.random()-0.7)*4), 1);
  RaOnScreen[index] = true;
 }

//---------------------------------------------------------==---==-//

 void BoDraw(Graphics g)
 {
  for(int i = 0; i < 10; i++)
  {
   for(int j = 0; j < 10; j++)
   {
    if(j > i && BoOnScreen[i])
    {
     if(BoY[j] + 14 >= BoY[i] && BoX[j] + 10 > BoX[i]
      && BoX[j] < BoX[i] + 10 && BoY[j] <= BoY[i] + 14)
     {
      BoDie(i);
      BoDie(j);
     }
    }
   }
  }
  for(byte i = 0; i < 10; i++)
  {
   if(BoOnScreen[i])
   {
    if(!paused)
    {
     BoY[i] += 3;
     BoX[i] += (int)((Math.random()-0.5)*3);
     if(PlAlive)
     {
      if(BoX[i] < PlX + 10)
      {BoX[i] += 1;}
      else
      {BoX[i] -= 1;}
     }
     else
     {
      if((i & 1) == 0)
      {BoX[i] += 1;}
      else
      {BoX[i] -= 1;}
     }

     if(inPl(BoX[i], BoY[i], BoWidth, BoHeight))
     {
      BoDie(i);
      PlDie();
     }

     if((BoY[i]>size().height+40)||(BoX[i]+BoWidth<0)||(BoY[i]>size().width))
     {
      BoDie(i);
      incPoints(3);
      if(PlAlive && Math.random() < 0.02)
      {PlThrusts += 1;}
     }
    }

    g.setColor(FgColor); // Stubinen
    g.drawLine(BoX[i]+BoWidth/2,BoY[i], BoX[i]+BoWidth/2,BoY[i]-3);
    g.setColor(ThColor);     // Glödande delen av stubinen
    g.drawLine(BoX[i]+BoWidth/2,BoY[i]-3, BoX[i]+BoWidth/2-3,BoY[i]-5);
    g.setColor(BoColor);     // Bombens färgade del
    g.fillOval(BoX[i], BoY[i], BoWidth, BoHeight);
    g.setColor(FgColor); // Bombens kontur
    g.drawOval(BoX[i], BoY[i], BoWidth, BoHeight);
   }
   if(BoCrushStadium[i] > 0)
   {
    if(BoCrushStadium[i] < 10)
    {
     g.setColor(Color.orange);
     g.fillOval(BoX[i]-BoCrushStadium[i]*BoWidth,BoY[i]-BoCrushStadium[i]*BoHeight,
        BoCrushStadium[i]*BoWidth*2,BoCrushStadium[i]*BoHeight*2);
     if(!paused)
     {BoCrushStadium[i] += 1;}
    }
   }
  }
 }

 void BoStart(int index)
 {
  BoX[index]  = (int)(Math.random() * size().width);
  if(Math.random() < 0.2)
  {BoX[index] = (int)((Math.random()+1)*size().width/2);}
  BoY[index]            = 21;
  BoCrushStadium[index] = 0;
  BoOnScreen[index]     = true;
 }

 void BoDie(int index)
 {
  if(playSounds && PlAlive && explodeSound != null)
  {explodeSound.play();}
  BoOnScreen[index]     = false;
  BoCrushStadium[index] = 1;
 }

//---------------------------------------------------------==---==-//

 void LiDraw(Graphics g)
 {
  for(int i = 0; i < LiSpeed; i++)
  {
   for(int j = 0; j < LiHeads; j++)
   {
    LiHeadX[j] += (int)(LiDirection[j]+(Math.random()-0.5)*3);
    LiHeadY[j] += 1;
    if(LiHeadY[j] > 20)
    {
     LiTailX[LiTails] = LiHeadX[j];
     LiTailY[LiTails] = LiHeadY[j];
     LiTails    += 1;
     if(inPl(LiHeadX[j], LiHeadY[j], 1, 1))
     {
      PlDie();
     }
     else if(LiHeadY[j] > size().height)
     {
      LiDie();
     }
     else if(LiHeads < 3 && LiHeadY[j] % 100 == (int)(Math.random()*100))
     {
      LiHeadY[LiHeads]     = LiHeadY[j];
      LiHeadX[LiHeads]     = LiHeadX[j];
      LiDirection[LiHeads] = (PlX+PlWidth/2-LiHeadX[j])/(size().height-LiHeadY[j]);
      if(LiDirection[LiHeads] < -1.2)
      {
       LiDirection[LiHeads] = -1.2;
      }
      else if(LiDirection[LiHeads] > 1.2)
      {
       LiDirection[LiHeads] = 1.2 ;
      }
      else if(LiDirection[LiHeads]-LiDirection[j] < 0.3
       && LiDirection[LiHeads]-LiDirection[j] >= 0)
      {
       LiDirection[LiHeads] = 0.5+LiDirection[j];
      }
      else if(LiDirection[LiHeads]-LiDirection[j] > -0.3
       && LiDirection[LiHeads]-LiDirection[j] < 0)
      {
       LiDirection[LiHeads] = -0.5+LiDirection[j];
      }
      LiHeads += 1;
     }
    }
   }
  }
  g.setColor(Color.white);
  for(int i = 0; i < LiTails; i++)
  {
   g.drawLine(LiTailX[i], LiTailY[i], LiTailX[i], LiTailY[i]);
  }
 }

 void LiStart()
 {
  LiHeads        = 1;
  LiTails        = 0;
  if(PlPoints < 1200)
  {LiSpeed       = size().width / 50;}
  else
  {LiSpeed       = (int)(PlPoints / 120);}
  LiOnScreen     = true;
  LiHeadY[0]     = 0;
  LiHeadX[0] = PlX + PlWidth/2;
  LiDirection[0] = (Math.random()-0.5);
  if(playSounds && lightningSound != null)
  {
   if(explodeSound != null)
   {explodeSound.stop();}
   lightningSound.play();
  }
  setBackground(new Color(0xEEEEEE));
 }

 void LiDie()
 {
  LiOnScreen = false;
 }

//---------------------------------------------------------==---==-//
 void seeInfo(Graphics g)
 {
  g.setFont(defFont);
  g.setColor(invert(BgColor));

  g.drawString("DropGame Info and Rules", 75, 20 + 12 * 2);
  g.drawString("Author: Daniel Ramsköld", 75, 20 + 12 * 3);
  g.drawString("Last modified: 13 May 2000", 75, 20 + 12 * 4);
  g.drawString("Catch the falling rain with your pad", 5, 20 + 12 * 6);
  g.drawString("and avoid fireballs, bombs and lightnings.", 5, 20 + 12 * 7);
  g.drawString("You have three lives.", 5, 20 + 12 * 8);
  g.drawString("Keys:", 5, 20 + 12 * 10);
  g.drawString("4 or LEFT: Go left", 5, 20 + 12 * 11);
  g.drawString("5 or DOWN: Break and stop a jump", 5, 20 + 12 * 12);
  g.drawString("6 or RIGHT: Go Right", 5, 20 + 12 * 13);
  g.drawString("7: Thrust to the left", 5, 20 + 12 * 14);
  g.drawString("8 or UP: Jump upwards", 5, 20 + 12 * 15);
  g.drawString("9: Thrust to the right", 5, 20 + 12 * 16);
  g.drawString("0 or TAB: Thrust in the current direction", 5, 20 + 12 * 17);
  g.drawString("P or SPACE: Pause/Resume game", 5, 20 + 12 * 18);
  g.drawString("M or S: Turn on/off sounds", 5, 20 + 12 * 19);
  g.drawString("F1: Show/Hide this message ", 5, 20 + 12 * 20);
  g.drawString("F2: Restart game", 5, 20 + 12 * 21);
  g.drawString("Points: ", 200, 20 + 12 * 6);
  g.drawString("Rain catched: 2 points", 200, 20 + 12 * 7);
  g.drawString("Fireball leaves window: 5 points", 200, 20 + 12 * 8);
  g.drawString("Bomb leaves window: 3 points", 200, 20 + 12 * 9);
  g.drawString("Thrust left at death: 10 points", 200, 20 + 12 * 10);
  g.drawString("Notes and Tips:", 200, 20 + 12 * 12);
  g.drawString("While you are using thrust,", 200, 20 + 12 * 13);
  g.drawString(" and just after death, you are immortal.", 200, 20 + 12 * 14);
  g.drawString("From 200 points and up to 1200 points,", 200, 20 + 12 * 15);
  g.drawString(" the number of bombs on screen", 200, 20 + 12 * 16);
  g.drawString(" increases at each 100 points.", 200, 20 + 12 * 17);
  g.drawString("Lightnings begin striking at 600 points.", 200, 20 + 12 * 18);
  g.drawString("This is a free applet, compiled in JDK 1.1", 200, 20 + 12 * 20);
 }

 Color invert(Color c)
 {
  int r = c.getRed();
  int g = c.getGreen();
  int b = c.getBlue();
  if(r <= 0x9a && r >= 0x66
   && g <= 0x9a && g >= 0x66
   && b <= 0x9a && b >= 0x66)
  {
   return Color.black;
  }
  r     = 255 - r;
  g     = 255 - g;
  b     = 255 - b;
  return new Color(r, g, b);
 }

 public void pauseGame()
 {
  if((PlAlive)&&(gameBegun))
  {
   if(paused)
   {
    begin();
    paused = false;
   }
   else
   {
    pauseNow();
    paused = true;
    repaint();
   }
  }
 }

 public String[][] getParameterInfo()
 {
  String[][] toReturn =
  {{"GameSpeed", "1-5",     "General speed in the game"},
   {"BgColor",   "#rrggbb", "Background Colour"},
   {"GameSpeed", "#rrggbb", "Foreground Colour"}};
  return toReturn;
 }

 public String getAppletInfo()
 {
  return "DropGame, Author: Daniel Ramsköld";
 }

 public void stop()
 {
  pauseNow();
  restartSound.stop();
  dieSound.stop();
  explodeSound.stop();
  thrustSound.stop();
  rainSound.stop();
 }

 void pauseNow()
 {
  genThread.interrupt();
 }

 public void destroy()
 {
  if(genThread != null)
  {
   genThread.stop();
   genThread = null;
  }
 }

}