/*************************************************************************** 
  alienBlaster 
  Copyright (C) 2004 
  Paul Grathwohl, Arne Hormann, Daniel Kuehn, Soenke Schwardt

  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, 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.
***************************************************************************/
using namespace std;

#include <iostream>
#include "shot.h"
#include "surfaceDB.h"
#include "racers.h"
#include "racer.h"
#include "explosions.h"
#include "explosion.h"
#include "enemys.h"
#include "enemy.h"
#include "smokePuffs.h"
#include "boundingBox.h"

Shot::Shot( ShotTypes shotType, int playerNr, Vector2D position, float angle ) {

  this->shotType = shotType;
  fromWhichPlayer = playerNr;
  
  pos = position;
  collidedWithGround = false;
  deflectedBySonicFromPlayer1 = false;
  deflectedBySonicFromPlayer2 = false;

  generatesSmokePuffs = false;
  timeToNextSmokePuff = 100;

  switch (shotType) {
    // primary shots
  case SHOT_NORMAL:
    {
      vel = Vector2D( VEL_SHOT_NORMAL, angle, POLAR);
      timeToLive = LIFETIME_SHOT_NORMAL;
      sprite = surfaceDB.loadSurface( FN_SHOT_NORMAL );
      break;
    }
  case SHOT_NORMAL_HEAVY:
    {
      vel = Vector2D( VEL_SHOT_NORMAL_HEAVY, angle, POLAR);
      timeToLive = LIFETIME_SHOT_NORMAL_HEAVY;
      sprite = surfaceDB.loadSurface( FN_SHOT_NORMAL_HEAVY );
      break;
    }
  case SHOT_DOUBLE:
    {
      vel = Vector2D(VEL_SHOT_DOUBLE, angle, POLAR);
      timeToLive = LIFETIME_SHOT_DOUBLE;
      sprite = surfaceDB.loadSurface( FN_SHOT_DOUBLE );
      break;
    }
  case SHOT_DOUBLE_HEAVY:
    {
      vel = Vector2D(VEL_SHOT_DOUBLE_HEAVY, angle, POLAR);
      timeToLive = LIFETIME_SHOT_DOUBLE_HEAVY;
      sprite = surfaceDB.loadSurface( FN_SHOT_DOUBLE_HEAVY );
      break;
    }
  case SHOT_TRIPLE:
    {
      vel = Vector2D(VEL_SHOT_TRIPLE, angle, POLAR);
      timeToLive = LIFETIME_SHOT_TRIPLE;
      sprite = surfaceDB.loadSurface( FN_SHOT_TRIPLE );
      break;
    }

    // primary shots heavy fighter
  case SHOT_HF_NORMAL:
    {
      vel = Vector2D( VEL_SHOT_HF_NORMAL, angle, POLAR);
      timeToLive = LIFETIME_SHOT_HF_NORMAL;
      sprite = surfaceDB.loadSurface( FN_SHOT_HF_NORMAL );
      break;
    }
  case SHOT_HF_DOUBLE:
    {
      vel = Vector2D( VEL_SHOT_HF_DOUBLE, angle, POLAR);
      timeToLive = LIFETIME_SHOT_HF_DOUBLE;
      sprite = surfaceDB.loadSurface( FN_SHOT_HF_DOUBLE );
      break;
    }
  case SHOT_HF_TRIPLE:
    {
      vel = Vector2D(VEL_SHOT_HF_TRIPLE, angle, POLAR);
      timeToLive = LIFETIME_SHOT_HF_TRIPLE;
      sprite = surfaceDB.loadSurface( FN_SHOT_HF_TRIPLE );
      break;
    }
  case SHOT_HF_QUATTRO:
    {
      vel = Vector2D(VEL_SHOT_HF_QUATTRO, angle, POLAR);
      timeToLive = LIFETIME_SHOT_HF_QUATTRO;
      sprite = surfaceDB.loadSurface( FN_SHOT_HF_QUATTRO );
      break;
    }
  case SHOT_HF_QUINTO:
    {
      vel = Vector2D(VEL_SHOT_HF_QUINTO, angle, POLAR);
      timeToLive = LIFETIME_SHOT_HF_QUINTO;
      sprite = surfaceDB.loadSurface( FN_SHOT_HF_QUINTO );
      break;
    }

    // secondary shots
  case SHOT_DUMBFIRE:
    {
      vel = Vector2D(VEL_SHOT_DUMBFIRE, angle, POLAR);
      timeToLive = LIFETIME_SHOT_DUMBFIRE;
      sprite = surfaceDB.loadSurface( FN_SHOT_DUMBFIRE );
      generatesSmokePuffs = true;
      break;
    }
  case SHOT_DUMBFIRE_DOUBLE:
    {
      vel = Vector2D(VEL_SHOT_DUMBFIRE_DOUBLE, angle, POLAR);
      timeToLive = LIFETIME_SHOT_DUMBFIRE_DOUBLE;
      sprite = surfaceDB.loadSurface( FN_SHOT_DUMBFIRE_DOUBLE );
      generatesSmokePuffs = true;
      break;
    }
  case SHOT_KICK_ASS_ROCKET: 
    {
      vel = Vector2D(VEL_SHOT_KICK_ASS_ROCKET, angle, POLAR);
      timeToLive = LIFETIME_SHOT_KICK_ASS_ROCKET;
      sprite = surfaceDB.loadSurface( FN_SHOT_KICK_ASS_ROCKET );
      spriteShadow = surfaceDB.loadSurface( FN_SHOT_KICK_ASS_ROCKET_SHADOW, true );
      break;
    }
  case SHOT_HELLFIRE:
    {
      vel = Vector2D(VEL_SHOT_HELLFIRE / 2.0, angle, POLAR);
      timeToLive = LIFETIME_SHOT_HELLFIRE;
      sprite = surfaceDB.loadSurface( FN_SHOT_HELLFIRE );
      spriteShadow = surfaceDB.loadSurface( FN_SHOT_HELLFIRE_SHADOW, true );
      generatesSmokePuffs = true;
      break;
    }
  case SHOT_MACHINE_GUN:
    {
      vel = Vector2D(VEL_SHOT_MACHINE_GUN, angle, POLAR);
      timeToLive = LIFETIME_SHOT_MACHINE_GUN;
      sprite = surfaceDB.loadSurface( FN_SHOT_MACHINE_GUN );
      break;
    }
  case SHOT_ENERGY_BEAM:
    {
      vel = Vector2D(VEL_SHOT_ENERGY_BEAM, angle, POLAR);
      timeToLive = LIFETIME_SHOT_ENERY_BEAM;
      sprite = surfaceDB.loadSurface( FN_SHOT_ENERGY_BEAM, true );
      break;
    }

    // secondary shots heavy fighter
  case SHOT_HF_DUMBFIRE:
    {
      vel = Vector2D(VEL_SHOT_HF_DUMBFIRE, angle, POLAR);
      timeToLive = LIFETIME_SHOT_HF_DUMBFIRE;
      sprite = surfaceDB.loadSurface( FN_SHOT_HF_DUMBFIRE );
      generatesSmokePuffs = true;
      break;
    }
  case SHOT_HF_DUMBFIRE_DOUBLE:
    {
      vel = Vector2D(VEL_SHOT_HF_DUMBFIRE_DOUBLE, angle, POLAR);
      timeToLive = LIFETIME_SHOT_HF_DUMBFIRE_DOUBLE;
      sprite = surfaceDB.loadSurface( FN_SHOT_HF_DUMBFIRE_DOUBLE );
      generatesSmokePuffs = true;
      break;
    }
  case SHOT_HF_KICK_ASS_ROCKET: 
    {
      vel = Vector2D(VEL_SHOT_HF_KICK_ASS_ROCKET, angle, POLAR);
      timeToLive = LIFETIME_SHOT_HF_KICK_ASS_ROCKET;
      sprite = surfaceDB.loadSurface( FN_SHOT_HF_KICK_ASS_ROCKET );
      spriteShadow = surfaceDB.loadSurface( FN_SHOT_HF_KICK_ASS_ROCKET_SHADOW, true );
      break;
    }
  case SHOT_HF_LASER:
    {
      vel = Vector2D(VEL_SHOT_HF_LASER, angle, POLAR);
      timeToLive = LIFETIME_SHOT_HF_LASER;
      sprite = surfaceDB.loadSurface( FN_SHOT_HF_LASER );
      break;
    }

    // special shots
  case SPECIAL_SHOT_HEATSEEKER:
    {
      vel = Vector2D(VEL_SPECIAL_SHOT_HEATSEEKER, angle, POLAR);
      timeToLive = LIFETIME_SPECIAL_SHOT_HEATSEEKER;
      sprite = surfaceDB.loadSurface( FN_SPECIAL_SHOT_HEATSEEKER );
      generatesSmokePuffs = true;
      break;
    }
  case SPECIAL_SHOT_NUKE:
    {
      vel = Vector2D( VEL_SPECIAL_SHOT_NUKE, angle, POLAR );
      timeToLive = LIFETIME_SPECIAL_SHOT_NUKE;
      sprite = surfaceDB.loadSurface( FN_SPECIAL_SHOT_NUKE );
      spriteShadow = surfaceDB.loadSurface( FN_SPECIAL_SHOT_NUKE_SHADOW, true );
      break;
    }

    // enemy shots
  case ENEMY_SHOT_NORMAL:
    {
      vel = Vector2D(VEL_ENEMY_SHOT_NORMAL, angle, POLAR);
      timeToLive = LIFETIME_ENEMY_SHOT_NORMAL;
      sprite = surfaceDB.loadSurface( FN_ENEMY_SHOT_NORMAL );
      break;
    }
  case ENEMY_SHOT_TANK_ROCKET:
    {
      vel = Vector2D(VEL_ENEMY_SHOT_TANK_ROCKET, angle, POLAR);
      timeToLive = LIFETIME_ENEMY_SHOT_TANK_ROCKET;
      sprite = surfaceDB.loadSurface( FN_ENEMY_SHOT_TANK_ROCKET );
      spriteShadow = surfaceDB.loadSurface( FN_ENEMY_SHOT_TANK_ROCKET_SHADOW, true );
      generatesSmokePuffs = true;
      break;
    }
    
  default: 
    {
      vel = Vector2D(0,0);
      timeToLive = 0;
      sprite = surfaceDB.loadSurface( FN_SHOT_NORMAL );
      break;
    }
  }

}


