/**
 * @file pilot.c
 *
 * @brief Handles the pilot stuff.
 */

#include <string.h>
#include <math.h>
#include <stdlib.h>

#include "lephisto.h"
#include "log.h"
#include "weapon.h"
#include "pack.h"
#include "lxml.h"
#include "spfx.h"
#include "rng.h"
#include "hook.h"
#include "map.h"
#include "explosion.h"
#include "escort.h"
#include "pilot.h"

#define XML_ID    "Fleets"  /**< XML document identifier. */
#define XML_FLEET "fleet"   /**< XML individual fleet identifier. */

#define FLEET_DATA  "../dat/fleet.xml" /**< Where to find fleet data. */

#define PILOT_CHUNK 32      /**< Chunks to increment pilot_stack by. */

#define CHUNK_SIZE  32      /**< Size to allocate memory by. */

/* ID generators. */
static unsigned int pilot_id = PLAYER_ID; /**< Stack of pilod ids to assure uniqueness. */
static unsigned int mission_cargo_id = 0; /**< ID generator for special mission cargo.
                                               Not garanteed to be absolutely unique,
                                               only unique for each pilot. */

/* Stack of pilots. */
Pilot** pilot_stack = NULL; /**< Not static, it is used in player.c and weapon.c and ai.c */
int pilot_nstack = 0;       /**< Same. */
static int pilot_mstack = 0; /** Memory allocated for pilot_stack. */

extern Pilot* player;
extern double player_crating; /**< Players combat rating. */
extern void player_abortAutonav(char* reason);

/* Stack of fleets. */
static Fleet* fleet_stack = NULL; /** Fleet stack. */
static int nfleets = 0;           /** Number of fleets. */

/* External. */
/* AI. */
extern AI_Profile* ai_pinit(Pilot* p, char* ai);
extern void ai_destroy(Pilot* p);
extern void ai_think(Pilot* pilot);
/* Player. */
extern void player_think(Pilot* pilot);
extern void player_brokeHyperspace(void);
extern double player_faceHyperspace(void);
extern void player_dead(void);
extern void player_destroyed(void);
extern int gui_load(const char* name);
extern void player_addLicense(char* license);
/* Internal. */
static int pilot_getStackPos(const unsigned int id);
static void pilot_shootWeapon(Pilot* p, PilotOutfit* w);
static void pilot_update(Pilot* pilot, const double dt);
static void pilot_hyperspace(Pilot* pilot);
void pilot_render(Pilot* pilot);
static void pilot_calcCargo(Pilot* pilot);
void pilot_free(Pilot* p);
static int fleet_parse(Fleet* tmp, const xmlNodePtr parent);
static void pilot_dead(Pilot* p);

/**
 * @fn static int pilot_getStackPos(const unsigned int id)
 *
 * @brief Get the pilots position in the stack.
 *    @param id ID of the pilot to get.
 *    @return Position of pilot in stack or -1 if not found.
 */
static int pilot_getStackPos(const unsigned int id) {
   /* Binary search. */
  int l, m, h;
  l = 0;
  h = pilot_nstack-1;
  while(l <= h) {
    m = (l+h) >> 1; /* For impossible overflow returning negative value. */
    if(pilot_stack[m]->id > id) h = m-1;
    else if(pilot_stack[m]->id < id) l = m+1;
    else return m;
  }

  /* Not found. */
  return -1;
}

/**
 * @fn unsinged int pilot_getNextID(const unsigned int id)
 *
 * @brief Get the next pilot on id.
 *    @param id ID of current pilot.
 *    @return ID of next pilot of PLAYER_ID if no next pilot.
 */
unsigned int pilot_getNextID(const unsigned int id) {
  int m;
  m = pilot_getStackPos(id);

  if((m == (pilot_nstack-1)) || (m == -1)) return PLAYER_ID;
  else return pilot_stack[m+1]->id;
}

/**
 * @fn unsigned int pilot_getPrevID(const unsigned int id)
 *
 * @brief Get the previous pilot based on ID.
 *    @param id ID of the current pilot.
 *    @return ID of previous pilot or PLAYER_ID if no previous pilot.
 */
unsigned int pilot_getPrevID(const unsigned int id) {
  int m;
  m = pilot_getStackPos(id);

  if(m == -1) return PLAYER_ID;
  else if(m == 0) return pilot_stack[pilot_nstack-1]->id;
  else return pilot_stack[m-1]->id;
}

/**
 * @brief Get the nearest enemy to the pilot.
 *    @param p Pilot to get his nearest enemy.
 *    @param ID of her nearest enemy.
 */
unsigned int pilot_getNearestEnemy(const Pilot* p) {
  unsigned int tp;
  int i;
  double d, td;

  for(tp = 0, d = 0., i = 0; i < pilot_nstack; i++) {
    /* Must not be bribed. */
    if((pilot_stack[i]->id == PLAYER_ID) && pilot_isFlag(p, PILOT_BRIBED))
      continue;

    if(areEnemies(p->faction, pilot_stack[i]->faction) ||
        ((pilot_stack[i]->id == PLAYER_ID) && (pilot_isFlag(p, PILOT_HOSTILE)))) {
      td = vect_dist(&pilot_stack[i]->solid->pos, &p->solid->pos);
      if(!pilot_isDisabled(pilot_stack[i]) && ((!tp) || (td < d))) {
        d = td;
        tp = pilot_stack[i]->id;
      }
    }
  }
  return tp;
}

/**
 * @fn unsinged int pilot_getNearesetPilot(const Pilot* p)
 *
 * @brief Get the nearest pilot to a pilot.
 *    @param p Pilot to get her nearest pilot.
 *    @return The nearest pilot.
 */
unsigned int pilot_getNearestPilot(const Pilot* p) {
  unsigned int tp;
  int i;
  double d, td;

  tp = PLAYER_ID;
  d = 0;
  for(i = 0; i < pilot_nstack; i++)
    if(pilot_stack[i] != p) {
      td = vect_dist(&pilot_stack[i]->solid->pos, &player->solid->pos);
      if(!pilot_isDisabled(pilot_stack[i]) && ((tp == PLAYER_ID) || (td < d))) {
        d = td;
        tp = pilot_stack[i]->id;
      }
    }
  return tp;
}

/* Pull a pilot out of the pilot_stack based on id. */
Pilot* pilot_get(const unsigned int id) {
  int m;

  if(id == PLAYER_ID) return player; /* Special case player. */

  m = pilot_getStackPos(id);

  if(m == -1)
    return NULL;
  else
    return pilot_stack[m];
}

/* Grab a fleet out of the stack. */
Fleet* fleet_get(const char* name) {
  int i;
  for(i = 0; i < nfleets; i++)
    if(strcmp(fleet_stack[i].name, name)==0)
      return &fleet_stack[i];

  WARN("Fleet '%s' not found in stack", name);
  return NULL;
}

/* Attempt to turn the pilot to face dir. */
double pilot_face(Pilot* p, const double dir) {
  double diff, turn;

  diff = angle_diff(p->solid->dir, dir);

  turn = -10.*diff;
  if(turn > 1.) turn = 1.;
  else if(turn < -1.) turn = -1.;

  p->solid->dir_vel = 0.;
  if(turn)
    p->solid->dir_vel -= p->turn * turn;

  return diff;
}

/* Get the amount of jumps the pilot has left. */
int pilot_getJumps(const Pilot* p) {
  return (int)(p->fuel) / HYPERSPACE_FUEL;
}

/* Return quantity of a pilot outfit. */
int pilot_oquantity(Pilot* p, PilotOutfit* w) {
  return (outfit_isAmmo(w->outfit) && p->secondary) ?
        p->secondary->quantity : w->quantity;
}

/* Get pilot's free weapon space. */
int pilot_freeSpace(Pilot* p) {
  int i, s;

  s = p->ship->cap_weapon;
  for(i = 0; i < p->noutfits; i++)
    s -= p->outfits[i].quantity * p->outfits[i].outfit->mass;

  return s;
}

