/*
 * See Licensing and Copyright notice in naev.h
 */

/**
 * @file weapon.c
 *
 * @brief Handles all the weapons in game.
 *
 * Weapons are what gets created when a pilot shoots.  They are based
 * on the outfit that created them.
 */


#include "weapon.h"

#include "naev.h"

#include <math.h>
#include <stdlib.h>
#include "nstring.h"

#include "log.h"
#include "rng.h"
#include "pilot.h"
#include "player.h"
#include "collision.h"
#include "spfx.h"
#include "opengl.h"
#include "explosion.h"
#include "gui.h"
#include "camera.h"
#include "ai.h"


#define weapon_isSmart(w)     (w->think != NULL) /**< Checks if the weapon w is smart. */

#define WEAPON_CHUNK_MAX      16384 /**< Maximum size to increase array with */
#define WEAPON_CHUNK_MIN      256 /**< Minimum size to increase array with */

/* Weapon status */
#define WEAPON_STATUS_OK         0 /**< Weapon is fine */
#define WEAPON_STATUS_JAMMED     1 /**< Got jammed */
#define WEAPON_STATUS_UNJAMMED   2 /**< Survived jamming */


/*
 * pilot stuff
 */
extern Pilot** pilot_stack;
extern int pilot_nstack;


/**
 * @struct Weapon
 *
 * @brief In-game representation of a weapon.
 */
typedef struct Weapon_ {
   Solid *solid; /**< Actually has its own solid :) */
   unsigned int ID; /**< Only used for beam weapons. */

   int faction; /**< faction of pilot that shot it */
   unsigned int parent; /**< pilot that shot it */
   unsigned int target; /**< target to hit, only used by seeking things */
   const Outfit* outfit; /**< related outfit that fired it or whatnot */

   double real_vel; /**< Keeps track of the real velocity. */
   double jam_power; /**< Power being jammed by. */
   double dam_mod; /**< Damage modifier. */
   int voice; /**< Weapon's voice. */
   double exp_timer; /**< Explosion timer for beams. */
   double life; /**< Total life. */
   double timer; /**< mainly used to see when the weapon was fired */
   double anim; /**< Used for beam weapon graphics and others. */
   int sprite; /**< Used for spinning outfits. */
   PilotOutfitSlot *mount; /**< Used for beam weapons. */
   double falloff; /**< Point at which damage falls off. */
   double strength; /**< Calculated with falloff. */
   int sx; /**< Current X sprite to use. */
   int sy; /**< Current Y sprite to use. */

   /* position update and render */
   void (*update)(struct Weapon_*, const double, WeaponLayer); /**< Updates the weapon */
   void (*think)(struct Weapon_*, const double); /**< for the smart missiles */

   char status; /**< Weapon status - to check for jamming */
} Weapon;


/* behind pilot_nstack layer */
static Weapon** wbackLayer = NULL; /**< behind pilots */
static int nwbackLayer = 0; /**< number of elements */
static int mwbacklayer = 0; /**< alloced memory size */
/* behind player layer */
static Weapon** wfrontLayer = NULL; /**< in front of pilots, behind player */
static int nwfrontLayer = 0; /**< number of elements */
static int mwfrontLayer = 0; /**< alloced memory size */

/* Graphics. */
static gl_vbo  *weapon_vbo     = NULL; /**< Weapon VBO. */
static GLfloat *weapon_vboData = NULL; /**< Data of weapon VBO. */
static int weapon_vboSize      = 0; /**< Size of the VBO. */


/* Internal stuff. */
static unsigned int beam_idgen = 0; /**< Beam identifier generator. */


/*
 * Prototypes
 */
/* Creation. */
static double weapon_aimTurret( const Outfit *outfit, const Pilot *parent,
      const Pilot *pilot_target, const Vector2d *pos, const Vector2d *vel, double dir,
      double swivel, double time );
static void weapon_createBolt( Weapon *w, const Outfit* outfit, double T,
      const double dir, const Vector2d* pos, const Vector2d* vel, const Pilot* parent, double time );
static void weapon_createAmmo( Weapon *w, const Outfit* outfit, double T,
      const double dir, const Vector2d* pos, const Vector2d* vel, const Pilot* parent, double time );
static Weapon* weapon_create( const Outfit* outfit, double T,
      const double dir, const Vector2d* pos, const Vector2d* vel,
      const Pilot *parent, const unsigned int target, double time );
/* Updating. */
static void weapon_render( Weapon* w, const double dt );
static void weapons_updateLayer( const double dt, const WeaponLayer layer );
static void weapon_update( Weapon* w, const double dt, WeaponLayer layer );
/* Destruction. */
static void weapon_destroy( Weapon* w, WeaponLayer layer );
static void weapon_free( Weapon* w );
static void weapon_explodeLayer( WeaponLayer layer,
      double x, double y, double radius,
      const Pilot *parent, int mode );
/* Hitting. */
static int weapon_checkCanHit( Weapon* w, Pilot *p );
static void weapon_hit( Weapon* w, Pilot* p, WeaponLayer layer, Vector2d* pos );
static void weapon_hitBeam( Weapon* w, Pilot* p, WeaponLayer layer,
      Vector2d pos[2], const double dt );
/* think */
static void think_seeker( Weapon* w, const double dt );
static void think_beam( Weapon* w, const double dt );
/* externed */
void weapon_minimap( const double res, const double w,
      const double h, const RadarShape shape, double alpha );
/* movement. */
static void weapon_setThrust( Weapon *w, double thrust );
static void weapon_setTurn( Weapon *w, double turn );


/**
 * @brief Draws the minimap weapons (used in player.c).
 *
 *    @param res Minimap resolution.
 *    @param w Width of minimap.
 *    @param h Height of minimap.
 *    @param shape Shape of the minimap.
 *    @param alpha Alpha to draw points at.
 */
