/* This file is part of OpenBubbles.
 *
 * OpenBubbles is an SDL clone of Bubbles.
 * Copyright (C) 2004  Benny Sperisen
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * sprite.cpp is the implementation file of sprite.h.
 *
 * Written by:
 *  Benny Sperisen
 *  lasindi@gmail.com
 *  www.freewebs.com/lasindi
 *
 * bounceOff() function written by Jason Woofenden
 *
 *****************************************************************************/

#include "sprite.h"

list<bubble*> bubs;
list<crumb> crumbs;

int bounceSound;

// sprite member functions implementations.
sprite::sprite(int x,int y,Uint8 r,Uint8 g,Uint8 b,double x_velocity,
               double y_velocity,double radius)
:_x(x),_y(y),_r(r),_g(g),_b(b),_x_velocity(x_velocity),_y_velocity(y_velocity),
 _radius(radius)
{}

void sprite::move()
{
  _x+=_x_velocity;
  _y+=_y_velocity;
}

double sprite::x() const
{
  return _x;
}

double sprite::y() const
{
  return _y;
}

Uint8 sprite::r() const
{
  return _r;
}

Uint8 sprite::g() const
{
  return _g;
}

Uint8 sprite::b() const
{
  return _b;
}

double sprite::x_velocity() const
{
  return _x_velocity;
}

double sprite::y_velocity() const
{
  return _y_velocity;
}

double sprite::radius() const
{
  return _radius;
}

bool sprite::testCollision(sprite* otherSprite)
{
	// I'm using Jason Woofenden's code here (the code from bounceOff()).
	double theta1, theta2, thetac;
	double rel1, rel2;
	double dist;
	dist = sqrt(pow(this->x()-otherSprite->x(),2.0)+
							pow(this->y()-otherSprite->y(),2.0));

	// exit if they didn't actually collide
	if(dist > this->radius() + otherSprite->radius()) {
		return false;
	}

	// angles for the direction they're moving
	theta1 = realarctan(this->y_velocity(), this->x_velocity());
	theta2 = realarctan(otherSprite->y_velocity(), otherSprite->x_velocity());

	// angle from this rel otherSprite
	thetac = realarctan(otherSprite->y() - this->y(),
											otherSprite->x() - this->x());

	// the speed along the line of impact (drawn between their centers)
	rel1 = cos(theta1 - thetac) * sqrt(pow(this->x_velocity(),2.0)+
																		 pow(this->y_velocity(),2.0));
	rel2 = cos(theta2 - thetac) * sqrt(pow(otherSprite->x_velocity(),2.0)+
																		 pow(otherSprite->y_velocity(),2.0));

	// exit if they are already moving away from eachother
	if(rel1 - rel2 <= 0.0) {
		return false;
	}
	return true;

/* This is the old code.
	char num_of_solutions;
  double m,b,h,k,r=_radius+otherSprite->radius(),sol1,sol2;
  sprite* projectile;
  sprite* target;
  // Are they already touching?
  if(sqrt(pow(_x-otherSprite->x(),2.0)+pow(_y-otherSprite->y(),2.0))<=r)
    return true;
  // Make sure projectile is moving.
  if((_x_velocity!=0)||(_y_velocity!=0))
  {
    projectile=this;
    target=otherSprite;
  }
  else if((otherSprite->x_velocity()!=0)||(otherSprite->y_velocity()!=0))
  {
    projectile=otherSprite;
    target=this;
  }
  else // If neither sprite is moving, they can't collide.
    return false;
  h=target->x();
  k=target->y();
  if(projectile->x_velocity()==0) // The projectile is going straight up.
  {
    quadraticFormula(1,-2*k,pow(k,2.0)+pow(projectile->x()-h,2.0)-pow(r,2.0),
                     num_of_solutions,sol1,sol2);
      switch(num_of_solutions)
    {
    case 2:
      if(((projectile->y()-projectile->y_velocity()<sol2)&&
          (sol2<projectile->y()))||
         ((projectile->y()<sol2)&&(sol2<projectile->y()-
                                 projectile->y_velocity())))
        return true;
    case 1:
      if(((projectile->y()-projectile->y_velocity()<sol1)&&
           (sol1<projectile->y()))||
           ((projectile->y()<sol1)&&(sol1<projectile->y()-
                                   projectile->y_velocity())))
        return true;
    }
    return false;
  }
  m=projectile->y_velocity()/projectile->x_velocity();
  b=projectile->y()-m*projectile->x();
  quadraticFormula(pow(m,2.0)+1,2*(h+m*(b-k)),pow(b-k,2.0)+pow(h,2.0)+
                                              pow(r,2.0),
                   num_of_solutions,sol1,sol2);
  switch(num_of_solutions)
  {
  case 2:
    if(((projectile->x()-projectile->x_velocity()<sol2)&&
         (sol2<projectile->x()))||
         ((projectile->x()<sol2)&&(sol2<projectile->x()-
                                 projectile->x_velocity())))
      return true;
  case 1:
    if(((projectile->x()-projectile->x_velocity()<sol2)&&
          (sol1<projectile->x()))||
          ((projectile->x()<sol1)&&(sol1<projectile->x()-
                                  projectile->x_velocity())))
      return true;
  }
  return false;*/
}