/* Mkay, this is how we shoot. Listen up. */
void pilot_shoot(Pilot* p, const int secondary) {
  int i;
  Outfit* o;

  if(!p->outfits) return; /* No outfits. */

  if(!secondary) {
    /* Primary weapons. */
    for(i = 0; i < p->noutfits; i++) {
      o = p->outfits[i].outfit;
      if(!outfit_isProp(o, OUTFIT_PROP_WEAP_SECONDARY) &&
          (outfit_isBolt(o) || outfit_isBeam(o) || outfit_isFighterBay(o)))
        /** @todo Possibly make this neater. */
        pilot_shootWeapon(p, &p->outfits[i]);
    }
  } else {
    if(!p->secondary) return; /* No secondary weapon. */
    pilot_shootWeapon(p, p->secondary);
  }
}

/**
 * @fn void pilot_shootStop(Pilot* p, const int secondary)
 *
 * @brief 
 */
void pilot_shootStop(Pilot* p, const int secondary) {
  int i;
  Outfit* o;

  if(!p->outfits) return; /* No outfits. */
  
  if(!secondary) { /* Primary weapons. */
    for(i = 0; i < p->noutfits; i++) {
      /* cycle through outfits to find primary weapons. */
      o = p->outfits[i].outfit;
      if(!outfit_isProp(o, OUTFIT_PROP_WEAP_SECONDARY) &&
          outfit_isBeam(o)) /** @todo Possibly make this neater. */
        if(p->outfits[i].beamid > 0) {
          p->outfits[i].beamid = 0;
        }
    }
  } else { /* Secondary weapons. */
    if(p->secondary == NULL) return; /* No secondary weapon. */

    o = p->secondary->outfit;

    if(outfit_isBeam(o) && (p->secondary->beamid > 0)) {
      beam_end(p->id, p->secondary->beamid);
      p->secondary->beamid = 0;
    }
  }
}

/**
 * @fn static void pilot_shootWeapon(Pilot* p, PilotOutfit* w)
 *
 * @brief Actually handles the shooting, how often the player can shoot and such.
 *    @param p Pilot that is shooting.
 *    @param w Pilot's outfit to shoot.
 *    @param t Pilot's target.
 */
static void pilot_shootWeapon(Pilot* p, PilotOutfit* w) {
  /* Check to see if weapon is ready. */
  if(w->timer > 0.)
    return;
  /* Regular bolt weapons. */
  if(outfit_isBolt(w->outfit)) {
    /* Enough energy? */
    if(outfit_energy(w->outfit) > p->energy) return;

    p->energy -= outfit_energy(w->outfit);
    weapon_add(w->outfit, p->solid->dir,
        &p->solid->pos, &p->solid->vel, p->id, p->target);
  }

  /* Beam Weapons. */
  else if(outfit_isBeam(w->outfit)) {
    if(outfit_energy(w->outfit) > p->energy) return;

    /** @todo Handle warmup stage. */
    w->state = PILOT_OUTFIT_ON;
    w->beamid = beam_start(w->outfit, p->solid->dir,    
      &p->solid->pos, &p->solid->vel, p->id, p->target);
  }

  /*
   * Missile Launchers.
   *
   * Must be a secondary weapon.
   */
  else if(outfit_isLauncher(w->outfit) && (w == p->secondary)) {
    /* Shooter can't be the target - Sanity check for the player. */
    if((w->outfit->type != OUTFIT_TYPE_MISSILE_DUMB) && (p->id == p->target))
      return;

    /* Must have ammo left. */
    if(p->ammo && (p->ammo->quantity <= 0))
      return;

    /* Enough energy? */
    if(outfit_energy(w->outfit) > p->energy)
      return;

    p->energy -= outfit_energy(w->outfit);
    weapon_add(p->ammo->outfit, p->solid->dir, &p->solid->pos,
               &p->solid->vel, p->id, p->target);

    p->ammo->quantity -= 1; /* There's no getting this one back. */
    if(p->ammo->quantity <= 0) /* Out of ammo. */
      pilot_rmOutfit(p, p->ammo->outfit, 0); /* It'll set p->ammo to NULL. */
  }
  /*
   * Fighter bays.
   *
   * Must be secondary weapon.
   */
  else if(outfit_isFighterBay(w->outfit) && (w == p->secondary)) {
    /* Must have ammo left. */
    if((p->ammo == NULL) || (p->ammo->quantity <= 0))
      return;

    /* Create escort. */
    escort_create(p->id, p->ammo->outfit->u.fig.ship,
        &p->solid->pos, &p->solid->vel, 1);

    p->ammo->quantity -= 1; /* We just shot it. */
    if(p->ammo->quantity <= 0) /* Out of ammo. */
      pilot_rmOutfit(p, p->ammo->outfit, 0); /* It'll set p->ammo to NULL. */
  } else {
    WARN("Shooting unknown weapon type: %s", w->outfit->name);
  }

  w->timer += ((double)outfit_delay(w->outfit) / (double)w->quantity)/1000.;
}

/**
 * @fn void pilot_switchSecondary(Pilot* p, int i)
 *
 * @brief 
 */
void pilot_switchSecondary(Pilot* p, int i) {
  PilotOutfit* cur;

  cur = player->secondary;

  if((i < 0) || (i >= player->noutfits))
    player->secondary = NULL;
  else
    player->secondary = &player->outfits[i];

  /* Check for weapon change. */
  if((cur != NULL) && (player->secondary != cur)) {
    if(outfit_isBeam(cur->outfit) && (cur->beamid > 0)) {
      beam_end(p->id, cur->beamid);
      cur->beamid = 0;
    }
  }
}

/**
 * @fn void pilot_hit(Pilot* p, const Solid* w, const unsigned int shooter,
 *                    const DamageType dtype, const double damage)
 *
 * @brief Damages the pilot.
 *    @param p Pilot that is taking damage.
 *    @param w Solid that is hitting pilot.
 *    @param shooter Attacker that shot the pilot.
 *    @param dtype type of damage.
 *    @param damage Amount of damage.
 */
void pilot_hit(Pilot* p, const Solid* w, const unsigned int shooter,
    const DamageType dtype, const double damage) {

  int mod;
  double damage_shield, damage_armour, knockback, dam_mod;
  Pilot* pshooter;

  /* Calculate the damage. */
  outfit_calcDamage(&damage_shield, &damage_armour, &knockback,  dtype, damage);

  if((p->id == PLAYER_ID) &&
      !pilot_isFlag(player, PILOT_HYP_BEGIN) &&
      !pilot_isFlag(player, PILOT_HYPERSPACE))
  	player_abortAutonav("Sustaining Damage");

  if(p->shield - damage_shield > 0.) { /* Shields take the whole blow. */
    p->shield -= damage_shield;
    dam_mod = damage_shield/p->shield_max;
  }
  else if(p->shield > 0.) {
    /* Shields can take part of the blow. */
    p->armour -= p->shield/damage_shield*damage_armour;
    p->shield = 0.;
    dam_mod = (damage_shield+damage_armour) / (p->shield_max + p->armour_max);
  }
  else if(p->armour-damage_armour > 0.) {
    p->armour -= damage_armour;
    dam_mod = damage_armour/p->armour_max;

    /* Shake us up a bit. */
    if(p->id == PLAYER_ID)
      spfx_shake(dam_mod*100.);
  }
  else {
    /* We are officially dead. */
    p->armour = 0.;
    dam_mod = 0.;

    if(!pilot_isFlag(p, PILOT_DEAD)) {
      pilot_dead(p);
      /* Adjust the combat rating based on pilot mass and ditto faction. */
      pshooter = pilot_get(shooter);
      if((pshooter != NULL) && (pshooter->faction == FACTION_PLAYER)) {
        mod = sqrt(p->ship->mass) / 5;
        player_crating += 2*mod; /* Crating chanes faster. */
        faction_modPlayer(p->faction, -mod);
      }
    }
  }

  if(shooter != 0)
    /* Knock back effect is dependent on both damage and mass of the weapon. */
    /* should probably turn it into a partial conservative collision.. */
    vect_cadd(&p->solid->vel,
        knockback * (w->vel.x * (dam_mod/6. + w->mass/p->solid->mass/6.)),
        knockback * (w->vel.y * (dam_mod/6. + w->mass/p->solid->mass/6.)));
}