void weapon_minimap( const double res, const double w,
      const double h, const RadarShape shape, double alpha )
{
   int i, rc, p;
   double x, y;
   Weapon *wp;
   const glColour *c;
   GLsizei offset;
   Pilot *par;

   /* Get offset. */
   p = 0;
   offset = weapon_vboSize;

   if (shape==RADAR_CIRCLE)
      rc = (int)(w*w);
   else
      rc = 0;

   /* Draw the points for weapons on all layers. */
   for (i=0; i<nwbackLayer; i++) {
      wp = wbackLayer[i];

      /* Make sure is in range. */
      if (!pilot_inRange( player.p, wp->solid->pos.x, wp->solid->pos.y ))
         continue;

      /* Get radar position. */
      x = (wp->solid->pos.x - player.p->solid->pos.x) / res;
      y = (wp->solid->pos.y - player.p->solid->pos.y) / res;

      /* Make sure in range. */
      if (shape==RADAR_RECT && (ABS(x)>w/2. || ABS(y)>h/2.))
         continue;
      if (shape==RADAR_CIRCLE && (((x)*(x)+(y)*(y)) > rc))
         continue;

      /* Choose colour based on if it'll hit player. */
      if ((outfit_isSeeker(wp->outfit) && (wp->target != PLAYER_ID)) ||
            (wp->faction == FACTION_PLAYER))
         c = &cNeutral;
      else {
         if (wp->target == PLAYER_ID)
            c = &cHostile;
         else {
            par = pilot_get(wp->parent);
            if ((par!=NULL) && pilot_isHostile(par))
               c = &cHostile;
            else
               c = &cNeutral;
         }
      }

      /* Set the colour. */
      weapon_vboData[ offset + 4*p + 0 ] = c->r;
      weapon_vboData[ offset + 4*p + 1 ] = c->g;
      weapon_vboData[ offset + 4*p + 2 ] = c->b;
      weapon_vboData[ offset + 4*p + 3 ] = alpha;

      /* Put the pixel. */
      weapon_vboData[ 2*p + 0 ] = x;
      weapon_vboData[ 2*p + 1 ] = y;

      /* "Add" pixel. */
      p++;
   }
   for (i=0; i<nwfrontLayer; i++) {
      wp = wfrontLayer[i];

      /* Make sure is in range. */
      if (!pilot_inRange( player.p, wp->solid->pos.x, wp->solid->pos.y ))
         continue;

      /* Get radar position. */
      x = (wp->solid->pos.x - player.p->solid->pos.x) / res;
      y = (wp->solid->pos.y - player.p->solid->pos.y) / res;

      /* Make sure in range. */
      if (shape==RADAR_RECT && (ABS(x)>w/2. || ABS(y)>h/2.))
         continue;
      if (shape==RADAR_CIRCLE && (((x)*(x)+(y)*(y)) > rc))
         continue;

      /* Choose colour based on if it'll hit player. */
      if (outfit_isSeeker(wp->outfit) && (wp->target != PLAYER_ID))
         c = &cNeutral;
      else if ((wp->target == PLAYER_ID && wp->target != wp->parent) ||
            areEnemies(FACTION_PLAYER, wp->faction))
         c = &cHostile;
      else
         c = &cNeutral;

      /* Set the colour. */
      weapon_vboData[ offset + 4*p + 0 ] = c->r;
      weapon_vboData[ offset + 4*p + 1 ] = c->g;
      weapon_vboData[ offset + 4*p + 2 ] = c->b;
      weapon_vboData[ offset + 4*p + 3 ] = alpha;

      /* Put the pixel. */
      weapon_vboData[ 2*p + 0 ] = x;
      weapon_vboData[ 2*p + 1 ] = y;

      /* "Add" pixel. */
      p++;
   }

   /* Only render with something to draw. */
   if (p > 0) {
      /* Upload data changes. */
      gl_vboSubData( weapon_vbo, 0, sizeof(GLfloat) * 2*p, weapon_vboData );
      gl_vboSubData( weapon_vbo, offset * sizeof(GLfloat),
            sizeof(GLfloat) * 4*p, &weapon_vboData[offset] );

      /* Activate VBO. */
      gl_vboActivateOffset( weapon_vbo, GL_VERTEX_ARRAY, 0, 2, GL_FLOAT, 0 );
      gl_vboActivateOffset( weapon_vbo, GL_COLOR_ARRAY, offset * sizeof(GLfloat),
            4, GL_FLOAT, 0 );

      /* Render VBO. */
      glDrawArrays( GL_POINTS, 0, p );

      /* Disable VBO. */
      gl_vboDeactivate();
   }
}


/**
 * @brief Sets the weapon's thrust.
 */
static void weapon_setThrust( Weapon *w, double thrust )
{
   w->solid->thrust = thrust;
}


/**
 * @brief Sets the weapon's turn.
 */
static void weapon_setTurn( Weapon *w, double turn )
{
   w->solid->dir_vel = turn;
}


/**
 * @brief The AI of seeker missiles.
 *
 *    @param w Weapon to do the thinking.
 *    @param dt Current delta tick.
 */
static void think_seeker( Weapon* w, const double dt )
{
   double diff;
   Pilot *p;
   Vector2d v;
   double t, turn_max;

   if (w->target == w->parent)
      return; /* no self shooting */

   p = pilot_get(w->target); /* no null pilot_nstack */
   if (p==NULL) {
      weapon_setThrust( w, 0. );
      weapon_setTurn( w, 0. );
      return;
   }

   /* Handle by status. */
   switch (w->status) {

      case WEAPON_STATUS_OK: /* Check to see if can get jammed */
      /* Purpose fallthrough */
      case WEAPON_STATUS_UNJAMMED: /* Work as expected */

         /* Smart seekers take into account ship velocity. */
         if (w->outfit->u.amm.ai == AMMO_AI_SMART) {

            /* Calculate time to reach target. */
            vect_cset( &v, p->solid->pos.x - w->solid->pos.x,
                  p->solid->pos.y - w->solid->pos.y );
            t = vect_odist( &v ) / w->outfit->u.amm.speed;

            /* Calculate target's movement. */
            vect_cset( &v, v.x + t*(p->solid->vel.x - w->solid->vel.x),
                  v.y + t*(p->solid->vel.y - w->solid->vel.y) );

            /* Get the angle now. */
            diff = angle_diff(w->solid->dir, VANGLE(v) );
         }
         /* Other seekers are stupid. */
         else {
            diff = angle_diff(w->solid->dir, /* Get angle to target pos */
                  vect_angle(&w->solid->pos, &p->solid->pos));
         }

         /* Set turn. */
         turn_max = w->outfit->u.amm.turn * (1. - w->jam_power);
         weapon_setTurn( w, CLAMP( -turn_max, turn_max,
                  10 * diff * w->outfit->u.amm.turn ));
         break;

      case WEAPON_STATUS_JAMMED: /* Continue doing whatever */
         /* Do nothing, dir_vel should be set already if needed */
         break;

      default:
         WARN("Unknown weapon status for '%s'", w->outfit->name);
         break;
   }

   /* Limit speed here */
   w->real_vel = MIN( w->outfit->u.amm.speed, w->real_vel + w->outfit->u.amm.thrust*dt );
   vect_pset( &w->solid->vel, (1. - w->jam_power) * w->real_vel, w->solid->dir );

   /* Modulate max speed. */
   //w->solid->speed_max = w->outfit->u.amm.speed * (1. - w->jam_power);
}


/**
 * @brief The pseudo-ai of the beam weapons.
 *
 *    @param w Weapon to do the thinking.
 *    @param dt Current delta tick.
 */
static void think_beam( Weapon* w, const double dt )
{
   (void)dt;
   Pilot *p, *t;
   double diff;
   Vector2d v;

   /* Get pilot, if pilot is dead beam is destroyed. */
   p = pilot_get(w->parent);
   if (p==NULL) {
      w->timer = -1.; /* Hack to make it get destroyed next update. */
      return;
   }

   /* Check if pilot has enough energy left to keep beam active. */
   p->energy -= dt*w->outfit->u.bem.energy;
   if (p->energy < 0.) {
      p->energy = 0.;
      w->timer = -1;
      return;
   }

   /* Use mount position. */
   pilot_getMount( p, w->mount, &v );
   w->solid->pos.x = p->solid->pos.x + v.x;
   w->solid->pos.y = p->solid->pos.y + v.y;

   /* Handle aiming. */
   switch (w->outfit->type) {
      case OUTFIT_TYPE_BEAM:
         w->solid->dir = p->solid->dir;
         break;

      case OUTFIT_TYPE_TURRET_BEAM:
         /* Get target, if target is dead beam stops moving. */
         t = pilot_get(w->target);
         if (t==NULL) {
            weapon_setTurn( w, 0. );
            return;
         }

         if (w->target == w->parent) /* Invalid target, tries to follow shooter. */
            diff = angle_diff(w->solid->dir, p->solid->dir);
         else
            diff = angle_diff(w->solid->dir, /* Get angle to target pos */
                  vect_angle(&w->solid->pos, &t->solid->pos));
         weapon_setTurn( w, CLAMP( -w->outfit->u.bem.turn, w->outfit->u.bem.turn,
                  10 * diff *  w->outfit->u.bem.turn ));
         break;

      default:
         return;
   }
}


/**
 * @brief Updates all the weapon layers.
 *
 *    @param dt Current delta tick.
 */