void Shot::moveAndCollide( int dT ) {

  if ( fromWhichPlayer == 666 ) {
    moveAndCollideEnemyShot( dT );
  } else {
    moveAndCollidePlayerShot( dT );
  }

  generateSmokePuff( dT );

  timeToLive -= dT;
}


void Shot::moveAndCollidePlayerShot( int dT ) {

  Vector2D posOld = pos;

  // move the shot
  movePlayerShot( dT );

  if ( !isExpired() && collidePlayerShot( posOld ) ) {
    addExplosion();
  }
}


void Shot::movePlayerShot( int dT ) {
  switch (shotType) {
  case SHOT_NORMAL:
  case SHOT_NORMAL_HEAVY:
  case SHOT_DOUBLE:
  case SHOT_DOUBLE_HEAVY:
  case SHOT_TRIPLE:
  case SHOT_HF_NORMAL:
  case SHOT_HF_DOUBLE:
  case SHOT_HF_TRIPLE:
  case SHOT_HF_QUATTRO:
  case SHOT_HF_QUINTO:
  case SHOT_DUMBFIRE:
  case SHOT_DUMBFIRE_DOUBLE:
  case SHOT_KICK_ASS_ROCKET:
  case SHOT_MACHINE_GUN:
  case SHOT_ENERGY_BEAM:
  case SHOT_HF_DUMBFIRE:
  case SHOT_HF_DUMBFIRE_DOUBLE:
  case SHOT_HF_KICK_ASS_ROCKET:
  case SHOT_HF_LASER:
    {
      pos = pos + vel * dT / 1000.0;
      break;
    }
  case SHOT_HELLFIRE:
    {
      pos = pos + vel * dT / 1000.0;
      if ( timeToLive < LIFETIME_SHOT_HELLFIRE - 100 ) {
	vel = Vector2D( 0, -VEL_SHOT_HELLFIRE );
      }
      break;
    }
  case SPECIAL_SHOT_HEATSEEKER:
    {
      pos = pos + vel * dT / 1000.0;
      if ( timeToLive >= LIFETIME_SPECIAL_SHOT_HEATSEEKER - 200 ) break;
      int idxNearestEnemy = 0;
      float distNearest = 10000;
      for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
	// vector from the shotPosition to the enemy
	Vector2D v = enemys->getEnemy(i)->getPos() - pos;
	if ( getAbsAngleDifference( vel.getDirection(), v.getDirection() ) < 180 ) {
	  float dist = (enemys->getEnemy(i)->getPos() - pos).getLength();
	  if ( dist < distNearest ) {
	    distNearest = dist;
	    idxNearestEnemy = i;
	  }
	}
      }
      if ( distNearest != 10000 ) {
	float angle = 
	  getAngleDifference( (enemys->getEnemy(idxNearestEnemy)->getPos()-pos).getDirection(),
			      vel.getDirection() );
	if ( fabs(angle) < 80 * dT / 1000.0 ) {
	  vel.rotate(angle);
	} else if ( angle < 0 ) {
	  vel.rotate( -80 * dT / 1000.0 );
	} else {
	  vel.rotate( 80 * dT / 1000.0 );
	}
      } 

      break;
    }

  case SPECIAL_SHOT_NUKE:
    {
      pos = pos + vel * dT / 1000.0;
      // Nuke is in its place!
      if ( (pos - Vector2D( SCREEN_WIDTH / 2.0, SCREEN_HEIGHT / 2.0 )).getLength()
	   <= (vel * dT / 1000.0).getLength() ) {
	nukeIsInPlace = true;
      }
      break;
    }

  default: 
    {
      cout << "movePlayerShot: unexpected shotType: " << shotType << endl;
      break;
    }
  }

  // clip at the outside of the window
  if ( !RectangleGeo( Vector2D( -SHOT_SCREEN_BORDER, -SHOT_SCREEN_BORDER ), 
		      Vector2D( SCREEN_WIDTH + SHOT_SCREEN_BORDER, 
				SCREEN_HEIGHT + SHOT_SCREEN_BORDER )).isInside(pos) ) {
    timeToLive = 0;
  }
}