/**
 * @fn void pilot_dead(Pilot* p)
 *
 * @brief Pilot is dead, now will slowly explode.
 *    @param p Pilot that just died.
 */
void pilot_dead(Pilot* p) {
  if(pilot_isFlag(p, PILOT_DEAD)) return; /* She's already dead. */
  
  /* Basically just set the timers.. */
  if(p->id == PLAYER_ID) player_dead();
  p->timer[0] = 0.; /* No need for AI anymore. */
  p->ptimer   = 1. + sqrt(10*p->armour_max * p->shield_max) / 1000.;
  p->timer[1] = 0.; /* Explosion timer. */

  /* Flag cleanup - fixes some issues. */
  if(pilot_isFlag(p, PILOT_HYP_PREP))   pilot_rmFlag(p, PILOT_HYP_PREP);
  if(pilot_isFlag(p, PILOT_HYP_BEGIN))  pilot_rmFlag(p, PILOT_HYP_BEGIN);
  if(pilot_isFlag(p, PILOT_HYPERSPACE)) pilot_rmFlag(p, PILOT_HYPERSPACE);

  /* Our pilot is now deadz. */
  pilot_setFlag(p, PILOT_DEAD);

  /* Run hook if pilot has a death hook. */
  pilot_runHook(p, PILOT_HOOK_DEATH);
}

/**
 * @fn static void pilot_runHook(Pilot* p, int hook_type)
 *
 * @brief Tries to run a pilot hook if she has it.
 *    @param p Pilot to run the hook.
 *    @param hook_type Type of hook to run.
 */
void pilot_runHook(Pilot* p, int hook_type) {
  int i;
  for(i = 0; i < PILOT_HOOKS; i++) {
    if(p->hook_type[i] == hook_type)
      hook_runID(p->hook[i]);
  }
}

/**
 * @fn void pilot_setSecondary(Pilot* p, const char* secondary)
 *
 * @brief Set the pilots secondary weapon based on its name.
 *    @param p Pilot to set secondary weapon.
 *    @param secondary Name of the secondary weapon to set.
 */
void pilot_setSecondary(Pilot* p, const char* secondary) {
  int i;

  /* No need for ammo if there is no secondary. */
  if(secondary == NULL) {
    p->secondary = NULL;
    p->ammo = NULL;
    return;
  }

  /* Find the secondary and set ammo appropriately. */
  for(i = 0; i < p->noutfits; i++) {
    if(strcmp(secondary, p->outfits[i].outfit->name)==0) {
      p->secondary = &p->outfits[i];
      pilot_setAmmo(p);
      return;
    }
  }
  p->secondary = NULL;
  p->ammo = NULL;
}

/**
 * @fn void pilot_setAmmo(Pilot* p)
 *
 * @param Set the pilots ammo based on their secondary weapon.
 *    @param p Pilot to set ammo.
 */
void pilot_setAmmo(Pilot* p) {
  int i;
  Outfit* ammo;

  /* Weapon must use ammo. */
  if((p->secondary == NULL) || (outfit_ammo(p->secondary->outfit)==NULL)) {
    p->ammo = NULL;
    return;
  }

  /* Find the ammo and set it. */
  ammo = outfit_ammo(p->secondary->outfit);
  for(i = 0; i < p->noutfits; i++)
    if(p->outfits[i].outfit == ammo) {
      p->ammo = &p->outfits[i];
      return;
    }

  /* None found, so we assume if doesn't need ammo. */
  p->ammo = NULL;
}

/**
 * @fn int pilot_getAmmo(Pilot* p, Outfit* o)
 *    @param p Pilot to get amount of ammo for.
 *    @param o Outfit to get ammo for. 
 *    @return Amount of ammo for o on p.
 */
int pilot_getAmmo(Pilot* p, Outfit* o) {
  int i;
  Outfit* ammo;

  /* Must be a launcher. */
  if(!outfit_isLauncher(o))
    return 0;

  /* Try to find the ammo. */
  ammo = o->u.lau.ammo;
  for(i = 0; i < p->noutfits; i++)
    if(p->outfits[i].outfit == ammo)
      return p->outfits[i].quantity;

  /* Assume none. */
  return 0;
}

/**
 * @fn void pilot_setAfterburner(Pilot* p)
 *
 * @brief Set the pilots afterburner if she has one.
 *    @param p Pilot to set afterburner.
 */
void pilot_setAfterburner(Pilot* p) {
  int i;

  for(i = 0; i < p->noutfits; i++)
    if(outfit_isAfterburner(p->outfits[i].outfit)) {
      p->afterburner = &p->outfits[i];
      return;
    }
  p->afterburner = NULL;
}

/**
 * @fn int pilot_dock(Pilot* p, Pilot* target)
 *
 * @brief Dock the pilot on its target pilot.
 *    @param p Pilot that wants to dock.
 *    @param target Pilot to dock on.
 *    @return 0 on successful docking.
 */
int pilot_dock(Pilot* p, Pilot* target) {
  int i;
  Outfit* o;

  /* Must be close. */
  if(vect_dist(&p->solid->pos, &target->solid->pos) > 30.)
    return -1;

  /* Cannot be going much faster. */
  if(vect_dist(&p->solid->vel, &target->solid->vel) > 2*MIN_VEL_ERR)
    return -1;

  /* Check to see if target has an available bay. */
  for(i = 0; i < target->noutfits; i++) {
    if(outfit_isFighterBay(target->outfits[i].outfit)) {
      o = outfit_ammo(target->outfits[i].outfit);
      if(outfit_isFighter(o) &&
          (strcmp(p->ship->name, o->u.fig.ship)==0))
        break;
    }
  }
  if(i >= target->noutfits)
    return -1;

  /* Add the pilots outfit. */
  if(pilot_addOutfit(target, o, 1) != 1)
    return -1;

  /* Destroy the pilot. */
  pilot_setFlag(p, PILOT_DELETE);

  return 0;
}

/**
 */
void pilot_explode(double x, double y, double radius,
    DamageType dtype, double damage, unsigned int parent) {
  
  int i;
  double rx, ry;
  double dist, rad2;
  Pilot* p;
  Solid s; /* Only need to manipulate mass and vel. */

  rad2 = radius*radius;

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

    /* Calculate a bit. */
    rx = p->solid->pos.x - x;
    ry = p->solid->pos.y - y;
    dist = pow2(rx) + pow2(ry);

    /* Pilot is hit. */
    if(dist < rad2) {
      /* Impact settings. */
      s.mass = (rad2 - dist) / 10.;
      s.vel.x = rx;
      s.vel.y = ry;

      /* Actual damage calculations. */
      pilot_hit(p, &s, parent, dtype, damage);
    }
  }
}

/**
 * @fn void pilot_render(Pilot* p)
 *
 * @brief Render the pilot.
 *    @param p Pilot to render.
 */
void pilot_render(Pilot* p) {
  gl_blitSprite(p->ship->gfx_space,
                p->solid->pos.x, p->solid->pos.y,
                p->tsx, p->tsy, NULL);
}

/**
 * @fn static void pilot_update(Pilot* pilot, const double dt)
 *
 * @brief Updates the pilot.
 *    @param pilot Pilot to update.
 *    @param dt Current delta tick.
 */