void weapons_update( const double dt )
{
   weapons_updateLayer(dt,WEAPON_LAYER_BG);
   weapons_updateLayer(dt,WEAPON_LAYER_FG);
}


/**
 * @brief Updates all the weapons in the layer.
 *
 *    @param dt Current delta tick.
 *    @param layer Layer to update.
 */
static void weapons_updateLayer( const double dt, const WeaponLayer layer )
{
   Weapon **wlayer;
   int *nlayer;
   Weapon *w;
   int i, j, k;
   int spfx;
   int s;
   Pilot *p;
   Outfit *o;

   /* Choose layer. */
   switch (layer) {
      case WEAPON_LAYER_BG:
         wlayer = wbackLayer;
         nlayer = &nwbackLayer;
         break;
      case WEAPON_LAYER_FG:
         wlayer = wfrontLayer;
         nlayer = &nwfrontLayer;
         break;

      default:
         WARN("Unknown weapon layer!");
         return;
   }

   /** @TODO optimize me plz. */
   /* Reset jam power. */
   for (k=0; k < *nlayer; k++) {
      w = wlayer[k];
      if (!outfit_isSeeker( w->outfit ))
         continue;
      w->jam_power = 0.;
   }
   /* Iterate over all pilots. */
   for (i=0; i<pilot_nstack; i++) {
      p = pilot_stack[i];

      /* Must be jamming. */
      if (!p->jamming)
         continue;

      /* Iterate over outfits to find jammers. */
      for (j=0; j<p->noutfits; j++) {
         o    = p->outfits[j]->outfit;
         if (o==NULL)
            continue;
         /* Must be on. */
         if (p->outfits[j]->state != PILOT_OUTFIT_ON)
            continue;
         /* Must be a jammer. */
         if (!outfit_isJammer(o))
            continue;
    
         /* Apply jamming. */
         for (k=0; k < *nlayer; k++) {
            w = wlayer[k];
            if (!outfit_isSeeker( w->outfit ))
               continue;

            /* Must be in range. */
            if (o->u.jam.range2 < vect_dist2( &w->solid->pos, &p->solid->pos ))
               continue;

            /* We only consider the strongest jammer. */
            w->jam_power = CLAMP( 0., 1., MAX( w->jam_power, (o->u.jam.power - w->outfit->u.amm.resist) ) );
         }
      }
   }

   i = 0;
   while (i < *nlayer) {
      w = wlayer[i];

      switch (w->outfit->type) {

         /* most missiles behave the same */
         case OUTFIT_TYPE_AMMO:

            w->timer -= dt;
            if (w->timer < 0.) {
               spfx = -1;
               /* See if we need armour death sprite. */
               if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_BLOWUP_ARMOUR))
                  spfx = outfit_spfxArmour(w->outfit);
               /* See if we need shield death sprite. */
               else if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_BLOWUP_SHIELD))
                  spfx = outfit_spfxShield(w->outfit);
               /* Add death sprite if needed. */
               if (spfx != -1) {
                  spfx_add( spfx, w->solid->pos.x, w->solid->pos.y,
                        w->solid->vel.x, w->solid->vel.y,
                        SPFX_LAYER_BACK ); /* presume back. */
                  /* Add sound if explodes and has it. */
                  s = outfit_soundHit(w->outfit);
                  if (s != -1)
                     w->voice = sound_playPos(s,
                           w->solid->pos.x,
                           w->solid->pos.y,
                           w->solid->vel.x,
                           w->solid->vel.y);
               }
               weapon_destroy(w,layer);
               break;
            }
            break;

         case OUTFIT_TYPE_BOLT:
         case OUTFIT_TYPE_TURRET_BOLT:
            w->timer -= dt;
            if (w->timer < 0.) {
               spfx = -1;
               /* See if we need armour death sprite. */
               if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_BLOWUP_ARMOUR))
                  spfx = outfit_spfxArmour(w->outfit);
               /* See if we need shield death sprite. */
               else if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_BLOWUP_SHIELD))
                  spfx = outfit_spfxShield(w->outfit);
               /* Add death sprite if needed. */
               if (spfx != -1) {
                  spfx_add( spfx, w->solid->pos.x, w->solid->pos.y,
                        w->solid->vel.x, w->solid->vel.y,
                        SPFX_LAYER_BACK ); /* presume back. */
                  /* Add sound if explodes and has it. */
                  s = outfit_soundHit(w->outfit);
                  if (s != -1)
                     w->voice = sound_playPos(s,
                           w->solid->pos.x,
                           w->solid->pos.y,
                           w->solid->vel.x,
                           w->solid->vel.y);
               }
               weapon_destroy(w,layer);
               break;
            }
            else if (w->timer < w->falloff)
               w->strength = w->timer / w->falloff;
            break;

         /* Beam weapons handled a part. */
         case OUTFIT_TYPE_BEAM:
         case OUTFIT_TYPE_TURRET_BEAM:
            w->timer -= dt;
            if (w->timer < 0. || (w->outfit->u.bem.min_duration > 0. &&
                  w->mount->stimer < 0.)) {
               p = pilot_get(w->parent);
               if (p != NULL)
                  pilot_stopBeam(p, w->mount);
               weapon_destroy(w,layer);
               break;
            }
            /* We use the explosion timer to tell when we have to create explosions. */
            w->exp_timer -= dt;
            if (w->exp_timer < 0.) {
               if (w->exp_timer < -1.)
                  w->exp_timer = 0.100;
               else
                  w->exp_timer = -1.;
            }
            break;
         default:
            WARN("Weapon of type '%s' has no update implemented yet!",
                  w->outfit->name);
            break;
      }

      /* Out of bounds, loop is over. */
      if (i >= *nlayer)
         break;

      /* Only increment if weapon wasn't deleted. */
      if (w == wlayer[i]) {
         weapon_update(w,dt,layer);
         if ((i < *nlayer) && (w == wlayer[i]))
            i++;
      }
   }
}


/**
 * @brief Renders all the weapons in a layer.
 *
 *    @param layer Layer to render.
 *    @param dt Current delta tick.
 */
void weapons_render( const WeaponLayer layer, const double dt )
{
   Weapon** wlayer;
   int* nlayer;
   int i;

   switch (layer) {
      case WEAPON_LAYER_BG:
         wlayer = wbackLayer;
         nlayer = &nwbackLayer;
         break;
      case WEAPON_LAYER_FG:
         wlayer = wfrontLayer;
         nlayer = &nwfrontLayer;
         break;

      default:
         WARN("Unknown weapon layer!");
         return;
   }

   for (i=0; i<(*nlayer); i++)
      weapon_render( wlayer[i], dt );
}


/**
 * @brief Renders an individual weapon.
 *
 *    @param w Weapon to render.
 *    @param dt Current delta tick.
 */
