]> git.lizzy.rs Git - minetest.git/blob - src/localplayer.cpp
Sneak: Fix sneaking on free-floating lower-half slabs
[minetest.git] / src / localplayer.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "localplayer.h"
21
22 #include "event.h"
23 #include "collision.h"
24 #include "nodedef.h"
25 #include "settings.h"
26 #include "environment.h"
27 #include "map.h"
28 #include "client.h"
29
30 /*
31         LocalPlayer
32 */
33
34 LocalPlayer::LocalPlayer(Client *client, const char *name):
35         Player(name, client->idef()),
36         parent(0),
37         hp(PLAYER_MAX_HP),
38         got_teleported(false),
39         isAttached(false),
40         touching_ground(false),
41         in_liquid(false),
42         in_liquid_stable(false),
43         liquid_viscosity(0),
44         is_climbing(false),
45         swimming_vertical(false),
46         // Movement overrides are multipliers and must be 1 by default
47         physics_override_speed(1.0f),
48         physics_override_jump(1.0f),
49         physics_override_gravity(1.0f),
50         physics_override_sneak(true),
51         physics_override_sneak_glitch(true),
52         overridePosition(v3f(0,0,0)),
53         last_position(v3f(0,0,0)),
54         last_speed(v3f(0,0,0)),
55         last_pitch(0),
56         last_yaw(0),
57         last_keyPressed(0),
58         last_camera_fov(0),
59         last_wanted_range(0),
60         camera_impact(0.f),
61         last_animation(NO_ANIM),
62         hotbar_image(""),
63         hotbar_selected_image(""),
64         light_color(255,255,255,255),
65         hurt_tilt_timer(0.0f),
66         hurt_tilt_strength(0.0f),
67         m_position(0,0,0),
68         m_sneak_node(32767,32767,32767),
69         m_sneak_node_bb_top(0,0,0,0,0,0),
70         m_sneak_node_exists(false),
71         m_need_to_get_new_sneak_node(true),
72         m_sneak_ladder_detected(false),
73         m_old_node_below(32767,32767,32767),
74         m_old_node_below_type("air"),
75         m_can_jump(false),
76         m_breath(PLAYER_MAX_BREATH),
77         m_yaw(0),
78         m_pitch(0),
79         camera_barely_in_ceiling(false),
80         m_collisionbox(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30),
81         m_cao(NULL),
82         m_client(client)
83 {
84         // Initialize hp to 0, so that no hearts will be shown if server
85         // doesn't support health points
86         hp = 0;
87         eye_offset_first = v3f(0,0,0);
88         eye_offset_third = v3f(0,0,0);
89 }
90
91 LocalPlayer::~LocalPlayer()
92 {
93 }
94
95 static aabb3f getTopBoundingBox(const std::vector<aabb3f> &nodeboxes)
96 {
97         aabb3f b_max;
98         b_max.reset(-BS, -BS, -BS);
99         for (std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
100                         it != nodeboxes.end(); ++it) {
101                 aabb3f box = *it;
102                 if (box.MaxEdge.Y > b_max.MaxEdge.Y)
103                         b_max = box;
104                 else if (box.MaxEdge.Y == b_max.MaxEdge.Y)
105                         b_max.addInternalBox(box);
106         }
107         return aabb3f(v3f(b_max.MinEdge.X, b_max.MaxEdge.Y, b_max.MinEdge.Z), b_max.MaxEdge);
108 }
109
110 #define GETNODE(map, p3, v2, y, valid) \
111         (map)->getNodeNoEx((p3) + v3s16((v2).X, y, (v2).Y), valid)
112
113 // pos is the node the player is standing inside(!)
114 static bool detectSneakLadder(Map *map, INodeDefManager *nodemgr, v3s16 pos)
115 {
116         // Detects a structure known as "sneak ladder" or "sneak elevator"
117         // that relies on bugs to provide a fast means of vertical transportation,
118         // the bugs have since been fixed but this function remains to keep it working.
119         // NOTE: This is just entirely a huge hack and causes way too many problems.
120         bool is_valid_position;
121         MapNode node;
122         // X/Z vectors for 4 neighboring nodes
123         static const v2s16 vecs[] = { v2s16(-1, 0), v2s16(1, 0), v2s16(0, -1), v2s16(0, 1) };
124
125         for (u16 i = 0; i < ARRLEN(vecs); i++) {
126                 const v2s16 vec = vecs[i];
127
128                 // walkability of bottom & top node should differ
129                 node = GETNODE(map, pos, vec, 0, &is_valid_position);
130                 if (!is_valid_position)
131                         continue;
132                 bool w = nodemgr->get(node).walkable;
133                 node = GETNODE(map, pos, vec, 1, &is_valid_position);
134                 if (!is_valid_position || w == nodemgr->get(node).walkable)
135                         continue;
136
137                 // check one more node above OR below with corresponding walkability
138                 node = GETNODE(map, pos, vec, -1, &is_valid_position);
139                 bool ok = is_valid_position && w != nodemgr->get(node).walkable;
140                 if (!ok) {
141                         node = GETNODE(map, pos, vec, 2, &is_valid_position);
142                         ok = is_valid_position && w == nodemgr->get(node).walkable;
143                 }
144
145                 if (ok)
146                         return true;
147         }
148
149         return false;
150 }
151
152 #undef GETNODE
153
154 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
155                 std::vector<CollisionInfo> *collision_info)
156 {
157         Map *map = &env->getMap();
158         INodeDefManager *nodemgr = m_client->ndef();
159
160         v3f position = getPosition();
161
162         // Copy parent position if local player is attached
163         if(isAttached)
164         {
165                 setPosition(overridePosition);
166                 m_sneak_node_exists = false;
167                 return;
168         }
169
170         // Skip collision detection if noclip mode is used
171         bool fly_allowed = m_client->checkLocalPrivilege("fly");
172         bool noclip = m_client->checkLocalPrivilege("noclip") &&
173                 g_settings->getBool("noclip");
174         bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
175         if (free_move) {
176                 position += m_speed * dtime;
177                 setPosition(position);
178                 m_sneak_node_exists = false;
179                 return;
180         }
181
182         /*
183                 Collision detection
184         */
185
186         bool is_valid_position;
187         MapNode node;
188         v3s16 pp;
189
190         /*
191                 Check if player is in liquid (the oscillating value)
192         */
193
194         // If in liquid, the threshold of coming out is at higher y
195         if (in_liquid)
196         {
197                 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
198                 node = map->getNodeNoEx(pp, &is_valid_position);
199                 if (is_valid_position) {
200                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
201                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
202                 } else {
203                         in_liquid = false;
204                 }
205         }
206         // If not in liquid, the threshold of going in is at lower y
207         else
208         {
209                 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
210                 node = map->getNodeNoEx(pp, &is_valid_position);
211                 if (is_valid_position) {
212                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
213                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
214                 } else {
215                         in_liquid = false;
216                 }
217         }
218
219
220         /*
221                 Check if player is in liquid (the stable value)
222         */
223         pp = floatToInt(position + v3f(0,0,0), BS);
224         node = map->getNodeNoEx(pp, &is_valid_position);
225         if (is_valid_position) {
226                 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
227         } else {
228                 in_liquid_stable = false;
229         }
230
231         /*
232                 Check if player is climbing
233         */
234
235
236         pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
237         v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
238         node = map->getNodeNoEx(pp, &is_valid_position);
239         bool is_valid_position2;
240         MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
241
242         if (!(is_valid_position && is_valid_position2)) {
243                 is_climbing = false;
244         } else {
245                 is_climbing = (nodemgr->get(node.getContent()).climbable
246                                 || nodemgr->get(node2.getContent()).climbable) && !free_move;
247         }
248
249
250         /*
251                 Collision uncertainty radius
252                 Make it a bit larger than the maximum distance of movement
253         */
254         //f32 d = pos_max_d * 1.1;
255         // A fairly large value in here makes moving smoother
256         f32 d = 0.15*BS;
257
258         // This should always apply, otherwise there are glitches
259         sanity_check(d > pos_max_d);
260
261         // Max. distance (X, Z) over border for sneaking determined by collision box
262         // * 0.49 to keep the center just barely on the node
263         v3f sneak_max = m_collisionbox.getExtent() * 0.49;
264         if (m_sneak_ladder_detected) {
265                 // restore legacy behaviour (this makes the m_speed.Y hack necessary)
266                 sneak_max = v3f(0.4 * BS, 0, 0.4 * BS);
267         }
268
269         /*
270                 If sneaking, keep in range from the last walked node and don't
271                 fall off from it
272         */
273         if (control.sneak && m_sneak_node_exists &&
274                         !(fly_allowed && g_settings->getBool("free_move")) &&
275                         !in_liquid && !is_climbing &&
276                         physics_override_sneak && !got_teleported) {
277                 v3f sn_f = intToFloat(m_sneak_node, BS);
278                 const v3f bmin = m_sneak_node_bb_top.MinEdge;
279                 const v3f bmax = m_sneak_node_bb_top.MaxEdge;
280
281                 position.X = rangelim(position.X,
282                                 sn_f.X+bmin.X - sneak_max.X, sn_f.X+bmax.X + sneak_max.X);
283                 position.Z = rangelim(position.Z,
284                                 sn_f.Z+bmin.Z - sneak_max.Z, sn_f.Z+bmax.Z + sneak_max.Z);
285
286                 // Because we keep the player collision box on the node, limiting
287                 // position.Y is not necessary but useful to prevent players from
288                 // being inside a node if sneaking on e.g. the lower part of a stair
289                 if (!m_sneak_ladder_detected) {
290                         position.Y = MYMAX(position.Y, sn_f.Y+bmax.Y);
291                 } else {
292                         // legacy behaviour that sometimes causes some weird slow sinking
293                         m_speed.Y = MYMAX(m_speed.Y, 0);
294                 }
295         }
296
297         if (got_teleported)
298                 got_teleported = false;
299
300         // TODO: this shouldn't be hardcoded but transmitted from server
301         float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
302
303 #ifdef __ANDROID__
304         player_stepheight += (0.6 * BS);
305 #endif
306
307         v3f accel_f = v3f(0,0,0);
308
309         collisionMoveResult result = collisionMoveSimple(env, m_client,
310                 pos_max_d, m_collisionbox, player_stepheight, dtime,
311                 &position, &m_speed, accel_f);
312
313         /*
314                 If the player's feet touch the topside of any node, this is
315                 set to true.
316
317                 Player is allowed to jump when this is true.
318         */
319         bool touching_ground_was = touching_ground;
320         touching_ground = result.touching_ground;
321
322         // We want the top of the sneak node to be below the players feet
323         f32 position_y_mod;
324         if (m_sneak_node_exists)
325                 position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - 0.05 * BS;
326         else
327                 position_y_mod = (0.5 - 0.05) * BS;
328         v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
329         /*
330                 Check the nodes under the player to see from which node the
331                 player is sneaking from, if any.  If the node from under
332                 the player has been removed, the player falls.
333         */
334         if (m_sneak_node_exists &&
335                         nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
336                         m_old_node_below_type != "air") {
337                 // Old node appears to have been removed; that is,
338                 // it wasn't air before but now it is
339                 m_need_to_get_new_sneak_node = false;
340                 m_sneak_node_exists = false;
341         } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
342                 // We are on something, so make sure to recalculate the sneak
343                 // node.
344                 m_need_to_get_new_sneak_node = true;
345         }
346
347         if (m_need_to_get_new_sneak_node && physics_override_sneak) {
348                 v2f player_p2df(position.X, position.Z);
349                 f32 min_distance_f = 100000.0 * BS;
350                 v3s16 new_sneak_node = m_sneak_node;
351                 for(s16 x=-1; x<=1; x++)
352                 for(s16 z=-1; z<=1; z++)
353                 {
354                         v3s16 p = current_node + v3s16(x,0,z);
355                         v3f pf = intToFloat(p, BS);
356                         v2f node_p2df(pf.X, pf.Z);
357                         f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
358
359                         if (distance_f > min_distance_f ||
360                                         fabs(player_p2df.X-node_p2df.X) > (.5+.1)*BS + sneak_max.X ||
361                                         fabs(player_p2df.Y-node_p2df.Y) > (.5+.1)*BS + sneak_max.Z)
362                                 continue;
363
364
365                         // The node to be sneaked on has to be walkable
366                         node = map->getNodeNoEx(p, &is_valid_position);
367                         if (!is_valid_position || !nodemgr->get(node).walkable)
368                                 continue;
369                         // And the node(s) above have to be nonwalkable
370                         bool ok = true;
371                         if (!physics_override_sneak_glitch) {
372                                 u16 height = ceilf(
373                                                 (m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
374                                 );
375                                 for (u16 y = 1; y <= height; y++) {
376                                         node = map->getNodeNoEx(p + v3s16(0,y,0), &is_valid_position);
377                                         if (!is_valid_position || nodemgr->get(node).walkable) {
378                                                 ok = false;
379                                                 break;
380                                         }
381                                 }
382                         } else {
383                                 // legacy behaviour: check just one node
384                                 node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
385                                 ok = is_valid_position && !nodemgr->get(node).walkable;
386                         }
387                         if (!ok)
388                                 continue;
389
390                         min_distance_f = distance_f;
391                         new_sneak_node = p;
392                 }
393
394                 bool sneak_node_found = (min_distance_f < 100000.0 * BS);
395                 m_sneak_node = new_sneak_node;
396                 m_sneak_node_exists = sneak_node_found;
397
398                 if (sneak_node_found) {
399                         // Update saved top bounding box of sneak node
400                         MapNode n = map->getNodeNoEx(m_sneak_node);
401                         std::vector<aabb3f> nodeboxes;
402                         n.getCollisionBoxes(nodemgr, &nodeboxes);
403                         m_sneak_node_bb_top = getTopBoundingBox(nodeboxes);
404
405                         m_sneak_ladder_detected = physics_override_sneak_glitch &&
406                                         detectSneakLadder(map, nodemgr, floatToInt(position, BS));
407                 } else {
408                         m_sneak_ladder_detected = false;
409                 }
410         }
411
412         /*
413                 Set new position
414         */
415         setPosition(position);
416
417         /*
418                 Report collisions
419         */
420
421         // Dont report if flying
422         if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
423                 for(size_t i=0; i<result.collisions.size(); i++) {
424                         const CollisionInfo &info = result.collisions[i];
425                         collision_info->push_back(info);
426                 }
427         }
428
429         if(!result.standing_on_object && !touching_ground_was && touching_ground) {
430                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
431                 m_client->event()->put(e);
432
433                 // Set camera impact value to be used for view bobbing
434                 camera_impact = getSpeed().Y * -1;
435         }
436
437         {
438                 camera_barely_in_ceiling = false;
439                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
440                 MapNode n = map->getNodeNoEx(camera_np);
441                 if(n.getContent() != CONTENT_IGNORE){
442                         if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
443                                 camera_barely_in_ceiling = true;
444                         }
445                 }
446         }
447
448         /*
449                 Update the node last under the player
450         */
451         m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
452         m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
453
454         /*
455                 Check properties of the node on which the player is standing
456         */
457         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
458         // Determine if jumping is possible
459         m_can_jump = touching_ground && !in_liquid;
460         if (itemgroup_get(f.groups, "disable_jump"))
461                 m_can_jump = false;
462         else if (control.sneak && m_sneak_ladder_detected && !in_liquid && !is_climbing)
463                 m_can_jump = true;
464
465         // Jump key pressed while jumping off from a bouncy block
466         if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
467                 m_speed.Y >= -0.5 * BS) {
468                 float jumpspeed = movement_speed_jump * physics_override_jump;
469                 if (m_speed.Y > 1) {
470                         // Reduce boost when speed already is high
471                         m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
472                 } else {
473                         m_speed.Y += jumpspeed;
474                 }
475                 setSpeed(m_speed);
476                 m_can_jump = false;
477         }
478 }
479
480 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
481 {
482         move(dtime, env, pos_max_d, NULL);
483 }
484
485 void LocalPlayer::applyControl(float dtime)
486 {
487         // Clear stuff
488         swimming_vertical = false;
489
490         setPitch(control.pitch);
491         setYaw(control.yaw);
492
493         // Nullify speed and don't run positioning code if the player is attached
494         if(isAttached)
495         {
496                 setSpeed(v3f(0,0,0));
497                 return;
498         }
499
500         v3f move_direction = v3f(0,0,1);
501         move_direction.rotateXZBy(getYaw());
502
503         v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
504         v3f speedV = v3f(0,0,0); // Vertical (Y)
505
506         bool fly_allowed = m_client->checkLocalPrivilege("fly");
507         bool fast_allowed = m_client->checkLocalPrivilege("fast");
508
509         bool free_move = fly_allowed && g_settings->getBool("free_move");
510         bool fast_move = fast_allowed && g_settings->getBool("fast_move");
511         // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
512         bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
513         bool continuous_forward = g_settings->getBool("continuous_forward");
514         bool always_fly_fast = g_settings->getBool("always_fly_fast");
515
516         // Whether superspeed mode is used or not
517         bool superspeed = false;
518
519         if (always_fly_fast && free_move && fast_move)
520                 superspeed = true;
521
522         // Old descend control
523         if(g_settings->getBool("aux1_descends"))
524         {
525                 // If free movement and fast movement, always move fast
526                 if(free_move && fast_move)
527                         superspeed = true;
528
529                 // Auxiliary button 1 (E)
530                 if(control.aux1)
531                 {
532                         if(free_move)
533                         {
534                                 // In free movement mode, aux1 descends
535                                 if(fast_move)
536                                         speedV.Y = -movement_speed_fast;
537                                 else
538                                         speedV.Y = -movement_speed_walk;
539                         }
540                         else if(in_liquid || in_liquid_stable)
541                         {
542                                 speedV.Y = -movement_speed_walk;
543                                 swimming_vertical = true;
544                         }
545                         else if(is_climbing)
546                         {
547                                 speedV.Y = -movement_speed_climb;
548                         }
549                         else
550                         {
551                                 // If not free movement but fast is allowed, aux1 is
552                                 // "Turbo button"
553                                 if(fast_move)
554                                         superspeed = true;
555                         }
556                 }
557         }
558         // New minecraft-like descend control
559         else
560         {
561                 // Auxiliary button 1 (E)
562                 if(control.aux1)
563                 {
564                         if(!is_climbing)
565                         {
566                                 // aux1 is "Turbo button"
567                                 if(fast_move)
568                                         superspeed = true;
569                         }
570                 }
571
572                 if(control.sneak)
573                 {
574                         if(free_move)
575                         {
576                                 // In free movement mode, sneak descends
577                                 if (fast_move && (control.aux1 || always_fly_fast))
578                                         speedV.Y = -movement_speed_fast;
579                                 else
580                                         speedV.Y = -movement_speed_walk;
581                         }
582                         else if(in_liquid || in_liquid_stable)
583                         {
584                                 if(fast_climb)
585                                         speedV.Y = -movement_speed_fast;
586                                 else
587                                         speedV.Y = -movement_speed_walk;
588                                 swimming_vertical = true;
589                         }
590                         else if(is_climbing)
591                         {
592                                 if(fast_climb)
593                                         speedV.Y = -movement_speed_fast;
594                                 else
595                                         speedV.Y = -movement_speed_climb;
596                         }
597                 }
598         }
599
600         if (continuous_forward)
601                 speedH += move_direction;
602
603         if (control.up) {
604                 if (continuous_forward) {
605                         if (fast_move)
606                                 superspeed = true;
607                 } else {
608                         speedH += move_direction;
609                 }
610         }
611         if (control.down) {
612                 speedH -= move_direction;
613         }
614         if (!control.up && !control.down) {
615                 speedH -= move_direction *
616                         (control.forw_move_joystick_axis / 32767.f);
617         }
618         if (control.left) {
619                 speedH += move_direction.crossProduct(v3f(0,1,0));
620         }
621         if (control.right) {
622                 speedH += move_direction.crossProduct(v3f(0,-1,0));
623         }
624         if (!control.left && !control.right) {
625                 speedH -= move_direction.crossProduct(v3f(0,1,0)) *
626                         (control.sidew_move_joystick_axis / 32767.f);
627         }
628         if(control.jump)
629         {
630                 if (free_move) {
631                         if (g_settings->getBool("aux1_descends") || always_fly_fast) {
632                                 if (fast_move)
633                                         speedV.Y = movement_speed_fast;
634                                 else
635                                         speedV.Y = movement_speed_walk;
636                         } else {
637                                 if(fast_move && control.aux1)
638                                         speedV.Y = movement_speed_fast;
639                                 else
640                                         speedV.Y = movement_speed_walk;
641                         }
642                 }
643                 else if(m_can_jump)
644                 {
645                         /*
646                                 NOTE: The d value in move() affects jump height by
647                                 raising the height at which the jump speed is kept
648                                 at its starting value
649                         */
650                         v3f speedJ = getSpeed();
651                         if(speedJ.Y >= -0.5 * BS)
652                         {
653                                 speedJ.Y = movement_speed_jump * physics_override_jump;
654                                 setSpeed(speedJ);
655
656                                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
657                                 m_client->event()->put(e);
658                         }
659                 }
660                 else if(in_liquid)
661                 {
662                         if(fast_climb)
663                                 speedV.Y = movement_speed_fast;
664                         else
665                                 speedV.Y = movement_speed_walk;
666                         swimming_vertical = true;
667                 }
668                 else if(is_climbing)
669                 {
670                         if(fast_climb)
671                                 speedV.Y = movement_speed_fast;
672                         else
673                                 speedV.Y = movement_speed_climb;
674                 }
675         }
676
677         // The speed of the player (Y is ignored)
678         if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
679                 speedH = speedH.normalize() * movement_speed_fast;
680         else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
681                 speedH = speedH.normalize() * movement_speed_crouch;
682         else
683                 speedH = speedH.normalize() * movement_speed_walk;
684
685         // Acceleration increase
686         f32 incH = 0; // Horizontal (X, Z)
687         f32 incV = 0; // Vertical (Y)
688         if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
689         {
690                 // Jumping and falling
691                 if(superspeed || (fast_move && control.aux1))
692                         incH = movement_acceleration_fast * BS * dtime;
693                 else
694                         incH = movement_acceleration_air * BS * dtime;
695                 incV = 0; // No vertical acceleration in air
696         }
697         else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
698                 incH = incV = movement_acceleration_fast * BS * dtime;
699         else
700                 incH = incV = movement_acceleration_default * BS * dtime;
701
702         // Accelerate to target speed with maximum increment
703         accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
704         accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
705 }
706
707 v3s16 LocalPlayer::getStandingNodePos()
708 {
709         if(m_sneak_node_exists)
710                 return m_sneak_node;
711         return floatToInt(getPosition() - v3f(0, BS, 0), BS);
712 }
713
714 v3s16 LocalPlayer::getFootstepNodePos()
715 {
716         if (touching_ground)
717                 // BS * 0.05 below the player's feet ensures a 1/16th height
718                 // nodebox is detected instead of the node below it.
719                 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
720         // A larger distance below is necessary for a footstep sound
721         // when landing after a jump or fall. BS * 0.5 ensures water
722         // sounds when swimming in 1 node deep water.
723         return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
724 }
725
726 v3s16 LocalPlayer::getLightPosition() const
727 {
728         return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
729 }
730
731 v3f LocalPlayer::getEyeOffset() const
732 {
733         float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f;
734         return v3f(0, BS * eye_height, 0);
735 }
736
737 // Horizontal acceleration (X and Z), Y direction is ignored
738 void LocalPlayer::accelerateHorizontal(const v3f &target_speed, const f32 max_increase)
739 {
740         if (max_increase == 0)
741                 return;
742
743         v3f d_wanted = target_speed - m_speed;
744         d_wanted.Y = 0;
745         f32 dl = d_wanted.getLength();
746         if (dl > max_increase)
747                 dl = max_increase;
748
749         v3f d = d_wanted.normalize() * dl;
750
751         m_speed.X += d.X;
752         m_speed.Z += d.Z;
753 }
754
755 // Vertical acceleration (Y), X and Z directions are ignored
756 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
757 {
758         if (max_increase == 0)
759                 return;
760
761         f32 d_wanted = target_speed.Y - m_speed.Y;
762         if (d_wanted > max_increase)
763                 d_wanted = max_increase;
764         else if (d_wanted < -max_increase)
765                 d_wanted = -max_increase;
766
767         m_speed.Y += d_wanted;
768 }
769