diff --git a/data/shaders/motion_blur.frag b/data/shaders/motion_blur.frag
index 837226d8e..2db9f093e 100644
--- a/data/shaders/motion_blur.frag
+++ b/data/shaders/motion_blur.frag
@@ -25,20 +25,17 @@ uniform float boost_amount;
 
 // The color buffer to use.
 uniform sampler2D color_buffer;
+uniform sampler2D dtex;
 
 // Center (in texture coordinates) at which the kart is. A small circle
 // around this center is not blurred (see mask_radius below)
 uniform vec2 center;
 
-// The direction to which the blurring aims at
-uniform vec2 direction;
-
 // Radius of mask around the character in which no blurring happens
 // so that the kart doesn't get blurred.
 uniform float mask_radius;
 
-// Maximum height of texture used
-uniform float max_tex_height;
+uniform mat4 previous_viewproj;
 
 layout (std140) uniform MatrixesData
 {
@@ -55,42 +52,44 @@ out vec4 FragColor;
 // Number of samples used for blurring
 #define NB_SAMPLES 8
 
+vec4 getPosFromUVDepth(vec3 uvDepth, mat4 InverseProjectionMatrix);
+
 void main()
 {
-	vec2 texcoords = gl_FragCoord.xy / screen;
+    vec2 texcoords = gl_FragCoord.xy / screen;
 
-	// Sample the color buffer
-	vec3 color = texture(color_buffer, texcoords).rgb;
+    // Sample the color buffer
+    vec3 color = texture(color_buffer, texcoords).rgb;
 
-	// Compute the blur direction.
-	// IMPORTANT: we don't normalize it so that it avoids a glitch around 'center',
-	// plus it naturally scales the motion blur in a cool way :)
-	vec2 blur_dir = direction - texcoords;
+    float z = texture(dtex, texcoords).x;
+    vec4 ViewPos = getPosFromUVDepth(vec3(texcoords, z), InverseProjectionMatrix);
+    vec4 OldScreenPos = previous_viewproj * InverseViewMatrix * ViewPos;
+    OldScreenPos /= OldScreenPos.w;
+    OldScreenPos = .5 * OldScreenPos + .5;
 
-	// Compute the blurring factor:
-	// - apply the mask, i.e. no blurring in a small circle around the kart
-	float blur_factor = max(0.0, length(texcoords - center) - mask_radius);
+    // Compute the blur direction.
+    // IMPORTANT: we don't normalize it so that it avoids a glitch around 'center',
+    // plus it naturally scales the motion blur in a cool way :)
+    vec2 blur_dir = texcoords - OldScreenPos.xy;
 
-	// - avoid blurring the top of the screen
-	blur_factor *= (max_tex_height - texcoords.t);
+    // Compute the blurring factor:
+    // - apply the mask, i.e. no blurring in a small circle around the kart
+    float blur_factor = max(0.0, length(texcoords - center) - mask_radius);
 
-	// - apply the boost amount
-	blur_factor *= boost_amount;
+    // - apply the boost amount
+    blur_factor *= boost_amount;
 
-	// Scale the blur direction
-	blur_dir *= blur_factor;
+    // Scale the blur direction
+    blur_dir *= boost_amount;
 
-	// Compute the blur
-	vec2 inc_vec = blur_dir / vec2(NB_SAMPLES);
-	vec2 blur_texcoords = texcoords + inc_vec;
-	for(int i=1 ; i < NB_SAMPLES ; i++)
-	{
-		color += texture(color_buffer, blur_texcoords).rgb;
-		blur_texcoords += inc_vec;
-	}
-	color /= vec3(NB_SAMPLES);
-	FragColor = vec4(color, 1.0);
-
-	// Keep this commented line for debugging:
-	//FragColor = vec4(blur_factor, blur_factor, blur_factor, 0.0);
+    // Compute the blur
+    vec2 inc_vec = blur_dir / vec2(NB_SAMPLES);
+    vec2 blur_texcoords = texcoords - inc_vec * NB_SAMPLES / 2;
+    for(int i=1 ; i < NB_SAMPLES ; i++)
+    {
+        color += texture(color_buffer, blur_texcoords).rgb;
+        blur_texcoords += inc_vec;
+    }
+    color /= vec3(NB_SAMPLES);
+    FragColor = vec4(color, 1.0);
 }
diff --git a/data/shaders/object_pass2.frag b/data/shaders/object_pass2.frag
index c984c7f34..d45ca01fb 100644
--- a/data/shaders/object_pass2.frag
+++ b/data/shaders/object_pass2.frag
@@ -8,7 +8,8 @@ vec3 getLightFactor(float specMapValue);
 
 void main(void)
 {
-    vec4 col = texture(Albedo, uv) * color;
+    vec4 col = texture(Albedo, uv);
+    col.xyz *= pow(color.xyz, vec3(2.2));
     vec3 LightFactor = getLightFactor(1.);
     FragColor = vec4(col.xyz * LightFactor, 1.);
 }
diff --git a/data/stk_config.xml b/data/stk_config.xml
index 84d3c09c0..57e012e6c 100644
--- a/data/stk_config.xml
+++ b/data/stk_config.xml
@@ -380,9 +380,6 @@
                downward-impulse-factor="0"
                track-connection-accel="2"/>
 
-    <!-- Parameters for the upright constraint, which keeps karts upright. -->
-    <upright tolerance="0.2" max-force="30"/>
-
     <!-- collision
          impulse-type: STK can apply an additional impulse in case of
            kart-track collision:
diff --git a/sources.cmake b/sources.cmake
index c054f1b47..317927706 100644
--- a/sources.cmake
+++ b/sources.cmake
@@ -1,5 +1,5 @@
 # Modify this file to change the last-modified date when you add/remove a file.
-# This will then trigger a new cmake run automatically. 
+# This will then trigger a new cmake run automatically.
 file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp")
 file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp")
-file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*")
\ No newline at end of file
+file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*")
diff --git a/src/challenges/challenge_data.cpp b/src/challenges/challenge_data.cpp
index 901f0ff6d..a65131611 100644
--- a/src/challenges/challenge_data.cpp
+++ b/src/challenges/challenge_data.cpp
@@ -376,8 +376,7 @@ void ChallengeData::setRace(RaceManager::Difficulty d) const
     else if(m_mode==CM_GRAND_PRIX)
     {
         race_manager->setMinorMode(m_minor);
-        const GrandPrixData *gp = grand_prix_manager->getGrandPrix(m_gp_id);
-        race_manager->setGrandPrix(*gp);
+        race_manager->setGrandPrix(grand_prix_manager->getGrandPrix(m_gp_id));
         race_manager->setDifficulty(d);
         race_manager->setNumKarts(m_num_karts[d]);
         race_manager->setNumLocalPlayers(1);
@@ -459,7 +458,7 @@ bool ChallengeData::isGPFulfilled() const
     // is no world objects to query at this stage.
     if (race_manager->getMajorMode()  != RaceManager::MAJOR_MODE_GRAND_PRIX  ||
         race_manager->getMinorMode()  != m_minor                             ||
-        race_manager->getGrandPrix()->getId() != m_gp_id                     ||
+        race_manager->getGrandPrix().getId() != m_gp_id                      ||
         race_manager->getNumberOfKarts() < (unsigned int)m_num_karts[d]      ||
         race_manager->getNumPlayers() > 1) return false;
 
diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp
index fcfe0feda..7be00a805 100644
--- a/src/graphics/irr_driver.cpp
+++ b/src/graphics/irr_driver.cpp
@@ -99,7 +99,7 @@ const int MIN_SUPPORTED_WIDTH  = 800;
  *  So we create a dummy device here to begin with, which is then later (once
  *  the real device exists) changed in initDevice().
  */
-IrrDriver::IrrDriver() : object_count{}
+IrrDriver::IrrDriver()
 {
     m_resolution_changing = RES_CHANGE_NONE;
     m_phase               = SOLID_NORMAL_AND_DEPTH_PASS;
@@ -112,6 +112,7 @@ IrrDriver::IrrDriver() : object_count{}
     m_mipviz = m_wireframe = m_normals = m_ssaoviz = \
         m_lightviz = m_shadowviz = m_distortviz = m_rsm = m_rh = m_gi = false;
     SkyboxCubeMap = m_last_light_bucket_distance = 0;
+    memset(object_count, 0, sizeof(object_count));
 }   // IrrDriver
 
 // ----------------------------------------------------------------------------
diff --git a/src/graphics/irr_driver.hpp b/src/graphics/irr_driver.hpp
index 948b48589..29baf0534 100644
--- a/src/graphics/irr_driver.hpp
+++ b/src/graphics/irr_driver.hpp
@@ -230,7 +230,7 @@ private:
     core::array<video::IRenderTarget> m_mrt;
 
     /** Matrixes used in several places stored here to avoid recomputation. */
-    core::matrix4 m_ViewMatrix, m_InvViewMatrix, m_ProjMatrix, m_InvProjMatrix, m_ProjViewMatrix, m_InvProjViewMatrix;
+    core::matrix4 m_ViewMatrix, m_InvViewMatrix, m_ProjMatrix, m_InvProjMatrix, m_ProjViewMatrix, m_previousProjViewMatrix, m_InvProjViewMatrix;
 
     std::vector<video::ITexture *> SkyboxTextures;
     std::vector<video::ITexture *> SphericalHarmonicsTextures;
@@ -694,7 +694,8 @@ public:
     void setProjMatrix(core::matrix4 matrix) { m_ProjMatrix = matrix; matrix.getInverse(m_InvProjMatrix); }
     const core::matrix4 &getProjMatrix() const { return m_ProjMatrix; }
     const core::matrix4 &getInvProjMatrix() const { return m_InvProjMatrix; }
-    void genProjViewMatrix() { m_ProjViewMatrix = m_ProjMatrix * m_ViewMatrix; m_InvProjViewMatrix = m_ProjViewMatrix; m_InvProjViewMatrix.makeInverse(); }
+    void genProjViewMatrix() { m_previousProjViewMatrix = m_ProjViewMatrix; m_ProjViewMatrix = m_ProjMatrix * m_ViewMatrix; m_InvProjViewMatrix = m_ProjViewMatrix; m_InvProjViewMatrix.makeInverse(); }
+    const core::matrix4 & getPreviousPVMatrix() { return m_previousProjViewMatrix; }
     const core::matrix4 &getProjViewMatrix() const { return m_ProjViewMatrix; }
     const core::matrix4 &getInvProjViewMatrix() const { return m_InvProjViewMatrix; }
 #ifdef DEBUG
diff --git a/src/graphics/post_processing.cpp b/src/graphics/post_processing.cpp
index 9fdbece98..d3073a5df 100644
--- a/src/graphics/post_processing.cpp
+++ b/src/graphics/post_processing.cpp
@@ -607,13 +607,17 @@ void PostProcessing::renderMotionBlur(unsigned cam, FrameBuffer &in_fbo, FrameBu
     glUseProgram(FullScreenShader::MotionBlurShader::Program);
     glBindVertexArray(FullScreenShader::MotionBlurShader::vao);
 
-    setTexture(0, in_fbo.getRTT()[0], GL_NEAREST, GL_NEAREST);
+    setTexture(0, in_fbo.getRTT()[0], GL_LINEAR, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    setTexture(1, irr_driver->getDepthStencilTexture(), GL_NEAREST, GL_NEAREST);
     FullScreenShader::MotionBlurShader
-                    ::setUniforms(cb->getBoostTime(cam), cb->getCenter(cam),
-                                  cb->getDirection(cam), 0.15f,
-                                  cb->getMaxHeight(cam) * 0.7f, 0);
+                    ::setUniforms(.1, // Todo : should be framerate dependent
+                                  // Todo : use a previousPVMatrix per cam, not global
+                                  irr_driver->getPreviousPVMatrix(),
+                                  cb->getCenter(cam),
+                                  0.15f,
+                                  0, 1);
 
     glDrawArrays(GL_TRIANGLES, 0, 3);
 }
@@ -868,7 +872,7 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo
     {
         PROFILER_PUSH_CPU_MARKER("- Motion blur", 0xFF, 0x00, 0x00);
         ScopedGPUTimer Timer(irr_driver->getGPUTimer(Q_MOTIONBLUR));
-        if (isRace && UserConfigParams::m_motionblur && m_any_boost && World::getWorld() != NULL) // motion blur
+        if (isRace && UserConfigParams::m_motionblur && World::getWorld() != NULL) // motion blur
         {
             renderMotionBlur(0, *in_fbo, *out_fbo);
             std::swap(in_fbo, out_fbo);
diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp
index 86a4adb2b..729d3992e 100644
--- a/src/graphics/render.cpp
+++ b/src/graphics/render.cpp
@@ -204,9 +204,6 @@ void IrrDriver::renderGLSL(float dt)
             else
                 fbo->BlitToDefault(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y);
         }
-        else
-            glDisable(GL_FRAMEBUFFER_SRGB);
-
 
         PROFILER_POP_CPU_MARKER();
     }   // for i<world->getNumKarts()
@@ -372,7 +369,10 @@ void IrrDriver::renderScene(scene::ICameraSceneNode * const camnode, unsigned po
         PROFILER_POP_CPU_MARKER();
     }
     if (!UserConfigParams::m_dynamic_lights && !forceRTT)
+    {
+        glDisable(GL_FRAMEBUFFER_SRGB);
         return;
+    }
 
     // Render displacement
     {
diff --git a/src/graphics/shaders.cpp b/src/graphics/shaders.cpp
index 05daf7bb6..02d759383 100644
--- a/src/graphics/shaders.cpp
+++ b/src/graphics/shaders.cpp
@@ -2663,33 +2663,34 @@ namespace FullScreenShader
     GLuint MotionBlurShader::uniform_boost_amount;
     GLuint MotionBlurShader::uniform_center;
     GLuint MotionBlurShader::uniform_color_buffer;
-    GLuint MotionBlurShader::uniform_direction;
+    GLuint MotionBlurShader::uniform_dtex;
+    GLuint MotionBlurShader::uniform_previous_viewproj;
     GLuint MotionBlurShader::uniform_mask_radius;
-    GLuint MotionBlurShader::uniform_max_tex_height;
     GLuint MotionBlurShader::vao;
 
     void MotionBlurShader::init()
     {
         Program = LoadProgram(
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
+            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getPosFromUVDepth.frag").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/motion_blur.frag").c_str());
         uniform_boost_amount = glGetUniformLocation(Program, "boost_amount");
         uniform_center = glGetUniformLocation(Program, "center");
         uniform_color_buffer = glGetUniformLocation(Program, "color_buffer");
-        uniform_direction = glGetUniformLocation(Program, "direction");
         uniform_mask_radius = glGetUniformLocation(Program, "mask_radius");
-        uniform_max_tex_height = glGetUniformLocation(Program, "max_tex_height");
+        uniform_dtex = glGetUniformLocation(Program, "dtex");
+        uniform_previous_viewproj = glGetUniformLocation(Program, "previous_viewproj");
         vao = createFullScreenVAO(Program);
     }
 
-    void MotionBlurShader::setUniforms(float boost_amount, const core::vector2df &center, const core::vector2df &direction, float mask_radius, float max_tex_height, unsigned TU_cb)
+    void MotionBlurShader::setUniforms(float boost_amount, const core::matrix4 &previousVP, const core::vector2df &center, float mask_radius, unsigned TU_cb, unsigned TU_dtex)
     {
+        glUniformMatrix4fv(uniform_previous_viewproj, 1, GL_FALSE, previousVP.pointer());
         glUniform1f(uniform_boost_amount, boost_amount);
         glUniform2f(uniform_center, center.X, center.Y);
-        glUniform2f(uniform_direction, direction.X, direction.Y);
         glUniform1f(uniform_mask_radius, mask_radius);
-        glUniform1f(uniform_max_tex_height, max_tex_height);
         glUniform1i(uniform_color_buffer, TU_cb);
+        glUniform1i(uniform_dtex, TU_dtex);
     }
 
     GLuint GodFadeShader::Program;
diff --git a/src/graphics/shaders.hpp b/src/graphics/shaders.hpp
index db57030df..f82277126 100644
--- a/src/graphics/shaders.hpp
+++ b/src/graphics/shaders.hpp
@@ -759,11 +759,11 @@ class MotionBlurShader
 {
 public:
     static GLuint Program;
-    static GLuint uniform_boost_amount, uniform_color_buffer, uniform_center, uniform_direction, uniform_mask_radius, uniform_max_tex_height;
+    static GLuint uniform_boost_amount, uniform_color_buffer, uniform_dtex, uniform_previous_viewproj, uniform_center, uniform_mask_radius;
     static GLuint vao;
 
     static void init();
-    static void setUniforms(float boost_amount, const core::vector2df &center, const core::vector2df &direction, float mask_radius, float max_tex_height, unsigned TU_cb);
+    static void setUniforms(float boost_amount, const core::matrix4 &previousVP, const core::vector2df &center,  float mask_radius, unsigned TU_cb, unsigned TU_dtex);
 };
 
 class GodFadeShader
diff --git a/src/guiengine/widgets/spinner_widget.cpp b/src/guiengine/widgets/spinner_widget.cpp
index 31a8ad3d2..d6a53e6f8 100644
--- a/src/guiengine/widgets/spinner_widget.cpp
+++ b/src/guiengine/widgets/spinner_widget.cpp
@@ -160,7 +160,7 @@ void SpinnerWidget::add()
         {
             label->setText(m_labels[m_value].c_str() );
         }
-    
+
     }
 
 
@@ -174,7 +174,7 @@ void SpinnerWidget::add()
     m_children[2].m_id = m_children[2].m_element->getID();
 
     // refresh display
-    
+
 
     setValue(m_value);
 }
@@ -333,7 +333,7 @@ void SpinnerWidget::setValue(const int new_value)
         assert(new_value >= 0);
         assert(new_value < (int)m_labels.size());
 
-        m_children[1].m_element->setText(m_labels[new_value].c_str() );
+        m_children[1].m_element->setText(m_labels[new_value].c_str());
     }
     else if (m_text.size() > 0 && m_children.size() > 0)
     {
diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp
index 4e93875e9..af67b278b 100644
--- a/src/karts/abstract_kart.hpp
+++ b/src/karts/abstract_kart.hpp
@@ -28,7 +28,6 @@ class AbstractKartAnimation;
 class Attachment;
 class btKart;
 class btQuaternion;
-class btUprightConstraint;
 class Controller;
 class Item;
 class KartModel;
@@ -342,9 +341,6 @@ public:
     /** Returns the bullet vehicle which represents this kart. */
     virtual btKart* getVehicle() const = 0;
     // ------------------------------------------------------------------------
-    /** Returns the upright constraint for this kart. */
-    virtual btUprightConstraint* getUprightConstraint() const = 0;
-    // ------------------------------------------------------------------------
     virtual btQuaternion getVisualRotation() const = 0;
     // ------------------------------------------------------------------------
     /** Returns true if the kart is 'resting', i.e. (nearly) not moving. */
diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp
index fd720eb9d..23f5dba20 100644
--- a/src/karts/kart.cpp
+++ b/src/karts/kart.cpp
@@ -58,7 +58,6 @@
 #include "network/network_manager.hpp"
 #include "physics/btKart.hpp"
 #include "physics/btKartRaycast.hpp"
-#include "physics/btUprightConstraint.hpp"
 #include "physics/physics.hpp"
 #include "race/history.hpp"
 #include "tracks/track.hpp"
@@ -269,7 +268,6 @@ Kart::~Kart()
         World::getWorld()->getPhysics()->removeKart(this);
         delete m_vehicle;
         delete m_vehicle_raycaster;
-        delete m_uprightConstraint;
     }
 
     for(int i=0; i<m_kart_chassis.getNumChildShapes(); i++)