bool Shot::collidePlayerShot( Vector2D posOld ) {
  switch (shotType) {
    // only against air
  case SHOT_ENERGY_BEAM:
    {
      BoundingBox box( lroundf(posOld.getX()) - sprite->w / 2,
		       lroundf(pos.getY()) - sprite->w / 2,
		       sprite->w,
		       lroundf((posOld-pos).getY()) + sprite->h );		       
      for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
	if ( ENEMY_FLYING[ enemys->getEnemy(i)->getType() ] &&
	     enemys->getEnemy(i)->collidesWith( &box ) ) {
	  enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
	  timeToLive = 0;
	  collidedWithGround = !ENEMY_FLYING[ enemys->getEnemy(i)->getType() ];
	  return true;
	}
      }
      break;
    }
    //only against air
  case SHOT_HF_LASER:
    {
      for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
	if ( ENEMY_FLYING[ enemys->getEnemy(i)->getType() ] &&
	     enemys->getEnemy(i)->collidesWith( posOld, pos ) ) {
	  enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
	  timeToLive = 0;
	  collidedWithGround = false;
	  return true;
	}
      }
      break;
    }
  // against air and ground
  case SHOT_NORMAL:
  case SHOT_NORMAL_HEAVY:
  case SHOT_DOUBLE:
  case SHOT_DOUBLE_HEAVY:
  case SHOT_TRIPLE:
  case SHOT_HF_NORMAL:
  case SHOT_HF_DOUBLE:
  case SHOT_HF_TRIPLE:
  case SHOT_HF_QUATTRO:
  case SHOT_HF_QUINTO:
    {
      for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
	if ( //ENEMY_FLYING[ enemys->getEnemy(i)->getType() ] &&
	     enemys->getEnemy(i)->collidesWith( posOld, pos ) ) {
	  enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
	  timeToLive = 0;
	  collidedWithGround = !ENEMY_FLYING[ enemys->getEnemy(i)->getType() ];
	  return true;
	}
      }
      break;
    }
    // against air and ground
  case SHOT_DUMBFIRE:
  case SHOT_DUMBFIRE_DOUBLE:
  case SHOT_HF_DUMBFIRE:
  case SHOT_HF_DUMBFIRE_DOUBLE:
    {
      for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
	if ( enemys->getEnemy(i)->collidesWith( Circle(pos, 15) ) ) {
	  enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
	  timeToLive = 0;
	  collidedWithGround = !ENEMY_FLYING[ enemys->getEnemy(i)->getType() ];
	  return true;
	}
      }
      break;
    }
    // only against ground
  case SHOT_KICK_ASS_ROCKET:
  case SHOT_HF_KICK_ASS_ROCKET:
    {
      for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
	if ( (!ENEMY_FLYING[ enemys->getEnemy(i)->getType() ]) && 
	     enemys->getEnemy(i)->collidesWith( Circle(pos, 15) ) ) {
	  enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
	  timeToLive = 0;
	  collidedWithGround = true;
	  return true;
	}
      }
      break;
    }
    // only against ground, but has to hit more exactly than kickAssRocket
  case SHOT_HELLFIRE:
    {
      for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
	if ( (!ENEMY_FLYING[ enemys->getEnemy(i)->getType() ]) && 
	     enemys->getEnemy(i)->collidesWith( Circle(pos, 5) ) ) {
	  enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
	  timeToLive = 0;
	  collidedWithGround = true;
	  return true;
	}
      }
      break;
    }
    // against air and ground
  case SHOT_MACHINE_GUN:
    {
      for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
	if ( enemys->getEnemy(i)->collidesWith( posOld, pos ) ) {
	  enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
	  timeToLive = 0;
	  collidedWithGround = !ENEMY_FLYING[ enemys->getEnemy(i)->getType() ];
	  return true;
	}
      }
      break;
    }

    // against air and ground
  case SPECIAL_SHOT_HEATSEEKER:
    {
      for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
	if ( enemys->getEnemy(i)->collidesWith( Circle(pos, 5) ) ) {
	  enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
	  timeToLive = 0;
	  collidedWithGround = !ENEMY_FLYING[ enemys->getEnemy(i)->getType() ];
	  return true;
	}
      }
      break;
    }
  case SPECIAL_SHOT_NUKE: break;
    
  default: 
    {
      cout << "collidePlayerShot: unexpected shotType: " << shotType << endl;
      return false;
    }
  }
  return false;
}

