]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/localplayer.cpp
Mgfractal: Move julia set selection into formula parameter
[dragonfireclient.git] / src / localplayer.cpp
index 16111629e60662a36178046e72f713174eb8944c..fd781f940f6f3c255d8edf0ff01a88c77d311655 100644 (file)
@@ -1,6 +1,6 @@
 /*
-Minetest-c55
-Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
+Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License as published by
@@ -19,12 +19,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "localplayer.h"
 
-#include "main.h" // For g_settings
 #include "event.h"
 #include "collision.h"
 #include "gamedef.h"
 #include "nodedef.h"
 #include "settings.h"
+#include "environment.h"
 #include "map.h"
 #include "util/numeric.h"
 
@@ -32,96 +32,137 @@ with this program; if not, write to the Free Software Foundation, Inc.,
        LocalPlayer
 */
 
-LocalPlayer::LocalPlayer(IGameDef *gamedef):
-       Player(gamedef),
+LocalPlayer::LocalPlayer(IGameDef *gamedef, const char *name):
+       Player(gamedef, name),
+       parent(0),
+       isAttached(false),
+       overridePosition(v3f(0,0,0)),
+       last_position(v3f(0,0,0)),
+       last_speed(v3f(0,0,0)),
+       last_pitch(0),
+       last_yaw(0),
+       last_keyPressed(0),
+       camera_impact(0.f),
+       last_animation(NO_ANIM),
+       hotbar_image(""),
+       hotbar_selected_image(""),
+       light_color(255,255,255,255),
        m_sneak_node(32767,32767,32767),
        m_sneak_node_exists(false),
+       m_need_to_get_new_sneak_node(true),
+       m_sneak_node_bb_ymax(0),
        m_old_node_below(32767,32767,32767),
        m_old_node_below_type("air"),
-       m_need_to_get_new_sneak_node(true),
-       m_can_jump(false)
+       m_can_jump(false),
+       m_cao(NULL)
 {
        // Initialize hp to 0, so that no hearts will be shown if server
        // doesn't support health points
        hp = 0;
+       eye_offset_first = v3f(0,0,0);
+       eye_offset_third = v3f(0,0,0);
 }
 
 LocalPlayer::~LocalPlayer()
 {
 }
 