static void pilot_update(Pilot* pilot, const double dt) {
  int i;
  unsigned int l;
  double a, px, py, vx, vy;
  char buf[16];
  PilotOutfit* o;

  /* Update timers. */
  pilot->ptimer -= dt;
  pilot->tcontrol -= dt;
  for(i = 0; i < MAX_AI_TIMERS; i++)
    pilot->timer[i] -= dt;
  for(i = 0; i < pilot->noutfits; i++) {
    o = &pilot->outfits[i];
    if(o->timer > 0.)
      o->timer -= dt;
  }

  /* She's dead. */
  if(pilot_isFlag(pilot, PILOT_DEAD)) {
    if(pilot->ptimer < 0.) { /* Completely destroyed with final explosion. */
      if(pilot->id == PLAYER_ID) /* Player handled differently. */
        player_destroyed();
      pilot_setFlag(pilot, PILOT_DELETE); /* It'll get deleted next frame. */
      return;
    }

    /* Pilot death sound. */
    if(!pilot_isFlag(pilot, PILOT_DEATH_SOUND) && (pilot->ptimer < 0.050)) {
      /* Play random explosion sound. */
      snprintf(buf, 16, "explosion%d", RNG(0,2));
      sound_playPos(sound_get(buf), pilot->solid->pos.x, pilot->solid->pos.y);

      pilot_setFlag(pilot, PILOT_DEATH_SOUND);
    }

    /* Final explosion. */
    else if(!pilot_isFlag(pilot, PILOT_EXPLODED) && (pilot->ptimer < 0.200)) {
      /* Damage from explosion. */
      a = sqrt(pilot->solid->mass);
      expl_explode(pilot->solid->pos.x, pilot->solid->pos.y,
          pilot->solid->vel.x, pilot->solid->vel.y,
          pilot->ship->gfx_space->sw / 2. + a,
          DAMAGE_TYPE_KINETIC, 2.*a - 20.,
          0, EXPL_MODE_SHIP);
      pilot_setFlag(pilot, PILOT_EXPLODED);

      /* Release cargo. */
      for(i = 0; i < pilot->ncommodities; i++)
        commodity_Jettison(pilot->id, pilot->commodities[i].commodity,
            pilot->commodities[i].quantity);
    }
    /* Reset random explosion time. */
    else if(pilot->timer[1] < 0.) {
      pilot->timer[1] = 0.08 * (pilot->ptimer - pilot->timer[1]) / pilot->ptimer;

      /* Random position on ship. */
      a = RNGF()*2.*M_PI;
      px = VX(pilot->solid->pos) + cos(a)*RNGF()*pilot->ship->gfx_space->sw/2.;
      py = VY(pilot->solid->pos) + sin(a)*RNGF()*pilot->ship->gfx_space->sh/2.;

      vx = VX(pilot->solid->vel);
      vy = VY(pilot->solid->vel);

      /* Set explosions. */
      l = (pilot->id == PLAYER_ID) ? SPFX_LAYER_FRONT : SPFX_LAYER_BACK;
      if(RNGF() > 0.8) spfx_add(spfx_get("ExpM"), px, py, vx, vy, l);
      else spfx_add(spfx_get("ExpS"), px, py, vx, vy, l);
    }
  }
  else if(pilot->armour <= 0.) /* PWNED! */
    pilot_dead(pilot); /* Start death stuff. */

  /* Purpose fallthrough to get the movement similar to disabled. */
  if((pilot != player) &&
      (pilot->armour < PILOT_DISABLED_ARMOUR*pilot->armour_max)) { /* Disabled. */
    /* First time pilot is disabled. */
    if(!pilot_isFlag(pilot, PILOT_DISABLED)) {
      pilot_setFlag(pilot, PILOT_DISABLED); /* Set as disabled. */
      /* Run hook. */
      pilot_runHook(pilot, PILOT_HOOK_DISABLE);
    }

    /* Come to a halt slowly. */
    vect_pset(&pilot->solid->vel,
              VMOD(pilot->solid->vel) * (1. - dt*0.10), VANGLE(pilot->solid->vel));
    vectnull(&pilot->solid->force); /* No more accel. */
    pilot->solid->dir_vel = 0.; /* Stop it from turning. */

    /* Update the solid. */
    pilot->solid->update(pilot->solid, dt);
    gl_getSpriteFromDir(&pilot->tsx, &pilot->tsy,
                        pilot->ship->gfx_space, pilot->solid->dir);
    return;
  }
  /* We are still alive. */
  else if(pilot->armour < pilot->armour_max)
    /* Regen armour. */
    pilot->armour += pilot->armour_regen*dt;
  else 
    /* And shields. */
    pilot->shield += pilot->shield_regen*dt;

  /* Update energy. */
  if((pilot->energy < 1.) && pilot_isFlag(pilot, PILOT_AFTERBURNER))
    pilot_rmFlag(pilot, PILOT_AFTERBURNER); /* Break afterburner. */
  pilot->energy += pilot->energy_regen * dt;

  /* Check limits. */
  if(pilot->armour > pilot->armour_max) pilot->armour = pilot->armour_max;
  if(pilot->shield > pilot->shield_max) pilot->shield = pilot->shield_max;
  if(pilot->energy > pilot->energy_max) pilot->energy = pilot->energy_max;

  /* Update the solid. */
  (*pilot->solid->update)(pilot->solid, dt);
  gl_getSpriteFromDir(&pilot->tsx, &pilot->tsy,
                      pilot->ship->gfx_space, pilot->solid->dir);

  if(!pilot_isFlag(pilot, PILOT_HYPERSPACE)) { /* Limit the speed. */

    /* Pilot is afterburning. */
    if(pilot_isFlag(pilot, PILOT_AFTERBURNER) && /* Must have enough energy. */
       (player->energy > pilot->afterburner->outfit->u.afb.energy * dt)) {
      limit_speed(&pilot->solid->vel, /* Limit is higher. */
                  pilot->speed * pilot->afterburner->outfit->u.afb.speed_perc +
                  pilot->afterburner->outfit->u.afb.speed_abs, dt);
      if(pilot->id == PLAYER_ID)
        spfx_shake(SHAKE_DECAY*0.75 * dt); /* Shake goes down at half speed. */
    pilot->energy -= pilot->afterburner->outfit->u.afb.energy * dt; /* Energy loss. */
    } else /* Normal limit. */
      limit_speed(&pilot->solid->vel, pilot->speed, dt);
  }
}

/* Pilot is getting ready or is in, hyperspace. */
static void pilot_hyperspace(Pilot* p) {
  double diff;

  /* Pilot is actually in hyperspace. */
  if(pilot_isFlag(p, PILOT_HYPERSPACE)) {

    /* Has the jump happened? */
    if(p->ptimer < 0.) {
      if(p == player) {
        player_brokeHyperspace();
      } else {
        pilot_runHook(p, PILOT_HOOK_JUMP);
        pilot_setFlag(p, PILOT_DELETE); /* Set flag to delete pilot. */
      }
      return;
    }
    /* Keep accelerating - hyperspace uses much bigger accel. */
    vect_pset(&p->solid->force, HYPERSPACE_THRUST*p->solid->mass, p->solid->dir);
  }
  /* Engines getting ready for the jump. */
  else if(pilot_isFlag(p, PILOT_HYP_BEGIN)) {
    if(p->ptimer < 0.) { /* Engines ready. */
      p->ptimer = HYPERSPACE_FLY_DELAY;
      pilot_setFlag(p, PILOT_HYPERSPACE);
    }
  } else {
    /* Pilot is getting ready for hyperspace. */

    /* Brake. */
    if(VMOD(p->solid->vel) > MIN_VEL_ERR) {
      diff = pilot_face(p, VANGLE(p->solid->vel) + M_PI);

      if(ABS(diff) < MAX_DIR_ERR)
        vect_pset(&p->solid->force, p->thrust, p->solid->dir);
    } else {
      /* Face target. */
      vectnull(&p->solid->force); /* Stop accelerating. */

      /* Player should actually face the system she's headed to. */
      if(p == player) diff = player_faceHyperspace();
      else diff = pilot_face(p, VANGLE(p->solid->pos));

      if(ABS(diff) < MAX_DIR_ERR) {
        /* We should prepare for the jump now. */
        p->solid->dir_vel = 0.;
        p->ptimer = HYPERSPACE_ENGINE_DELAY;
        pilot_setFlag(p, PILOT_HYP_BEGIN);
      }
    }
  }
}

/**
 * @fn void pilot_hyperspaceAbort(Pilot* p)
 *
 * @brief Stops the pilot from hyperspaceing.
 *
 * Can only stop in preperation mode.
 *    @param p Pilot to handle stop hyperspace.
 */
void pilot_hyperspaceAbort(Pilot* p) {
  if(!pilot_isFlag(p, PILOT_HYPERSPACE)) {
    if(pilot_isFlag(p, PILOT_HYP_BEGIN))
      pilot_rmFlag(p, PILOT_HYP_BEGIN);
    if(pilot_isFlag(p, PILOT_HYP_PREP))
      pilot_rmFlag(p, PILOT_HYP_PREP);
  }
}