static void weapon_render( Weapon* w, const double dt )
{
   double x,y, cx,cy, gx,gy;
   glTexture *gfx;
   double z;
   glColour c = { .r=1., .g=1., .b=1. };

   switch (w->outfit->type) {
      /* Weapons that use sprites. */
      case OUTFIT_TYPE_AMMO:
      case OUTFIT_TYPE_BOLT:
      case OUTFIT_TYPE_TURRET_BOLT:
         gfx = outfit_gfx(w->outfit);

         /* Alpha based on strength. */
         c.a = w->strength;

         /* Outfit spins around. */
         if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_SPIN)) {
            /* Check timer. */
            w->anim -= dt;
            if (w->anim < 0.) {
               w->anim = outfit_spin(w->outfit);

               /* Increment sprite. */
               w->sprite++;
               if (w->sprite >= gfx->sx*gfx->sy)
                  w->sprite = 0;
            }

            /* Render. */
            if (outfit_isBolt(w->outfit) && w->outfit->u.blt.gfx_end)
               gl_blitSpriteInterpolate( gfx, w->outfit->u.blt.gfx_end,
                     w->timer / w->life,
                     w->solid->pos.x, w->solid->pos.y,
                     w->sprite % (int)gfx->sx, w->sprite / (int)gfx->sx, &c );
            else
               gl_blitSprite( gfx, w->solid->pos.x, w->solid->pos.y,
                     w->sprite % (int)gfx->sx, w->sprite / (int)gfx->sx, &c );
         }
         /* Outfit faces direction. */
         else {
            if (outfit_isBolt(w->outfit) && w->outfit->u.blt.gfx_end)
               gl_blitSpriteInterpolate( gfx, w->outfit->u.blt.gfx_end,
                     w->timer / w->life,
                     w->solid->pos.x, w->solid->pos.y, w->sx, w->sy, &c );
            else
               gl_blitSprite( gfx, w->solid->pos.x, w->solid->pos.y, w->sx, w->sy, &c );
         }
         break;

      /* Beam weapons. */
      case OUTFIT_TYPE_BEAM:
      case OUTFIT_TYPE_TURRET_BEAM:
         gfx = outfit_gfx(w->outfit);

         /* Zoom. */
         z = cam_getZoom();

         /* Position. */
         cam_getPos( &cx, &cy );
         gui_getOffset( &gx, &gy );
         x = (w->solid->pos.x - cx)*z + gx;
         y = (w->solid->pos.y - cy)*z + gy;

         /* Set up the matrix. */
         glPushMatrix();
            glTranslated( SCREEN_W/2.+x, SCREEN_H/2.+y, 0. );
            glRotated( 270. + w->solid->dir / M_PI * 180., 0., 0., 1. );

         /* Preparatives. */
         glEnable(GL_TEXTURE_2D);
         glBindTexture( GL_TEXTURE_2D, gfx->texture);
         glShadeModel(GL_SMOOTH);

         /* Actual rendering. */
         glBegin(GL_QUAD_STRIP);

            /* Start faded. */
            ACOLOUR(cWhite, 0.);

            glTexCoord2d( w->anim, 0. );
            glVertex2d( -gfx->sh/2.*z, 0. );

            glTexCoord2d( w->anim, 1. );
            glVertex2d( +gfx->sh/2.*z, 0. );

            /* Full strength. */
            COLOUR(cWhite);

            glTexCoord2d( w->anim + 10. / gfx->sw, 0. );
            glVertex2d( -gfx->sh/2.*z, 10.*z );

            glTexCoord2d( w->anim + 10. / gfx->sw, 1. );
            glVertex2d( +gfx->sh/2.*z, 10.*z );

            glTexCoord2d( w->anim + 0.8*w->outfit->u.bem.range / gfx->sw, 0. );
            glVertex2d( -gfx->sh/2.*z, 0.8*w->outfit->u.bem.range*z );

            glTexCoord2d( w->anim + 0.8*w->outfit->u.bem.range / gfx->sw, 1. );
            glVertex2d( +gfx->sh/2.*z, 0.8*w->outfit->u.bem.range*z );

            /* Fades out. */
            ACOLOUR(cWhite, 0.);

            glTexCoord2d( w->anim + w->outfit->u.bem.range / gfx->sw, 0. );
            glVertex2d( -gfx->sh/2.*z, w->outfit->u.bem.range*z );

            glTexCoord2d( w->anim + w->outfit->u.bem.range / gfx->sw, 1. );
            glVertex2d( +gfx->sh/2.*z, w->outfit->u.bem.range*z );
         glEnd(); /* GL_QUAD_STRIP */

         /* Do the beam movement. */
         w->anim -= 5. * dt;
         if (w->anim <= -gfx->sw)
            w->anim += gfx->sw;

         /* Clean up. */
         glDisable(GL_TEXTURE_2D);
         glShadeModel(GL_FLAT);
         glPopMatrix();
         gl_checkErr();
         break;

      default:
         WARN("Weapon of type '%s' has no render implemented yet!",
               w->outfit->name);
         break;
   }
}


/**
 * @brief Checks to see if the weapon can hit the pilot.
 *
 *    @param w Weapon to check if hits pilot.
 *    @param p Pilot to check if is hit by weapon.
 *    @return 1 if can be hit, 0 if can't.
 */
static int weapon_checkCanHit( Weapon* w, Pilot *p )
{
   Pilot *parent;

   /* Can't hit invincible stuff. */
   if (pilot_isFlag(p, PILOT_INVINCIBLE))
      return 0;

   /* Can't hit invisible stuff. */
   if (pilot_isFlag(p, PILOT_INVISIBLE))
      return 0;

   /* Can never hit same faction. */
   if (p->faction == w->faction)
      return 0;

   /* Must not be landing nor taking off. */
   if (pilot_isFlag(p, PILOT_LANDING) ||
         pilot_isFlag(p, PILOT_TAKEOFF))
      return 0;

   /* Go "through" dead pilots. */
   if (pilot_isFlag(p, PILOT_DEAD))
      return 0;

   /* Player can not hit special pilots. */
   if ((w->faction == FACTION_PLAYER) &&
         pilot_isFlag(p, PILOT_INVINC_PLAYER))
      return 0;

   /* Always hit target. */
   if (w->target == p->id)
      return 1;

   /* Player behaves differently. */
   if (w->faction == FACTION_PLAYER) {

      /* Always hit hostiles. */
      if (pilot_isFlag(p, PILOT_HOSTILE))
         return 1;

      /* Always hit unbribed enemies. */
      else if (!pilot_isFlag(p, PILOT_BRIBED) &&
            areEnemies(w->faction, p->faction))
        return 1;

      /* Miss rest - can be neutral/ally. */
      else
         return 0;
   }

   /* Let hostiles hit player. */
   if (p->faction == FACTION_PLAYER) {
      parent = pilot_get(w->parent);
      if (parent != NULL) {
         if (pilot_isFlag(parent, PILOT_BRIBED))
            return 0;
         if (pilot_isFlag(parent, PILOT_HOSTILE))
            return 1;
      }
   }

   /* Hit non-allies. */
   if (areEnemies(w->faction, p->faction))
      return 1;

   return 0;
}


/**
 * @brief Updates an individual weapon.
 *
 *    @param w Weapon to update.
 *    @param dt Current delta tick.
 *    @param layer Layer to which the weapon belongs.
 */