///////////////////////////

void Shot::moveAndCollideEnemyShot( int dT ) {
  Vector2D posOld = pos;

  moveEnemyShot( dT );

  if ( !isExpired() && collideEnemyShot( posOld ) ) {
    addExplosion();
  }
}


void Shot::moveEnemyShot( int dT ) {
  switch (shotType) {
  case ENEMY_SHOT_NORMAL: {
    // is this shot near the deflector of a racer?
    for ( unsigned int i = 0; i < racers->getNrRacers(); i++) {
      if ( racers->getRacer(i)->getShipType() == HEAVY_FIGHTER ) {
        Vector2D racerToShot = pos - racers->getRacer(i)->getPos();
        if ( racerToShot.getLength() < RACER_DEFLECTOR_ACTIVATION_DIST ) {
          vel += Vector2D( RACER_DEFLECTOR_POWER * (dT / 1000.0), 
			   racerToShot.getDirection(), POLAR );
        }
        if ( racers->getRacer(i)->isDeflectorSpecialActive() ) {
          if ( racerToShot.getLength() < ITEM_DEFLECTOR_ACTIVATION_DIST ) {
            vel += Vector2D( ITEM_DEFLECTOR_POWER * (dT / 1000.0), 
			     racerToShot.getDirection(), POLAR );
          }
        }
      }
    }
    pos = pos + vel * dT / 1000.0;
    break;
  }
  case ENEMY_SHOT_TANK_ROCKET: {
    if ( deflectedBySonicFromPlayer1 ) {
      Vector2D racerToShot = pos - racers->getRacer(0)->getPos();
      if ( racerToShot.getLength() < RACER_SONIC_ACTIVATION_DIST ) {
        vel += Vector2D( RACER_SONIC_POWER * (dT / 1000.0), 
			 racerToShot.getDirection(), POLAR );
        deflectedBySonicFromPlayer1 = false;
      }
    }
    if ( deflectedBySonicFromPlayer2 ) {
      Vector2D racerToShot = pos - racers->getRacer(1)->getPos();
      if ( racerToShot.getLength() < RACER_SONIC_ACTIVATION_DIST ) {
        vel += Vector2D( RACER_SONIC_POWER * (dT / 1000.0),
			 racerToShot.getDirection(), POLAR );
        deflectedBySonicFromPlayer2 = false;
      }
    }
    pos = pos + vel * dT / 1000.0;
    break;
  }
  default: {
    cout << "moveEnemyShot: unexpected shotType: " << shotType << endl;
    break;
  }
  }

  // clip at the outside of the window
  if ( !RectangleGeo(Vector2D( -SHOT_SCREEN_BORDER, -SHOT_SCREEN_BORDER ), 
		     Vector2D( SCREEN_WIDTH + SHOT_SCREEN_BORDER, 
			       SCREEN_HEIGHT + SHOT_SCREEN_BORDER )).isInside(pos) ) {
    timeToLive = 0;
  }
}


