From bed1c183c714a87f12c59b223544843069393547 Mon Sep 17 00:00:00 2001
From: Allanis <allanis.saracraft.studios@gmail.com>
Date: Sun, 21 Jan 2018 00:12:28 +0000
Subject: [PATCH] [Add] Landing on ground based stations. Uses the same geom
 flag 1 thing to indicate docking surface, but they are visible surfaces in
 this case.

---
 src/Makefile.am       |   2 +-
 src/body.h            |   4 +-
 src/gameconsts.h      |   4 ++
 src/l3d.h             |   1 +
 src/main.cpp          |  16 +++----
 src/model_body.cpp    |   4 +-
 src/model_body.h      |   4 +-
 src/planet.cpp        |   2 +-
 src/planet.h          |   2 +-
 src/sbre/models.cpp   |  74 +++++++++++++++++++-----------
 src/ship.cpp          |  26 +++++------
 src/ship.h            |   3 +-
 src/space_station.cpp | 102 +++++++++++++++++++++++++++++++++++++++---
 src/space_station.h   |   9 +++-
 src/star.cpp          |   2 +-
 src/star.h            |   2 +-
 16 files changed, 186 insertions(+), 71 deletions(-)
 create mode 100644 src/gameconsts.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 4af40fd..20b0628 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -9,7 +9,7 @@ include_HEADERS = body.h frame.h generic_system_view.h glfreetype.h gui_button.h
 		gui_radio_group.h gui_screen.h gui_toggle_button.h gui_widget.h libs.h matrix4x4.h mtrand.h l3d.h \
 		planet.h player.h dynamic_body.h sector.h sector_view.h ship_cpanel.h ship.h space.h star.h star_system.h system_info_view.h \
 		system_view.h vector3.h view.h world_view.h date.h space_station.h space_station_view.h model_body.h gui_iselectable.h \
-		ship_type.h object.h info_view.h model_coll_mesh_data.h object_viewer_view.h fixed.h custom_starsystems.h
+		ship_type.h object.h info_view.h model_coll_mesh_data.h object_viewer_view.h fixed.h custom_starsystems.h gameconsts.h
 
 libgui_a_SOURCES = gui_button.cpp gui.cpp gui_fixed.cpp gui_screen.cpp gui_label.cpp gui_toggle_button.cpp gui_radio_button.cpp \
 									 gui_radio_group.cpp gui_image_button.cpp gui_image.cpp gui_image_radio_button.cpp gui_multi_state_image_button.cpp gui_widget.cpp \
diff --git a/src/body.h b/src/body.h
index dd5c51b..48dce3c 100644
--- a/src/body.h
+++ b/src/body.h
@@ -14,13 +14,13 @@ public:
   virtual ~Body(void);
   virtual Object::Type GetType(void) { return Object::BODY; }
   virtual void SetPosition(vector3d p) = 0;
-  virtual vector3d GetPosition(void) = 0; /* Within frame. */
+  virtual vector3d GetPosition(void) const = 0; /* Within frame. */
   virtual void SetVelocity(vector3d v) { assert(0); }
   virtual vector3d GetVelocity(void) { assert(0); return vector3d(0.0); }
   virtual double GetRadius(void) const = 0 ;
   virtual double GetMass(void)   const { assert(0); return 0; }
   virtual void SetRotMatrix(const matrix4x4d& r) {};
-  virtual void GetRotMatrix(matrix4x4d& m) {};
+  virtual void GetRotMatrix(matrix4x4d& m) const {};
   virtual void Render(const Frame* camFrame) = 0;
   virtual void SetFrame(Frame* f) { m_frame = f; }
   /* return true if to we do collision response and apply damage. */