int pilot_addOutfit(Pilot* pilot, Outfit* outfit, int quantity) {
  int i, q, free_space;
  char* osec;

  free_space = pilot_freeSpace(pilot);
  q = quantity;

  /* Special case if it's a map. */
  if(outfit_isMap(outfit)) {
    if(pilot == player) /* Only player can get it. */
      map_map(NULL, outfit->u.map.radius);
    return 1; /* Must return 1 for paying purposes. */
  }
  /* Special case if it's a license. */
  else if(outfit_isLicense(outfit)) {
    if(pilot == player) /* Only player can get it. */
      player_addLicense(outfit->name);
    return 1; /* Must return 1 for paying purposes. */
  }

  /* Mod quantity down if it doesn't fit. */
  if(q*outfit->mass > free_space)
    q = free_space / outfit->mass;

  /* Can we actually add any? */
  if(q == 0)
    return 0;

  /* Does outfit already exist? */
  for(i = 0; i < pilot->noutfits; i++)
    if(strcmp(outfit->name, pilot->outfits[i].outfit->name)==0) {
      pilot->outfits[i].quantity += q;
      /* Can't be over max. */
      if(pilot->outfits[i].quantity > outfit->max) {
        q -= pilot->outfits[i].quantity - outfit->max;
        pilot->outfits[i].quantity = outfit->max;
      }
      /* Recalculate the stats. */
      pilot_calcStats(pilot);
      return q;
    }

  /* Hacks in case it reallocs. */
  osec = (pilot->secondary) ? pilot->secondary->outfit->name : NULL;
  /* No need for ammo since it's already handled in setSecondary, */
  /* since pilot has only one afterburner it's handled at the end. */

  /* Grow the outfits. */
  pilot->outfits = realloc(pilot->outfits,  (pilot->noutfits+1)*sizeof(PilotOutfit));
  pilot->outfits[pilot->noutfits].outfit    = outfit;
  pilot->outfits[pilot->noutfits].quantity  = q;
  pilot->outfits[pilot->noutfits].timer     = 0;
  pilot->outfits[pilot->noutfits].beamid    = 0;

  /* Can't be over max. */
  if(pilot->outfits[pilot->noutfits].quantity > outfit->max) {
    q -= pilot->outfits[pilot->noutfits].quantity - outfit->max;
    pilot->outfits[i].quantity = outfit->max;
  }
  pilot->outfits[pilot->noutfits].timer = 0; /* Reset time. */
  (pilot->noutfits)++;

  if(outfit_isTurret(outfit))
    /* Used to speed up AI. */
    pilot_setFlag(pilot, PILOT_HASTURRET);

  if(outfit_isBeam(outfit))
    /* Used to speed up some calculation. */
    pilot_setFlag(pilot, PILOT_HASBEAMS);

  /* Hack due to realloc possibility. */
  pilot_setSecondary(pilot, osec);
  pilot_setAfterburner(pilot);

  pilot_calcStats(pilot);
  return q;
}

/* Remove an outfit from the pilot. */
int pilot_rmOutfit(Pilot* pilot, Outfit* outfit, int quantity) {
  int i, q, c;
  char* osec;

  c = (outfit_isMod(outfit)) ? outfit->u.mod.cargo : 0;
  q = quantity;

  for(i = 0; i < pilot->noutfits; i++)
    if(strcmp(outfit->name, pilot->outfits[i].outfit->name)==0) {
      pilot->outfits[i].quantity -= quantity;
      if(pilot->outfits[i].quantity <= 0) {
        /* We didn't actually remove the full amount. */
        q += pilot->outfits[i].quantity;

        /* Hack in case it reallocs - Can happen even when shrinking. */
        osec = (pilot->secondary) ? pilot->secondary->outfit->name : NULL;

        /* Remove the outfit. */
        memmove(&pilot->outfits[i], &pilot->outfits[i+1],
                sizeof(PilotOutfit)*(pilot->noutfits-i-1));
        pilot->noutfits--;
        pilot->outfits = realloc(pilot->outfits,
                                 sizeof(PilotOutfit)*(pilot->noutfits));

        /* Set secondary and afterburner. */
        pilot_setSecondary(pilot, osec);
        pilot_setAfterburner(pilot);
      }
      pilot_calcStats(pilot); /* Recalculate stats. */
      pilot->cargo_free -= c;
      return q;
    }
  WARN("Failure attempting to remove %d '%s' from pilot '%s'",
       quantity, outfit->name, pilot->name);

  return 0;
}

/* Return all the outfits in a nice text form. */
char* pilot_getOutfits(Pilot* pilot) {
  int i;
  char buf[64], *str;

  str = malloc(sizeof(char)*1024);
  buf[0] = '\0';
  /* First outfit. */
  if(pilot->noutfits > 0)
    snprintf(str, 1024, "%dx %s",
        pilot->outfits[0].quantity, pilot->outfits[0].outfit->name);
  else
    snprintf(str, 1024, "None");

  /* Rest of the outfits. */
  for(i = 1; i < pilot->noutfits; i++) {
    snprintf(buf, 64, ", %dx %s",
        pilot->outfits[i].quantity, pilot->outfits[i].outfit->name);
    strcat(str, buf);
  }
  return str;
}

/* Recalculate the pilot's stats based on her outfits. */
void pilot_calcStats(Pilot* pilot) {
  int i;
  double q;
  double wrange, wspeed;
  int nweaps;
  Outfit* o;
  double ac, sc, ec, fc; /* Temp health coeficients to set. */

  /* -- Set up the basic stuff. */
  /* Movement. */
  pilot->thrust = pilot->ship->thrust;
  pilot->turn  = pilot->ship->turn;
  pilot->speed  = pilot->ship->speed;
  /* Health. */
  ac = pilot->armour  / pilot->armour_max;
  sc = pilot->shield  / pilot->shield_max;
  ec = pilot->energy  / pilot->energy_max;
  fc = pilot->fuel    / pilot->fuel_max;
  pilot->armour_max   = pilot->ship->armour;
  pilot->shield_max   = pilot->ship->shield;
  pilot->energy_max   = pilot->ship->energy;
  pilot->fuel_max     = pilot->ship->fuel;
  pilot->armour_regen = pilot->ship->armour_regen;
  pilot->shield_regen = pilot->ship->shield_regen;
  pilot->energy_regen = pilot->ship->energy_regen;
  /* Jamming. */
  pilot->jam_range    = 0.;
  pilot->jam_chance   = 0.;

  /* Cargo has to be reset. */
  pilot_calcCargo(pilot);

  /* Now add outfit changes. */
  nweaps = 0;
  wrange = wspeed = 0.;
  for(i = 0; i < pilot->noutfits; i++) {
    o = pilot->outfits[i].outfit;     
    q = (double) pilot->outfits[i].quantity;

    if(outfit_isMod(o)) { /* Modification. */
      /* Movement. */
      pilot->thrust += o->u.mod.thrust  * q;
      pilot->turn  += o->u.mod.turn   * q;
      pilot->speed += o->u.mod.speed  * q;
      /* Health. */
      pilot->armour_max  += o->u.mod.armour     * q;
      pilot->armour_regen += o->u.mod.armour_regen  * q;
      pilot->shield_max  += o->u.mod.shield     * q;
      pilot->shield_regen += o->u.mod.shield_regen  * q;
      pilot->energy_max  += o->u.mod.energy    * q;
      pilot->energy_regen += o->u.mod.energy_regen  * q;
      /* Fuel. */
      pilot->fuel_max += o->u.mod.fuel * q;
      /* Misc. */
      pilot->cargo_free += o->u.mod.cargo * q;
    }
    else if(outfit_isAfterburner(o)) /* Afterburner. */
      pilot->afterburner = &pilot->outfits[i]; /* Set afterburner. */
    else if(outfit_isJammer(o)) { /* Jammer. */
      if(pilot->jam_chance < o->u.jam.chance) { /* Substitude. */
        /* @todo Make more jammers improve overall. */
        pilot->jam_range  = o->u.jam.range;
        pilot->jam_chance = o->u.jam.chance;
      }
      pilot->energy_regen -= o->u.jam.energy;
    }
    else if((outfit_isWeapon(o) || outfit_isTurret(o)) && /* Primary weapon. */
        !outfit_isProp(o, OUTFIT_PROP_WEAP_SECONDARY)) {
      nweaps++;
      wrange = MAX(wrange, outfit_range(o));
      wspeed += outfit_speed(o);
    }
  }

  /* Set weapon range and speed. */
  pilot->weap_range = wrange; /* Range is max. */
  pilot->weap_speed = wspeed / (double)nweaps;

  /* Give the pilot her health proportion back. */
  pilot->armour = ac * pilot->armour_max;
  pilot->shield = sc * pilot->shield_max;
  pilot->energy = ec * pilot->energy_max;
  pilot->fuel   = fc * pilot->fuel_max;
}