/*
 * THEORY
 *
 * ******************  In 1 Dimention  *****************
 *
 * For now we will imagine bouncing A and B off eachother in 1 dimention (along
 * a line). We can safely save the other dimention for later.
 *
 * A and B are the same weight, and are both traveling 1m/sec, to collide right
 * at the origin. With perfect bouncyness, their full momentum is reversed.
 *
 * If we cut the weight of A down by half, then the center of our colision will
 * drift towards A (the speeds of A and B are not simply reversed as in our
 * last example.) However, there is always a place between A and B on the line
 * (I'll call it x) such that the speeds of A and B relative to x, are simply
 * reversed. Thus we can find the new speed for A like so:
 *
 *     new A = x -(A - x)
 *
 *     new B = x -(B - x)
 * 
 * or, simply:
 *
 *     new A = 2x - A
 *
 *     new B = 2x - B
 *
 * this point x is the sort of center of momentum. If, instead of bouncing, A
 * and B just globbed together, x would be center of the new glob.
 *
 * x is the point where there's an equal amount of force coming in from both
 * sides. ie the weighted average of the speeds of A and B.
 *
 * average force = (A force + B force) / total mass
 *
 * x.speed = (a.speed * a.mass + b.speed * b.mass) / (a.mass + b.mas)
 *
 * then we apply the formula above for calculating the new A and B.
 *
 *
 *
 *
 * ******************  In 2 Dimentions  *****************
 *
 * OK, that's how we do it in 1D. Now we need to deal with 2D.
 * 
 * Imagine (or draw) the two balls just as they are bouncing off eachother.
 * Imagine drawing a line through the centers of the balls. The balls are
 * exerting force on eachother only alling this axis. So if we rotate
 * everything, we can do our earlier 1D math along this line.
 *
 * It doesn't matter what direction the balls are going in, they only exert
 * force on eachother along this line. What we will do is split up the balls'
 * momentum into the part that is going along this line, and the part going
 * perpendicular to it. The part going along the line, we can bounce according
 * to our math above. The other part is unefected by the bounce, and we can
 * just add it back in after.
 *
 * To get the momentum split up this way, we convert to polar notation
 * (angle,length), then subtract the angle of the line going from A to B. Then
 * when we convert back to rectangular (x,y) the x coordinate is going along
 * the line (we use this for the 1D math) and the y is going perpendicular.
 *
 * So then we bounce these relative x values with the 1D math above, then
 * convert to polar, add the angle of the line back in, then convert back to
 * rectangular.
 */