static void weapon_update( Weapon* w, const double dt, WeaponLayer layer )
{
   int i, b, psx,psy;
   glTexture *gfx;
   Vector2d crash[2];
   Pilot *p;

   /* Get the sprite direction to speed up calculations. */
   b     = outfit_isBeam(w->outfit);
   if (!b) {
      gfx = outfit_gfx(w->outfit);
      gl_getSpriteFromDir( &w->sx, &w->sy, gfx, w->solid->dir );
   }
   else
      gfx = NULL;

   for (i=0; i<pilot_nstack; i++) {
      p = pilot_stack[i];

      psx = pilot_stack[i]->tsx;
      psy = pilot_stack[i]->tsy;

      if (w->parent == pilot_stack[i]->id) continue; /* pilot is self */

      /* Beam weapons have special collisions. */
      if (b) {
         /* Check for collision. */
         if (weapon_checkCanHit(w,p) &&
               CollideLineSprite( &w->solid->pos, w->solid->dir,
                     w->outfit->u.bem.range,
                     p->ship->gfx_space, psx, psy,
                     &p->solid->pos,
                     crash)) {
            weapon_hitBeam( w, p, layer, crash, dt );
            /* No return because beam can still think, it's not
             * destroyed like the other weapons.*/
         }
      }
      /* smart weapons only collide with their target */
      else if (weapon_isSmart(w)) {

         if ((pilot_stack[i]->id == w->target) &&
               (w->status == WEAPON_STATUS_OK) &&
               weapon_checkCanHit(w,p) &&
               CollideSprite( gfx, w->sx, w->sy, &w->solid->pos,
                     p->ship->gfx_space, psx, psy,
                     &p->solid->pos,
                     &crash[0] )) {
            weapon_hit( w, p, layer, &crash[0] );
            return; /* Weapon is destroyed. */
         }
      }
      /* dumb weapons hit anything not of the same faction */
      else {
         if (weapon_checkCanHit(w,p) &&
               CollideSprite( gfx, w->sx, w->sy, &w->solid->pos,
                     p->ship->gfx_space, psx, psy,
                     &p->solid->pos,
                     &crash[0] )) {
            weapon_hit( w, p, layer, &crash[0] );
            return; /* Weapon is destroyed. */
         }
      }
   }

   /* smart weapons also get to think their next move */
   if (weapon_isSmart(w))
      (*w->think)(w,dt);

   /* Update the solid position. */
   (*w->solid->update)(w->solid, dt);

   /* Update the sound. */
   sound_updatePos(w->voice, w->solid->pos.x, w->solid->pos.y,
         w->solid->vel.x, w->solid->vel.y);
}


/**
 * @brief Informs the AI if needed that it's been hit.
 *
 *    @param p Pilot being hit.
 *    @param shooter Pilot that shot.
 *    @param dmg Damage done to p.
 */
static void weapon_hitAI( Pilot *p, Pilot *shooter, double dmg )
{
   int i;
   double d;

   /* Must be a valid shooter. */
   if (shooter == NULL)
      return;

   /* Must not be disabled. */
   if (pilot_isDisabled(p))
      return;

   /* Player is handled differently. */
   if (shooter->faction == FACTION_PLAYER) {

      /* Increment damage done to by player. */
      p->player_damage += dmg / (p->shield_max + p->armour_max);

      /* If damage is over threshold, inform pilot or if is targeted. */
      if ((p->player_damage > PILOT_HOSTILE_THRESHOLD) ||
            (shooter->target==p->id)) {
         /* Inform attacked. */
         ai_attacked( p, shooter->id, dmg );

         /* Trigger a pseudo-distress that incurs no faction loss. */
         for (i=0; i<pilot_nstack; i++) {
            /* Skip if unsuitable. */
            if ((pilot_stack[i]->ai == NULL) || (pilot_stack[i]->id == p->id) ||
                  (pilot_isFlag(pilot_stack[i], PILOT_DEAD)) ||
                  (pilot_isFlag(pilot_stack[i], PILOT_DELETE)))
               continue;

            /*
             * Pilots within a radius of 1500 (in a zero-interference system)
             * will immediately notice hostile actions.
             */
            d = vect_dist2( &p->solid->pos, &pilot_stack[i]->solid->pos );
            if (d > (pilot_sensorRange() * 0.04 )) /* 0.2^2 */
               continue;

            /* Send AI the distress signal. */
            ai_getDistress( pilot_stack[i], p, shooter );
         }

         /* Set as hostile. */
         pilot_setHostile(p);
         pilot_rmFlag( p, PILOT_BRIBED );
         pilot_rmFlag( p, PILOT_FRIENDLY );
      }
   }
   /* Otherwise just inform of being attacked. */
   else
      ai_attacked( p, shooter->id, dmg );
}


/**
 * @brief Weapon hit the pilot.
 *
 *    @param w Weapon involved in the collision.
 *    @param p Pilot that got hit.
 *    @param layer Layer to which the weapon belongs.
 *    @param pos Position of the hit.
 */
static void weapon_hit( Weapon* w, Pilot* p, WeaponLayer layer, Vector2d* pos )
{
   Pilot *parent;
   int s, spfx;
   double damage;
   WeaponLayer spfx_layer;
   Damage dmg;
   const Damage *odmg;

   /* Get general details. */
   odmg              = outfit_damage( w->outfit );
   parent            = pilot_get( w->parent );
   dmg.damage        = MAX( 0., w->dam_mod * w->strength * odmg->damage );
   dmg.penetration   = odmg->penetration;
   dmg.type          = odmg->type;
   dmg.disable       = odmg->disable;

   /* Play sound if they have it. */
   s = outfit_soundHit(w->outfit);
   if (s != -1)
      w->voice = sound_playPos( s,
            w->solid->pos.x,
            w->solid->pos.y,
            w->solid->vel.x,
            w->solid->vel.y);

   /* Have pilot take damage and get real damage done. */
   damage = pilot_hit( p, w->solid, w->parent, &dmg, 1 );

   /* Get the layer. */
   spfx_layer = (p==player.p) ? SPFX_LAYER_FRONT : SPFX_LAYER_BACK;
   /* Choose spfx. */
   if (p->shield > 0.)
      spfx = outfit_spfxShield(w->outfit);
   else
      spfx = outfit_spfxArmour(w->outfit);
   /* Add sprite, layer depends on whether player shot or not. */
   spfx_add( spfx, pos->x, pos->y,
         VX(p->solid->vel), VY(p->solid->vel), spfx_layer );

   /* Inform AI that it's been hit. */
   weapon_hitAI( p, parent, damage );

   /* no need for the weapon particle anymore */
   weapon_destroy(w,layer);
}


/**
 * @brief Weapon hit the pilot.
 *
 *    @param w Weapon involved in the collision.
 *    @param p Pilot that got hit.
 *    @param layer Layer to which the weapon belongs.
 *    @param pos Position of the hit.
 *    @param dt Current delta tick.
 */
static void weapon_hitBeam( Weapon* w, Pilot* p, WeaponLayer layer,
      Vector2d pos[2], const double dt )
{
   (void) layer;
   Pilot *parent;
   int spfx;
   double damage;
   WeaponLayer spfx_layer;
   Damage dmg;
   const Damage *odmg;

   /* Get general details. */
   odmg              = outfit_damage( w->outfit );
   parent            = pilot_get( w->parent );
   dmg.damage        = MAX( 0., w->dam_mod * w->strength * odmg->damage * dt );
   dmg.penetration   = odmg->penetration;
   dmg.type          = odmg->type;
   dmg.disable       = odmg->disable * dt;

   /* Have pilot take damage and get real damage done. */
   damage = pilot_hit( p, w->solid, w->parent, &dmg, 1 );

   /* Add sprite, layer depends on whether player shot or not. */
   if (w->exp_timer == -1.) {
      /* Get the layer. */
      spfx_layer = (p==player.p) ? SPFX_LAYER_FRONT : SPFX_LAYER_BACK;

      /* Choose spfx. */
      if (p->shield > 0.)
         spfx = outfit_spfxShield(w->outfit);
      else
         spfx = outfit_spfxArmour(w->outfit);

      /* Add graphic. */
      spfx_add( spfx, pos[0].x, pos[0].y,
            VX(p->solid->vel), VY(p->solid->vel), spfx_layer );
      spfx_add( spfx, pos[1].x, pos[1].y,
            VX(p->solid->vel), VY(p->solid->vel), spfx_layer );
         w->exp_timer = -2;

      /* Inform AI that it's been hit, to not saturate ai Lua with messages. */
      weapon_hitAI( p, parent, damage );
   }
}


/**
 * @brief Gets the aim position of a turret weapon.
 *
 *    @param outfit Weapon outfit.
 *    @param parent Parent of the weapon.
 *    @param pilot_target Target of the weapon.
 *    @param pos Position of the turret.
 *    @param vel Velocity of the turret.
 *    @param dir Direction facing parent ship and turret.
 *    @param time Expected flight time.
 */