/* Pilot free cargo space. */
int pilot_cargoFree(Pilot* p) {
  return p->cargo_free;
}

/**
 *
 */
int pilot_moveCargo(Pilot* dest, Pilot* src) {
  int i;

  /* Nothing to copy, success! */
  if(src->ncommodities == 0)
    return 0;

  /* Check if it fits. */
  if(pilot_cargoUsed(src) > pilot_cargoFree(dest)) {
    WARN("Unable to copy cargo over from pilot '%s' to '%s'", src->name, dest->name);
    return -1;
  }

  /* Alloate new space. */
  i = dest->ncommodities;
  dest->ncommodities += src->ncommodities;
  dest->commodities = realloc(dest->commodities,
      sizeof(PilotCommodity)*dest->ncommodities);

  /* Copy over. */
  memmove(&dest->commodities[i], &src->commodities[0],
      sizeof(PilotCommodity) * src->ncommodities);

  /* Clean src. */
  src->ncommodities = 0;
  if(src->commodities != NULL)
    free(src->commodities);
  src->commodities = NULL;

  return 0;
}

/* Try to add quantity of cargo to pilot, return quantity actually added. */
int pilot_addCargo(Pilot* pilot, Commodity* cargo, int quantity) {
  int i, q;

  /* Check if pilot has it first. */
  q = quantity;
  for(i = 0; i < pilot->ncommodities; i++)
    if(!pilot->commodities[i].id && (pilot->commodities[i].commodity == cargo)) {
      if(pilot_cargoFree(pilot) < quantity)
        q = pilot_cargoFree(pilot);
      pilot->commodities[i].quantity += q;
      pilot->cargo_free -= q;
      pilot->solid->mass += q;
      return q;
    }

  /* Must add another one. */
  pilot->commodities = realloc(pilot->commodities,
                               sizeof(PilotCommodity) * (pilot->ncommodities+1));
  pilot->commodities[pilot->ncommodities].commodity = cargo;
  if(pilot_cargoFree(pilot) < quantity)
    q = pilot_cargoFree(pilot);
  pilot->commodities[pilot->ncommodities].id = 0;
  pilot->commodities[pilot->ncommodities].quantity = q;
  pilot->cargo_free -= q;
  pilot->ncommodities++;

  return q;
}

/* Return the amount of cargo onboard the ship. */
int pilot_cargoUsed(Pilot* pilot) {
  int i, q;

  q = 0;
  for(i = 0; i < pilot->ncommodities; i++)
    q += pilot->commodities[i].quantity;

  return q;
}

/* Calculate how much cargo ship has left etc. */
static void pilot_calcCargo(Pilot* pilot) {
  int q;

  q = pilot_cargoUsed(pilot);

  pilot->cargo_free = pilot->ship->cap_cargo - q; /* Reduce space left. */
  pilot->solid->mass = pilot->ship->mass + q;     /* Cargo affects weight. */
}

/**
 * @brief Add special mission cargo, can't sell it and such.
 *    @param pilot Pilot to add it to.
 *    @param cargo Comodity to add.
 *    @param quantity Quantity to add.
 *    @return The mission cargo ID of created cargo.
 */
unsigned int pilot_addMissionCargo(Pilot* pilot, Commodity* cargo, int quantity) {
  int i;
  unsigned int id, max_id;
  int q;
  q = quantity;

  /* Get ID. */
  id = ++mission_cargo_id;

  /* Check for collisions with pilot and set ID generator to the max. */
  max_id = 0;
  for(i = 0; i < pilot->ncommodities; i++)
    if(pilot->commodities[i].id > max_id)
      max_id = pilot->commodities[i].id;
  if(max_id > id)
    mission_cargo_id = max_id;
  id = ++mission_cargo_id;

  /* Grow commodities. */
  pilot->commodities = realloc(pilot->commodities,
      sizeof(PilotCommodity) * (pilot->ncommodities+1));
  pilot->commodities[pilot->ncommodities].commodity = cargo;
  /* Add commodity. */
  if(pilot_cargoFree(pilot) < quantity)
    q = pilot_cargoFree(pilot);
  pilot->commodities[pilot->ncommodities].id = id;
  pilot->commodities[pilot->ncommodities].quantity = q;
  /* Postfixing. */
  pilot->cargo_free -= q;
  pilot->solid->mass += q;
  pilot->ncommodities++;

  return id;
}

/**
 * @brief Remove special mission cargo based on id.
 *    @param pilot Pilot to remove cargo from.
 *    @param cargo_id ID of the cargo to remove.
 *    @param jettison Should we jettison the cargo?
 *    @return 0 on success (cargo removed).
 */
int pilot_rmMissionCargo(Pilot* pilot, unsigned int cargo_id, int jettison) {
  int i;

  /* Check if pilot has it. */
  for(i = 0; i < pilot->ncommodities; i++)
    if(pilot->commodities[i].id == cargo_id)
      break;

  if(i >= pilot->ncommodities)
    return 1; /* Pilot doesn't have it. */

  if(jettison)
    commodity_Jettison(pilot->id, pilot->commodities[i].commodity,
        pilot->commodities[i].quantity);

  /* Remove cargo. */
  pilot->cargo_free += pilot->commodities[i].quantity;
  pilot->solid->mass -= pilot->commodities[i].quantity;
  memmove(&pilot->commodities[i], &pilot->commodities[i+1],
      sizeof(PilotCommodity) * (pilot->ncommodities-i-1));

  pilot->ncommodities--;
  pilot->commodities = realloc(pilot->commodities,
      sizeof(PilotCommodity) * pilot->ncommodities);

  return 0;
}

/* Try to get rid of quantity cargo from pilot, */
/* return quantity actually removed. */
int pilot_rmCargo(Pilot* pilot, Commodity* cargo, int quantity) {
  int i, q;

  /* Check if pilot has it. */
  q = quantity;
  for(i = 0; i < pilot->ncommodities; i++)
    /* Doesn't remove mission cargo. */
    if(!pilot->commodities[i].id && (pilot->commodities[i].commodity == cargo)) {
      if(quantity >= pilot->commodities[i].quantity) {
        q = pilot->commodities[i].quantity;

        /* Remove cargo. */
        memmove(pilot->commodities+i, pilot->commodities+i+1,
                sizeof(PilotCommodity)*(pilot->ncommodities-i));
        pilot->ncommodities--;
        pilot->commodities = realloc(pilot->commodities,
                                     sizeof(PilotCommodity)*pilot->ncommodities);
      } else
        pilot->commodities[i].quantity -= q;

      pilot->cargo_free += q;
      pilot->solid->mass -= q;
      return q;
    }

  return 0; /* Pilot didn't have it. */
}

/**
 * @fn void pilot_addHook(Pilot* pilot, int type, int hook)
 *
 * @brief Add a hook to the pilot.
 *    @param pilot Pilot to add the hook to.
 *    @param type Type of the hook to add.
 *    @param hook ID of the hook to add.
 */
void pilot_addHook(Pilot* pilot, int type, int hook) {
  int i;

  for(i = 0; i < PILOT_HOOKS; i++) {
    if(pilot->hook_type[i] == PILOT_HOOK_NONE) {
      pilot->hook_type[i] = type;
      pilot->hook[i] = hook;
      return;
    }
  }
  WARN("Pilot has maximum amount of hooks, cannot add another.");
}