#define SQUARE(a) ((a) * (a))
#define CUBE(a) ((a) * (a) * (a))
#define DIST2(x,y) (sqrt(SQUARE((x)) + SQUARE((y))))
#define DIST4(x1,y1,x2,y2) DIST2((x2) - (x1), (y2) - (y1))

void bounceOff(bubble* bub1,bubble* bub2)
{
	double theta1, theta2, thetac;
	double r1, r2, dist;
	double rel1, rel2;
	double perp1, perp2;
	double avg;
	double mass1, mass2;

	dist = DIST4(bub1->_x, bub1->_y, bub2->_x, bub2->_y);

	// exit if they didn't actually collide
	if(dist > bub1->_radius + bub2->_radius) {
		return;
	}

	audio->playSound(bounceSound);

	// angles for the direction they're moving
	theta1 = realarctan(bub1->_y_velocity, bub1->_x_velocity);
	theta2 = realarctan(bub2->_y_velocity, bub2->_x_velocity);

	// angle from bub1 rel bub2
	thetac = realarctan(bub2->_y - bub1->_y, bub2->_x - bub1->_x);

	// the speed along the line of impact (drawn between their centers)
	rel1 = cos(theta1 - thetac) * DIST2(bub1->_x_velocity, bub1->_y_velocity);
	rel2 = cos(theta2 - thetac) * DIST2(bub2->_x_velocity, bub2->_y_velocity);

	// exit if they are already moving away from eachother
	if(rel1 - rel2 <= 0.0) {
		return;
	}

	// calculate perpendicular part of their speeds
	perp1 = sin(theta1 - thetac) * DIST2(bub1->_x_velocity, bub1->_y_velocity);
	perp2 = sin(theta2 - thetac) * DIST2(bub2->_x_velocity, bub2->_y_velocity);

	// calculate masses from radii
	// flat disks:
	mass1 = SQUARE(bub1->_radius) * PI;
	mass2 = SQUARE(bub2->_radius) * PI;
	// spheres:
	// mass1 = CUBE(bub1->radius) * PI * 4.0 / 3.0;
	// mass2 = CUBE(bub2->radius) * PI * 4.0 / 3.0;


	// find momentum center
	avg = (rel1 * mass1 + rel2 * mass2) / (mass1 + mass2);

	// bounce parallel vectors
	rel1 = avg * 2 - rel1;
	rel2 = avg * 2 - rel2;

	// back to polar, relative to thetac
	theta1 = realarctan(perp1, rel1);
	theta2 = realarctan(perp2, rel2);

	r1 = DIST2(perp1, rel1);
	r2 = DIST2(perp2, rel2);

	// back to absolute polar
	theta1 += thetac;
	theta2 += thetac;

	// back to rectangular
	bub1->_x_velocity = cos(theta1) * r1;
	bub1->_y_velocity = sin(theta1) * r1;
	bub2->_x_velocity = cos(theta2) * r2;
	bub2->_y_velocity = sin(theta2) * r2;

}

// bubble member functions implementations.
bubble::bubble(int x,int y,Uint8 r,Uint8 g,Uint8 b,double radius,
                           double x_velocity,double y_velocity)
:sprite(x,y,r,g,b,x_velocity,y_velocity,radius),number(0),_immune(0)
{
	this->loadPicture();
	_src.x=_src.y=0;
	_src.w=_dest.w=_picture->w;
	_src.h=_dest.h=_picture->h;
}

bubble::~bubble()
{
	freeSurfaces();
}

void bubble::loadPicture()
{
	string datadir=DATA_PREFIX;
	SDL_Surface* temp;
	datadir+="/bubble.png";
	_picture=IMG_Load(datadir.c_str());
	if(_picture==NULL)
	{
		cerr<<"ERROR: failed to load bubble.\n";
		exit(1);
	}
	temp=zoomSurface(_picture,_radius*2/_picture->w,_radius*2/_picture->h,1);
	SDL_FreeSurface(_picture);
	_picture=temp;
}