-void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
-               core::list<CollisionInfo> *collision_info)
+void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
+               std::vector<CollisionInfo> *collision_info)
 {
+       Map *map = &env->getMap();
        INodeDefManager *nodemgr = m_gamedef->ndef();
 
        v3f position = getPosition();
 
-       v3f old_speed = m_speed;
+       // Copy parent position if local player is attached
+       if(isAttached)
+       {
+               setPosition(overridePosition);
+               m_sneak_node_exists = false;
+               return;
+       }
 
-       // Skip collision detection if a special movement mode is used
+       // Skip collision detection if noclip mode is used
        bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
-       bool free_move = fly_allowed && g_settings->getBool("free_move");
-       if(free_move)
-       {
-        position += m_speed * dtime;
+       bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
+               g_settings->getBool("noclip");
+       bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
+       if (free_move) {
+               position += m_speed * dtime;
                setPosition(position);
+               m_sneak_node_exists = false;
                return;
        }
 
        /*
                Collision detection
        */
-       
+
+       bool is_valid_position;
+       MapNode node;
+       v3s16 pp;
+
        /*
-               Check if player is in water (the oscillating value)
+               Check if player is in liquid (the oscillating value)
        */
-       try{
-               // If in water, the threshold of coming out is at higher y
-               if(in_water)
-               {
-                       v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
-                       in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
-               }
-               // If not in water, the threshold of going in is at lower y
-               else
-               {
-                       v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
-                       in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
+
+       // If in liquid, the threshold of coming out is at higher y
+       if (in_liquid)
+       {
+               pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
+               node = map->getNodeNoEx(pp, &is_valid_position);
+               if (is_valid_position) {
+                       in_liquid = nodemgr->get(node.getContent()).isLiquid();
+                       liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
+               } else {
+                       in_liquid = false;
                }
        }
-       catch(InvalidPositionException &e)
+       // If not in liquid, the threshold of going in is at lower y
+       else
        {
-               in_water = false;
+               pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
+               node = map->getNodeNoEx(pp, &is_valid_position);
+               if (is_valid_position) {
+                       in_liquid = nodemgr->get(node.getContent()).isLiquid();
+                       liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
+               } else {
+                       in_liquid = false;
+               }
        }
 
+
        /*
-               Check if player is in water (the stable value)
+               Check if player is in liquid (the stable value)
        */
-       try{
-               v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
-               in_water_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
-       }
-       catch(InvalidPositionException &e)
-       {
-               in_water_stable = false;
+       pp = floatToInt(position + v3f(0,0,0), BS);
+       node = map->getNodeNoEx(pp, &is_valid_position);
+       if (is_valid_position) {
+               in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
+       } else {
+               in_liquid_stable = false;
        }
 
        /*
                Check if player is climbing
        */
 
-       try {
-               v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
-               v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
-               is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable ||
-               nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move);
-       }
-       catch(InvalidPositionException &e)
-       {
+
+       pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
+       v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
+       node = map->getNodeNoEx(pp, &is_valid_position);
+       bool is_valid_position2;
+       MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
+
+       if (!(is_valid_position && is_valid_position2)) {
                is_climbing = false;
+       } else {
+               is_climbing = (nodemgr->get(node.getContent()).climbable
+                               || nodemgr->get(node2.getContent()).climbable) && !free_move;
        }
 
+
        /*
                Collision uncertainty radius
                Make it a bit larger than the maximum distance of movement
@@ -131,11 +172,8 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
        f32 d = 0.15*BS;
 
        // This should always apply, otherwise there are glitches
-       assert(d > pos_max_d);
+       sanity_check(d > pos_max_d);
 
-       float player_radius = BS*0.30;
-       float player_height = BS*1.55;
-       
        // Maximum distance over border for sneaking
        f32 sneak_max = BS*0.4;
 
@@ -143,41 +181,40 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
                If sneaking, keep in range from the last walked node and don't
                fall off from it
        */
-       if(control.sneak && m_sneak_node_exists)
-       {
-               f32 maxd = 0.5*BS + sneak_max;
+       if (control.sneak && m_sneak_node_exists &&
+                       !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
+                       physics_override_sneak) {
+               f32 maxd = 0.5 * BS + sneak_max;
                v3f lwn_f = intToFloat(m_sneak_node, BS);
                position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
                position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
-               
-               f32 min_y = lwn_f.Y + 0.5*BS;
-               if(position.Y < min_y)
-               {
-                       position.Y = min_y;
 
-                       if(m_speed.Y < 0)
+               if (!is_climbing) {
+                       // Move up if necessary
+                       f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
+                       if (position.Y < new_y)
+                               position.Y = new_y;
+                       /*
+                               Collision seems broken, since player is sinking when
+                               sneaking over the edges of current sneaking_node.
+                               TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
+                       */
+                       if (m_speed.Y < 0)
                                m_speed.Y = 0;
                }
        }
 
-       /*
-               Calculate player collision box (new and old)
-       */
-       core::aabbox3d<f32> playerbox(
-               -player_radius,
-               0.0,
-               -player_radius,
-               player_radius,
-               player_height,
-               player_radius
-       );
-
+       // this shouldn't be hardcoded but transmitted from server
        float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
 
+#ifdef __ANDROID__
+       player_stepheight += (0.5 * BS);
+#endif
+
        v3f accel_f = v3f(0,0,0);
 
-       collisionMoveResult result = collisionMoveSimple(&map, m_gamedef,
-                       pos_max_d, playerbox, player_stepheight, dtime,
+       collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
+                       pos_max_d, m_collisionbox, player_stepheight, dtime,
                        position, m_speed, accel_f);
 
        /*
@@ -188,35 +225,36 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
        */
        bool touching_ground_was = touching_ground;
        touching_ground = result.touching_ground;
-    
-    bool standing_on_unloaded = result.standing_on_unloaded;
+
+    //bool standing_on_unloaded = result.standing_on_unloaded;
 
        /*
                Check the nodes under the player to see from which node the
                player is sneaking from, if any.  If the node from under
                the player has been removed, the player falls.
        */
-       v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
-       if(m_sneak_node_exists &&
-          nodemgr->get(map.getNodeNoEx(m_old_node_below)).name == "air" &&
-          m_old_node_below_type != "air")
-       {
+       f32 position_y_mod = 0.05 * BS;
+       if (m_sneak_node_bb_ymax > 0)
+               position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
+       v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
+       if (m_sneak_node_exists &&
+                       nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
+                       m_old_node_below_type != "air") {
                // Old node appears to have been removed; that is,
                // it wasn't air before but now it is
                m_need_to_get_new_sneak_node = false;
                m_sneak_node_exists = false;
-       }
-       else if(nodemgr->get(map.getNodeNoEx(current_node)).name != "air")
-       {
+       } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
                // We are on something, so make sure to recalculate the sneak
                // node.
                m_need_to_get_new_sneak_node = true;
        }
-       if(m_need_to_get_new_sneak_node)
-       {
-               v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
+
+       if (m_need_to_get_new_sneak_node && physics_override_sneak) {
+               m_sneak_node_bb_ymax = 0;
+               v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
                v2f player_p2df(position.X, position.Z);
-               f32 min_distance_f = 100000.0*BS;
+               f32 min_distance_f = 100000.0 * BS;
                // If already seeking from some node, compare to it.
                /*if(m_sneak_node_exists)
                {
@@ -239,33 +277,49 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
                        f32 max_axis_distance_f = MYMAX(
                                        fabs(player_p2df.X-node_p2df.X),
                                        fabs(player_p2df.Y-node_p2df.Y));
-                                       
+
                        if(distance_f > min_distance_f ||
                                        max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
                                continue;
 
-                       try{
-                               // The node to be sneaked on has to be walkable
-                               if(nodemgr->get(map.getNode(p)).walkable == false)
-                                       continue;
-                               // And the node above it has to be nonwalkable
-                               if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true)
-                                       continue;
-                       }
-                       catch(InvalidPositionException &e)
-                       {
+
+                       // The node to be sneaked on has to be walkable
+                       node = map->getNodeNoEx(p, &is_valid_position);
+                       if (!is_valid_position || nodemgr->get(node).walkable == false)
                                continue;
+                       // And the node above it has to be nonwalkable
+                       node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
+                       if (!is_valid_position || nodemgr->get(node).walkable) {
+                               continue;
+                       }
+                       if (!physics_override_sneak_glitch) {
+                               node =map->getNodeNoEx(p + v3s16(0,2,0), &is_valid_position);
+                               if (!is_valid_position || nodemgr->get(node).walkable)
+                                       continue;
                        }
 
                        min_distance_f = distance_f;
                        new_sneak_node = p;
                }
-               
-               bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
+
+               bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
 
                m_sneak_node = new_sneak_node;
                m_sneak_node_exists = sneak_node_found;
 
+               if (sneak_node_found) {
+                       f32 cb_max = 0;
+                       MapNode n = map->getNodeNoEx(m_sneak_node);
+                       std::vector<aabb3f> nodeboxes = n.getCollisionBoxes(nodemgr);
+                       for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
+                                       it != nodeboxes.end(); ++it) {
+                               aabb3f box = *it;
+                               if (box.MaxEdge.Y > cb_max)
+                                       cb_max = box.MaxEdge.Y;
+                       }
+                       m_sneak_node_bb_ymax = cb_max;
+               }
+
                /*
                        If sneaking, the player's collision box can be in air, so
                        this has to be set explicitly
@@ -273,38 +327,38 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
                if(sneak_node_found && control.sneak)
                        touching_ground = true;
        }
-       
+
        /*
                Set new position
        */
        setPosition(position);
-       
+
        /*
                Report collisions
        */
-       if(collision_info)
-       {
-               // Report fall collision
-               if(old_speed.Y < m_speed.Y - 0.1 && !standing_on_unloaded)
-               {
-                       CollisionInfo info;
-                       info.t = COLLISION_FALL;
-                       info.speed = m_speed.Y - old_speed.Y;
+
+       // Dont report if flying
+       if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
+               for(size_t i=0; i<result.collisions.size(); i++) {
+                       const CollisionInfo &info = result.collisions[i];
                        collision_info->push_back(info);
                }
        }
 
-       if(!touching_ground_was && touching_ground){
+       if(!result.standing_on_object && !touching_ground_was && touching_ground) {
                MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
                m_gamedef->event()->put(e);
+
+               // Set camera impact value to be used for view bobbing
+               camera_impact = getSpeed().Y * -1;
        }
 
        {
                camera_barely_in_ceiling = false;
                v3s16 camera_np = floatToInt(getEyePosition(), BS);
-               MapNode n = map.getNodeNoEx(camera_np);
+               MapNode n = map->getNodeNoEx(camera_np);
                if(n.getContent() != CONTENT_IGNORE){
-                       if(nodemgr->get(n).walkable){
+                       if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
                                camera_barely_in_ceiling = true;
                        }
                }
@@ -314,82 +368,99 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
                Update the node last under the player
        */
        m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
-       m_old_node_below_type = nodemgr->get(map.getNodeNoEx(m_old_node_below)).name;
-       
+       m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
+
        /*
                Check properties of the node on which the player is standing
        */
-       const ContentFeatures &f = nodemgr->get(map.getNodeNoEx(getStandingNodePos()));
+       const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
        // Determine if jumping is possible
-       m_can_jump = touching_ground;
+       m_can_jump = touching_ground && !in_liquid;
        if(itemgroup_get(f.groups, "disable_jump"))
                m_can_jump = false;
+       // Jump key pressed while jumping off from a bouncy block
+       if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
+               m_speed.Y >= -0.5 * BS) {
+               float jumpspeed = movement_speed_jump * physics_override_jump;
+               if (m_speed.Y > 1) {
+                       // Reduce boost when speed already is high
+                       m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
+               } else {
+                       m_speed.Y += jumpspeed;
+               }
+               setSpeed(m_speed);
+               m_can_jump = false;
+       }
 }
 
-void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
+void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
 {
-       move(dtime, map, pos_max_d, NULL);
+       move(dtime, env, pos_max_d, NULL);
 }
 
 void LocalPlayer::applyControl(float dtime)
 {
        // Clear stuff
-       swimming_up = false;
+       swimming_vertical = false;
 
-       // Random constants
-       f32 walk_acceleration = 4.0 * BS;
-       f32 walkspeed_max = 4.0 * BS;
-       
        setPitch(control.pitch);
        setYaw(control.yaw);
-       
+
+       // Nullify speed and don't run positioning code if the player is attached
+       if(isAttached)
+       {
+               setSpeed(v3f(0,0,0));
+               return;
+       }
+
        v3f move_direction = v3f(0,0,1);
        move_direction.rotateXZBy(getYaw());
-       
-       v3f speed = v3f(0,0,0);
-       
+
+       v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
+       v3f speedV = v3f(0,0,0); // Vertical (Y)
+
        bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
        bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
 
        bool free_move = fly_allowed && g_settings->getBool("free_move");
        bool fast_move = fast_allowed && g_settings->getBool("fast_move");
+       // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
+       bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
        bool continuous_forward = g_settings->getBool("continuous_forward");
-
-       if(free_move || is_climbing)
-       {
-               v3f speed = getSpeed();
-               speed.Y = 0;
-               setSpeed(speed);
-       }
+       bool always_fly_fast = g_settings->getBool("always_fly_fast");
 
        // Whether superspeed mode is used or not
        bool superspeed = false;
-       
-       // If free movement and fast movement, always move fast
-       if(free_move && fast_move)
+
+       if (always_fly_fast && free_move && fast_move)
                superspeed = true;
-       
+
        // Old descend control
        if(g_settings->getBool("aux1_descends"))
        {
+               // If free movement and fast movement, always move fast
+               if(free_move && fast_move)
+                       superspeed = true;
+
                // Auxiliary button 1 (E)
                if(control.aux1)
                {
                        if(free_move)
                        {
                                // In free movement mode, aux1 descends
-                               v3f speed = getSpeed();
                                if(fast_move)
-                                       speed.Y = -20*BS;
+                                       speedV.Y = -movement_speed_fast;
                                else
-                                       speed.Y = -walkspeed_max;
-                               setSpeed(speed);
+                                       speedV.Y = -movement_speed_walk;
+                       }
+                       else if(in_liquid || in_liquid_stable)
+                       {
+                               speedV.Y = -movement_speed_walk;
+                               swimming_vertical = true;
                        }
                        else if(is_climbing)
                        {
-                                       v3f speed = getSpeed();
-                               speed.Y = -3*BS;
-                               setSpeed(speed);
+                               speedV.Y = -movement_speed_climb;
                        }
                        else
                        {
@@ -406,10 +477,9 @@ void LocalPlayer::applyControl(float dtime)
                // Auxiliary button 1 (E)
                if(control.aux1)
                {
-                       if(!free_move && !is_climbing)
+                       if(!is_climbing)
                        {
-                               // If not free movement but fast is allowed, aux1 is
-                               // "Turbo button"
+                               // aux1 is "Turbo button"
                                if(fast_move)
                                        superspeed = true;
                        }
@@ -420,54 +490,66 @@ void LocalPlayer::applyControl(float dtime)
                        if(free_move)
                        {
                                // In free movement mode, sneak descends
-                               v3f speed = getSpeed();
-                               if(fast_move)
-                                       speed.Y = -20*BS;
+                               if (fast_move && (control.aux1 || always_fly_fast))
+                                       speedV.Y = -movement_speed_fast;
+                               else
+                                       speedV.Y = -movement_speed_walk;
+                       }
+                       else if(in_liquid || in_liquid_stable)
+                       {
+                               if(fast_climb)
+                                       speedV.Y = -movement_speed_fast;
                                else
-                                       speed.Y = -walkspeed_max;
-                                       setSpeed(speed);
+                                       speedV.Y = -movement_speed_walk;
+                               swimming_vertical = true;
                        }
                        else if(is_climbing)
                        {
-                               v3f speed = getSpeed();
-                               speed.Y = -3*BS;
-                               setSpeed(speed);
+                               if(fast_climb)
+                                       speedV.Y = -movement_speed_fast;
+                               else
+                                       speedV.Y = -movement_speed_climb;
                        }
                }
        }
 
-       if(continuous_forward)
-               speed += move_direction;
+       if (continuous_forward)
+               speedH += move_direction;
 
-       if(control.up)
-       {
-               if(continuous_forward)
-                       superspeed = true;
-               else
-                       speed += move_direction;
+       if (control.up) {
+               if (continuous_forward) {
+                       if (fast_move)
+                               superspeed = true;
+               } else {
+                       speedH += move_direction;
+               }
        }
        if(control.down)
        {
-               speed -= move_direction;
+               speedH -= move_direction;
        }
        if(control.left)
        {
-               speed += move_direction.crossProduct(v3f(0,1,0));
+               speedH += move_direction.crossProduct(v3f(0,1,0));
        }
        if(control.right)
        {
-               speed += move_direction.crossProduct(v3f(0,-1,0));
+               speedH += move_direction.crossProduct(v3f(0,-1,0));
        }
        if(control.jump)
        {
-               if(free_move)
-               {
-                       v3f speed = getSpeed();
-                       if(fast_move)
-                               speed.Y = 20*BS;
-                       else
-                               speed.Y = walkspeed_max;
-                       setSpeed(speed);
+               if (free_move) {
+                       if (g_settings->getBool("aux1_descends") || always_fly_fast) {
+                               if (fast_move)
+                                       speedV.Y = movement_speed_fast;
+                               else
+                                       speedV.Y = movement_speed_walk;
+                       } else {
+                               if(fast_move && control.aux1)
+                                       speedV.Y = movement_speed_fast;
+                               else
+                                       speedV.Y = movement_speed_walk;
+                       }
                }
                else if(m_can_jump)
                {
@@ -476,55 +558,67 @@ void LocalPlayer::applyControl(float dtime)
                                raising the height at which the jump speed is kept
                                at its starting value
                        */
-                       v3f speed = getSpeed();
-                       if(speed.Y >= -0.5*BS)
+                       v3f speedJ = getSpeed();
+                       if(speedJ.Y >= -0.5 * BS)
                        {
-                               speed.Y = 6.5*BS;
-                               setSpeed(speed);
-                               
+                               speedJ.Y = movement_speed_jump * physics_override_jump;
+                               setSpeed(speedJ);
+
                                MtEvent *e = new SimpleTriggerEvent("PlayerJump");
                                m_gamedef->event()->put(e);
                        }
                }
-               // Use the oscillating value for getting out of water
-               // (so that the player doesn't fly on the surface)
-               else if(in_water)
+               else if(in_liquid)
                {
-                       v3f speed = getSpeed();
-                       speed.Y = 1.5*BS;
-                       setSpeed(speed);
-                       swimming_up = true;
+                       if(fast_climb)
+                               speedV.Y = movement_speed_fast;
+                       else
+                               speedV.Y = movement_speed_walk;
+                       swimming_vertical = true;
                }
                else if(is_climbing)
                {
-                       v3f speed = getSpeed();
-                       speed.Y = 3*BS;
-                       setSpeed(speed);
+                       if(fast_climb)
+                               speedV.Y = movement_speed_fast;
+                       else
+                               speedV.Y = movement_speed_climb;
                }
        }
 
        // The speed of the player (Y is ignored)
-       if(superspeed)
-               speed = speed.normalize() * walkspeed_max * 5.0;
-       else if(control.sneak)
-               speed = speed.normalize() * walkspeed_max / 3.0;
+       if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
+               speedH = speedH.normalize() * movement_speed_fast;
+       else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
+               speedH = speedH.normalize() * movement_speed_crouch;
        else
-               speed = speed.normalize() * walkspeed_max;
-       
-       f32 inc = walk_acceleration * BS * dtime;
-       
-       // Faster acceleration if fast and free movement
-       if(free_move && fast_move)
-               inc = walk_acceleration * BS * dtime * 10;
-       
+               speedH = speedH.normalize() * movement_speed_walk;
+
+       // Acceleration increase
+       f32 incH = 0; // Horizontal (X, Z)
+       f32 incV = 0; // Vertical (Y)
+       if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
+       {
+               // Jumping and falling
+               if(superspeed || (fast_move && control.aux1))
+                       incH = movement_acceleration_fast * BS * dtime;
+               else
+                       incH = movement_acceleration_air * BS * dtime;
+               incV = 0; // No vertical acceleration in air
+       }
+       else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
+               incH = incV = movement_acceleration_fast * BS * dtime;
+       else
+               incH = incV = movement_acceleration_default * BS * dtime;
+
        // Accelerate to target speed with maximum increment
-       accelerate(speed, inc);
+       accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
+       accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
 }
 
 v3s16 LocalPlayer::getStandingNodePos()
 {
        if(m_sneak_node_exists)
                return m_sneak_node;
-       return floatToInt(getPosition(), BS);
+       return floatToInt(getPosition() - v3f(0, BS, 0), BS);
 }