/**
 * @fn void pilot_init(Pilot* pilot, Ship* ship, char* name, int faction,
 *                     char* ai, const double dir, const Vec2* pos,
 *                     const Vec2* vel, const int flags)
 *
 * @brief Initialize pilot.
 *    @param ship Ship pilot will be flying.
 *    @param name Pilots name, if NULL ships name will be used.
 *    @param faction Faction of the pilot.
 *    @param ai Name of the AI profile to use for the pilot.
 *    @param dir Initial direction to face (radians).
 *    @param vel Initial velocity.
 *    @param pos Initial position.
 *    @param flags Used for tweaking the pilot.
 */
void pilot_init(Pilot* pilot, Ship* ship, char* name, int faction,
                char* ai, const double dir, const Vec2* pos,
                const Vec2* vel, const unsigned int flags) {

  ShipOutfit* so;

  /* Clear memory. */
  memset(pilot, 0, sizeof(Pilot));

  if(flags & PILOT_PLAYER) /* Player is ID 0 */
    pilot->id = PLAYER_ID;
  else
    pilot->id = ++pilot_id; /* New unique pilot id based on pilot_id, Can't be 0. */

  /* Basic information. */
  pilot->ship = ship;
  pilot->name = strdup((name == NULL) ? ship->name : name);

  /* Faction. */
  pilot->faction = faction;

  /* Solid. */
  pilot->solid = solid_create(ship->mass, dir, pos, vel);

  /* Outfits. */
  if(!(flags & PILOT_NO_OUTFITS)) {
    if(ship->outfit) {
      pilot->noutfits = 0;
      for(so = ship->outfit; so; so = so->next) {
        pilot->outfits = realloc(pilot->outfits, (pilot->noutfits+1)*sizeof(PilotOutfit));
        pilot->outfits[pilot->noutfits].outfit = so->data;
        pilot->outfits[pilot->noutfits].quantity = so->quantity;
        pilot->outfits[pilot->noutfits].timer = 0;
        (pilot->noutfits)++;
        if(outfit_isTurret(so->data)) /* Used to speed up AI a bit. */
          pilot_setFlag(pilot, PILOT_HASTURRET);
      }
    }
  }

  /* Cargo must be set before calcStats. */
  pilot->cargo_free = pilot->ship->cap_cargo; /* should get redone with calcCargo. */

  /* Set the pilot stats based on her ship and outfits. */
  /* Hack to have full armour/shield/energy/fuel. */
  pilot->armour = pilot->armour_max = 1.;
  pilot->shield = pilot->shield_max = 1.;
  pilot->energy = pilot->energy_max = 1.;
  pilot->fuel   = pilot->fuel_max   = 1.;
  pilot_calcStats(pilot);

  /* Set flags and functions. */
  if(flags & PILOT_PLAYER) {
    pilot->think = player_think; /* Players don't need to thing! :P */
    pilot->render = NULL; /* Render will be called from player_think */
    pilot_setFlag(pilot, PILOT_PLAYER); /* It's a player! */
    /* Bit of a hack. */
    if(!(flags & PILOT_EMPTY)) {
      player = pilot;
      gui_load(pilot->ship->gui); /* Load the GUI. */
    }
  } else {
    pilot->think = ai_think;
    pilot->render = pilot_render;
  }
  /* Set enter hyperspace flag if needed. */
  if(flags & PILOT_HYP_END)
    pilot_setFlag(pilot, PILOT_HYP_END);

  /* All update the same way. */
  pilot->update = pilot_update;

  /* Escort stuff. */
  if(flags & PILOT_ESCORT) {
    pilot_setFlag(pilot, PILOT_ESCORT);
    if(flags & PILOT_CARRIED)
      pilot_setFlag(pilot, PILOT_CARRIED);
  }

  /* AI. */
  pilot->target = pilot->id;  /* Self = no target. */
  if(ai != NULL)
    ai_pinit(pilot, ai); /* Must run before ai_create. */
}

/**
 * @fn unsigned int pilot_create(Ship* ship, char* name, int faction,
 *                         char* ai, const double dir, const Vec2* pos,
 *                         const Vec2* vel, const int flags)
 *
 * @brief Create a new pilot.
 *
 * See pilot_init for parameters.
 *    @return Pilots id.
 *
 * @sa pilot_init
 */
unsigned int pilot_create(Ship* ship, char* name, int faction,
                          char* ai,  const double dir, const Vec2* pos,
                          const Vec2* vel, const unsigned int flags) {

  Pilot* dyn;

  dyn = MALLOC_L(Pilot);
  if(dyn == NULL) {
    WARN("Unable to allocate memory.");
    return 0;
  }
  pilot_init(dyn, ship, name, faction, ai, dir, pos, vel, flags);

  /* See if memory needs to grow. */
  if(pilot_nstack+1 > pilot_mstack) { /* Needs to grow. */
    pilot_mstack += PILOT_CHUNK;
    pilot_stack = realloc(pilot_stack, pilot_mstack*sizeof(Pilot*));
  }

  /* Set the pilot in the stack. */
  pilot_stack[pilot_nstack] = dyn;
  pilot_nstack++; /* There's a new pilot. */

  return dyn->id;
}

/**
 * @fn Pilot* pilot_CreateEmpty(Ship* ship, char* name,
 *                          int faction, char* ai, const int flags)
 *
 * @brief Create a pilot without adding it to the stack.
 *    @param ship Ship for the pilot to use.
 *    @param name Name of the pilot ship (NULL uses ship name).
 *    @param faction Faction of the ship.
 *    @param ai AI to use.
 *    @param flags Flags for tweaking, PILOT_EMPTY is added.
 *    @return Pointer to the new pilot (not added to stack).
 */
Pilot* pilot_createEmpty(Ship* ship, char* name,
    int faction, char* ai, const unsigned int flags) {
  
  Pilot* dyn;
  dyn = MALLOC_L(Pilot);
  pilot_init(dyn, ship, name, faction, ai, 0., NULL, NULL, flags | PILOT_EMPTY);
  return dyn;
}

/**
 * @fn Pilot* pilot_copy(Pilot* src)
 *
 * @brief Copies src pilot to dest.
 *    @param src Pilot to copy.
 *    @return Copy of src.
 */
Pilot* pilot_copy(Pilot* src) {
  Pilot* dest = malloc(sizeof(Pilot));
  memcpy(dest, src, sizeof(Pilot));
  if(src->name) dest->name = strdup(src->name);

  /* Solid. */
  dest->solid = malloc(sizeof(Solid));
  memcpy(dest->solid, src->solid, sizeof(Solid));

  /* Copy outfits. */
  dest->outfits = malloc(sizeof(PilotOutfit)*src->noutfits);
  memcpy(dest->outfits, src->outfits,
      sizeof(PilotOutfit)*src->noutfits);
  dest->secondary   = NULL;
  dest->ammo        = NULL;
  dest->afterburner = NULL;

  /* Copy commodities. */
  dest->commodities = malloc(sizeof(PilotCommodity)*src->ncommodities);
  memcpy(dest->commodities, src->commodities,
      sizeof(PilotCommodity)*src->ncommodities);

  /* Ai is not copied. */
  dest->task = NULL;

  /* Will set afterburner and correct stats. */
  pilot_calcStats(dest);

  return dest;
}

/**
 * @fn void pilot_free(Pilot* p)
 *
 * @brief Free and cleans up a pilot.
 *    @param p Pilot to free.
 */
void pilot_free(Pilot* p) {
  int i;

  /* Clear up pilot hooks. */
  for(i = 0; i < PILOT_HOOKS; i++)
    if(p->hook_type[i] != PILOT_HOOK_NONE)
      hook_rm(p->hook[i]);

  /* Clean up data. */
  if(p->ai != NULL)
    ai_destroy(p); /* Must be destroyed first if applicable. */
  if(player == p) player = NULL;
  solid_free(p->solid);
  if(p->outfits) free(p->outfits);
  free(p->name);
  if(p->commodities) free(p->commodities);
  if(p->escorts)     free(p->escorts);
  free(p);
}