static double weapon_aimTurret( const Outfit *outfit, const Pilot *parent,
      const Pilot *pilot_target, const Vector2d *pos, const Vector2d *vel, double dir,
      double swivel, double time )
{
   Vector2d relative_location;
   double rdir, lead_angle;
   double x, y, t;
   double off;

   if (pilot_target == NULL)
      rdir        = dir;
   else {
      /* Get the vector : shooter -> target*/
      vect_cset( &relative_location, VX(pilot_target->solid->pos) - VX(parent->solid->pos),
            VY(pilot_target->solid->pos) - VY(parent->solid->pos) );

         /* Try to predict where the enemy will be. */
      t = time;
      if (t == INFINITY)  /*Postprocess (t = INFINITY means target is not hittable)*/
         t = 0.;

      /* Position is calculated on where it should be */
      x = (pilot_target->solid->pos.x + pilot_target->solid->vel.x*t)
         - (pos->x + vel->x*t);
      y = (pilot_target->solid->pos.y + pilot_target->solid->vel.y*t)
         - (pos->y + vel->y*t);

      /* Set angle to face. */
      rdir = ANGLE(x, y);

      /* Lead angle is determined from ewarfare. */
      lead_angle = M_PI*pilot_ewWeaponTrack( parent, pilot_target, outfit->u.blt.track );

      /*only do this if the lead angle is implemented; save compute cycled on fixed weapons*/
      if (lead_angle && fabs( angle_diff(ANGLE(x, y), VANGLE(relative_location)) ) > lead_angle) {
         /* the target is moving too fast for the turret to keep up */
         if (ANGLE(x, y) < VANGLE(relative_location))
            rdir = angle_diff(lead_angle, VANGLE(relative_location));
         else
            rdir = angle_diff(-1*lead_angle, VANGLE(relative_location));
      }

      /* Calculate bounds. */
      off = angle_diff( rdir, dir );
      if (fabs(off) > swivel) {
         if (off > 0.)
            rdir = dir - swivel;
         else
            rdir = dir + swivel;
      }
   }

   return rdir;
}



/**
 * @brief Creates the bolt specific properties of a weapon.
 *
 *    @param w Weapon to create bolt specific properties of.
 *    @param outfit Outfit which spawned the weapon.
 *    @param T temperature of the shooter.
 *    @param dir Direction the shooter is facing.
 *    @param pos Position of the shooter.
 *    @param vel Velocity of the shooter.
 *    @param parent Shooter.
 *    @param time Expected flight time.
 */
static void weapon_createBolt( Weapon *w, const Outfit* outfit, double T,
      const double dir, const Vector2d* pos, const Vector2d* vel, const Pilot* parent, double time )
{
   Vector2d v;
   double mass, rdir;
   Pilot *pilot_target;
   double acc;
   glTexture *gfx;

   /* Only difference is the direction of fire */
   if ((w->parent!=w->target) && (w->target != 0)) { /* Must have valid target */
      pilot_target = pilot_get(w->target);
      rdir = weapon_aimTurret( outfit, parent, pilot_target, pos, vel, dir, outfit->u.blt.swivel, time );
   }
   else /* fire straight */
      rdir = dir;

   /* Calculate accuracy. */
   acc =  HEAT_WORST_ACCURACY * pilot_heatAccuracyMod( T );

   /* Stat modifiers. */
   if (outfit->type == OUTFIT_TYPE_TURRET_BOLT)
      w->dam_mod *= parent->stats.tur_damage;
   else
      w->dam_mod *= parent->stats.fwd_damage;

   /* Calculate direction. */
   rdir += RNG_2SIGMA() * acc;
   if (rdir < 0.)
      rdir += 2.*M_PI;
   else if (rdir >= 2.*M_PI)
      rdir -= 2.*M_PI;

   mass = 1; /* Lasers are presumed to have unitary mass */
   v = *vel;
   vect_cadd( &v, outfit->u.blt.speed*cos(rdir), outfit->u.blt.speed*sin(rdir));
   w->timer = outfit->u.blt.range / outfit->u.blt.speed;
   w->falloff = w->timer - outfit->u.blt.falloff / outfit->u.blt.speed;
   w->solid = solid_create( mass, rdir, pos, &v, SOLID_UPDATE_EULER );
   w->voice = sound_playPos( w->outfit->u.blt.sound,
         w->solid->pos.x,
         w->solid->pos.y,
         w->solid->vel.x,
         w->solid->vel.y);

   /* Set facing direction. */
   gfx = outfit_gfx( w->outfit );
   gl_getSpriteFromDir( &w->sx, &w->sy, gfx, w->solid->dir );
}


/**
 * @brief Creates the ammo specific properties of a weapon.
 *
 *    @param w Weapon to create ammo specific properties of.
 *    @param launcher Outfit which spawned the weapon.
 *    @param T temperature of the shooter.
 *    @param dir Direction the shooter is facing.
 *    @param pos Position of the shooter.
 *    @param vel Velocity of the shooter.
 *    @param parent Shooter.
 *    @param time Expected flight time.
 */
static void weapon_createAmmo( Weapon *w, const Outfit* launcher, double T,
      const double dir, const Vector2d* pos, const Vector2d* vel, const Pilot* parent, double time )
{
   (void) T;
   Vector2d v;
   double mass, rdir;
   Pilot *pilot_target;
   glTexture *gfx;
   Outfit* ammo;

   pilot_target = NULL;
   ammo = launcher->u.lau.ammo;
   if (w->outfit->type == OUTFIT_TYPE_AMMO &&
            launcher->type == OUTFIT_TYPE_TURRET_LAUNCHER) {
      pilot_target = pilot_get(w->target);
      rdir = weapon_aimTurret( ammo, parent, pilot_target, pos, vel, dir, M_PI, time );
   }
   else
      rdir = dir;

   /*if (ammo->u.amm.accuracy != 0.) {
      rdir += RNG_2SIGMA() * ammo->u.amm.accuracy/2. * 1./180.*M_PI;
      if ((rdir > 2.*M_PI) || (rdir < 0.))
         rdir = fmod(rdir, 2.*M_PI);
   }*/
   if (rdir < 0.)
      rdir += 2.*M_PI;
   else if (rdir >= 2.*M_PI)
      rdir -= 2.*M_PI;

   /* If thrust is 0. we assume it starts out at speed. */
   v = *vel;
   if (ammo->u.amm.thrust == 0.)
      vect_cadd( &v, cos(rdir) * w->outfit->u.amm.speed,
            sin(rdir) * w->outfit->u.amm.speed );
   w->real_vel = VMOD(v);

   /* Set up ammo details. */
   mass        = w->outfit->mass;
   w->timer    = ammo->u.amm.duration;
   w->solid    = solid_create( mass, rdir, pos, &v, SOLID_UPDATE_RK4 );
   if (w->outfit->u.amm.thrust != 0.) {
      weapon_setThrust( w, w->outfit->u.amm.thrust * mass );
      w->solid->speed_max = w->outfit->u.amm.speed; /* Limit speed, we only care if it has thrust. */
   }

   /* Handle seekers. */
   if (w->outfit->u.amm.ai != AMMO_AI_DUMB) {
      w->think = think_seeker; /* AI is the same atm. */

      /* If they are seeking a pilot, increment lockon counter. */
      if (pilot_target == NULL)
         pilot_target = pilot_get(w->target);
      if (pilot_target != NULL)
         pilot_target->lockons++;
   }

   /* Play sound. */
   w->voice    = sound_playPos(w->outfit->u.amm.sound,
         w->solid->pos.x,
         w->solid->pos.y,
         w->solid->vel.x,
         w->solid->vel.y);

   /* Set facing direction. */
   gfx = outfit_gfx( w->outfit );
   gl_getSpriteFromDir( &w->sx, &w->sy, gfx, w->solid->dir );
}