bool Shot::collideEnemyShot( Vector2D posOld ) {
  switch (shotType) {
  case ENEMY_SHOT_NORMAL:
    {
      for ( unsigned int i = 0; i < racers->getNrRacers(); i++ ) {
	if ( racers->getRacer(i)->collidesWith( posOld, pos ) ) {
	  racers->getRacer(i)->doDamage( shotType );
	  timeToLive = 0;
	  collidedWithGround = false;
	  return true;
	}
      }
      break;
    }
  case ENEMY_SHOT_TANK_ROCKET:
    {
      for ( unsigned int i = 0; i < racers->getNrRacers(); i++ ) {
	if ( racers->getRacer(i)->collidesWith( Circle(pos, 5) ) ) {
	  racers->getRacer(i)->doDamage( shotType );
	  timeToLive = 0;
	  collidedWithGround = false;
	  return true;
	}
      }
      break;
    }
  default:
    {
      cout << "collideEnemyShot: unexpected shotType: " << shotType << endl;
      return false;
    }
  }
  return false;
}

/////////////

void Shot::addExplosion() {
  Explosion *explosion;
  switch (shotType) {
  default: 
    {
      if ( collidedWithGround ) {
	explosion = new Explosion( FN_EXPLOSION_NORMAL, pos, 
				   vel / 10.0, EXPLOSION_NORMAL_GROUND );
      } else {
	if ( shotType == SHOT_HF_LASER ) {
	  // Laser is too fast...
	  explosion = new Explosion( FN_EXPLOSION_NORMAL, pos, 
				     vel / 30.0, EXPLOSION_NORMAL_AIR );
	} else {
	  explosion = new Explosion( FN_EXPLOSION_NORMAL, pos, 
				     vel / 10.0, EXPLOSION_NORMAL_AIR );
	}
      }
      break;
    }
  }
  explosions->addExplosion( explosion );
}