/**
 * @fn void pilot_destroy(Pilot* p)
 *
 * @brief Destroy pilot from stack.
 *    @param p Pilot to destroy.
 */
void pilot_destroy(Pilot* p) {
  int i;

  /* Find the pilot. */
  for(i = 0; i < pilot_nstack; i++)
    if(pilot_stack[i] == p)
      break;

  /* Pilot is eliminated. */
  pilot_free(p);
  pilot_nstack--;

  /* Copy other pilots down. */
  memmove(&pilot_stack[i], &pilot_stack[i+1], (pilot_nstack-i)*sizeof(Pilot*));
}

/**
 * @fn voud pilots_free(void)
 *
 * @brief Free the pilot stack.
 */
void pilots_free(void) {
  int i;
  for(i = 0; i < pilot_nstack; i++)
    pilot_free(pilot_stack[i]);
  free(pilot_stack);
  pilot_stack = NULL;
  player = NULL;
  pilot_nstack = 0;
}

/**
 * @fn void pilots_clean(void)
 *
 * @brief Clean up the pilot stack - Leave the player.
 */
void pilots_clean(void) {
  int i;
  for(i = 0; i < pilot_nstack; i++)
    /* We'll set player at priveleged position. */
    if((player != NULL) && (pilot_stack[i] == player)) {
      pilot_stack[0] = player;
      pilot_stack[0]->lockons = 0; /* Clear lockons. */
    }
    else /* Rest get killed. */
      pilot_free(pilot_stack[i]);

  if(player != NULL) /* Set stack to 1 if pilot exists. */
    pilot_nstack = 1;
}

/**
 * @fn void player_cleanAll(void)
 *
 * @brief Even cleans up the player.
 */
void pilots_cleanAll(void) {
  pilots_clean();
  if(player != NULL) {
    pilot_free(player);
    player = NULL;
  }
  pilot_nstack = 0;
}

/**
 * @fn void pilots_update(double dt)
 *
 * @brief Updates all the pilots.
 *    @param dt Delta tick for the update.
 */
void pilots_update(double dt) {
  int i;
  Pilot* p;

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

    /* See if should think. */
    if(p->think && !pilot_isDisabled(p)) {
      /* Hyperspace gets special treatment. */
      if(pilot_isFlag(p, PILOT_HYP_PREP))
        pilot_hyperspace(p);
      /* Entering hyperspace. */
      else if(pilot_isFlag(p, PILOT_HYP_END)) {
        if(VMOD(p->solid->vel) < 2*p->speed)
          pilot_rmFlag(p, PILOT_HYP_END);
      }
      else
        p->think(p);
    }
    if(p->update) { /* Update. */
      if(pilot_isFlag(p, PILOT_DELETE))
        pilot_destroy(p);
      else
        p->update(p, dt);
    }
  }
}

/**
 * @fn void pilots_render(void)
 *
 * @brief Render all the pilots.
 */
void pilots_render(void) {
  int i;
  for(i = 0; i < pilot_nstack; i++) {
    if(player == pilot_stack[i]) continue; /* Skip the player. */
    if(pilot_stack[i]->render != NULL) /* Render. */
      pilot_stack[i]->render(pilot_stack[i]);
  }
}

/* Parse the fleet node. */
static int fleet_parse(Fleet* tmp, const xmlNodePtr parent) {
  xmlNodePtr cur, node;
  FleetPilot* pilot;
  char* c;
  int mem;
  node = parent->xmlChildrenNode;

  /* Sane defaults and clean up. */
  memset(tmp, 0, sizeof(Fleet));
  tmp->faction = -1;

  tmp->name = (char*)xmlGetProp(parent, (xmlChar*)"name"); /* Already mallocs. */
  if(tmp->name == NULL) WARN("Fleet in "FLEET_DATA" has invalid or no name");

  do {
    /* Load all the data. */
    if(xml_isNode(node, "faction"))
      tmp->faction = faction_get(xml_get(node));
    else if(xml_isNode(node, "ai"))
      tmp->ai = xml_getStrd(node);
    else if(xml_isNode(node, "pilots")) {
      cur = node->children;
      mem = 0;
      do {
        if(xml_isNode(cur, "pilot")) {
          /* See if we must grow. */
          tmp->npilots++;
          if(tmp->npilots > mem) {
            mem += CHUNK_SIZE;
            tmp->pilots = realloc(tmp->pilots, mem * sizeof(FleetPilot));
          }
          pilot = &tmp->pilots[tmp->npilots-1];

          /* Clear memory. */
          memset(pilot, 0, sizeof(FleetPilot));

          /* Check for name override. */
          xmlr_attr(cur, "name", c);
          pilot->name = c; /* No need to free since it will have to later. */

          /* Check for ai override. */
          xmlr_attr(cur, "ai", pilot->ai);

          /* Load pilots ship. */
          pilot->ship = ship_get(xml_get(cur));
          if(pilot->ship == NULL)
            WARN("Pilot %s in Fleet %s has null ship", pilot->name, tmp->name);

          /* Load chance. */
          xmlr_attr(cur, "chance", c);
          pilot->chance = atoi(c);
          if(pilot->chance == 0)
            WARN("Pilot %s in Fleet %s has 0%% chance of appearing",
                 pilot->name, tmp->name);

          if(c != NULL)
            free(c); /* Free the external malloc. */
        }
      } while(xml_nextNode(cur));

      /* Resize to minimum. */
      tmp->pilots = realloc(tmp->pilots, sizeof(FleetPilot)*tmp->npilots);
    }
  } while(xml_nextNode(node));
#define MELEMENT(o,s) if(o) WARN("Fleet '%s' missing '"s"' element", tmp->name)
  MELEMENT(tmp->ai==NULL,       "ai");
  MELEMENT(tmp->faction==-1, "faction");
  MELEMENT(tmp->pilots==NULL,   "pilots");
#undef MELEMENT

  return 0;
}

/* Load the fleets. */
int fleet_load(void) {
  int mem;
  uint32_t bufsize;
  char* buf = pack_readfile(DATA, FLEET_DATA, &bufsize);

  xmlNodePtr node;
  xmlDocPtr doc = xmlParseMemory(buf, bufsize);

  node = doc->xmlChildrenNode; /* Ships node. */
  if(strcmp((char*)node->name, XML_ID)) {
    ERR("Malformed "FLEET_DATA" file: missing root element '"XML_ID"'");
    return -1;
  }
  node = node->xmlChildrenNode; /* First ship node. */
  if(node == NULL) {
    ERR("Malformed "FLEET_DATA" file: does not contain elements");
    return -1;
  }

  mem = 0;
  do {
    if(xml_isNode(node, XML_FLEET)) {
      /* See if memory must grow. */
      nfleets++;
      if(nfleets > mem) {
        mem += CHUNK_SIZE;
        fleet_stack = realloc(fleet_stack, sizeof(Fleet)*mem);
      }

      /* Load the fleet. */
      fleet_parse(&fleet_stack[nfleets-1], node);
    }
  } while(xml_nextNode(node));

  /* Shrink to minimum. */
  fleet_stack = realloc(fleet_stack, sizeof(Fleet)*nfleets);

  xmlFreeDoc(doc);
  free(buf);

  DEBUG("Loaded %d fleet%s", nfleets, (nfleets==1) ? "" : "s");

  return 0;
}

/* Free the fleets. */
void fleet_free(void) {
  int i, j;
  if(fleet_stack != NULL) {
    for(i = 0; i < nfleets; i++) {
      for(j = 0; j < fleet_stack[i].npilots; j++) {
        if(fleet_stack[i].pilots[j].name)
          free(fleet_stack[i].pilots[j].name);
        if(fleet_stack[i].pilots[j].ai)
          free(fleet_stack[i].pilots[j].ai);
      }
      free(fleet_stack[i].name);
      free(fleet_stack[i].pilots);
      free(fleet_stack[i].ai);
    }
    free(fleet_stack);
    fleet_stack = NULL;
  }
  nfleets = 0;
}