diff --git a/src/gameconsts.h b/src/gameconsts.h
new file mode 100644
index 0000000..1d514f6
--- /dev/null
+++ b/src/gameconsts.h
@@ -0,0 +1,4 @@
+#pragma once
+
+#define MAX_LANDING_SPEED 20.0
+
diff --git a/src/l3d.h b/src/l3d.h
index 6f34726..453fc36 100644
--- a/src/l3d.h
+++ b/src/l3d.h
@@ -5,6 +5,7 @@
 #include "gui.h"
 #include "view.h"
 #include "mtrand.h"
+#include "gameconsts.h"
 
 class Player;
 class SectorView;
diff --git a/src/main.cpp b/src/main.cpp
index 6a36d0c..fe94878 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -227,27 +227,23 @@ void L3D::MainLoop(void) {
     Space::AddBody(body);
   }
 
-  SpaceStation* station = new SpaceStation();
+  SpaceStation* station = new SpaceStation(SpaceStation::JJHOOP);
   station->SetLabel("Poemi-chan's Folly");
   station->SetFrame(stationFrame);
   station->SetPosition(vector3d(0,0,0));
   Space::AddBody(station);
 
-  SpaceStation* station2 = new SpaceStation();
+  SpaceStation* station2 = new SpaceStation(SpaceStation::GROUND_FLAVOURED);;
   station2->SetLabel("Dfighter's point");
   station2->SetFrame(*pframe->m_children.begin()); /* Rotating frame of planet. */
   station2->OrientOnSurface(EARTH_RADIUS, M_PI/4, M_PI/4);
   Space::AddBody(station2);
 
-  player->SetFrame(stationFrame);
-  player->SetPosition(vector3d(0,0,0));
+  player->SetFrame(pframe);
+  //player->SetPosition(vector3d(0,0,0));
+  player->OrientOnSurface(EARTH_RADIUS*1.001, M_PI/4, M_PI/4);
   //player->SetPosition(vector3d(0,0,2000));
   //player->SetFrame(pframe);
-  float ang1 = L3D::rng.Double(0,M_PI);
-  float ang2 = L3D::rng.Double(0,M_PI);
-  double r = EARTH_RADIUS*1.0001;
-  //player->SetPosition(vector3d(r*cos(ang1)*cos(ang2), r*sin(ang1)*cos(ang2), r*sin(ang2)));
-  //player->SetPosition(vector3d(r, 0, 0);
 
   Gui::Init(scrWidth, scrHeight, 640, 480);
 
@@ -263,7 +259,7 @@ void L3D::MainLoop(void) {
   infoView          = new InfoView();
 
   SetView(worldView);
-  player->SetDockedWith(station);
+  player->SetDockedWith(station2);
 
   Uint32 last_stats = SDL_GetTicks();
   int frame_stat = 0;
diff --git a/src/model_body.cpp b/src/model_body.cpp
index 186d0be..b4aa7fa 100644
--- a/src/model_body.cpp
+++ b/src/model_body.cpp
@@ -57,7 +57,7 @@ void ModelBody::SetPosition(vector3d p) {
   }
 }
 
-vector3d ModelBody::GetPosition(void) {
+vector3d ModelBody::GetPosition(void) const {
   const dReal* pos = dGeomGetPosition(geoms[0]);
   return vector3d(pos[0], pos[1], pos[2]);
 }
@@ -96,7 +96,7 @@ void ModelBody::SetRotMatrix(const matrix4x4d& r) {
   }
 }
 