////////////////////

void Shot::drawShadow(SDL_Surface *screen) {
  switch (shotType) {
  case SHOT_KICK_ASS_ROCKET:
  case SHOT_HF_KICK_ASS_ROCKET:
  case SHOT_HELLFIRE:
  case SPECIAL_SHOT_NUKE:
    {
      SDL_Rect shadowR;
      shadowR.x = lroundf(pos.getX()) - spriteShadow->w / 2 - 7;
      shadowR.y = lroundf(pos.getY()) - spriteShadow->h / 2 + 7;
      shadowR.w = spriteShadow->w;
      shadowR.h = spriteShadow->h;
      SDL_BlitSurface( spriteShadow, 0, screen, &shadowR );
      break;
    }
  case ENEMY_SHOT_TANK_ROCKET: 
    {
      SDL_Rect destR;
      SDL_Rect srcR;
      destR.x = lroundf(pos.getX()) - spriteShadow->w / 16 - 10;
      destR.y = lroundf(pos.getY()) - spriteShadow->h / 2 + 10;
      destR.w = spriteShadow->w / 8;
      destR.h = spriteShadow->h;
      float angle = vel.getDirection() + 202.5;
      int idx = lroundf(angle) % 360;
      idx = idx / 45;
      srcR.x = idx * spriteShadow->w / 8;
      srcR.y = 0;
      srcR.w = spriteShadow->w / 8;
      srcR.h = spriteShadow->h;

      SDL_BlitSurface( spriteShadow, &srcR, screen, &destR );
      break;
    }
  default: break;
  }
}

