diff --git a/src/llua_pilot.c b/src/llua_pilot.c
new file mode 100644
index 0000000..4260f67
--- /dev/null
+++ b/src/llua_pilot.c
@@ -0,0 +1,417 @@
+/**
+ * @file llua_space.c
+ *
+ * @brief Handle the Lua space bindings.
+ *
+ * These bindings control the planets and systems.
+ */
+
+#include "lauxlib.h"
+#include "lephisto.h"
+#include "llua_space.h"
+#include "lluadef.h"
+#include "rng.h"
+#include "pilot.h"
+#include "pilot.h"
+#include "llua_pilot.h"
+
+static int pilotL_createmetatable(lua_State* L);
+/* Pilots. */
+static int pilot_addFleet(lua_State* L);
+static int pilot_clear(lua_State* L);
+static int pilot_toggleSpawn(lua_State* L);
+static const luaL_reg pilot_methods[] = {
+  { "add",    pilot_addFleet },
+  { "clear",  pilot_clear },
+  { "clear",  pilot_toggleSpawn },
+  { 0, 0 }
+}; /**< Pilot lua methods. */
+
+static int pilotL_eq(lua_State* L);
+static int pilotL_name(lua_State* L);
+static int pilotL_rename(lua_State* L);
+static int pilotL_position(lua_State* L);
+static int pilotL_warp(lua_State* L);
+static const luaL_reg pilotL_methods[] = {
+  { "__eq",   pilotL_eq },
+  { "name",   pilotL_name },
+  { "rename", pilotL_rename },
+  { "pos",    pilotL_position },
+  { "warp",   pilotL_warp },
+  { 0, 0 }
+}; /**< Pilot metatable methods. */
+
+/**
+ * @fn int lua_loadPilot(lua_State* L, int readonly)
+ *
+ * @brief Load the space library.
+ *    @param L State to load space library into.
+ *    @return 0 on success.
+ */
+int lua_loadPilot(lua_State* L, int readonly) {
+  if(readonly) /* Nothing is read only. */
+    return 0;
+
+  /* Register the functions. */
+  luaL_register(L, "pilot", pilot_methods);
+
+  /* Register the metatables. */
+  pilotL_createmetatable(L);
+
+  return 0;
+}
+
+/**
+ * @fn static int pilotL_createmetatable(lua_State* L)
+ *
+ * @brief Registers the pilot metatable.
+ *    @param L Lua state to register metatable in.
+ *    @return 0 on success.
+ */
+static int pilotL_createmetatable(lua_State* L) {
+  /* Create the metatable. */
+  luaL_newmetatable(L, PILOT_METATABLE);
+
+  /* Create the access table. */
+  lua_pushvalue(L, -1);
+  lua_setfield(L, -2, "__index");
+
+  /* Register the values. */
+  luaL_register(L, NULL, pilotL_methods);
+
+  return 0;
+}
+
+/**
+ * @defgroup PILOT Pilot Lua bindings.
+ *
+ * @brief Lua bindings to interact with pilots.
+ *
+ * Functions should be called like:
+ *
+ * @code
+ * pilot.function(parameters)
+ * @endcode
+ */
+/**
+ * @defgroup META_PILOT Pilot Metatable
+ *
+ * @brief Represents a pilot in Lua.
+ *
+ * To call members of the metatable always use:
+ *
+ * @code
+ * pilot:function(param)
+ * @endcode
+ */
+
+/**
+ * @fn LuaPilot* lua_topilot(lua_State* L, int ind)
+ *
+ * @brief Get pilot at index.
+ *    @param L Lua state to get pilot from.
+ *    @param ind Index position to find the pilot.
+ *    @return Pilot found at the index in the state.
+ */
+LuaPilot* lua_topilot(lua_State* L, int ind) {
+  if(lua_isuserdata(L, ind)) {
+    return (LuaPilot*)lua_touserdata(L, ind);
+  }
+  luaL_typerror(L, ind, PILOT_METATABLE);
+  return NULL;
+}
+
+/**
+ * @fn LuaPilot* lua_pushpilot(lua_State* L, LuaPilot pilot)
+ *
+ * @brief Push a pilot on the stack.
+ *    @param L Lua state to push pilot into.
+ *    @param pilot Pilot to push.
+ *    @return Newly pushed pilot.
+ */
+LuaPilot* lua_pushpilot(lua_State* L, LuaPilot pilot) {
+  LuaPilot* p;
+  p = (LuaPilot*)lua_newuserdata(L, sizeof(LuaPilot));
+  *p = pilot;
+  luaL_getmetatable(L, PILOT_METATABLE);
+  lua_setmetatable(L, -2);
+  return p;
+}
+
+/**
+ * @fn int lua_ispilot(lua_State* L, int ind)
+ *
+ * @brief Check to see if ind is a pilot.
+ *    @param L Lua state to check.
+ *    @param ind Index position to check.
+ *    @return 1 if ind is a pilot.
+ */
+int lua_ispilot(lua_State* L, int ind) {
+  int ret;
+
+  if(lua_getmetatable(L, ind)==0)
+    return 0;
+  lua_getfield(L, LUA_REGISTRYINDEX, PILOT_METATABLE);
+  
+  ret = 0;
+  if(lua_rawequal(L, -1, -2)) /* Does it have the correct mt?*/
+    ret = 1;
+
+  lua_pop(L, 2); /* Remove both metatables. */
+  return ret;
+}
+
+/**
+ * @fn static int pilot_addFleet(lua_State* L)
+ * @ingroup PILOT
+ *
+ * @brief table add(string fleetname [, string ai ])
+ *
+ * Adds a fleet to the system.
+ *    @param fleetname Name of the fleet to add.
+ *    @param ai If set will override the standard fleet AI.
+ *    @return Table populated with all the identifiers of the pilots created.
+ */
+static int pilot_addFleet(lua_State* L) {
+  LLUA_MIN_ARGS(1);
+  Fleet* flt;
+  char* fltname, *fltai;
+  int i, j;
+  unsigned int p;
+  double a;
+  Vec2 vv, vp, vn;
+  FleetPilot* plt;
+  LuaPilot lp;
+
+
+  /* Parse first argument - Fleet name */
+  if(lua_isstring(L, 1)) fltname = (char*) lua_tostring(L, 1);
+  else LLUA_INVALID_PARAMETER();
+
+  /* Parse second argument - Fleet IA override. */
+  if((lua_gettop(L) > 1) && lua_isstring(L, 2))
+    fltai = (char*)lua_tostring(L, 2);
+  else fltai = NULL;
+
+  /* Pull the fleet. */
+  flt = fleet_get(fltname);
+  if(flt == NULL) {
+    LLUA_DEBUG("Fleet not found!");
+    return 0;
+  }
+
+  /* This should probably be done better. */
+  vect_pset(&vp, RNG(MIN_HYPERSPACE_DIST, MIN_HYPERSPACE_DIST*1.5),
+      RNG(0,360)*M_PI/180.);
+  vectnull(&vn);
+
+  j = 0;
+  lua_newtable(L);
+  for(i = 0; i < flt->npilots; i++) {
+    plt = &flt->pilots[i];
+
+    if(RNG(0, 100) <= plt->chance) {
+      /* Fleet displacement. */
+      vect_cadd(&vp, RNG(75, 150) * (RNG(0,1) ? 1 : -1),
+          RNG(75, 150) * (RNG(0,1) ? 1 : -1));
+
+      a = vect_angle(&vp, &vn);
+      vectnull(&vv);
+      p = pilot_create(plt->ship,
+          plt->name,
+          flt->faction,
+          (fltai != NULL) ? /* Lua AI override. */
+            ai_getProfile(fltai) :
+            (plt->ai != NULL) ? /* Pilot AI override */
+            plt->ai : flt->ai,
+          a,
+          &vp,
+          &vv,
+          0);
+
+      /* We push each pilot created into a table and return it. */
+      lua_pushnumber(L, ++j); /* Index, starts with 1. */
+      lp.pilot = p;
+      lua_pushpilot(L, lp);   /* value = LuaPilot */
+      lua_rawset(L, -3);      /* Store the value in the table. */
+    }
+  }
+
+  return 1;
+
+}
+
+/**
+ * @fn static int pilot_clear(lua_State* L)
+ * @ingroup PILOT
+ *
+ * @brief clear(nil)
+ *
+ * Clears the current system of pilots. Used for epic battles and such.
+ */
+static int pilot_clear(lua_State* L) {
+  (void) L;
+  pilots_clean();
+  return 0;
+}
+
+/**
+ * @fn static int pilot_toggleSpawn(lua_State* L)
+ * @ingroup PILOT
+ *
+ * @brief bool togglespawn( [bool enable] )
+ *
+ * Disable or enable pilot spawning in the current system. If player jumps
+ * the spawn is enabled again automatically.
+ *    @param enable true enables spawn, false disables it.
+ *    @return The current spawn state.
+ */
+static int pilot_toggleSpawn(lua_State* L) {
+  /* Setting it directly. */
+  if((lua_gettop(L) > 0) && lua_isboolean(L, 1))
+      space_spawn = lua_toboolean(L, 1);
+    /* Toggling. */
+  else
+    space_spawn = !space_spawn;
+
+  lua_pushboolean(L, space_spawn);
+  return 1;
+}
+
+/**
+ * @fn static int pilotL_eq(lua_State* L)
+ *
+ * @brief bool __eq(Pilot p)
+ *
+ * Check to see if pilot and p are the same.
+ *    @param p Pilot to compare against.
+ *    @return true if they are the same.
+ */
+static int pilotL_eq(lua_State* L) {
+  LLUA_MIN_ARGS(2);
+  LuaPilot* p1, *p2;
+
+  /* Get parameters. */
+  p1 = lua_topilot(L, 1);
+  if(lua_ispilot(L, 2))
+    p2 = lua_topilot(L, 2);
+  else LLUA_INVALID_PARAMETER();
+
+  /* Push result. */
+  lua_pushboolean(L, p1->pilot == p2->pilot);
+  return 1;
+}
+
+/**
+ * @fn static int pilotL_name(lua_State* L)
+ * @ingroup META_PILOT
+ *
+ * @brief string name(nil)
+ *
+ * Get the pilots current name.
+ *    @return The current name of the pilot.
+ */
+static int pilotL_name(lua_State* L) {
+  LLUA_MIN_ARGS(1);
+  LuaPilot* p1;
+  Pilot* p;
+
+  /* Parse parameters. */
+  p1 = lua_topilot(L, 1);
+  p = pilot_get(p1->pilot);
+
+  /* Pilot must exist. */
+  if(p == NULL) return 0;
+
+  /* Get name. */
+  lua_pushstring(L, p->name);
+  return 1;
+}
+
+/**
+ * @fn static int pilotL_rename(lua_State* L)
+ *
+ * @brief rename(string name)
+ *
+ * Changes the pilots name.
+ *    @param name Name to change to.
+ */
+static int pilotL_rename(lua_State* L) {
+  LLUA_MIN_ARGS(2);
+  LuaPilot* p1;
+  char* name;
+  Pilot* p;
+
+  /* Parse parameters. */
+  p1 = lua_topilot(L, 1);
+  p = pilot_get(p1->pilot);
+  if(lua_isstring(L, 2))
+    name = (char*)lua_tostring(L, 2);
+  else LLUA_INVALID_PARAMETER();
+
+  /* Pilot must exist. */
+  if(p == NULL) return 0;
+
+  /* Change name. */
+  if(p->name != NULL)
+    free(p->name);
+  p->name = strdup(name);
+
+  return 0;
+}
+
+/**
+ * @fn static int pilotL_position(lua_State* L)
+ *
+ * @brief Vec2 position( nil )
+ *
+ * Get the pilots position.
+ *    @return The pilots current position.
+ */
+static int pilotL_position(lua_State* L) {
+  LLUA_MIN_ARGS(1);
+  LuaPilot* p1;
+  Pilot* p;
+  LuaVector v;
+
+  /* Parse parameters */
+  p1 = lua_topilot(L, 1);
+  p = pilot_get(p1->pilot);
+
+  /* Pilot must exist. */
+  if(p == NULL) return 0;
+
+  /* Push position. */
+  vectcpy(&v.vec, &p->solid->pos);
+  lua_pushvector(L, v);
+  return 1;
+}
+
+/**
+ * @fn static int pilotL_warp(lua_State* L)
+ * @ingroup META_PILOT
+ *
+ * @brief Vec2 position.
+ *    @return The pilots current position.
+ */
+static int pilotL_warp(lua_State* L) {
+  LLUA_MIN_ARGS(2);
+  LuaPilot* p1;
+  Pilot* p;
+  LuaVector* v;
+
+  /* Parse parameters. */
+  p1 = lua_topilot(L, 1);
+  p = pilot_get(p1->pilot);
+  if(lua_isvector(L,2))
+    v = lua_tovector(L, 2);
+  else LLUA_INVALID_PARAMETER();
+
+  /* Pilot must exist. */
+  if(p == NULL) return 0;
+
+  /* Warp pilot to new position. */
+  vectcpy(&p->solid->pos, &v->vec);
+  return 0;
+}
+
diff --git a/src/llua_pilot.h b/src/llua_pilot.h
new file mode 100644
index 0000000..6b8d762
--- /dev/null
+++ b/src/llua_pilot.h
@@ -0,0 +1,18 @@
+#pragma once
+#include "lua.h"
+#include "pilot.h"
+
+#define PILOT_METATABLE "Pilot"
+
+/* Lua wrappers. */
+typedef struct LuaPilot_s {
+  unsigned int pilot;
+} LuaPilot;
+
+/* Library loading. */
+int lua_loadPilot(lua_State* L, int readonly);
+
+LuaPilot* lua_topilot(lua_State* L, int ind);
+LuaPilot* lua_pushpilot(lua_State* L, LuaPilot pilot);
+int lua_ispilot(lua_State* L, int ind);
+
diff --git a/src/misn_lua.c b/src/misn_lua.c
index abd629f..22d7a8e 100644
--- a/src/misn_lua.c
+++ b/src/misn_lua.c
@@ -8,6 +8,7 @@
 
 #include "llua.h"
 #include "llua_space.h"