void bubble::draw()
{
	_dest.x=(short)(_x-_radius);
	_dest.y=(short)(_y-_radius);
	SDL_BlitSurface(_picture,&_src,screen,&_dest);
}

void bubble::setRadius(double radius)
{
  _radius=radius;
}

bool bubble::immune() const
{
  if(_immune==0)
    return false;
  return true;
}

void bubble::setImmune()
{
  _immune=IMMUNE_TIME;
}

char* bubble::identify() const
{
  return "bubble";
}

/* This function moves the bubble back to the edge of the other bubble; it's
 * intended to correct bubbles going too far into one another between frames.
 * It passes back the x and y coordinates of the center of the bubble at the
 * moment of collision in the realx and realy parameters. */
void bubble::moveBack(bubble* other,double & realx,double & realy)
{
  double sol1x,sol1y,sol2x,sol2y;
  if(_x_velocity==0)
  {
    realx=_x; // realx is obvious.
    // The linear path of the bubble intersects the other bubble at one or
    // (more likely) two points.
    sol1y=other->y()+other->radius()+_radius;
    sol2y=other->y()-other->radius()-_radius;
    // Choose between them based on the direction of the bubble.
    if(_y_velocity>0)
      if(sol1y<sol2y)
        realy=sol1y;
    else
      realy=sol2y;
    else
      if(sol1y>sol2y)
        realy=sol1y;
    else
      realy=sol2y;
    return;
  }
  else if(_y_velocity==0)
  {
    realy=_y; // realy is obvious.
    // The linear path of the bubble intersects the other bubble at one or
    // (more likely) two points.
    sol1x=other->x()+other->radius()+_radius;
    sol2x=other->x()-other->radius()-_radius;
    // Choose between them based on the direction of the bubble.
    if(_x_velocity>0)
      if(sol1x<sol2x)
        realx=sol1x;
    else
      realx=sol2x;
    else
      if(sol1x>sol2x)
        realx=sol1x;
    else
      realx=sol2x;
    return;
  }
  // The bubble is moving diagonally.
  char numOfSols;
  double m=_y_velocity/_x_velocity,b=_y-m*_x;
  // Solve for the two x coordinates. If you solve for the intersections of a
  // given circle and line, you end up solving a quadratic equation.
  quadraticFormula(pow(m,2.0)+1,2*(m*(b-other->y())-other->x()),
                   pow(b-other->y(),2.0)+pow(other->x(),2.0)-
                   pow(other->radius()+_radius,2.0),numOfSols,sol1x,sol2x);
  // The y coordinates are easy to find now.
  sol1y=m*sol1x+b;
  sol2y=m*sol2x+b;
  // Based on the direction of the bubble, choose one of the solutions.
  if(_x_velocity-other->x_velocity()>0)
    if(sol1x<sol2x)
    {
      realx=sol1x;
      realy=sol1y;
    }
    else
    {
      realx=sol2x;
      realy=sol2y;
    }
  else
    if(sol1x>sol2x)
    {
      realx=sol1x;
      realy=sol1y;
    }
    else
    {
      realx=sol2x;
      realy=sol2y;
    }
}

void bubble::freeSurfaces()
{
	SDL_FreeSurface(_picture);
}

// playerBubble member functions implementations.
playerBubble::playerBubble(int x,int y,Uint8 r,Uint8 g,Uint8 b,double radius,
                           double x_velocity,double y_velocity)
:bubble(x,y,r,g,b,radius,x_velocity,y_velocity),_state(-1)
{
	SDL_FreeSurface(_picture);
	loadPicture();
}

playerBubble::~playerBubble()
{
	freeSurfaces();
}