@@ -682,13 +680,6 @@ void Kart::createPhysics()
     // Obviously these allocs have to be properly managed/freed
     btTransform t;
     t.setIdentity();
-    m_uprightConstraint=new btUprightConstraint(this, t);
-    m_uprightConstraint->setLimit(m_kart_properties->getUprightTolerance());
-    m_uprightConstraint->setBounce(0.0f);
-    m_uprightConstraint->setMaxLimitForce(m_kart_properties->getUprightMaxForce());
-    m_uprightConstraint->setErp(1.0f);
-    m_uprightConstraint->setLimitSoftness(1.0f);
-    m_uprightConstraint->setDamping(0.0f);
     World::getWorld()->getPhysics()->addKart(this);
 
 }   // createPhysics
@@ -699,8 +690,9 @@ void Kart::flyUp()
 {
     m_flying = true;
     Moveable::flyUp();
-}
+}   // flyUp
 
+// ----------------------------------------------------------------------------
 void Kart::flyDown()
 {
     if (isNearGround())
@@ -712,7 +704,7 @@ void Kart::flyDown()
     {
         Moveable::flyDown();
     }
-}   // flyUp
+}   // flyDown
 
 // ----------------------------------------------------------------------------
 /** Starts the engine sound effect. Called once the track intro phase is over.
@@ -957,8 +949,7 @@ bool Kart::isOnGround() const
 
 //-----------------------------------------------------------------------------
 /** The kart is near the ground, but not necessarily on it (small jumps). This
- *  is used to determine when to switch off the upright constraint, so that
- *  explosions can be more violent, while still
+ *  is used to determine when to stop flying.
 */
 bool Kart::isNearGround() const
 {
@@ -1102,21 +1093,6 @@ void Kart::update(float dt)
 
     m_slipstream->update(dt);
 
-    if (!m_flying)
-    {
-        // When really on air, free fly, when near ground, try to glide /
-        // adjust for landing. If zipped, be stable, so ramp+zipper can
-        // allow nice jumps without scripting the fly
-        // Also disable he upright constraint when gravity is changed by
-        // the terrain
-        if( (!isNearGround() &&
-              m_max_speed->getSpeedIncreaseTimeLeft(MaxSpeed::MS_INCREASE_ZIPPER)<=0.0f ) ||
-              (getMaterial() && getMaterial()->hasGravity())                                  )
-            m_uprightConstraint->setLimit(M_PI);
-        else
-            m_uprightConstraint->setLimit(m_kart_properties->getUprightTolerance());
-    }
-
     // TODO: hiker said this probably will be moved to btKart or so when updating bullet engine.
     // Neutralize any yaw change if the kart leaves the ground, so the kart falls more or less
     // straight after jumping, but still allowing some "boat shake" (roll and pitch).
@@ -2041,6 +2017,30 @@ void Kart::updatePhysics(float dt)
     m_max_speed->setMinSpeed(min_speed);
     m_max_speed->update(dt);
 
+    // If the kart is flying, keep its up-axis aligned to gravity (which in
+    // turn typically means the kart is parallel to the ground). This avoids
+    // that the kart rotates in mid-air and lands on its side.
+    if(m_vehicle->getNumWheelsOnGround()==0)
+    {
+        btVector3 kart_up = getTrans().getBasis().getColumn(1);  // up vector
+        btVector3 terrain_up = m_body->getGravity();
+        float g = World::getWorld()->getTrack()->getGravity();
+        // Normalize the gravity, g is the length of the vector
+        btVector3 new_up = 0.9f * kart_up + 0.1f * terrain_up/-g;
+        // Get the rotation (hpr) based on current heading.
+        Vec3 rotation(getHeading(), new_up);
+        btMatrix3x3 m;
+        m.setEulerZYX(rotation.getX(), rotation.getY(), rotation.getZ());
+        // We can't use getXYZ() for the position here, since the position is
+        // based on interpolation, while the actual center-of-mass-transform
+        // is based on the actual value every 1/60 of a second (using getXYZ()
+        // would result in the kart being pushed ahead a bit, making it jump
+        // much further, depending on fps)
+        btTransform new_trans(m, m_body->getCenterOfMassTransform().getOrigin());
+        //setTrans(new_trans);
+        m_body->setCenterOfMassTransform(new_trans);
+    }
+
     // To avoid tunneling (which can happen on long falls), clamp the
     // velocity in Y direction. Tunneling can happen if the Y velocity
     // is larger than the maximum suspension travel (per frame), since then
@@ -2048,6 +2048,7 @@ void Kart::updatePhysics(float dt)
     // not sure if this is enough in all cases!). So the speed is limited
     // to suspensionTravel / dt with dt = 1/60 (since this is the dt
     // bullet is using).
+
     // Only apply if near ground instead of purely based on speed avoiding
     // the "parachute on top" look.
     const Vec3 &v = m_body->getLinearVelocity();
@@ -2056,7 +2057,7 @@ void Kart::updatePhysics(float dt)
         Vec3 v_clamped = v;
         // clamp the speed to 99% of the maxium falling speed.
         v_clamped.setY(-m_kart_properties->getSuspensionTravelCM()*0.01f*60 * 0.99f);
-        m_body->setLinearVelocity(v_clamped);
+        //m_body->setLinearVelocity(v_clamped);
     }
 
     //at low velocity, forces on kart push it back and forth so we ignore this
diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp
index eaf7e835c..8ce5ea519 100644
--- a/src/karts/kart.hpp
+++ b/src/karts/kart.hpp
@@ -35,7 +35,6 @@
 #include "utils/no_copy.hpp"
 
 class btKart;
-class btUprightConstraint;
 
 class Attachment;
 class Controller;
@@ -146,7 +145,6 @@ private:
     btCompoundShape          m_kart_chassis;
     btVehicleRaycaster      *m_vehicle_raycaster;
     btKart                  *m_vehicle;
-    btUprightConstraint     *m_uprightConstraint;
 
      /** The amount of energy collected by hitting coins. Note that it
       *  must be float, since dt is subtraced in each timestep. */
@@ -342,11 +340,7 @@ public:
     virtual Skidding *getSkidding() { return m_skidding; }
     // ------------------------------------------------------------------------
     /** Returns the bullet vehicle which represents this kart. */
-    virtual btKart    *getVehicle      () const {return m_vehicle;                }
-    // ------------------------------------------------------------------------
-    /** Returns the upright constraint for this kart. */
-    virtual btUprightConstraint *getUprightConstraint() const
-                                                  {return m_uprightConstraint;}
+    virtual btKart    *getVehicle() const {return m_vehicle;               }
     // ------------------------------------------------------------------------
     /** Returns the speed of the kart in meters/second. */
     virtual float        getSpeed() const {return m_speed;                 }
diff --git a/src/karts/kart_properties.cpp b/src/karts/kart_properties.cpp
index 940bf5c7a..68706516e 100644
--- a/src/karts/kart_properties.cpp
+++ b/src/karts/kart_properties.cpp
@@ -72,9 +72,8 @@ KartProperties::KartProperties(const std::string &filename)
         m_wheel_radius = m_chassis_linear_damping = m_max_suspension_force =
         m_chassis_angular_damping = m_suspension_rest =
         m_max_speed_reverse_ratio = m_rescue_vert_offset =
-        m_upright_tolerance = m_collision_terrain_impulse =
-        m_collision_impulse = m_restitution = m_collision_impulse_time =
-        m_upright_max_force = m_suspension_travel_cm =
+        m_collision_terrain_impulse = m_collision_impulse = m_restitution =
+        m_collision_impulse_time = m_suspension_travel_cm =
         m_track_connection_accel = m_rubber_band_max_length =
         m_rubber_band_force = m_rubber_band_duration =
         m_rubber_band_speed_increase = m_rubber_band_fade_out_time =
@@ -367,12 +366,6 @@ void KartProperties::getAllData(const XMLNode * root)
                                                    &m_track_connection_accel );
     }
 
-    if(const XMLNode *upright_node = root->getNode("upright"))
-    {
-        upright_node->get("tolerance", &m_upright_tolerance);
-        upright_node->get("max-force", &m_upright_max_force);
-    }
-
     if(const XMLNode *collision_node = root->getNode("collision"))
     {
         collision_node->get("impulse",         &m_collision_impulse        );
@@ -673,8 +666,6 @@ void KartProperties::checkAllSet(const std::string &filename)
     CHECK_NEG(m_bevel_factor.getX(),        "collision bevel-factor"        );
     CHECK_NEG(m_bevel_factor.getY(),        "collision bevel-factor"        );
     CHECK_NEG(m_bevel_factor.getZ(),        "collision bevel-factor"        );
-    CHECK_NEG(m_upright_tolerance,          "upright tolerance"             );
-    CHECK_NEG(m_upright_max_force,          "upright max-force"             );
     CHECK_NEG(m_rubber_band_max_length,     "plunger band-max-length"       );
     CHECK_NEG(m_rubber_band_force,          "plunger band-force"            );
     CHECK_NEG(m_rubber_band_duration,       "plunger band-duration"         );
diff --git a/src/karts/kart_properties.hpp b/src/karts/kart_properties.hpp
index e9b50c6d1..d90dec561 100644
--- a/src/karts/kart_properties.hpp
+++ b/src/karts/kart_properties.hpp
@@ -328,9 +328,6 @@ private:
     /** The restitution factor to be used in collsions for this kart. */
     float m_restitution;
 
-    float m_upright_tolerance;
-    float m_upright_max_force;
-
     /** How far behind a kart slipstreaming is effective. */
     float m_slipstream_length;
     /** How wide the slipstream area is at the end. */
@@ -699,15 +696,6 @@ public:
     float getExplosionInvulnerabilityTime() const
                                    { return m_explosion_invulnerability_time; }
 
-    // ------------------------------------------------------------------------
-    /** Returns how much a kart can roll/pitch before the upright constraint
-     *  counteracts. */
-    float getUprightTolerance       () const {return m_upright_tolerance;     }
-
-    // ------------------------------------------------------------------------
-    /** Returns the maximum value of the upright counteracting force. */
-    float getUprightMaxForce        () const {return m_upright_max_force;     }
-
     // ------------------------------------------------------------------------
     /** Returns the maximum length of a rubber band before it breaks. */
     float getRubberBandMaxLength    () const {return m_rubber_band_max_length;}
diff --git a/src/main.cpp b/src/main.cpp
index 3e438a95c..8bc70861a 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -854,7 +854,7 @@ int handleCmdLine()
             Log::warn("main", "There is no GP named '%s'.", s.c_str());
             return 0;
         }
-        race_manager->setGrandPrix(*gp);
+        race_manager->setGrandPrix(gp);
     }   // --gp
 
     if(CommandLine::has("--numkarts", &n) ||CommandLine::has("-k", &n))
diff --git a/src/network/race_config.cpp b/src/network/race_config.cpp
index bb4ac65ae..a60af9015 100644
--- a/src/network/race_config.cpp
+++ b/src/network/race_config.cpp
@@ -3,6 +3,8 @@
 #include "race/race_manager.hpp"
 #include "utils/log.hpp"
 