/**
 * @brief Creates a new weapon.
 *
 *    @param outfit Outfit which spawned the weapon.
 *    @param T temperature of the shooter.
 *    @param dir Direction the shooter is facing.
 *    @param pos Position of the shooter.
 *    @param vel Velocity of the shooter.
 *    @param parent Shooter.
 *    @param target Target ID of the shooter.
 *    @param time Expected flight time.
 *    @return A pointer to the newly created weapon.
 */
static Weapon* weapon_create( const Outfit* outfit, double T,
      const double dir, const Vector2d* pos, const Vector2d* vel,
      const Pilot* parent, const unsigned int target, double time )
{
   double mass, rdir;
   Pilot *pilot_target;
   Weapon* w;

   /* Create basic features */
   w           = calloc( 1, sizeof(Weapon) );
   w->dam_mod  = 1.; /* Default of 100% damage. */
   w->faction  = parent->faction; /* non-changeable */
   w->parent   = parent->id; /* non-changeable */
   w->target   = target; /* non-changeable */
   if (outfit_isLauncher(outfit))
      w->outfit   = outfit->u.lau.ammo; /* non-changeable */
   else
      w->outfit   = outfit; /* non-changeable */
   w->update   = weapon_update;
   w->status   = WEAPON_STATUS_OK;
   w->strength = 1.;

   switch (outfit->type) {

      /* Bolts treated together */
      case OUTFIT_TYPE_BOLT:
      case OUTFIT_TYPE_TURRET_BOLT:
         weapon_createBolt( w, outfit, T, dir, pos, vel, parent, time );
         break;

      /* Beam weapons are treated together. */
      case OUTFIT_TYPE_BEAM:
      case OUTFIT_TYPE_TURRET_BEAM:
         if ((outfit->type == OUTFIT_TYPE_TURRET_BEAM) && (w->parent!=w->target)) {
            pilot_target = pilot_get(target);
            rdir = (pilot_target == NULL) ? dir :
                  vect_angle(pos, &pilot_target->solid->pos);
         }
         else
            rdir = dir;
         if (rdir < 0.)
            rdir += 2.*M_PI;
         else if (rdir >= 2.*M_PI)
            rdir -= 2.*M_PI;
         mass = 1.; /**< Needs a mass. */
         w->solid = solid_create( mass, rdir, pos, vel, SOLID_UPDATE_EULER );
         w->think = think_beam;
         w->timer = outfit->u.bem.duration;
         w->voice = sound_playPos( w->outfit->u.bem.sound,
               w->solid->pos.x,
               w->solid->pos.y,
               w->solid->vel.x,
               w->solid->vel.y);
         break;

      /* Treat seekers together. */
      case OUTFIT_TYPE_LAUNCHER:
      case OUTFIT_TYPE_TURRET_LAUNCHER:
         weapon_createAmmo( w, outfit, T, dir, pos, vel, parent, time );
         break;

      /* just dump it where the player is */
      default:
         WARN("Weapon of type '%s' has no create implemented yet!",
               w->outfit->name);
         w->solid = solid_create( 1., dir, pos, vel, SOLID_UPDATE_EULER );
         break;
   }

   /* Set life to timer. */
   w->life = w->timer;

   return w;
}


/**
 * @brief Creates a new weapon.
 *
 *    @param outfit Outfit which spawns the weapon.
 *    @param T Temperature of the shooter.
 *    @param dir Direction of the shooter.
 *    @param pos Position of the shooter.
 *    @param vel Velocity of the shooter.
 *    @param parent Pilot ID of the shooter.
 *    @param target Target ID that is getting shot.
 *    @param time Expected flight time.
 */
void weapon_add( const Outfit* outfit, const double T, const double dir,
      const Vector2d* pos, const Vector2d* vel,
      const Pilot *parent, unsigned int target, double time )
{
   WeaponLayer layer;
   Weapon *w;
   Weapon **curLayer;
   int *mLayer, *nLayer;
   GLsizei size;

   if (!outfit_isBolt(outfit) &&
         !outfit_isLauncher(outfit)) {
      ERR("Trying to create a Weapon from a non-Weapon type Outfit");
      return;
   }

   layer = (parent->id==PLAYER_ID) ? WEAPON_LAYER_FG : WEAPON_LAYER_BG;
   w     = weapon_create( outfit, T, dir, pos, vel, parent, target, time );

   /* set the proper layer */
   switch (layer) {
      case WEAPON_LAYER_BG:
         curLayer = wbackLayer;
         nLayer = &nwbackLayer;
         mLayer = &mwbacklayer;
         break;
      case WEAPON_LAYER_FG:
         curLayer = wfrontLayer;
         nLayer = &nwfrontLayer;
         mLayer = &mwfrontLayer;
         break;

      default:
         WARN("Unknown weapon layer!");
   }

   if (*mLayer > *nLayer) /* more memory alloced than needed */
      curLayer[(*nLayer)++] = w;
   else { /* need to allocate more memory */
      if ((*mLayer) == 0)
         (*mLayer) = WEAPON_CHUNK_MIN;
      else
         (*mLayer) += MIN( (*mLayer), WEAPON_CHUNK_MAX );

      switch (layer) {
         case WEAPON_LAYER_BG:
            curLayer   = wbackLayer = realloc(curLayer, (*mLayer)*sizeof(Weapon*));
            break;
         case WEAPON_LAYER_FG:
            curLayer   = wfrontLayer = realloc(curLayer, (*mLayer)*sizeof(Weapon*));
            break;
      }
      curLayer[(*nLayer)++] = w;

      /* Grow the vertex stuff. */
      weapon_vboSize = mwfrontLayer + mwbacklayer;
      size = sizeof(GLfloat) * (2+4) * weapon_vboSize;
      weapon_vboData = realloc( weapon_vboData, size );
      if (weapon_vbo == NULL)
         weapon_vbo = gl_vboCreateStream( size, NULL );
      gl_vboData( weapon_vbo, size, weapon_vboData );
   }
}


/**
 * @brief Starts a beam weapon.
 *
 *    @param outfit Outfit which spawns the weapon.
 *    @param dir Direction of the shooter.
 *    @param pos Position of the shooter.
 *    @param vel Velocity of the shooter.
 *    @param parent Pilot shooter.
 *    @param target Target ID that is getting shot.
 *    @param mount Mount on the ship.
 *    @return The identifier of the beam weapon.
 *
 * @sa beam_end
 */