void playerBubble::loadPicture()
{
	string datadir=DATA_PREFIX;
	SDL_Surface* temp;
	datadir+="/player.png";
	_picture=IMG_Load(datadir.c_str());
	if(_picture==NULL)
	{
		cerr<<"ERROR: failed to load player.\n";
		exit(1);
	}
	_initpic=IMG_Load(datadir.c_str());
	if(_picture==NULL)
	{
		cerr<<"ERROR: failed to load player.\n";
		exit(1);
	}
	temp=zoomSurface(_picture,_radius*2/_picture->w,_radius*2/_picture->h,1);
	SDL_FreeSurface(_picture);
	_picture=temp;
}

void playerBubble::updatePicture()
{
	SDL_Surface* temp;
	temp=zoomSurface(_initpic,_radius*2/_initpic->w,_radius*2/_initpic->h,1);
	SDL_FreeSurface(_picture);
	_picture=temp;
}

void playerBubble::setRadius(double radius)
{
	bubble::setRadius(radius);
	updatePicture();
}

void playerBubble::move()
{
  if(SDL_GetKeyState(NULL)[SDLK_UP]==1) // up arrow is pressed
    _y_velocity-=KEYBOARD_SENSITIVITY;
  if(SDL_GetKeyState(NULL)[SDLK_DOWN]==1) // down arrow is pressed
    _y_velocity+=KEYBOARD_SENSITIVITY;
  if(SDL_GetKeyState(NULL)[SDLK_LEFT]==1) // left arrow is pressed
    _x_velocity-=KEYBOARD_SENSITIVITY;
  if(SDL_GetKeyState(NULL)[SDLK_RIGHT]==1) // right arrow is pressed
    _x_velocity+=KEYBOARD_SENSITIVITY;
  sprite::move(); // Actually move the bubble.
  /* Make the bubble go to the other side of the screen if it goes off the
   * screen. */
  if(_x<-_radius*2)
    _x+=SCREEN_WIDTH+_radius*4;
  if(_x>SCREEN_WIDTH+_radius*2)
    _x-=SCREEN_WIDTH+_radius*4;
  if(_y<-_radius*2)
    _y+=SCREEN_HEIGHT+_radius*4;
  if(_y>SCREEN_HEIGHT+_radius*2)
    _y-=SCREEN_HEIGHT+_radius*4;
  if(_immune!=0)
    _immune--;
}

void playerBubble::draw()
{
	_src.w=_picture->w;
	_src.h=_picture->h;
	_dest.w=_src.w;
	_dest.h=_src.h;
  if(_immune%2==0) // Draw every other frame.
		bubble::draw();
}

char* playerBubble::identify() const
{
  return "playerBubble";
}

int playerBubble::state() const
{
  return _state;
}

void playerBubble::kill()
{
  _state++;
}

void playerBubble::freeSurfaces()
{
	SDL_FreeSurface(_initpic);
	SDL_FreeSurface(_picture);
//	bubble::freeSurfaces();
}

// crumb member functions implementations.
crumb::crumb(int x,int y,Uint8 r,Uint8 g,Uint8 b,double x_velocity,
             double y_velocity,double value,bubble* sucker)
:sprite(x,y,r,g,b,x_velocity,y_velocity),_sucker(sucker),
 _state(EXPLODE),_suck_counter(0),_value(value)
{}

crumb::~crumb()
{}

void crumb::draw()
{
	list<double>::iterator itx=oldx.begin(),ity=oldy.begin();
	for(int i=0;(unsigned)i<oldx.size();i++)
	{
		boxRGBA(screen,(int)(*itx),(int)(*ity),(int)(*itx)+1,(int)(*ity)+1,_r,_g,
						_b,255-i*255/oldx.size());
		itx++;
		ity++;
	}
}