+#include <stdexcept>
+
 /** \brief Gets the element with the highest count in a std::map<S,int>.
  *  \param histogram : A pointer to the histogram.
  *  \return The key of type S that has the highest second value.
diff --git a/src/physics/btUprightConstraint.cpp b/src/physics/btUprightConstraint.cpp
deleted file mode 100644
index 3b183c5e6..000000000
--- a/src/physics/btUprightConstraint.cpp
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
-
-Bullet Continuous Collision Detection and Physics Library
-Copyright (C) 2003-2006 Erwin Coumans  http://continuousphysics.com/Bullet/
-
-This software is provided 'as-is', without any express or implied warranty.
-In no event will the authors be held liable for any damages arising from the
-use of this software.
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it freely,
-subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not claim
-   that you wrote the original software. If you use this software in a
-   product, an acknowledgment in the product documentation would be
-   appreciated but is not required.
-2. Altered source versions must be plainly marked as such, and must not be
-   misrepresented as being the original software.
-3. This notice may not be removed or altered from any source distribution.
-
-*/
-
-#include "physics/btUprightConstraint.hpp"
-
-#include <new>
-#include <stdio.h>
-
-#include "BulletDynamics/Dynamics/btRigidBody.h"
-#include "LinearMath/btTransformUtil.h"
-
-#include "karts/kart.hpp"
-
-//!
-//!
-//!
-
-void btUprightConstraint::solveAngularLimit(
-            btUprightConstraintLimit *limit,
-            btScalar timeStep, btScalar jacDiagABInv,
-            btRigidBody * body0 )
-{
-
-    // Work out if limit is violated
-    if(limit->m_angle>=m_loLimit && limit->m_angle<=m_hiLimit) return;
-
-    limit->m_currentLimitError = (limit->m_angle<m_loLimit)
-                               ? limit->m_angle - m_loLimit
-                               : limit->m_angle - m_hiLimit;
-
-    btScalar targetVelocity = -m_ERP*limit->m_currentLimitError
-                            / (3.1415f/8.0f*timeStep);
-    btScalar maxMotorForce  = m_maxLimitForce;
-
-    maxMotorForce *= timeStep;
-
-    // current velocity difference
-    btVector3 angularVelocity       = body0->getAngularVelocity();
-    btScalar  axisAngularVelocity   = limit->m_axis.dot( angularVelocity );
-
-     // correction velocity
-    btScalar motorVelocity          = m_limitSoftness*(targetVelocity
-                                    - m_damping*axisAngularVelocity);
-
-    // correction impulse
-    btScalar unclippedMotorImpulse = (1+m_bounce)*motorVelocity*jacDiagABInv;
-
-    // clip correction impulse
-    btScalar clippedMotorImpulse = unclippedMotorImpulse;
-
-    //todo: should clip against accumulated impulse
-
-    if (unclippedMotorImpulse>0.0f)
-    {
-        clippedMotorImpulse = unclippedMotorImpulse > maxMotorForce
-                            ? maxMotorForce : unclippedMotorImpulse;
-    }
-    else
-    {
-        clippedMotorImpulse = unclippedMotorImpulse < -maxMotorForce
-                            ? -maxMotorForce : unclippedMotorImpulse;
-    }
-
-    // sort with accumulated impulses
-    btScalar      lo = btScalar(-1e30);
-    btScalar      hi = btScalar(1e30);
-
-    btScalar oldaccumImpulse = limit->m_accumulatedImpulse;
-
-    btScalar sum = oldaccumImpulse + clippedMotorImpulse;
-
-    limit->m_accumulatedImpulse = sum > hi ? btScalar(0.)
-                                           : sum < lo ? btScalar(0.) : sum;
-
-    clippedMotorImpulse = limit->m_accumulatedImpulse - oldaccumImpulse;
-
-    btVector3 motorImp = clippedMotorImpulse * limit->m_axis;
-    body0->applyTorqueImpulse(motorImp);
-}   // solveAngularLimit
-
-//!
-//!
-//!
-
-btUprightConstraint::btUprightConstraint(const Kart* kart,
-                                         const btTransform& frameInA)
-                   : btTypedConstraint(D6_CONSTRAINT_TYPE, *(kart->getBody()))
-                   , m_frameInA(frameInA)
-
-{
-    m_kart                          = kart;
-    m_ERP                           = 1.0f;
-    m_bounce                        = 0.0f;
-    m_damping                       = 1.0f;
-    m_limitSoftness                 = 1.0f;
-    m_maxLimitForce                 = 3000.0f;
-    m_disable_time                  = 0.0f;
-    m_limit[0].m_accumulatedImpulse = 0.0f;
-    m_limit[1].m_accumulatedImpulse = 0.0f;
-    m_limit[ 0 ].m_axis             = btVector3( 1, 0, 0 );
-    m_limit[ 1 ].m_axis             = btVector3( 0, 0, 1 );
-    setLimit( SIMD_PI * 0.4f );
-}   // btUprightConstraint
-
-//!
-//!
-//!
-
-void btUprightConstraint::buildJacobian()
-{
-    m_limit[ 0 ].m_angle       =  m_kart->getPitch();
-    m_limit[ 1 ].m_angle       = -m_kart->getRoll();
-    for ( int i = 0; i < 2; i++ )
-    {
-        new (&m_jacAng[ i ])      btJacobianEntry(  m_limit[ i ].m_axis,
-            m_rbA.getCenterOfMassTransform().getBasis().transpose(),
-            m_rbB.getCenterOfMassTransform().getBasis().transpose(),
-            m_rbA.getInvInertiaDiagLocal(),
-            m_rbB.getInvInertiaDiagLocal());
-    }
-}   // buildJacobian
-
-//!
-//!
-//!
-
-void btUprightConstraint::solveConstraintObsolete(btRigidBody& /*bodyA*/,btRigidBody& /*bodyB*/,btScalar timeStep)
-{
-    m_timeStep = timeStep;
-
-    // Update disable time and return if constraint is still disabled
-    if(m_disable_time>0.0f)
-    {
-        m_disable_time -= timeStep;
-        if(m_disable_time>0.0f) return;
-    }
-
-    solveAngularLimit( &m_limit[ 0 ], m_timeStep, btScalar(1.) / m_jacAng[ 0 ].getDiagonal(), &m_rbA );
-    solveAngularLimit( &m_limit[ 1 ], m_timeStep, btScalar(1.) / m_jacAng[ 1 ].getDiagonal(), &m_rbA );
-}   // solveConstraint
-
-void btUprightConstraint::getInfo1(btConstraintInfo1* info) {
-                info->m_numConstraintRows = 0;
-                info->nub = 0;
-}
-
-void btUprightConstraint::getInfo2(btConstraintInfo2* info) {
-}
-
-btScalar btUprightConstraint::getParam(int num, int axis) const
-{
-    return 0;
-}
-
-void btUprightConstraint::setParam(int num, btScalar value, int axis)
-{
-}
diff --git a/src/physics/btUprightConstraint.hpp b/src/physics/btUprightConstraint.hpp
deleted file mode 100644
index a2711fa9f..000000000
--- a/src/physics/btUprightConstraint.hpp
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
-
-Bullet Continuous Collision Detection and Physics Library
-Copyright (C) 2003-2006 Erwin Coumans  http://continuousphysics.com/Bullet/
-
-This software is provided 'as-is', without any express or implied warranty.
-In no event will the authors be held liable for any damages arising from the
-use of this software.
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it freely,
-subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not claim
-   that you wrote the original software. If you use this software in a
-   product, an acknowledgment in the product documentation would be
-   appreciated but is not required.
-2. Altered source versions must be plainly marked as such, and must not be
-   misrepresented as being the original software.
-3. This notice may not be removed or altered from any source distribution.
-*/
-
-#ifndef HEADER_UPRIGHT_CONSTRAINT_HPP
-#define HEADER_UPRIGHT_CONSTRAINT_HPP
-
-#include "LinearMath/btVector3.h"
-#include "BulletDynamics/ConstraintSolver/btJacobianEntry.h"
-#include "BulletDynamics/ConstraintSolver/btTypedConstraint.h"
-
-class btRigidBody;
-class Kart;
-
-/**
-  * \ingroup physics
-  */
-class btUprightConstraint : public btTypedConstraint
-{
-private:
-    class btUprightConstraintLimit
-    {
-    public:
-        btVector3   m_axis;
-        btScalar    m_angle;
-        btScalar    m_accumulatedImpulse;
-        btScalar    m_currentLimitError;
-    };
-
-    //! relative_frames
-
-    //!@{
-    btTransform     m_frameInA;//!< the constraint space w.r.t body A
-    //!@}
-
-    //! Jacobians
-    //!@{
-    btJacobianEntry m_jacAng[ 2 ];//!< angular constraint
-    //!@}
-
-    const Kart     *m_kart;
-protected:
-
-    //! temporal variables
-    //!@{
-        btScalar    m_timeStep;
-        btScalar    m_ERP;
-        btScalar    m_bounce;
-        btScalar    m_damping;
-        btScalar    m_maxLimitForce;
-        btScalar    m_limitSoftness;
-        btScalar    m_hiLimit;
-        btScalar    m_loLimit;
-        btScalar    m_disable_time;
-
-        btUprightConstraintLimit        m_limit[ 2 ];
-
-    //!@}
-
-    btUprightConstraint& operator=(btUprightConstraint& other)
-    {
-        btAssert(0);
-        (void) other;
-        return *this;
-    }
-
-    void buildAngularJacobian(btJacobianEntry & jacAngular,
-                              const btVector3 & jointAxisW);
-
-    void solveAngularLimit(btUprightConstraintLimit *limit,
-                           btScalar timeStep, btScalar jacDiagABInv,
-                           btRigidBody * body0 );
-
-public:
-
-         btUprightConstraint(const Kart* kart, const btTransform& frameInA);
-
-    // -PI,+PI                      is the full range
-    // 0,0                          is no rotation around x or z
-    // -PI*0.2,+PI*0.2      is a nice bit of tilt
-    void setLimit( btScalar range )            { m_loLimit = -range;
-                                                 m_hiLimit = +range;        }
-    // Error correction scaling
-    // 0 - 1
-    void setErp( btScalar erp )                { m_ERP = erp;                }
-    void setBounce( btScalar bounce )          { m_bounce = bounce;          }
-    void setMaxLimitForce( btScalar force )    { m_maxLimitForce = force;    }
-    void setLimitSoftness( btScalar softness ) { m_limitSoftness = softness; }
-    void setDamping( btScalar damping )        { m_damping = damping;        }
-    void setDisableTime( btScalar t )          { m_disable_time = t;         }
-    virtual void buildJacobian();
-    virtual void solveConstraintObsolete(btRigidBody& /*bodyA*/,
-                                         btRigidBody& /*bodyB*/, btScalar
-                                         timeStep);
-    virtual void getInfo1 (btConstraintInfo1* info);
-    virtual void getInfo2 (btConstraintInfo2* info);
-    virtual void setParam(int num, btScalar value, int axis = -1);
-    virtual btScalar getParam(int num, int axis) const;
-
-};
-
-
-
-#endif //UPRIGHT_CONSTRAINT_H
-
diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp
index 1a845ca4c..1d2995a5c 100644
--- a/src/physics/physics.cpp
+++ b/src/physics/physics.cpp
@@ -33,7 +33,6 @@
 #include "modes/world.hpp"
 #include "karts/explosion_animation.hpp"
 #include "physics/btKart.hpp"
-#include "physics/btUprightConstraint.hpp"
 #include "physics/irr_debug_drawer.hpp"
 #include "physics/physical_object.hpp"
 #include "physics/stk_dynamics_world.hpp"
@@ -86,8 +85,8 @@ Physics::~Physics()
 
 // ----------------------------------------------------------------------------
 /** Adds a kart to the physics engine.
- *  This adds the rigid body, the vehicle, and the upright constraint, but only
- *  if the kart is not already in the physics world.
+ *  This adds the rigid body and the vehicle but only if the kart is not 
+ *  already in the physics world.
  *  \param kart The kart to add.
  *  \param vehicle The raycast vehicle object.
  */
@@ -102,7 +101,6 @@ void Physics::addKart(const AbstractKart *kart)
     }
     m_dynamics_world->addRigidBody(kart->getBody());
     m_dynamics_world->addVehicle(kart->getVehicle());
-    m_dynamics_world->addConstraint(kart->getUprightConstraint());
 }   // addKart
 
 //-----------------------------------------------------------------------------
@@ -130,7 +128,6 @@ void Physics::removeKart(const AbstractKart *kart)
     {
         m_dynamics_world->removeRigidBody(kart->getBody());
         m_dynamics_world->removeVehicle(kart->getVehicle());
-        m_dynamics_world->removeConstraint(kart->getUprightConstraint());
     }
 }   // removeKart
 
diff --git a/src/race/grand_prix_data.cpp b/src/race/grand_prix_data.cpp
index e1c293a8d..f717edc0c 100644
--- a/src/race/grand_prix_data.cpp
+++ b/src/race/grand_prix_data.cpp
@@ -24,13 +24,15 @@
 #include "config/player_manager.hpp"
 #include "io/file_manager.hpp"
 #include "io/utf_writer.hpp"
+#include "states_screens/dialogs/random_gp_dialog.hpp"
 #include "tracks/track_manager.hpp"
 #include "tracks/track.hpp"
 #include "utils/string_utils.hpp"
 
-#include <iostream>
-#include <memory>
 #include <algorithm>
+#include <cassert>
+#include <cstdlib>
+#include <memory>
 #include <stdexcept>
 
 
@@ -44,6 +46,94 @@ GrandPrixData::GrandPrixData(const std::string& filename)
     reload();
 }
 