unsigned int beam_start( const Outfit* outfit,
      const double dir, const Vector2d* pos, const Vector2d* vel,
      const Pilot *parent, const unsigned int target,
      PilotOutfitSlot *mount )
{
   WeaponLayer layer;
   Weapon *w;
   Weapon **curLayer;
   int *mLayer, *nLayer;
   GLsizei size;

   if (!outfit_isBeam(outfit)) {
      ERR("Trying to create a Beam Weapon from a non-beam outfit.");
      return -1;
   }

   layer = (parent->id==PLAYER_ID) ? WEAPON_LAYER_FG : WEAPON_LAYER_BG;
   w = weapon_create( outfit, 0., dir, pos, vel, parent, target, 0. );
   w->ID = ++beam_idgen;
   w->mount = mount;
   w->exp_timer = 0.;

   /* set the proper layer */
   switch (layer) {
      case WEAPON_LAYER_BG:
         curLayer = wbackLayer;
         nLayer = &nwbackLayer;
         mLayer = &mwbacklayer;
         break;
      case WEAPON_LAYER_FG:
         curLayer = wfrontLayer;
         nLayer = &nwfrontLayer;
         mLayer = &mwfrontLayer;
         break;

      default:
         ERR("Invalid WEAPON_LAYER specified");
         return -1;
   }

   if (*mLayer > *nLayer) /* more memory alloced than needed */
      curLayer[(*nLayer)++] = w;
   else { /* need to allocate more memory */
      if ((*mLayer) == 0)
         (*mLayer) = WEAPON_CHUNK_MIN;
      else
         (*mLayer) += MIN( (*mLayer), WEAPON_CHUNK_MAX );

      switch (layer) {
         case WEAPON_LAYER_BG:
            curLayer = wbackLayer = realloc(curLayer, (*mLayer)*sizeof(Weapon*));
            break;
         case WEAPON_LAYER_FG:
            curLayer = wfrontLayer = realloc(curLayer, (*mLayer)*sizeof(Weapon*));
            break;
      }
      curLayer[(*nLayer)++] = w;

      /* Grow the vertex stuff. */
      weapon_vboSize = mwfrontLayer + mwbacklayer;
      size = sizeof(GLfloat) * (2+4) * weapon_vboSize;
      weapon_vboData = realloc( weapon_vboData, size );
      if (weapon_vbo == NULL)
         weapon_vbo = gl_vboCreateStream( size, NULL );
      gl_vboData( weapon_vbo, size, weapon_vboData );
   }

   return w->ID;
}


/**
 * @brief Ends a beam weapon.
 *
 *    @param parent ID of the parent of the beam.
 *    @param beam ID of the beam to destroy.
 */
void beam_end( const unsigned int parent, unsigned int beam )
{
   int i;
   WeaponLayer layer;
   Weapon **curLayer;
   int *nLayer;

   layer = (parent==PLAYER_ID) ? WEAPON_LAYER_FG : WEAPON_LAYER_BG;

   /* set the proper layer */
   switch (layer) {
      case WEAPON_LAYER_BG:
         curLayer = wbackLayer;
         nLayer = &nwbackLayer;
         break;
      case WEAPON_LAYER_FG:
         curLayer = wfrontLayer;
         nLayer = &nwfrontLayer;
         break;

      default:
         ERR("Invalid WEAPON_LAYER specified");
         return;
   }


   /* Now try to destroy the beam. */
   for (i=0; i<*nLayer; i++) {
      if (curLayer[i]->ID == beam) { /* Found it. */
         weapon_destroy(curLayer[i], layer);
         break;
      }
   }
}


/**
 * @brief Destroys a weapon.
 *
 *    @param w Weapon to destroy.
 *    @param layer Layer to which the weapon belongs.
 */
static void weapon_destroy( Weapon* w, WeaponLayer layer )
{
   int i;
   Weapon** wlayer;
   int *nlayer;

   switch (layer) {
      case WEAPON_LAYER_BG:
         wlayer = wbackLayer;
         nlayer = &nwbackLayer;
         break;
      case WEAPON_LAYER_FG:
         wlayer = wfrontLayer;
         nlayer = &nwfrontLayer;
         break;

      default:
         WARN("Unknown weapon layer!");
         return;
   }

   for (i=0; (wlayer[i] != w) && (i < *nlayer); i++); /* get to the current position */
   if (i >= *nlayer) {
      WARN("Trying to destroy weapon not found in stack!");
      return;
   }

   weapon_free(wlayer[i]);
   wlayer[i] = NULL;
   (*nlayer)--;

   for ( ; i < (*nlayer); i++)
      wlayer[i] = wlayer[i+1];
}


/**
 * @brief Frees the weapon.
 *
 *    @param w Weapon to free.
 */
static void weapon_free( Weapon* w )
{
   Pilot *pilot_target;

   /* Decrement target lockons if needed */
   if (outfit_isSeeker(w->outfit)) {
      pilot_target = pilot_get( w->target );
      if (pilot_target != NULL)
         pilot_target->lockons--;
   }

   /* Stop playing sound if beam weapon. */
   if (outfit_isBeam(w->outfit)) {
      sound_stop( w->voice );
      sound_playPos(w->outfit->u.bem.sound_off,
            w->solid->pos.x,
            w->solid->pos.y,
            w->solid->vel.x,
            w->solid->vel.y);
   }

   /* Free the solid. */
   solid_free(w->solid);

#ifdef DEBUGGING
   memset(w, 0, sizeof(Weapon));
#endif /* DEBUGGING */

   free(w);
}

/**
 * @brief Clears all the weapons, does NOT free the layers.
 */
void weapon_clear (void)
{
   int i;
   /* Don't forget to stop the sounds. */
   for (i=0; i < nwbackLayer; i++) {
      sound_stop(wbackLayer[i]->voice);
      weapon_free(wbackLayer[i]);
   }
   nwbackLayer = 0;
   for (i=0; i < nwfrontLayer; i++) {
      sound_stop(wfrontLayer[i]->voice);
      weapon_free(wfrontLayer[i]);
   }
   nwfrontLayer = 0;
}

/**
 * @brief Destroys all the weapons and frees it all.
 */
void weapon_exit (void)
{
   weapon_clear();

   /* Destroy front layer. */
   if (wbackLayer != NULL) {
      free(wbackLayer);
      wbackLayer  = NULL;
      mwbacklayer = 0;
   }

   /* Destroy back layer. */
   if (wfrontLayer != NULL) {
      free(wfrontLayer);
      wfrontLayer  = NULL;
      mwfrontLayer = 0;
   }

   /* Destroy VBO. */
   if (weapon_vbo != NULL) {
      free( weapon_vboData );
      weapon_vboData = NULL;
      gl_vboDestroy( weapon_vbo );
      weapon_vbo = NULL;
   }
}


/**
 * @brief Clears possible exploded weapons.
 */
void weapon_explode( double x, double y, double radius,
      int dtype, double damage,
      const Pilot *parent, int mode )
{
   (void)dtype;
   (void)damage;
   weapon_explodeLayer( WEAPON_LAYER_FG, x, y, radius, parent, mode );
   weapon_explodeLayer( WEAPON_LAYER_BG, x, y, radius, parent, mode );
}


/**
 * @brief Explodes all the things on a layer.
 */
static void weapon_explodeLayer( WeaponLayer layer,
      double x, double y, double radius,
      const Pilot *parent, int mode )
{
   (void)parent;
   int i;
   Weapon **curLayer;
   int *nLayer;
   double dist, rad2;

   /* set the proper layer */
   switch (layer) {
      case WEAPON_LAYER_BG:
         curLayer = wbackLayer;
         nLayer = &nwbackLayer;
         break;
      case WEAPON_LAYER_FG:
         curLayer = wfrontLayer;
         nLayer = &nwfrontLayer;
         break;

      default:
         ERR("Invalid WEAPON_LAYER specified");
         return;
   }

   rad2 = radius*radius;

   /* Now try to destroy the weapons affected. */
   for (i=0; i<*nLayer; i++) {
      if (((mode & EXPL_MODE_MISSILE) && outfit_isAmmo(curLayer[i]->outfit)) ||
            ((mode & EXPL_MODE_BOLT) && outfit_isBolt(curLayer[i]->outfit))) {

         dist = pow2(curLayer[i]->solid->pos.x - x) +
               pow2(curLayer[i]->solid->pos.y - y);

         if (dist < rad2) {
            weapon_destroy(curLayer[i], layer);
            i--;
         }
      }
   }
}