void crumb::move()
{
  double speed,distance;
  if((_state==EXPLODE)&&(_sucker!=NULL))
  {
    speed=sqrt(pow(_x_velocity,2)+pow(_y_velocity,2));
    _x_velocity+=EXPLODE_FRICTION*(-_x_velocity)/speed;
    _y_velocity+=EXPLODE_FRICTION*(-_y_velocity)/speed;
    // It should slow down to a negative velocity, not change signs.
    if(abs(_x_velocity)<EXPLODE_FRICTION*_x_velocity/speed)
      _x_velocity=0;
    if(abs(_y_velocity)<EXPLODE_FRICTION*_y_velocity/speed)
      _y_velocity=0;
    if((_x_velocity==0)&&(_y_velocity==0))
      _state=SUCK;
  }
  if((_sucker!=NULL)&&(_state==SUCK))
  {
    distance=sqrt(pow(_sucker->x()-_x,2)+pow(_sucker->y()-_y,2));
    _x_velocity=SUCK_STRENGTH*_suck_counter*(_sucker->x()-_x)/distance;
    _y_velocity=SUCK_STRENGTH*_suck_counter*(_sucker->y()-_y)/distance;
    _suck_counter++;
  }
  sprite::move();
	oldx.push_front(_x);
	oldy.push_front(_y);
	if(oldx.size()>CRUMB_LENGTH)
	{
		oldx.pop_back();
		oldy.pop_back();
	}
}

bool crumb::testCollision(sprite* otherSprite)
{
	char num_of_solutions;
  double m,b,h,k,r=_radius+otherSprite->radius(),sol1,sol2;
  sprite* projectile;
  sprite* target;
  // Are they already touching?
  if(sqrt(pow(_x-otherSprite->x(),2.0)+pow(_y-otherSprite->y(),2.0))<=r)
    return true;
  // Make sure projectile is moving.
  if((_x_velocity!=0)||(_y_velocity!=0))
  {
    projectile=this;
    target=otherSprite;
  }
  else if((otherSprite->x_velocity()!=0)||(otherSprite->y_velocity()!=0))
  {
    projectile=otherSprite;
    target=this;
  }
  else // If neither sprite is moving, they can't collide.
    return false;
  h=target->x();
  k=target->y();
  if(projectile->x_velocity()==0) // The projectile is going straight up.
  {
    quadraticFormula(1,-2*k,pow(k,2.0)+pow(projectile->x()-h,2.0)-pow(r,2.0),
                     num_of_solutions,sol1,sol2);
      switch(num_of_solutions)
    {
    case 2:
      if(((projectile->y()-projectile->y_velocity()<sol2)&&
          (sol2<projectile->y()))||
         ((projectile->y()<sol2)&&(sol2<projectile->y()-
                                 projectile->y_velocity())))
        return true;
    case 1:
      if(((projectile->y()-projectile->y_velocity()<sol1)&&
           (sol1<projectile->y()))||
           ((projectile->y()<sol1)&&(sol1<projectile->y()-
                                   projectile->y_velocity())))
        return true;
    }
    return false;
  }
  m=projectile->y_velocity()/projectile->x_velocity();
  b=projectile->y()-m*projectile->x();
  quadraticFormula(pow(m,2.0)+1,2*(h+m*(b-k)),pow(b-k,2.0)+pow(h,2.0)+
                                              pow(r,2.0),
                   num_of_solutions,sol1,sol2);
  switch(num_of_solutions)
  {
  case 2:
    if(((projectile->x()-projectile->x_velocity()<sol2)&&
         (sol2<projectile->x()))||
         ((projectile->x()<sol2)&&(sol2<projectile->x()-
                                 projectile->x_velocity())))
      return true;
  case 1:
    if(((projectile->x()-projectile->x_velocity()<sol2)&&
          (sol1<projectile->x()))||
          ((projectile->x()<sol1)&&(sol1<projectile->x()-
                                  projectile->x_velocity())))
      return true;
  }
  return false;
}

void crumb::setSuck(bubble* sucker)
{
  _sucker=sucker;
}

double crumb::value() const
{
  return _value;
}

bool crumb::isExploding() const
{
  if(_state==EXPLODE)
    return true;
  return false;
}

bool crumb::hasSucker() const
{
  if(_sucker!=NULL)
    return true;
  return false;
}