+// ----------------------------------------------------------------------------
+GrandPrixData::GrandPrixData(const unsigned int number_of_tracks,
+                             const std::string&  track_group,
+                             const RandomGPInfoDialog::REVERSED use_reverse)
+{
+    m_filename = "Random GP - Not loaded from a file!";
+    m_id       = "random";
+    m_name     = "Random Grand Prix";
+    m_editable = false;
+
+    m_tracks.reserve(number_of_tracks);
+    m_laps.reserve(number_of_tracks);
+    m_reversed.reserve(number_of_tracks);
+
+    changeTrackNumber(number_of_tracks, track_group);
+    changeReverse(use_reverse);
+}
+
+// ----------------------------------------------------------------------------
+void GrandPrixData::changeTrackNumber(const unsigned int number_of_tracks,
+                                      const std::string& track_group)
+{
+    // The problem with the track groups is that "all" isn't a track group
+    // TODO: Add "all" to the track groups and rewrite this more elegant
+    std::vector<int> track_indices;
+    size_t available_tracks;
+    if (track_group == "all")
+    {
+        available_tracks = track_manager->getNumberOfTracks();
+    }
+    else
+    {
+        track_indices = track_manager->getTracksInGroup(track_group);
+        available_tracks = track_indices.size();
+    }
+    assert(number_of_tracks <= available_tracks);
+
+    // add or remove the right number of tracks
+    if (m_tracks.size() < number_of_tracks)
+    {
+        while (m_tracks.size() < number_of_tracks)
+        {
+            int index = (track_group == "all") ?
+                         rand() % available_tracks :
+                         track_indices[rand() % available_tracks];
+
+            std::string id = track_manager->getTrack(index)->getIdent();
+            // Avoid duplicate tracks
+            if (std::find(m_tracks.begin(), m_tracks.end(), id) != m_tracks.end())
+                continue;
+
+            m_tracks.push_back(id);
+            m_laps.push_back(3); // TODO: Take the default number from the track
+            m_reversed.push_back(false); // This will be changed later
+        }
+    }
+    else if (m_tracks.size() > number_of_tracks)
+    {
+        while (m_tracks.size() > number_of_tracks)
+        {
+            m_tracks.pop_back();
+            m_laps.pop_back();
+            m_reversed.pop_back();
+        }
+    }
+
+    assert(m_tracks.size() == m_laps.size()    );
+    assert(m_laps.size()   == m_reversed.size());
+}
+
+// ----------------------------------------------------------------------------
+
+void GrandPrixData::changeReverse(const RandomGPInfoDialog::REVERSED use_reverse)
+{
+    for (unsigned int i = 0; i < m_tracks.size(); i++)
+    {
+        if (use_reverse == RandomGPInfoDialog::NO_REVERSE)
+            m_reversed[i] = false;
+        else if (use_reverse == RandomGPInfoDialog::MIXED)
+            if (track_manager->getTrack(m_tracks[i])->reverseAvailable())
+                m_reversed[i] = (rand() % 2!=0);
+            else
+                m_reversed[i] = false;
+        else // all reversed
+            m_reversed[i] = true;
+    }
+}
+
 // ----------------------------------------------------------------------------
 void GrandPrixData::setId(const std::string& id)
 {
diff --git a/src/race/grand_prix_data.hpp b/src/race/grand_prix_data.hpp
index c2013d75c..742ec15f7 100644
--- a/src/race/grand_prix_data.hpp
+++ b/src/race/grand_prix_data.hpp
@@ -20,14 +20,15 @@
 #ifndef HEADER_GRAND_PRIX_DATA_HPP
 #define HEADER_GRAND_PRIX_DATA_HPP
 
+#include <irrString.h>
 #include <string>
 #include <vector>
-#include <cassert>
-#include <irrString.h>
-#include <stdexcept>
 
+#include "states_screens/dialogs/random_gp_dialog.hpp"
 #include "utils/translation.hpp"
 
+using irr::core::stringw;
+
 class Track;
 
 /** Simple class that hold the data relevant to a 'grand_prix', aka. a number
@@ -76,6 +77,13 @@ public:
     GrandPrixData(const std::string& filename);
     /** Needed for simple creation of an instance of GrandPrixData */
     GrandPrixData() {};
+    /** Creates a new random GP */
+    GrandPrixData(const unsigned int number_of_tracks,
+                  const std::string& track_group,
+                  const RandomGPInfoDialog::REVERSED use_reverse);
+    void changeTrackNumber(const unsigned int number_of_tracks,
+                           const std::string& track_group);
+    void changeReverse(const RandomGPInfoDialog::REVERSED use_reverse);
 
     // Methods for the GP editor
     void setId(const std::string& id);
@@ -117,7 +125,6 @@ public:
     // ------------------------------------------------------------------------
     /** Returns the filename of the grand prix xml file. */
     const std::string& getFilename() const { return m_filename;           }
-
 };   // GrandPrixData
 
 #endif
diff --git a/src/race/grand_prix_manager.cpp b/src/race/grand_prix_manager.cpp
index 48b02046e..9270bac06 100644
--- a/src/race/grand_prix_manager.cpp
+++ b/src/race/grand_prix_manager.cpp
@@ -31,6 +31,23 @@ GrandPrixManager *grand_prix_manager = NULL;
 
 const char* GrandPrixManager::SUFFIX = ".grandprix";
 
+// ----------------------------------------------------------------------------
+GrandPrixManager::GrandPrixManager()
+{
+    m_random_gp = NULL; // better do it explicitly and avoid weird stuff
+    loadFiles();
+}   // GrandPrixManager
+
+// ----------------------------------------------------------------------------
+GrandPrixManager::~GrandPrixManager()
+{
+    for(unsigned int i=0; i<m_gp_data.size(); i++)
+    {
+        delete m_gp_data[i];
+    }
+    delete m_random_gp;
+}   // ~GrandPrixManager
+
 // ----------------------------------------------------------------------------
 void GrandPrixManager::loadFiles()
 {
@@ -48,7 +65,7 @@ void GrandPrixManager::loadFiles()
         if (!dir.empty() && dir[dir.size() - 1] == '/')
             loadDir(dir);
     }
-}
+}   // loadFiles
 
 // ----------------------------------------------------------------------------
 void GrandPrixManager::loadDir(const std::string& dir)
@@ -64,7 +81,7 @@ void GrandPrixManager::loadDir(const std::string& dir)
                                         i != result.end(); i++)
         if (StringUtils::hasSuffix(*i, SUFFIX))
             load(dir + *i);
-}
+}   // loadDir
 
 // ----------------------------------------------------------------------------
 void GrandPrixManager::load(const std::string& filename)
@@ -92,7 +109,7 @@ void GrandPrixManager::reload()
     m_gp_data.clear();
 
     loadFiles();
-}
+}   // reload
 
 // ----------------------------------------------------------------------------
 std::string GrandPrixManager::generateId()
@@ -118,7 +135,7 @@ std::string GrandPrixManager::generateId()
     }
 
     return s.str();
-}
+}   // generateId
 
 // ----------------------------------------------------------------------------
 bool GrandPrixManager::existsName(const irr::core::stringw& name) const
@@ -128,32 +145,20 @@ bool GrandPrixManager::existsName(const irr::core::stringw& name) const
             return true;
 
     return false;
-}
-
-// ----------------------------------------------------------------------------
-GrandPrixManager::GrandPrixManager()
-{
-    loadFiles();
-}
-
-// ----------------------------------------------------------------------------
-GrandPrixManager::~GrandPrixManager()
-{
-    for(unsigned int i=0; i<m_gp_data.size(); i++)
-    {
-        delete m_gp_data[i];
-    }
-}
+}   // existsName
 
 // ----------------------------------------------------------------------------
 GrandPrixData* GrandPrixManager::getGrandPrix(const std::string& s) const
 {
     return editGrandPrix(s);
-}
+}   // getGrandPrix
 
 // ----------------------------------------------------------------------------
 GrandPrixData* GrandPrixManager::editGrandPrix(const std::string& s) const
 {
+    if (s == "random")
+        return m_random_gp;
+
     for(unsigned int i=0; i<m_gp_data.size(); i++)
     {
         if(m_gp_data[i]->getId() == s)
@@ -161,7 +166,7 @@ GrandPrixData* GrandPrixManager::editGrandPrix(const std::string& s) const
     }   // for i in m_gp_data
 
     return NULL;
-}
+}   // editGrandPrix
 
 // ----------------------------------------------------------------------------
 void GrandPrixManager::checkConsistency()
@@ -194,7 +199,7 @@ GrandPrixData* GrandPrixManager::createNewGP(const irr::core::stringw& newName)
     m_gp_data.push_back(gp);
 
     return gp;
-}
+}   // createNewGP
 
 // ----------------------------------------------------------------------------
 GrandPrixData* GrandPrixManager::copy(const std::string& id,
@@ -214,7 +219,7 @@ GrandPrixData* GrandPrixManager::copy(const std::string& id,
     m_gp_data.push_back(gp);
 
     return gp;
-}
+}   // copy
 
 // ----------------------------------------------------------------------------
 void GrandPrixManager::remove(const std::string& id)
@@ -232,4 +237,4 @@ void GrandPrixManager::remove(const std::string& id)
         Log::warn("GrandPrixManager",
                   "Grand Prix '%s' can not be removed", gp->getId().c_str());
     }
-}
+}   // remove
diff --git a/src/race/grand_prix_manager.hpp b/src/race/grand_prix_manager.hpp
index c8510bf3a..314ddcc59 100644
--- a/src/race/grand_prix_manager.hpp
+++ b/src/race/grand_prix_manager.hpp
@@ -47,6 +47,10 @@ private:
     bool existsName(const irr::core::stringw& name) const;
 
 public:
+    /** saved here by a random GP dialog to avoid dangling pinters or
+     * memory leaks */
+    GrandPrixData* m_random_gp;
+
                    GrandPrixManager();
                   ~GrandPrixManager();
     void           reload();