void Shot::drawGroundShot(SDL_Surface *screen) {
  switch (shotType) {
  case SHOT_KICK_ASS_ROCKET:
  case SHOT_HF_KICK_ASS_ROCKET:
  case SHOT_HELLFIRE:
    {
      SDL_Rect destR;
      SDL_Rect srcR;
      destR.x = lroundf(pos.getX()) - sprite->w / 2;
      destR.y = lroundf(pos.getY()) - sprite->h / 2;
      destR.w = sprite->w;
      destR.h = sprite->h;
      srcR.x = 0;
      srcR.y = 0;
      srcR.w = sprite->w;
      srcR.h = sprite->h;

      SDL_BlitSurface( sprite, &srcR, screen, &destR );
      break;
    }
  default: break;
  }
}
 
void Shot::drawGroundAirShot(SDL_Surface *screen) {
  switch (shotType) {
  case SHOT_DUMBFIRE:
  case SHOT_DUMBFIRE_DOUBLE:
  case SHOT_HF_DUMBFIRE:
  case SHOT_HF_DUMBFIRE_DOUBLE:
    {
      SDL_Rect destR;
      SDL_Rect srcR;
      destR.x = lroundf(pos.getX()) - sprite->w / 16;
      destR.y = lroundf(pos.getY()) - sprite->h / 2;
      destR.w = sprite->w / 8;
      destR.h = sprite->h;
      // TODO: eight directions are outdated for dumbfire, but existent in the image
      float angle = vel.getDirection() + 202.5;
      int idx = lroundf(angle) % 360;
      idx = idx / 45;
      srcR.x = idx * 8;
      srcR.y = 0;
      srcR.w = 8;
      srcR.h = 8;

      SDL_BlitSurface( sprite, &srcR, screen, &destR );
      break;
    }
  case SHOT_MACHINE_GUN:
  case SHOT_HF_LASER:
    {
      SDL_Rect destR;
      destR.x = lroundf(pos.getX()) - sprite->w / 2;
      destR.y = lroundf(pos.getY()) - sprite->h / 2;
      destR.w = sprite->w;
      destR.h = sprite->h;
      SDL_BlitSurface( sprite, 0, screen, &destR );
      break;
    }
  case SHOT_ENERGY_BEAM:
    {
      SDL_Rect destR;
      destR.x = lroundf(pos.getX()) - sprite->w / 2;
      destR.y = lroundf(pos.getY()) - sprite->h / 2;
      destR.w = sprite->w;
      destR.h = sprite->h;
      SDL_BlitSurface( sprite, 0, screen, &destR );
      destR.x = lroundf(pos.getX()) - sprite->w / 2;
      destR.y = lroundf(pos.getY()) - sprite->h / 2;
      SDL_BlitSurface( sprite, 0, screen, &destR );
      break;
    }
  case SPECIAL_SHOT_HEATSEEKER:
    {
      SDL_Rect destR;
      SDL_Rect srcR;
      destR.x = lroundf(pos.getX()) - sprite->w / 16;
      destR.y = lroundf(pos.getY()) - sprite->h / 2;
      destR.w = sprite->w / 8;
      destR.h = sprite->h;

      float angle = vel.getDirection() + 202.5;
      int idx = lroundf(angle) % 360;
      idx = idx / 45;
      srcR.x = idx * 8;
      srcR.y = 0;
      srcR.w = 8;
      srcR.h = 8;

      SDL_BlitSurface( sprite, &srcR, screen, &destR );
      break;
    }
  case ENEMY_SHOT_TANK_ROCKET:
    {
      SDL_Rect destR;
      SDL_Rect srcR;
      destR.x = lroundf(pos.getX()) - sprite->w / 16;
      destR.y = lroundf(pos.getY()) - sprite->h / 2;
      destR.w = sprite->w / 8;
      destR.h = sprite->h;

      float angle = vel.getDirection() + 202.5;
      int idx = lroundf(angle) % 360;
      idx = idx / 45;
      srcR.x = idx * sprite->w / 8;
      srcR.y = 0;
      srcR.w = sprite->w / 8;
      srcR.h = sprite->h;

      SDL_BlitSurface( sprite, &srcR, screen, &destR );
      break;
    }

  default: break;
  }
}