void sprayCrumbs(int x,int y,double value,int number,Uint8 r,Uint8 g,Uint8 b,
                 playerBubble* sucker)
{
  double angle;
  for(int i=0;i<number;i++)
  {
    angle=random_double(2.0*PI);
    crumbs.push_back(crumb(x,y,r,g,b,
                     random_double(EXPLODE_SPEED)*cos(angle),
                     random_double(EXPLODE_SPEED)*sin(angle),value,sucker));
  }
}

bouncerBubble::bouncerBubble(int x,int y,Uint8 r,Uint8 g,Uint8 b,double radius,
                             double x_velocity,double y_velocity)
:bubble(x,y,r,g,b,radius,x_velocity,y_velocity)
{
	SDL_FreeSurface(_picture);
	loadPicture();
}

void bouncerBubble::loadPicture()
{
	string datadir=DATA_PREFIX;
	SDL_Surface* temp;
	datadir+="/bouncer.png";
	_picture=IMG_Load(datadir.c_str());
	if(_picture==NULL)
	{
		cerr<<"ERROR: failed to load bouncer.\n";
		exit(1);
	}
	temp=zoomSurface(_picture,_radius*2/_picture->w,_radius*2/_picture->h,1);
	SDL_FreeSurface(_picture);
	_picture=temp;
}

char* bouncerBubble::identify() const
{
  return "bouncerBubble";
}

sharkyBubble::sharkyBubble(int x,int y,Uint8 r,Uint8 g,Uint8 b,double radius,
                           playerBubble* player)
:bubble(x,y,r,g,b,radius,0,0),_player(player)
{
	SDL_FreeSurface(_picture);
	loadPicture();
}

void sharkyBubble::loadPicture()
{
	string datadir=DATA_PREFIX;
	SDL_Surface* temp;
	datadir+="/sharky.png";
	_picture=IMG_Load(datadir.c_str());
	if(_picture==NULL)
	{
		cerr<<"ERROR: failed to load sharky.\n";
		exit(1);
	}
	temp=zoomSurface(_picture,_radius*2/_picture->w,_radius*2/_picture->h,1);
	SDL_FreeSurface(_picture);
	_picture=temp;
}

void sharkyBubble::move()
{
  if(_player->state()==-1)
  {
    double pVelocity=sqrt(pow(_player->x_velocity(),2.0)+
                          pow(_player->y_velocity(),2.0)),
    distance=sqrt(pow(_player->x()-_x,2)+pow(_player->y()-_y,2));
    _x_velocity=SHARKY_SPEED*pVelocity*(_player->x()-_x)/distance;
    _y_velocity=SHARKY_SPEED*pVelocity*(_player->y()-_y)/distance;
  }
  _x+=_x_velocity;
  _y+=_y_velocity;
}

char* sharkyBubble::identify() const
{
  return "sharkyBubble";
}

bonusBubble::bonusBubble(int x,int y,Uint8 r,Uint8 g,Uint8 b,double radius,
                         double x_velocity,double y_velocity)
:bubble(x,y,r,g,b,radius,x_velocity,x_velocity),isblack(false)
{
	loadPicture();
}

bonusBubble::~bonusBubble()
{
	freeSurfaces();
}

void bonusBubble::loadPicture()
{
  string datadir;
//	SDL_FreeSurface(_picture);
  datadir=DATA_PREFIX;
  datadir+="/bonus1.png";
  _originalbonus1=IMG_Load(datadir.c_str());
  if(_originalbonus1==NULL)
  {
    cerr<<"ERROR: unable to load bonus.\n";
    exit(1);
  }
  datadir=DATA_PREFIX;
  datadir+="/bonus1.png";
  _bonus1=IMG_Load(datadir.c_str());
  if(_originalbonus1==NULL)
  {
    cerr<<"ERROR: unable to load bonus.\n";
    exit(1);
  }
  datadir=DATA_PREFIX;
  datadir+="/bonus2.png";
  _originalbonus2=IMG_Load(datadir.c_str());
  if(_originalbonus2==NULL)
  {
    cerr<<"ERROR: unable to load bonus.\n";
    exit(1);
  }
  datadir=DATA_PREFIX;
  datadir+="/bonus2.png";
  _bonus2=IMG_Load(datadir.c_str());
  if(_bonus2==NULL)
  {
    cerr<<"ERROR: unable to load bonus.\n";
    exit(1);
  }
  updateBonus();
}