-void ModelBody::GetRotMatrix(matrix4x4d& m) {
+void ModelBody::GetRotMatrix(matrix4x4d& m) const {
   m.LoadFromOdeMatrix(dGeomGetRotation(geoms[0]));
 }
 
diff --git a/src/model_body.h b/src/model_body.h
index 3d51172..d98ab2a 100644
--- a/src/model_body.h
+++ b/src/model_body.h
@@ -14,10 +14,10 @@ public:
   void SetPosition(vector3d p);
   virtual void SetRotMatrix(const matrix4x4d& r);
   /* Not valid to SetVelocity on these. If you want them to move, use a DynamicBody. */
-  vector3d GetPosition(void);
+  vector3d GetPosition(void) const;
   virtual double GetRadius(void) const;
   void TransformToModelCoords(const Frame* camFrame);
-  void GetRotMatrix(matrix4x4d& m);
+  void GetRotMatrix(matrix4x4d& m) const;
   virtual void SetFrame(Frame* f);
   void GeomsSetBody(dBodyID body);
   /* To remove from simulation for a period. */
diff --git a/src/planet.cpp b/src/planet.cpp
index f654675..e080536 100644
--- a/src/planet.cpp
+++ b/src/planet.cpp
@@ -19,7 +19,7 @@ Planet::~Planet(void) {
   dGeomDestroy(geom);
 }
 
-vector3d Planet::GetPosition(void) {
+vector3d Planet::GetPosition(void) const {
   return pos;
 }
 
diff --git a/src/planet.h b/src/planet.h
index 40a428f..0fd8deb 100644
--- a/src/planet.h
+++ b/src/planet.h
@@ -8,7 +8,7 @@ public:
   Planet(StarSystem::SBody*);
   virtual ~Planet(void);
   virtual void SetPosition(vector3d p);
-  virtual vector3d GetPosition(void);
+  virtual vector3d GetPosition(void) const;
   void SetRadius(double radius);
   virtual double GetRadius(void) const { return sbody.GetRadius(); }
   virtual void Render(const Frame* camFrame);
diff --git a/src/sbre/models.cpp b/src/sbre/models.cpp
index 9d68bff..15f0825 100644
--- a/src/sbre/models.cpp
+++ b/src/sbre/models.cpp
@@ -1256,45 +1256,67 @@ static PlainVertex metalFrameTowerVtx1[] = {
   { VTYPE_PLAIN, { -1, 10, -1 } },
 };
 
-static CompoundVertex metalFrameTowerVtx2[] = {
-  { VTYPE_ANIMLIN, { 6, 10, static_cast<uint16>(-1), static_cast<uint16>(-1), 0 } },
-  { VTYPE_ANIMLIN, { 7, 11, static_cast<uint16>(-1), static_cast<uint16>(-1), 0 } },
-  { VTYPE_ANIMLIN, { 8, 12, static_cast<uint16>(-1), static_cast<uint16>(-1), 0 } },
-  { VTYPE_ANIMLIN, { 9, 13, static_cast<uint16>(-1), static_cast<uint16>(-1), 0 } },
-};
-
 static uint16 metalFrameTowerData[] = {
-  PTYPE_CYLINDER, 0x8000, 4, 6, 14, 0, 10,
-  PTYPE_CYLINDER, 0x8000, 4, 7, 15, 0, 10,
-  PTYPE_CYLINDER, 0x8000, 4, 8, 16, 0, 10,
-  PTYPE_CYLINDER, 0x8000, 4, 9, 17, 0, 10,
-  PTYPE_CYLINDER, 0x8000, 4, 6, 15, 0, 10,
-  PTYPE_CYLINDER, 0x8000, 4, 7, 14, 0, 10,
-  PTYPE_CYLINDER, 0x8000, 4, 7, 16, 0, 10,
-  PTYPE_CYLINDER, 0x8000, 4, 8, 15, 0, 10,
-  PTYPE_CYLINDER, 0x8000, 4, 8, 17, 0, 10,
-  PTYPE_CYLINDER, 0x8000, 4, 9, 16, 0, 10,
-  PTYPE_CYLINDER, 0x8000, 4, 6, 17, 0, 10,
-  PTYPE_CYLINDER, 0x8000, 4, 9, 14, 0, 10,
-  PTYPE_CYLINDER, 0x8000, 4, 14, 15, 1, 10,
-  PTYPE_CYLINDER, 0x8000, 4, 15, 16, 1, 10,
-  PTYPE_CYLINDER, 0x8000, 4, 16, 17, 1, 10,
-  PTYPE_CYLINDER, 0x8000, 4, 17, 14, 1, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 6, 10, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 7, 11, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 8, 12, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 9, 13, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 6, 11, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 7, 10, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 7, 12, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 8, 11, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 8, 13, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 9, 12, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 6, 13, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 9, 10, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 10, 11, 1, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 11, 12, 1, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 12, 13, 1, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 13, 10, 1, 10,
   PTYPE_END,
 };
 
-Model metalFrameTowerModel = { 0.1f, 20.0f, 14, metalFrameTowerVtx1, 14, 4, metalFrameTowerVtx2, 0,
+Model metalFrameTowerModel = { 0.1f, 20.0f, 14, metalFrameTowerVtx1, 14, 4, dummyvtx2, 0,
 { { 0, metalFrameTowerData, 0, 0, 0 } } };
 
 static PlainVertex starport1vtx1[] = {
  	{ VTYPE_PLAIN, { 0, 0, 0 } },
  	{ VTYPE_PLAIN, { 0, .01, 0 } },
   { VTYPE_PLAIN, { -0.1, .01, -0.1 } },
+  { VTYPE_PLAIN, { 0, 0, -2 } },
+  { VTYPE_PLAIN, { 0, 0, 2 } },
+  { VTYPE_PLAIN, { 0.5, 0, 0 } },
+  { VTYPE_PLAIN, { -0.5, 0, 0 } },
 };
 
+#if 0
+uint16 PFUNC_COMPSMOOTH
+  uint16 cacheidx
+  uint16 steps
+  uint16 centpos
+  uint16 centnorm
+  uint16 starpos
+  uint16 startnorm
+    uint16 COMP_END
+    uint16 COMP_LINE
+      uint16 pos
+      uint16 norm
+    uint16 COMP_HERMITE
+      uint16 pos
+      uint16 norm
+      uint16 tan0
+      uint16 tan1
+#endif
+
 static uint16 starport1data[] = {
   PTYPE_MATFIXED, 30, 30, 30, 0, 0, 0, 0, 0, 0, 0,
-  PTYPE_CYLINDER, 0x8000, 8, 6, 7, 0, 50,
+  PTYPE_SETCFLAG, 1,
+  PTYPE_COMPFLAT, 0x8000, 20, 6, 1, 11, 1,
+    COMP_HERMITE, 12, 1, 9, 10,
+    COMP_HERMITE, 11, 1, 10, 9,
+    COMP_END,
+  //PTYPE_CYLINDER, 0x8000, 8, 6, 7, 0, 50,
+  PTYPE_SETCFLAG, 0,
   PTYPE_MATFIXED, 100, 100, 100, 0, 0, 0, 0, 0, 0, 0,
   PTYPE_ZBIAS, 6, 1, 0,
   PTYPE_TEXT, 0, 10, 8, 1, 0, 0, 0, 20,
@@ -1304,6 +1326,6 @@ static uint16 starport1data[] = {
   PTYPE_END,
 };
 
-Model starport1model = { 100.0f, 55.0f, 9, starport1vtx1, 9, 0, dummyvtx2, 1,
+Model starport1model = { 100.0f, 55.0f, 13, starport1vtx1, 13, 0, dummyvtx2, 1,
   { { 0, starport1data, 0, 0, 0 } } };
 
diff --git a/src/ship.cpp b/src/ship.cpp
index 1b4019e..428e928 100644
--- a/src/ship.cpp
+++ b/src/ship.cpp
@@ -127,6 +127,7 @@ void Ship::Blastoff(void) {
   dBodySetAngularVel(m_body, 0, 0, 0);
   dBodySetForce(m_body, 0, 0, 0);
   dBodySetTorque(m_body, 0, 0, 0);
+  /* TODO: We need to be able to get sbre aabb. */
   SetPosition(up*planetRadius+10.0*up);
   SetThrusterState(ShipType::THRUSTER_TOP, 1.0f);
 }
@@ -138,7 +139,7 @@ void Ship::TestLanded(void) {
     double speed = vector3d(vel[0], vel[1], vel[2]).Length();
     const double planetRadius = GetFrame()->m_astroBody->GetRadius();
 
-    if(speed < 20) {
+    if(speed < MAX_LANDING_SPEED) {
       /* Orient the damn thing right! 
        * Why is the inverse of the body rot matrix being used?!?!
        * *shrugs* it just works this way.
@@ -188,6 +189,13 @@ void Ship::TimeStepUpdate(const float timeStep) {
 
   m_launchLockTimeout -= timeStep;
   if(m_launchLockTimeout < 0) m_launchLockTimeout = 0;
+  /*
+   * Can't orient ships in SetDockedWith() because it gets
+   * called from ode collision handler, and body is locked
+   * and can't be positioned. Instead we do it every freaking
+   * update which is stupid.
+   */
+  if(m_dockedWith) m_dockedWith->OrientDockedShip(this);
 
   const ShipType& stype = GetShipType();
   for(int i = 0; i < ShipType::THRUSTER_MAX; i++) {
@@ -248,26 +256,14 @@ const ShipType& Ship::GetShipType(void) {
 
 void Ship::SetDockedWith(SpaceStation* s) {
   if(m_dockedWith && !s) {
-    /*
-     * Position player in middle of docking bay, pointing out of it.
-     * TODO Need to do forced thrusting thingy.
-     * TODO ang vel not zeroed for some reason..
-     */
-    matrix4x4d stationRot;
-    m_dockedWith->GetRotMatrix(stationRot);
-    vector3d port_y = vector3d::Cross(-m_dockedWith->port.horiz, m_dockedWith->port.normal);
-    matrix4x4d rot = stationRot * matrix4x4d::MakeRotMatrix(m_dockedWith->port.horiz, port_y, m_dockedWith->port.normal);
-    vector3d pos = m_dockedWith->GetPosition() + stationRot*m_dockedWith->port.center;
-    SetPosition(pos);
-    SetRotMatrix(rot);
-    SetVelocity(vector3d(0, 0, 0));
-    SetAngVelocity(vector3d(0, 0, 0));
+    m_dockedWith->OrientLaunchingShip(this);
     Enable();
 
     m_dockedWith = 0;
   } else {
     m_dockedWith = s;
     m_dockingTimer = 0.0f;
+    if(s->IsGroundStation()) m_flightState = LANDED;
     SetVelocity(vector3d(0, 0, 0));
     SetAngVelocity(vector3d(0, 0, 0));
     Disable();
diff --git a/src/ship.h b/src/ship.h
index 4e9ce00..ec77b00 100644
--- a/src/ship.h
+++ b/src/ship.h
@@ -41,7 +41,8 @@ public:
   virtual void NotifyDeath(const Body* const dyingBody);
   virtual bool OnCollision(Body* b, Uint32 flags);
   enum FlightState { FLYING, LANDED };
-  FlightState GetFlightState(void) { return m_flightState; }
+  FlightState GetFlightState(void) const { return m_flightState; }
+  float GetWheelState(void) const { return m_wheelState; }
 
   class LaserObj : public Object {
   public:
diff --git a/src/space_station.cpp b/src/space_station.cpp
index 4db8b58..6cbdb45 100644
--- a/src/space_station.cpp
+++ b/src/space_station.cpp
@@ -1,8 +1,17 @@
 #include "space_station.h"
 #include "ship.h"
 #include "model_coll_mesh_data.h"
+#include "gameconsts.h"
 
-#define STATION_SBRE_MODEL  65
+struct SpaceStationType {
+  Uint32 sbreModel;
+  enum { ORBITAL, SURFACE } dockMethod;
+};
+
+struct SpaceStationType stationTypes[SpaceStation::TYPE_MAX] = {
+  { 65, SpaceStationType::ORBITAL },
+  { 90, SpaceStationType::SURFACE },
+};
 
 static ObjParams params = {
   { 0.5, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
@@ -55,10 +64,12 @@ void SpaceStation::GetDockingSurface(CollMeshSet* mset, int midx) {
           port.horiz.z); */
 }
 
-SpaceStation::SpaceStation(void) : ModelBody() {
-  SetModel(STATION_SBRE_MODEL);
+SpaceStation::SpaceStation(TYPE type) : ModelBody() {
+  const Uint32 sbreModel = stationTypes[type].sbreModel;
+  m_type = type;
+  SetModel(sbreModel);
 
-  CollMeshSet* mset = GetModelCollMeshSet(STATION_SBRE_MODEL);
+  CollMeshSet* mset = GetModelCollMeshSet(sbreModel);
   for(unsigned int i = 0; i < geomColl.size(); i++) {
     if(geomColl[i].flags == 0x1) {
       /* Docking surface. */
@@ -79,6 +90,64 @@ SpaceStation::~SpaceStation(void) {
 
 }
 
+bool SpaceStation::IsGroundStation(void) const {
+  return (stationTypes[m_type].dockMethod == SpaceStationType::SURFACE);
+}
+
+void SpaceStation::OrientDockedShip(Ship* ship) const {
+  const int dockMethod = stationTypes[m_type].dockMethod;
+  if(dockMethod == SpaceStationType::SURFACE) {
+    matrix4x4d stationRot;
+    GetRotMatrix(stationRot);
+    vector3d port_y = vector3d::Cross(-port.horiz, port.normal);
+    matrix4x4d rot = stationRot * matrix4x4d::MakeRotMatrix(-port.horiz, -port.normal, -port_y);
+    vector3d pos = GetPosition() + stationRot*port.center;
+    ship->SetPosition(pos - stationRot*port.normal);
+    ship->SetRotMatrix(rot);
+  }
+}
+
+void SpaceStation::OrientLaunchingShip(Ship* ship) const {
+  const int dockMethod = stationTypes[m_type].dockMethod;
+  if(dockMethod == SpaceStationType::ORBITAL) {
+    /*
+     * Position ship in middle of docking bat, pointing out of it.
+     * TODO: Need to do forced thrusting thing.
+     * TODO: ang vel not zeroed for some reason..
+     */
+    matrix4x4d stationRot;
+    GetRotMatrix(stationRot);
+    vector3d port_y = vector3d::Cross(-port.horiz, port.normal);
+    matrix4x4d rot = stationRot * matrix4x4d::MakeRotMatrix(port.horiz, port_y, port.normal);
+    vector3d pos = GetPosition() + stationRot*port.center;
+    ship->SetPosition(pos);
+    ship->SetRotMatrix(rot);
+    ship->SetVelocity(vector3d(0,0,0));
+    ship->SetAngVelocity(vector3d(0,0,0));
+  }
+  else if(dockMethod == SpaceStationType::SURFACE) {
+    ship->Blastoff();
+
+    /*
+     * Not necessary, since for the time being 'SURFACE' starports are on planets
+     * so the positioning Blastoff does is fine.
+     */
+#if 0
+    matrix4x4d stationRot;
+    GetRotMatrix(stationRot);
+    vector3d port_y = vector3d::Cross(-port.horiz, port.normal);
+    matrix4x4d rot = stationRot * matrix4x4d::MakeRotMatrix(-port.horiz, -port.normal, -port_y);
+    vector3d pos = GetPosition() + stationRot*port.center;
+    ship->SetPosition(pos - stationRot*(10*port.normal));
+    ship->SetRotMatrix(rot);
+    ship->SetVelocity(vector3d(0,0,0));
+    ship->SetAngVelocity(vector3d(0,0,0));
+#endif
+  } else {
+    assert(0);
+  }
+}
+
 bool SpaceStation::GetDockingClearance(Ship* s) {
   s->SetDockingTimer(68*10);
   return true;
@@ -89,9 +158,28 @@ bool SpaceStation::OnCollision(Body* b, Uint32 flags) {
     /* Hitting docking area of a station. */
     if(b->GetType() == Object::SHIP) {
       Ship* s = static_cast<Ship*>(b);
-      if((!s->GetDockedWith()) && (s->GetDockingTimer() != 0.0f)) {
+      const dReal* vel = dBodyGetLinearVel(s->m_body);
+      double speed = vector3d(vel[0], vel[1], vel[2]).Length();
+
+      /* Must be oriented sensibly and have wheels down. */
+      if(IsGroundStation()) {
+        matrix4x4d rot;
+        s->GetRotMatrix(rot);
+        matrix4x4d invRot = rot.InverseOf();
+
+        matrix4x4d stationRot;
+        GetRotMatrix(stationRot);
+        vector3d dockingNormal = stationRot*port.normal;
+
+        /* Check player is sort of sensibly oriented for landing. */
+        const double dot = vector3d::Dot(vector3d(-invRot[1], -invRot[5], -invRot[9]), dockingNormal);
+        if((dot < 0.99) || (s->GetWheelState() != 1.0)) return false;
+      }
+
+      if((speed < MAX_LANDING_SPEED) &&
+          (!s->GetDockedWith()) &&
+          (s->GetDockingTimer() != 0.0f)) {
         s->SetDockedWith(this);
-        printf("Docking!\n");
       }
     }
     return false;
@@ -101,6 +189,6 @@ bool SpaceStation::OnCollision(Body* b, Uint32 flags) {
 }
 
 void SpaceStation::Render(const Frame* camFrame) {
-  RenderSbreModel(camFrame, STATION_SBRE_MODEL, &params);
+  RenderSbreModel(camFrame, stationTypes[m_type].sbreModel, &params);
 }
 
diff --git a/src/space_station.h b/src/space_station.h
index cae27cc..c528b39 100644
--- a/src/space_station.h
+++ b/src/space_station.h
@@ -7,17 +7,24 @@ class Ship;
 
 class SpaceStation : public ModelBody {
 public:
-  SpaceStation(void);
+  enum TYPE { JJHOOP, GROUND_FLAVOURED, TYPE_MAX };
+  SpaceStation(TYPE);
   virtual ~SpaceStation(void);
   virtual bool OnCollision(Body* b, Uint32 flags);
   virtual Object::Type GetType(void) { return Object::SPACESTATION; }
   virtual void Render(const Frame* camFrame);
+  void OrientLaunchingShip(Ship* ship) const;
+  void OrientDockedShip(Ship* ship) const;
   void GetDockingSurface(CollMeshSet* mset, int midx);
   bool GetDockingClearance(Ship* s);
+  bool IsGroundStation(void) const;
   struct dockingport_t {
     vector3d center;
     vector3d normal;
     vector3d horiz;
   } port;
+
+private:
+  TYPE m_type;
 };
 
diff --git a/src/star.cpp b/src/star.cpp
index 3ce7e05..13e79d7 100644
--- a/src/star.cpp
+++ b/src/star.cpp
@@ -9,7 +9,7 @@ Star::Star(StarSystem::SBody* sbody): Body() {
   pos        = vector3d(0,0,0);
 }
 
-vector3d Star::GetPosition(void) {
+vector3d Star::GetPosition(void) const {
   return pos;
 }
 
diff --git a/src/star.h b/src/star.h
index 6572614..e21358e 100644
--- a/src/star.h
+++ b/src/star.h
@@ -9,7 +9,7 @@ public:
   Star(StarSystem::SBody* sbody);
   virtual ~Star(void) { };
   virtual void SetPosition(vector3d p);
-  virtual vector3d GetPosition(void);
+  virtual vector3d GetPosition(void) const;
   virtual double GetRadius(void) const { return radius; }
   virtual void Render(const Frame* camFrame);
   virtual double GetMass(void) const { return mass; }