void Shot::drawAirShot(SDL_Surface *screen) {
  switch (shotType) {
  case SHOT_NORMAL:
  case SHOT_NORMAL_HEAVY:
  case SHOT_DOUBLE:
  case SHOT_DOUBLE_HEAVY:
  case SHOT_TRIPLE:
  case SHOT_HF_NORMAL:
  case SHOT_HF_DOUBLE:
  case SHOT_HF_TRIPLE:
  case SHOT_HF_QUATTRO:
  case SHOT_HF_QUINTO:
  case SPECIAL_SHOT_NUKE:
  case ENEMY_SHOT_NORMAL:
    {
      SDL_Rect destR;
      destR.x = lroundf(pos.getX()) - sprite->w / 2;
      destR.y = lroundf(pos.getY()) - sprite->h / 2;
      destR.w = sprite->w;
      destR.h = sprite->h;
      SDL_BlitSurface( sprite, 0, screen, &destR );
      break;
    }
  default: break;
  }
}

ShotTypes Shot::getShotType() {
  return shotType;
}


void Shot::generateSmokePuff( int dT) {
  if ( ! generatesSmokePuffs ) return;

  timeToNextSmokePuff -= dT;
  if ( timeToNextSmokePuff < 0 ) {
    Vector2D relPos = -vel;
    relPos.setLength( sprite->h / 2 );
    if ( shotType == SHOT_HELLFIRE ||
	 shotType == ENEMY_SHOT_TANK_ROCKET ) {

      smokePuffs->addSmokePuff( pos + relPos, 
				vel * SMOKE_PUFF_VELOCITY_FACTOR,
				SMOKE_PUFF_MEDIUM );
      timeToNextSmokePuff += SMOKE_PUFF_DELAY_TO_NEXT_PUFF[ SMOKE_PUFF_MEDIUM ];
    } else {
      smokePuffs->addSmokePuff( pos + relPos, 
				vel * SMOKE_PUFF_VELOCITY_FACTOR,
				SMOKE_PUFF_SMALL );
      timeToNextSmokePuff += SMOKE_PUFF_DELAY_TO_NEXT_PUFF[ SMOKE_PUFF_SMALL ];
    }
  }
}

			      
  