diff --git a/src/race/race_manager.cpp b/src/race/race_manager.cpp
index 98460295a..2626530b4 100644
--- a/src/race/race_manager.cpp
+++ b/src/race/race_manager.cpp
@@ -771,9 +771,10 @@ void RaceManager::startGP(const GrandPrixData* gp, bool from_overworld,
                           bool continue_saved_gp)
 {
     assert(gp != NULL);
+    //std::cout << gp->getId();
 
     StateManager::get()->enterGameState();
-    setGrandPrix(*gp);
+    setGrandPrix(gp);
     setCoinTarget( 0 ); // Might still be set from a previous challenge
     race_manager->setupPlayerKartInfo();
     m_continue_saved_gp = continue_saved_gp;
diff --git a/src/race/race_manager.hpp b/src/race/race_manager.hpp
index 12834fac8..379fd3ed0 100644
--- a/src/race/race_manager.hpp
+++ b/src/race/race_manager.hpp
@@ -331,7 +331,7 @@ private:
     int                              m_coin_target;
     bool                             m_has_time_target;
     float                            m_time_target;
-    int                                 m_goal_target;
+    int                              m_goal_target;
 
     void startNextRace();    // start a next race
 
@@ -344,7 +344,7 @@ private:
 
     bool m_have_kart_last_position_on_overworld;
     Vec3 m_kart_last_position_on_overworld;
-    
+
     /** Determines if saved GP should be continued or not*/
     bool m_continue_saved_gp;
 
@@ -410,9 +410,9 @@ public:
     void setDifficulty(Difficulty diff);
 
     // ------------------------------------------------------------------------
-    void setGrandPrix(const GrandPrixData &gp)
+    void setGrandPrix(const GrandPrixData *gp)
     {
-        m_grand_prix = gp;
+        m_grand_prix = *gp;
         m_coin_target = 0;
     }
     // ------------------------------------------------------------------------
@@ -525,7 +525,7 @@ public:
     // ------------------------------------------------------------------------
     const std::string& getTrackName() const { return m_tracks[m_track_number];}
     // ------------------------------------------------------------------------
-    const GrandPrixData *getGrandPrix() const { return &m_grand_prix; }
+    const GrandPrixData& getGrandPrix() const { return m_grand_prix; }
     // ------------------------------------------------------------------------
     unsigned int getFinishedKarts() const { return m_num_finished_karts; }
     // ------------------------------------------------------------------------
diff --git a/src/states_screens/dialogs/enter_gp_name_dialog.cpp b/src/states_screens/dialogs/enter_gp_name_dialog.cpp
index 6434a747d..e75c64abc 100644
--- a/src/states_screens/dialogs/enter_gp_name_dialog.cpp
+++ b/src/states_screens/dialogs/enter_gp_name_dialog.cpp
@@ -81,7 +81,7 @@ void EnterGPNameDialog::onEnterPressedInternal()
     TextBoxWidget* textCtrl = getWidget<TextBoxWidget>("textfield");
     assert(textCtrl != NULL);
     stringw name = textCtrl->getText().trim();
-    if (name.size() > 0)
+    if (name.size() > 0 && name != "Random Grand Prix")
     {
         // check for duplicate names
         for (unsigned int i = 0; i < grand_prix_manager->getNumberOfGrandPrix(); i++)
diff --git a/src/states_screens/dialogs/gp_info_dialog.cpp b/src/states_screens/dialogs/gp_info_dialog.cpp
index 9f8ff35ff..36fc61646 100644
--- a/src/states_screens/dialogs/gp_info_dialog.cpp
+++ b/src/states_screens/dialogs/gp_info_dialog.cpp
@@ -28,6 +28,7 @@
 #include "guiengine/widgets/label_widget.hpp"
 #include "io/file_manager.hpp"
 #include "race/grand_prix_manager.hpp"
+#include "race/grand_prix_data.hpp"
 #include "race/race_manager.hpp"
 #include "states_screens/state_manager.hpp"
 #include "states_screens/tracks_screen.hpp"
@@ -35,143 +36,190 @@
 #include "tracks/track_manager.hpp"
 #include "utils/translation.hpp"
 
-#include <iostream>
 #include <IGUIEnvironment.h>
 #include <IGUIStaticText.h>
 
-using namespace irr::gui;
-using namespace irr::video;
-using namespace irr::core;
-using namespace GUIEngine;
+using irr::gui::IGUIStaticText;
+using GUIEngine::PROP_ID;
 
-// ------------------------------------------------------------------------------------------------------
+typedef GUIEngine::LabelWidget Label;
 
-GPInfoDialog::GPInfoDialog(const std::string& gpIdent, const float w, const float h) : ModalDialog(w, h)
+const float GPInfoDialog::PERCENT_WIDTH  = 0.8f;
+const float GPInfoDialog::PERCENT_HEIGHT = 0.7f;
+
+GPInfoDialog::GPInfoDialog(const std::string& gp_ident)
+            : ModalDialog(PERCENT_WIDTH, PERCENT_HEIGHT)
 {
     doInit();
     m_curr_time = 0.0f;
 
-    const int y1 = m_area.getHeight()/7;
-    const int y2 = m_area.getHeight()*6/7;
+    m_gp = grand_prix_manager->getGrandPrix(gp_ident);
+    m_gp->checkConsistency();
 
-    m_gp_ident = gpIdent;
+    m_under_title = m_area.getHeight()/7;
+    m_over_body = m_area.getHeight()/7;
+    m_lower_bound = m_area.getHeight()*6/7;
 
-    const GrandPrixData* gp = grand_prix_manager->getGrandPrix(gpIdent);
-    assert (gp != NULL);
+    addTitle();
+    addTracks();
+    addScreenshot();
+    addButtons();
+}
 
-    // ---- GP Name
-    core::rect< s32 > area_top(0, 0, m_area.getWidth(), y1);
-    IGUIStaticText* title = GUIEngine::getGUIEnv()->addStaticText( translations->fribidize(gp->getName()),
-                                                               area_top, false, true, // border, word wrap
-                                                               m_irrlicht_window);
+// ----------------------------------------------------------------------------
+
+GPInfoDialog::~GPInfoDialog()
+{
+    GUIEngine::Screen* curr_screen = GUIEngine::getCurrentScreen();
+    if (curr_screen->getName() == "tracks.stkgui")
+        static_cast<TracksScreen*>(curr_screen)->setFocusOnGP(m_gp->getId());
+}
+
+// ----------------------------------------------------------------------------
+
+void GPInfoDialog::addTitle()
+{
+    core::rect< s32 > area_top(0, 0, m_area.getWidth(), m_under_title);
+    IGUIStaticText* title = GUIEngine::getGUIEnv()->addStaticText(
+        translations->fribidize(m_gp->getName()),
+        area_top, false, true, // border, word wrap
+        m_irrlicht_window);
     title->setTabStop(false);
-    title->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER);
+    title->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
+}
 
+// ----------------------------------------------------------------------------
 
-    // ---- Track listings
-    const std::vector<std::string> tracks = gp->getTrackNames();
-    const int trackAmount = tracks.size();
+void GPInfoDialog::addTracks()
+{
+    const std::vector<std::string> tracks = m_gp->getTrackNames();
+    const unsigned int track_amount = tracks.size();
 
-    int height_of_one_line = (y2 - y1)/(trackAmount+1);
-    const int textHeight = GUIEngine::getFontHeight();
-    if (height_of_one_line > (int)(textHeight*1.5f)) height_of_one_line = (int)(textHeight*1.5f);
+    int height_of_one_line = std::min((m_lower_bound - m_over_body)/(track_amount+1),
+                                      (unsigned int)(GUIEngine::getFontHeight()*1.5f));
 
-    bool gp_ok = true;
-
-    for (int t=0; t<trackAmount; t++)
+    // Count the number of label already existing labels representing a track
+    unsigned int existing = 0;
+    for (unsigned int i = 0; i < m_widgets.size(); i++)
     {
-        const int from_y = y1 + height_of_one_line*(t+1);
-
-        Track* track = track_manager->getTrack(tracks[t]);
-        stringw lineText;
-        if (track == NULL)
-        {
-            lineText = L"MISSING : ";
-            lineText.append( stringw(tracks[t].c_str()) );
-            gp_ok = false;
-        }
-        else
-        {
-            lineText = track->getName();
-        }
-
-        LabelWidget* widget = new LabelWidget();
-        widget->setText(translations->fribidize(lineText), false);
-        widget->m_x = 20;
-        widget->m_y = from_y;
-        widget->m_w = m_area.getWidth()/2 - 20;
-        widget->m_h = height_of_one_line;
-        widget->setParent(m_irrlicht_window);
-
-        m_widgets.push_back(widget);
-        widget->add();
-
-        // IGUIStaticText* line = GUIEngine::getGUIEnv()->addStaticText( lineText.c_str(),
-        //                                       entry_area, false , true , // border, word wrap
-        //                                       m_irrlicht_window);
+        if (m_widgets.get(i)->m_properties[PROP_ID] == "Track label")
+            existing++;
     }
 
-    // ---- Track screenshot
+    unsigned int reuse = std::min(existing, track_amount);
+    // m_widgets has the type PtrVector<Widget, HOLD>
+    unsigned int widgets_iter = 0;
+    for (unsigned int i = 0; i < reuse; i++)
+    {
+        Track* track = track_manager->getTrack(tracks[i]);
 
-    m_screenshot_widget = new IconButtonWidget(IconButtonWidget::SCALE_MODE_KEEP_CUSTOM_ASPECT_RATIO,
-                                               false /* tab stop */, false /* focusable */,
-                                               IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE /* Track gives us absolute paths */);
+        // Find the next widget that is a track label
+        while (m_widgets.get(widgets_iter)->m_properties[PROP_ID] != "Track label")
+            widgets_iter++;
+
+        Label* widget = dynamic_cast<Label*>(m_widgets.get(widgets_iter));
+        widget->setText(translations->fribidize(track->getName()), false);
+        widget->move(20, m_over_body + height_of_one_line*(i+1),
+                     m_area.getWidth()/2 - 20, height_of_one_line);
+
+        widgets_iter++;
+    }
+
+    if (existing < track_amount)
+    {
+        // There are not enough labels for all the track names, so we have to
+        // add some more
+        for (unsigned int i = reuse; i < track_amount; i++)
+        {
+            Track* track = track_manager->getTrack(tracks[i]);
+            assert(track != NULL);
+
+            Label* widget = new Label();
+            widget->m_properties[PROP_ID] = "Track label";
+            widget->setText(translations->fribidize(track->getName()), false);
+            widget->setParent(m_irrlicht_window);
+            m_widgets.push_back(widget);
+            widget->add();
+
+            widget->move(20, m_over_body + height_of_one_line*(i+1),
+                         m_area.getWidth()/2 - 20, height_of_one_line);
+        }
+    }
+    else if (existing > track_amount)
+    {
+        // There are label which are not necessary anymore so they're deleted
+        for (unsigned int i = widgets_iter; i < m_widgets.size(); i++)
+        {
+            if (m_widgets.get(i)->m_properties[PROP_ID] == "Track label")
+            {
+                m_irrlicht_window->removeChild(m_widgets.get(i)->getIrrlichtElement());
+                m_widgets.remove(i);
+                i--;
+            }
+        }
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+void GPInfoDialog::addScreenshot()
+{
+    m_screenshot_widget = new GUIEngine::IconButtonWidget(
+        GUIEngine::IconButtonWidget::SCALE_MODE_KEEP_CUSTOM_ASPECT_RATIO,
+        false, false, GUIEngine::IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);
     // images are saved squared, but must be stretched to 4:3
     m_screenshot_widget->setCustomAspectRatio(4.0f / 3.0f);
 
-    m_screenshot_widget->m_x = m_area.getWidth()/2;
-    m_screenshot_widget->m_y = y1;
-    m_screenshot_widget->m_w = m_area.getWidth()/2;
-    m_screenshot_widget->m_h = y2 - y1 - 10;
+    m_screenshot_widget->m_x = m_area.getWidth()/2-20;
+    m_screenshot_widget->m_y = m_over_body + 10;
 
-    Track* track = (tracks.size() == 0 ? NULL : track_manager->getTrack(tracks[0]));
+    // Scale the picture to the biggest possible size without an overflow
+    if (m_lower_bound - m_over_body - 20 < m_area.getWidth()/2*3/4)
+    {
+        m_screenshot_widget->m_w = (m_lower_bound - m_over_body - 30)*4/3;
+        m_screenshot_widget->m_h = m_lower_bound - m_over_body - 30;
+    }
+    else
+    {
+        m_screenshot_widget->m_w = m_area.getWidth()/2;
+        m_screenshot_widget->m_h = m_area.getWidth()*3/8; // *(3/4)*(1/2)
+    }
 
-    m_screenshot_widget->m_properties[PROP_ICON] = (track  != NULL ?
-                                                    track->getScreenshotFile().c_str() :
-                                                    file_manager->getAsset(FileManager::GUI,"main_help.png"));
+    Track* track = track_manager->getTrack(m_gp->getTrackNames()[0]);
+    m_screenshot_widget->m_properties[GUIEngine::PROP_ICON] = (track->getScreenshotFile().c_str());
     m_screenshot_widget->setParent(m_irrlicht_window);
     m_screenshot_widget->add();
     m_widgets.push_back(m_screenshot_widget);
+}
 
 
+// ----------------------------------------------------------------------------
+void GPInfoDialog::addButtons()
+{
     // ---- Start button
-    ButtonWidget* okBtn = new ButtonWidget();
-    ButtonWidget* continueBtn = new ButtonWidget();
+    GUIEngine::ButtonWidget* okBtn = new GUIEngine::ButtonWidget();
+    GUIEngine::ButtonWidget* continueBtn = new GUIEngine::ButtonWidget();
 
     SavedGrandPrix* saved_gp = SavedGrandPrix::getSavedGP( StateManager::get()
                                                ->getActivePlayerProfile(0)
                                                ->getUniqueID(),
-                                               gpIdent,
+                                               m_gp->getId(),
                                                race_manager->getDifficulty(),
                                                race_manager->getNumberOfKarts(),
                                                race_manager->getNumLocalPlayers());
 
-    if (tracks.size() == 0)
-    {
-        okBtn->m_properties[PROP_ID] = "cannot_start";
-        okBtn->setText(_("Sorry, no tracks available"));
-    }
-    else if (gp_ok)
-    {
-        okBtn->m_properties[PROP_ID] = "start";
-        okBtn->setText(_("Start Grand Prix"));
+    okBtn->m_properties[PROP_ID] = "start";
+    okBtn->setText(_("Start Grand Prix"));
 
-        continueBtn->m_properties[PROP_ID] = "continue";
-        continueBtn->setText(_("Continue"));
-    }
-    else
-    {
-        okBtn->m_properties[PROP_ID] = "cannot_start";
-        okBtn->setText(_("This Grand Prix is broken!"));
-        okBtn->setBadge(BAD_BADGE);
-    }
+    continueBtn->m_properties[PROP_ID] = "continue";
+    continueBtn->setText(_("Continue"));
 
-    if (saved_gp && gp_ok)
+    if (saved_gp)
     {
         continueBtn->m_x = m_area.getWidth()/2 + 110;
-        continueBtn->m_y = y2;
+        continueBtn->m_y = m_lower_bound;
         continueBtn->m_w = 200;
-        continueBtn->m_h = m_area.getHeight() - y2 - 15;
+        continueBtn->m_h = m_area.getHeight() - m_lower_bound - 15;
         continueBtn->setParent(m_irrlicht_window);
         m_widgets.push_back(continueBtn);
         continueBtn->add();
@@ -185,9 +233,9 @@ GPInfoDialog::GPInfoDialog(const std::string& gpIdent, const float w, const floa
         okBtn->m_x = m_area.getWidth()/2 - 200;
     }
 
-    okBtn->m_y = y2;
+    okBtn->m_y = m_lower_bound;
     okBtn->m_w = 400;
-    okBtn->m_h = m_area.getHeight() - y2 - 15;
+    okBtn->m_h = m_area.getHeight() - m_lower_bound - 15;
     okBtn->setParent(m_irrlicht_window);
     m_widgets.push_back(okBtn);
     okBtn->add();
@@ -195,87 +243,57 @@ GPInfoDialog::GPInfoDialog(const std::string& gpIdent, const float w, const floa
     okBtn->getIrrlichtElement()->setTabGroup(false);
 
     okBtn->setFocusForPlayer( PLAYER_ID_GAME_MASTER );
-
 }
 
-// ------------------------------------------------------------------------------------------------------
-
-GPInfoDialog::~GPInfoDialog()
-{
-    // Place focus back on selected GP, in case the dialog was cancelled and we're back to
-    // the track selection screen after
-    Screen* curr_screen = GUIEngine::getCurrentScreen();
-    if (curr_screen->getName() == "tracks.stkgui")
-    {
-        ((TracksScreen*)curr_screen)->setFocusOnGP(m_gp_ident);
-    }
-
-}
-
-// ------------------------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
 
 void GPInfoDialog::onEnterPressedInternal()
 {
-    // Save the gp identifier, since dismiss will delete this object.
-    std::string gp_id = m_gp_ident;
+    // Save the GP id because dismiss() will destroy this instance
+    std::string gp_id = m_gp->getId();
     ModalDialog::dismiss();
     // Disable accidentally unlocking of a challenge
     PlayerManager::getCurrentPlayer()->setCurrentChallenge("");
     race_manager->startGP(grand_prix_manager->getGrandPrix(gp_id), false, false);
 }
 
-// ------------------------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
 
 GUIEngine::EventPropagation GPInfoDialog::processEvent(const std::string& eventSource)
 {
-    if (eventSource == "start")
+    if (eventSource == "start" || eventSource == "continue")
     {
         // Save GP identifier, since dismiss will delete this object.
-        std::string gp_id = m_gp_ident;
+        std::string gp_id = m_gp->getId();
         ModalDialog::dismiss();
-        race_manager->startGP(grand_prix_manager->getGrandPrix(gp_id), false, false);
+        race_manager->startGP(grand_prix_manager->getGrandPrix(gp_id), false,
+                              (eventSource == "continue"));
         return GUIEngine::EVENT_BLOCK;
     }
-    if (eventSource == "continue")
-    {
-        // Save GP identifier, since dismiss will delete this object.
-        std::string gp_id = m_gp_ident;
-        ModalDialog::dismiss();
-        race_manager->startGP(grand_prix_manager->getGrandPrix(gp_id), false, true);
-        return GUIEngine::EVENT_BLOCK;
-    }
-    else if (eventSource == "cannot_start")
-    {
-        sfx_manager->quickSound( "anvil" );
-    }
 
     return GUIEngine::EVENT_LET;
 }
 
-// ------------------------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
 
 void GPInfoDialog::onUpdate(float dt)
 {
-    const int frameBefore = (int)(m_curr_time / 1.5f);
+    if (dt == 0)
+        return; // if nothing changed, return right now
+
     m_curr_time += dt;
     int frameAfter = (int)(m_curr_time / 1.5f);
 
-    if (frameAfter == frameBefore) return; // if nothing changed, return right now
-
-    const GrandPrixData* gp = grand_prix_manager->getGrandPrix(m_gp_ident);
-    assert(gp != NULL);
-    const std::vector<std::string> tracks = gp->getTrackNames();
+    const std::vector<std::string> tracks = m_gp->getTrackNames();
     if (frameAfter >= (int)tracks.size())
     {
         frameAfter = 0;
         m_curr_time = 0;
     }
 
-    Track* track = (tracks.size() == 0 ? NULL :
-        track_manager->getTrack(tracks[frameAfter]));
-    std::string fn = track ? track->getScreenshotFile()
-                           : file_manager->getAsset(FileManager::GUI, "main_help.png");
-    m_screenshot_widget->setImage(fn.c_str(), IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);
+    Track* track = track_manager->getTrack(tracks[frameAfter]);
+    std::string file = track->getScreenshotFile();
+    typedef GUIEngine::IconButtonWidget Icon;
+    m_screenshot_widget->setImage(file.c_str(), Icon::ICON_PATH_TYPE_ABSOLUTE);
 }
 
-// ------------------------------------------------------------------------------------------------------
diff --git a/src/states_screens/dialogs/gp_info_dialog.hpp b/src/states_screens/dialogs/gp_info_dialog.hpp
index b8959bf2d..d221d44d9 100644
--- a/src/states_screens/dialogs/gp_info_dialog.hpp
+++ b/src/states_screens/dialogs/gp_info_dialog.hpp
@@ -20,6 +20,10 @@
 #define HEADER_GP_INFO_DIALOG_HPP
 
 #include "guiengine/modaldialog.hpp"
+// Don't include grand_prix_data.hpp here or the compilation will fail
+
+
+class GrandPrixData;
 
 namespace GUIEngine
 {
@@ -32,23 +36,46 @@ namespace GUIEngine
  */
 class GPInfoDialog : public GUIEngine::ModalDialog
 {
-    std::string m_gp_ident;
+protected: // Necessary for RandomGPInfoDialog
     GUIEngine::IconButtonWidget* m_screenshot_widget;
-    
     float m_curr_time;
-    
+    GrandPrixData* m_gp;
+
+    /** height of the separator over the body */
+    int m_over_body;
+    /** height of the separator under the titlebar, which is equal to
+     * m_over_body in a normal GPInfoDialo and lower in RandomGPInfoDialog. */
+    int m_under_title;
+    /** height of the seperator over the buttons */
+    int m_lower_bound;
+
+    void addTitle();
+    /** \brief display all the tracks according to the current gp
+     * For a normal gp info dialog, it just creates a label for every track.
+     * But with a random gp info dialog, it tries to reuse as many
+     * labels as possible by just changing their text. */
+    void addTracks();
+    void addScreenshot();
+    /** display a ok-button and eventually a continue-button */
+    void addButtons();
+
+    /** only used for track_screen.cpp */
+    GPInfoDialog() : ModalDialog(PERCENT_WIDTH, PERCENT_HEIGHT) {}
+
+private:
+    static const float PERCENT_WIDTH;
+    static const float PERCENT_HEIGHT;
+
 public:
-    /**
-     * Creates a modal dialog with given percentage of screen width and height
-     */
-    GPInfoDialog(const std::string& gpIdent, const float percentWidth, const float percentHeight);
+    GPInfoDialog(const std::string& gpIdent);
+    /** Places the focus back on the selected GP, in the case that the dialog
+     * was cancelled and we're returning to the track selection screen */
     virtual ~GPInfoDialog();
-    
+
     void onEnterPressedInternal();
     GUIEngine::EventPropagation processEvent(const std::string& eventSource);
-    
-    virtual void onUpdate(float dt);
 
+    virtual void onUpdate(float dt);
 };
 
 #endif
diff --git a/src/states_screens/dialogs/random_gp_dialog.cpp b/src/states_screens/dialogs/random_gp_dialog.cpp
new file mode 100644
index 000000000..5a7406e5e
--- /dev/null
+++ b/src/states_screens/dialogs/random_gp_dialog.cpp
@@ -0,0 +1,195 @@
+//  SuperTuxKart - a fun racing game with go-kart
+//  Copyright (C) 2014 konstin
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 3
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include "guiengine/engine.hpp"
+#include "guiengine/widgets/icon_button_widget.hpp"
+#include "guiengine/widgets/spinner_widget.hpp"
+#include "race/grand_prix_manager.hpp"
+#include "race/race_manager.hpp"
+#include "states_screens/dialogs/random_gp_dialog.hpp"
+#include "tracks/track_manager.hpp"
+
+#include <IGUIEnvironment.h>
+#include <IGUIStaticText.h>
+
+using irr::core::stringc;
+using irr::core::stringw;
+using irr::gui::IGUIStaticText;
+
+typedef GUIEngine::SpinnerWidget Spinner;
+
+RandomGPInfoDialog::RandomGPInfoDialog()
+{
+    // Defaults - loading selection from last time frrom a file would be better
+    m_number_of_tracks = 2; // We can assume that there are at least 2 standard tracks
+    m_trackgroup = "standard";
+    m_use_reverse = NO_REVERSE;
+
+    doInit();
+    m_curr_time = 0.0f;
+
+    m_under_title = m_area.getHeight()/7;
+    m_over_body = m_area.getHeight()/7 + SPINNER_HEIGHT + 10; // 10px space
+    m_lower_bound = m_area.getHeight()*6/7;
+
+    // The GP manager is be used to make the GP live longer than this dialog
+    if (grand_prix_manager->m_random_gp)
+    {
+        delete grand_prix_manager->m_random_gp;
+        grand_prix_manager->m_random_gp = NULL;
+    }
+    m_gp = new GrandPrixData(m_number_of_tracks, m_trackgroup, m_use_reverse);
+    grand_prix_manager->m_random_gp = m_gp;
+
+    addTitle();
+    addSpinners();
+    addTracks();
+    addScreenshot();
+    addButtons();
+    addRestartButton();
+}
+
+// ----------------------------------------------------------------------------
+
+void RandomGPInfoDialog::addSpinners()
+{
+    const int trackgroup_width = 200, laps_with = 150, reverse_width = 200;
+    const int left =  (m_area.getWidth() - trackgroup_width - 150 - 250)/2;
+
+    // Trackgroup chooser
+    Spinner* spinner = new Spinner(false);
+    spinner->m_properties[GUIEngine::PROP_ID] = "Trackgroup";
+    spinner->m_properties[GUIEngine::PROP_WRAP_AROUND] = "true";
+    spinner->setParent(m_irrlicht_window);
+    m_widgets.push_back(spinner);
+    spinner->add();
+    spinner->move(left, m_under_title, trackgroup_width, SPINNER_HEIGHT);
+    // Fill it with all the track group names
+    spinner->addLabel("all");
+    int index_standard;
+    const std::vector<std::string>& groups = track_manager->getAllTrackGroups();
+    for (unsigned int i = 0; i < groups.size(); i++)
+    {
+        // FIXME: The NULL check is necessary until #1348 on github is fixed
+        if (groups[i].c_str() != NULL)
+        {
+            spinner->addLabel(stringw(groups[i].c_str()));
+            if(groups[i] == "standard")
+                index_standard  = i+1;
+        }
+    }
+    // The value can only be set here because SpinnerWidget resets the value
+    // every time a label is added
+    spinner->setValue(index_standard);
+
+    // Number of laps chooser
+    spinner = new Spinner(false);
+    spinner->setValue(m_number_of_tracks);
+    spinner->setMin(1);
+    spinner->setMax(track_manager->getTracksInGroup("standard").size());
+    spinner->setParent(m_irrlicht_window);
+    spinner->m_properties[GUIEngine::PROP_ID] = "Number of tracks";
+    spinner->m_properties[GUIEngine::PROP_WRAP_AROUND] = "true";
+    m_widgets.push_back(spinner);
+    spinner->add();
+    spinner->move(left + trackgroup_width + 10, m_under_title, laps_with, SPINNER_HEIGHT);
+
+    // reverse choose
+    spinner = new Spinner(false);
+    spinner->setParent(m_irrlicht_window);
+    spinner->m_properties[GUIEngine::PROP_ID] = "reverse";
+    spinner->m_properties[GUIEngine::PROP_WRAP_AROUND] = "true";
+    m_widgets.push_back(spinner);
+    spinner->add();
+    spinner->move(left + trackgroup_width + laps_with + 10, m_under_title, reverse_width, SPINNER_HEIGHT);
+    spinner->addLabel("no reverse");
+    spinner->addLabel("all reverse");
+    spinner->addLabel("mixed");
+}
+
+// ----------------------------------------------------------------------------
+
+void RandomGPInfoDialog::addRestartButton()
+{
+    GUIEngine::IconButtonWidget* button = new GUIEngine::IconButtonWidget();
+    button->setImage("gui/restart.png");
+    button->setParent(m_irrlicht_window);
+    button->m_properties[GUIEngine::PROP_ID] = "reload";
+    m_widgets.push_back(button);
+    button->add();
+    button->move(m_area.getWidth() - 20 - 32, 20, 32, 32);
+}
+
+// ----------------------------------------------------------------------------
+
+GUIEngine::EventPropagation RandomGPInfoDialog::processEvent(
+                                                 const std::string& eventSource)
+{
+    if (eventSource == "start")
+    {
+        ModalDialog::dismiss();
+        race_manager->startGP(grand_prix_manager->m_random_gp, false, false);
+        return GUIEngine::EVENT_BLOCK;
+    }
+    else if (eventSource == "Number of tracks")
+    {
+        // The old gp can be reused because there's only track deletion/adding
+        m_number_of_tracks = getWidget<Spinner>("Number of tracks")->getValue();
+        m_gp->changeTrackNumber(m_number_of_tracks, m_trackgroup);
+        addTracks();
+    }
+    else if (eventSource == "Trackgroup")
+    {
+        Spinner* t = getWidget<Spinner>("Trackgroup");
+        Spinner* s = getWidget<Spinner>("Number of tracks");
+
+        m_trackgroup = stringc(t->getStringValue()).c_str();
+
+        // Update the maximum for the number of tracks since it's depending on
+        // the current track. The current value in the Number-of-tracks-spinner
+        // has to be updated, since otherwise the displayed (and used) value
+        // can be bigger than the maximum. (Might be a TODO to fix this)
+        unsigned int max = (m_trackgroup == "all") ?
+                           track_manager->getNumberOfTracks() :
+                           track_manager->getTracksInGroup(m_trackgroup).size();
+        m_number_of_tracks = std::min(max, m_number_of_tracks);
+        s->setMax(max);
+        if (s->getValue() > (signed)max)
+            s->setValue(max);
+
+        delete m_gp;
+        m_gp = new GrandPrixData(m_number_of_tracks, m_trackgroup, m_use_reverse);
+        grand_prix_manager->m_random_gp = m_gp;
+        addTracks();
+    }
+    else if (eventSource == "reverse")
+    {
+        Spinner* r = getWidget<Spinner>("reverse");
+        m_use_reverse = static_cast<REVERSED>(r->getValue());
+        m_gp->changeReverse(m_use_reverse);
+    }
+    else if (eventSource == "reload")
+    {
+        delete m_gp;
+        m_gp = new GrandPrixData(m_number_of_tracks, m_trackgroup, m_use_reverse);
+        grand_prix_manager->m_random_gp = m_gp;
+        addTracks();
+    }
+
+    return GUIEngine::EVENT_LET;
+}
+
diff --git a/src/states_screens/dialogs/random_gp_dialog.hpp b/src/states_screens/dialogs/random_gp_dialog.hpp
new file mode 100644
index 000000000..698217eec
--- /dev/null
+++ b/src/states_screens/dialogs/random_gp_dialog.hpp
@@ -0,0 +1,53 @@
+//  SuperTuxKart - a fun racing game with go-kart
+//  Copyright (C) 2014 konstin
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 3
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef HEADER_RANDOM_GP_INFO_DIALOG_HPP
+#define HEADER_RANDOM_GP_INFO_DIALOG_HPP
+
+#include "states_screens/dialogs/gp_info_dialog.hpp"
+
+#include <string>
+
+class RandomGPInfoDialog : public GPInfoDialog
+{
+public:
+    enum REVERSED
+    {
+        NO_REVERSE = 0,
+        ALL_REVERSE = 1,
+        MIXED = 2
+    };
+private:
+    unsigned int m_number_of_tracks;
+    std::string m_trackgroup;
+    REVERSED m_use_reverse;
+
+public:
+    static const int SPINNER_HEIGHT = 40;
+
+    RandomGPInfoDialog();
+
+    /** Adds a SpinnerWidgets to choose the track groups, one to choose the
+     * number of tracks and one to choose if  the tracks should be raced in
+     * reverse. The Spinners are centered. */
+    void addSpinners();
+    void addRestartButton();
+
+    GUIEngine::EventPropagation processEvent(const std::string& eventSource);
+};
+
+#endif
diff --git a/src/states_screens/kart_selection.cpp b/src/states_screens/kart_selection.cpp
index 2e84fd276..93695466e 100644
--- a/src/states_screens/kart_selection.cpp
+++ b/src/states_screens/kart_selection.cpp
@@ -1602,6 +1602,11 @@ void KartSelectionScreen::updateKartWidgetModel(uint8_t widget_id,
                           kart_model.getWheelGraphicsPosition(2) );
             w3->addModel( kart_model.getWheelModel(3),
                           kart_model.getWheelGraphicsPosition(3) );
+            for (size_t i = 0; i < kart_model.getSpeedWeightedObjectsCount(); i++)
+            {
+                const SpeedWeightedObject&  obj = kart_model.getSpeedWeightedObject(i);
+                w3->addModel(obj.m_model, obj.m_position);
+            }
             w3->update(0);
 
             m_kart_widgets[widget_id].m_kart_name
diff --git a/src/states_screens/race_result_gui.cpp b/src/states_screens/race_result_gui.cpp
index b8a6e88da..9b5803421 100644
--- a/src/states_screens/race_result_gui.cpp
+++ b/src/states_screens/race_result_gui.cpp
@@ -92,7 +92,7 @@ void RaceResultGUI::init()
 
     // Calculate screenshot scrolling parameters
     const std::vector<std::string> tracks =
-        race_manager->getGrandPrix()->getTrackNames();
+        race_manager->getGrandPrix().getTrackNames();
     int n_tracks = tracks.size();
     int currentTrack = race_manager->getTrackNumber();
     m_start_track = currentTrack;
@@ -208,7 +208,7 @@ void RaceResultGUI::enableAllButtons()
 void RaceResultGUI::eventCallback(GUIEngine::Widget* widget,
                                   const std::string& name, const int playerID)
 {
-    int n_tracks = race_manager->getGrandPrix()->getNumberOfTracks();
+    int n_tracks = race_manager->getGrandPrix().getNumberOfTracks();
     if (name == "up_button" && n_tracks > m_max_tracks && m_start_track > 0)
     {
         m_start_track--;
@@ -1121,7 +1121,7 @@ void RaceResultGUI::enableGPProgress()
         status_label->m_h = font_height;
         status_label->add();
         status_label->setText(_("Track %i/%i", currentTrack + 1,
-            race_manager->getGrandPrix()->getNumberOfTracks()), true);
+            race_manager->getGrandPrix().getNumberOfTracks()), true);
         addGPProgressWidget(status_label);
         y = (status_label->m_y + status_label->m_h + 5);
 
@@ -1316,7 +1316,7 @@ void RaceResultGUI::displayHighScores()
 void RaceResultGUI::displayScreenShots()
 {
     const std::vector<std::string> tracks =
-        race_manager->getGrandPrix()->getTrackNames();
+        race_manager->getGrandPrix().getTrackNames();
     int currentTrack = race_manager->getTrackNumber();
 
     int n_sshot = 1;
diff --git a/src/states_screens/tracks_screen.cpp b/src/states_screens/tracks_screen.cpp
index b00cd47b4..94f715553 100644
--- a/src/states_screens/tracks_screen.cpp
+++ b/src/states_screens/tracks_screen.cpp
@@ -29,6 +29,7 @@
 #include "race/grand_prix_manager.hpp"
 #include "states_screens/state_manager.hpp"
 #include "states_screens/dialogs/gp_info_dialog.hpp"
+#include "states_screens/dialogs/random_gp_dialog.hpp"
 #include "states_screens/dialogs/track_info_dialog.hpp"
 #include "tracks/track.hpp"
 #include "tracks/track_manager.hpp"
@@ -121,9 +122,16 @@ void TracksScreen::eventCallback(Widget* widget, const std::string& name,
                        gps_widget->getSelectionIDString(PLAYER_ID_GAME_MASTER);
 
         if (selection == "locked")
+        {
             unlock_manager->playLockSound();
+        }
         else
-            new GPInfoDialog(selection, 0.8f, 0.7f);
+        {
+            if (selection == "Random Grand Prix")
+                new RandomGPInfoDialog();
+            else
+                new GPInfoDialog(selection);
+        }
     }
     else if (name == "trackgroups")
     {
@@ -214,12 +222,13 @@ void TracksScreen::init()
         }
     }
 
-    /*// Random GP - not finished yet
+    // Random GP
     std::vector<std::string> screenshots;
-    screenshots.push_back("gui/main_help.png");
-    gps_widget->addAnimatedItem(translations->fribidize("Random"), "Random",
+    screenshots.push_back(file_manager->getAsset(FileManager::GUI, "main_help.png"));
+    gps_widget->addAnimatedItem(translations->fribidize("Random Grand Prix"),
+                                "Random Grand Prix",
                                 screenshots, 1.5f, 0,
-                                IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);*/
+                                IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);
 
     gps_widget->updateItemDisplay();
 
diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp
index 9f81172e2..d7d9cbd7d 100644
--- a/src/tracks/track.cpp
+++ b/src/tracks/track.cpp
@@ -523,10 +523,10 @@ void Track::loadTrackInfo()
     delete root;
 
     std::string dir = StringUtils::getPath(m_filename);
-    std::string easter_name = dir+"/easter_eggs.xml";
+    std::string easter_name = dir + "/easter_eggs.xml";
 
     XMLNode *easter = file_manager->createXMLTree(easter_name);
-  
+
     if(easter)
     {
         for(unsigned int i=0; i<easter->getNumNodes(); i++)
@@ -934,7 +934,7 @@ bool Track::loadMainTrack(const XMLNode &root)
     {
         mesh = irr_driver->getMesh(full_path);
     }
-    
+
     if(!mesh)
     {
         Log::fatal("track",
@@ -1969,7 +1969,7 @@ void Track::loadObjects(const XMLNode* root, const std::string& path, ModelDefin
                 libroot = library_nodes[name];
                 create_lod_definitions = false; // LOD definitions are already created, don't create them again
             }
-    
+
             scene::ISceneNode* parent = irr_driver->getSceneManager()->addEmptySceneNode();
             parent->setPosition(xyz);
             parent->setRotation(hpr);