+#include "llua_pilot.h"
 #include "lluadef.h"
 #include "hook.h"
 #include "mission.h"
@@ -146,20 +147,6 @@ static const luaL_reg hook_methods[] = {
   { 0, 0 }
 };
 
-/* Pilots. */
-static int pilot_addFleet(lua_State* L);
-static int pilot_rename(lua_State* L);
-static int pilot_clear(lua_State* L);
-static int pilot_toggleSpawn(lua_State* L);
-static const luaL_reg pilot_methods[] = {
-  { "add",          pilot_addFleet },
-  { "rename",       pilot_rename },
-  { "clear",        pilot_clear },
-  { "togglespawn",  pilot_toggleSpawn },
-  { "clear",        pilot_toggleSpawn },
-  { 0, 0 }
-};
-
 /* Register all the libaries. */
 int misn_loadLibs(lua_State* L) {
   lua_loadLephisto(L);
@@ -171,7 +158,7 @@ int misn_loadLibs(lua_State* L) {
   lua_loadRnd(L);
   lua_loadTk(L);
   lua_loadHook(L);
-  lua_loadPilot(L);
+  lua_loadPilot(L, 0);
   return 0;
 }
 
@@ -210,11 +197,6 @@ int lua_loadHook(lua_State* L) {
   return 0;
 }
 
-int lua_loadPilot(lua_State* L) {
-  luaL_register(L, "pilot", pilot_methods);
-  return 0;
-}
-
 /* 
  * Run a mission function.
  * -1 on error, 1 on misn.finish() call and 0 normally.
@@ -773,7 +755,7 @@ static int hook_enter(lua_State* L) {
  * @fn static int hook_pilot(lua_State* L)
  * @ingroup HOOK
  *
- * @brief number pilot(number pilot, string type, string func)
+ * @brief number pilot(Pilot pilot, string type, string func)
  *
  * Hooks the function to a specific pilot.
  *
@@ -790,12 +772,13 @@ static int hook_enter(lua_State* L) {
  */
 static int hook_pilot(lua_State* L) {
   LLUA_MIN_ARGS(2);
-  unsigned int h, p;
+  unsigned int h;
+  LuaPilot* p;
   int type;
   char* hook_type;
 
   /* First parameter - pilot to hook. */
-  if(lua_isnumber(L, 1)) p = (unsigned int)lua_tonumber(L, 1);
+  if(lua_ispilot(L, 1)) p = lua_topilot(L, 1);
   else LLUA_INVALID_PARAMETER();
 
   /* Second parameer - Hook name. */
@@ -815,122 +798,8 @@ static int hook_pilot(lua_State* L) {
 
   /* Actually add the hook. */
   h = hook_generic(L, hook_type);
-  pilot_addHook(pilot_get(p), type, h);
+  pilot_addHook(pilot_get(p->pilot), type, h);
 
   return 0;
 }
 
-/* -- Pilot. -- */
-static int pilot_addFleet(lua_State* L) {
-  LLUA_MIN_ARGS(1);
-  Fleet* flt;
-  char* fltname, *fltai;;
-  int i, j;
-  unsigned int p;
-  double a;
-  Vec2 vv, vp, vn;
-  FleetPilot* plt;
-
-  /* Parse first argument - Fleet name. */
-  if(lua_isstring(L, 1)) fltname = (char*) lua_tostring(L, 1);
-  else LLUA_INVALID_PARAMETER();
-
-  /* Parse second arguement - Fleet AI override. */
-  if(lua_isstring(L, 2)) fltai = (char*) lua_tostring(L, 2);
-  else fltai = NULL;
-
-  /* Pull the fleet. */
-  flt = fleet_get(fltname);
-  if(flt == NULL) {
-    LLUA_DEBUG("Fleet not found!");
-    return 0;
-  }
-
-  /* This should probably be done better.. */
-  vect_pset(&vp, RNG(MIN_HYPERSPACE_DIST, MIN_HYPERSPACE_DIST*1.5),
-      RNG(0, 360)*M_PI/180.);
-  vectnull(&vn);
-
-  /* Now we start adding pilots and toss ids into the table we return. */
-  j = 0;
-  for(i = 0; i < flt->npilots; i++) {
-    plt = &flt->pilots[i];
-    if(RNG(0, 100) <= plt->chance) {
-      /* Fleet displacement. */
-      vect_cadd(&vp, RNG(75, 150) & (RNG(0, 1) ? 1: -1),
-          RNG(75, 150) * (RNG(0, 1) ? 1 : -1));
-
-      a = vect_angle(&vp, &vn);
-      vectnull(&vv);
-      p = pilot_create(plt->ship,
-          plt->name,
-          flt->faction,
-          (fltai != NULL) ? /* Lua AI override */
-            ai_getProfile(fltai) :
-            (plt->ai != NULL) ? /* Pilot AI override. */
-              plt->ai : flt->ai,
-          a,
-          &vp,
-          &vv,
-          0);
-
-      /* We push each pilot created into a table and return it. */
-      lua_pushnumber(L, ++j); /* Index start with 1. */
-      lua_pushnumber(L, p);   /* value = pilot id. */
-      lua_rawset(L, -3);      /* Store the value in the table. */
-    }
-  }
-  return 1;
-}
-
-static int pilot_rename(lua_State* L) {
-  LLUA_MIN_ARGS(2);
-  char* name;
-  unsigned int id;
-  Pilot* p;
-
-  if(lua_isnumber(L, 1)) id = (unsigned int) lua_tonumber(L, 1);
-  else LLUA_INVALID_PARAMETER();
-  if(lua_isstring(L, 2)) name = (char*) lua_tostring(L, 2);
-  else LLUA_INVALID_PARAMETER();
-
-  p = pilot_get(id);
-  free(p->name);
-  p->name = strdup(name);
-  return 0;
-}
-
-/**
- * @fn static int pilot_clear(lua_State* L)
- *
- * @brief clear(nil)
- *
- * Clears the current  system of pilots. Used for epic battles etc.
- */
-static int pilot_clear(lua_State* L) {
-  (void) L;
-  pilots_clean();
-  return 0;
-}
-
-/**
- * @fn static int pilot_toggleSpawn(lua_State* L)
- *
- * @brief bool togglespawn( [bool enable] )
- *
- * Disable or enable pilot spawning in the current system. If player jumps
- * the spawn is enabled again automagically.
- *    @param enable true Enables spawn, false disables it.
- *    @return The current spawn state.
- */
-static int pilot_toggleSpawn(lua_State* L) {
-  if((lua_gettop(L) > 0) && lua_isboolean(L, 1))
-    space_spawn = lua_toboolean(L, 1);
-  /* Toggling. */
-  else
-    space_spawn = !space_spawn;
-
-  lua_pushboolean(L, space_spawn);
-  return 1;
-}
-
diff --git a/src/misn_lua.h b/src/misn_lua.h
index ee6ab64..f0756c1 100644
--- a/src/misn_lua.h
+++ b/src/misn_lua.h
@@ -15,5 +15,4 @@ int lua_loadMisn(lua_State* L);
 int lua_loadVar(lua_State* L, int readonly);
 int lua_loadPlayer(lua_State* L, int readonly);
 int lua_loadHook(lua_State* L);
-int lua_loadPilot(lua_State* L);