void bonusBubble::draw()
{
  SDL_Rect src,dest;
  src.x=src.y=0;
  if(isblack)
  {
    src.w=_bonus1->w;
    src.h=_bonus1->h;
  }
  else
  {
    src.w=_bonus2->w;
    src.h=_bonus2->h;
  }
  dest.x=(int)_x-(int)_radius;
  dest.y=(int)_y-(int)_radius;
  dest.w=src.w;
  dest.h=src.h;
  if(isblack)
  {
    SDL_BlitSurface(_bonus1,&src,screen,&dest);
    isblack=false;
  }
  else
  {
    SDL_BlitSurface(_bonus2,&src,screen,&dest);
    isblack=true;
  }
}

void bonusBubble::move()
{
  if(_immune!=0)
    _immune--;
  sprite::move();
}

void bonusBubble::setRadius(double radius)
{
  bubble::setRadius(radius);
  updateBonus();
}

char* bonusBubble::identify() const
{
  return "bonusBubble";
}

void bonusBubble::updateBonus()
{
	SDL_FreeSurface(_bonus1);
	SDL_FreeSurface(_bonus2);
  _bonus1=zoomSurface(_originalbonus1,_radius*2/_originalbonus1->w,
                     _radius*2/_originalbonus1->h,1);
  _bonus2=zoomSurface(_originalbonus2,_radius*2/_originalbonus2->w,
                      _radius*2/_originalbonus2->h,1);
}

void bonusBubble::freeSurfaces()
{
  SDL_FreeSurface(_originalbonus1);
  SDL_FreeSurface(_originalbonus2);
  SDL_FreeSurface(_bonus1);
  SDL_FreeSurface(_bonus2);
}

void getBonus(bubble* bonus,playerBubble* recipient)
{
  if(strcmp(bonus->identify(),"bonusBubble")!=0)
  {
    cerr<<"ERROR: bonus in getBonus() isn't a bonusBubble.\n";
    exit(1);
  }
  bounceOff(bonus,recipient);
  sprayCrumbs((int)bonus->x(),(int)bonus->y(),1,(int)recipient->radius()*4,255,
							0,0,recipient);
  bonus->setRadius(bonus->radius()-2);
bonus->setImmune();
}

missleBubble::missleBubble(int x,int y,Uint8 r,Uint8 g,Uint8 b,double radius,
                           double x_velocity,double y_velocity)
:bubble(x,y,r,g,b,radius,x_velocity,y_velocity)
{
	SDL_FreeSurface(_picture);
	loadPicture();
}

void missleBubble::loadPicture()
{
	string datadir=DATA_PREFIX;
	SDL_Surface* temp;
	datadir+="/missle.png";
	_picture=IMG_Load(datadir.c_str());
	if(_picture==NULL)
	{
		cerr<<"ERROR: failed to load missle.\n";
		exit(1);
	}
	temp=zoomSurface(_picture,_radius*2/_picture->w,_radius*2/_picture->h,1);
	SDL_FreeSurface(_picture);
	_picture=temp;
}

void missleBubble::move()
{
  sprite::move();
  if(_x<0)
    _x+=SCREEN_WIDTH;
  if(_y<0)
    _y+=SCREEN_HEIGHT;
  if(_x>=SCREEN_WIDTH)
    _x-=SCREEN_WIDTH;
  if(_y>=SCREEN_HEIGHT)
    _y-=SCREEN_HEIGHT;
}

char* missleBubble::identify() const
{
  return "missleBubble";
}

list<missleBubble*> missles;
