]> git.lizzy.rs Git - minetest.git/blob - src/localplayer.cpp
ab30df22b8e9ba8ef836bfe97cec71f95e920715
[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                 sneak_max = v3f(0.4 * BS, 0, 0.4 * BS); // restore legacy behaviour
266
267         /*
268                 If sneaking, keep in range from the last walked node and don't
269                 fall off from it
270         */
271         if (control.sneak && m_sneak_node_exists &&
272                         !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
273                         physics_override_sneak && !got_teleported) {
274                 v3f sn_f = intToFloat(m_sneak_node, BS);
275                 const v3f bmin = m_sneak_node_bb_top.MinEdge;
276                 const v3f bmax = m_sneak_node_bb_top.MaxEdge;
277
278                 position.X = rangelim(position.X,
279                                 sn_f.X+bmin.X - sneak_max.X, sn_f.X+bmax.X + sneak_max.X);
280                 position.Z = rangelim(position.Z,
281                                 sn_f.Z+bmin.Z - sneak_max.Z, sn_f.Z+bmax.Z + sneak_max.Z);
282                 // Because we keep the player collision box on the node,
283                 // limiting position.Y is not necessary
284
285                 if (m_sneak_ladder_detected) {
286                         // this sometimes causes some weird slow sinking but *shrug*
287                         m_speed.Y = MYMAX(m_speed.Y, 0);
288                 }
289         }
290
291         if (got_teleported)
292                 got_teleported = false;
293
294         // TODO: this shouldn't be hardcoded but transmitted from server
295         float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
296
297 #ifdef __ANDROID__
298         player_stepheight += (0.6 * BS);
299 #endif
300
301         v3f accel_f = v3f(0,0,0);
302
303         collisionMoveResult result = collisionMoveSimple(env, m_client,
304                 pos_max_d, m_collisionbox, player_stepheight, dtime,
305                 &position, &m_speed, accel_f);
306
307         /*
308                 If the player's feet touch the topside of any node, this is
309                 set to true.
310
311                 Player is allowed to jump when this is true.
312         */
313         bool touching_ground_was = touching_ground;
314         touching_ground = result.touching_ground;
315
316         // We want the top of the sneak node to be below the players feet
317         f32 position_y_mod;
318         if (m_sneak_node_exists)
319                 position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - 0.05 * BS;
320         else
321                 position_y_mod = (1.0 - 0.05) * BS;
322         v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
323         /*
324                 Check the nodes under the player to see from which node the
325                 player is sneaking from, if any.  If the node from under
326                 the player has been removed, the player falls.
327         */
328         if (m_sneak_node_exists &&
329                         nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
330                         m_old_node_below_type != "air") {
331                 // Old node appears to have been removed; that is,
332                 // it wasn't air before but now it is
333                 m_need_to_get_new_sneak_node = false;
334                 m_sneak_node_exists = false;
335         } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
336                 // We are on something, so make sure to recalculate the sneak
337                 // node.
338                 m_need_to_get_new_sneak_node = true;
339         }
340
341         if (m_need_to_get_new_sneak_node && physics_override_sneak) {
342                 v2f player_p2df(position.X, position.Z);
343                 f32 min_distance_f = 100000.0 * BS;
344                 v3s16 new_sneak_node = m_sneak_node;
345                 for(s16 x=-1; x<=1; x++)
346                 for(s16 z=-1; z<=1; z++)
347                 {
348                         v3s16 p = current_node + v3s16(x,0,z);
349                         v3f pf = intToFloat(p, BS);
350                         v2f node_p2df(pf.X, pf.Z);
351                         f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
352
353                         if (distance_f > min_distance_f ||
354                                         fabs(player_p2df.X-node_p2df.X) > (.5+.1)*BS + sneak_max.X ||
355                                         fabs(player_p2df.Y-node_p2df.Y) > (.5+.1)*BS + sneak_max.Z)
356                                 continue;
357
358
359                         // The node to be sneaked on has to be walkable
360                         node = map->getNodeNoEx(p, &is_valid_position);
361                         if (!is_valid_position || !nodemgr->get(node).walkable)
362                                 continue;
363                         // And the node(s) above have to be nonwalkable
364                         bool ok = true;
365                         if (!physics_override_sneak_glitch) {
366                                 u16 height = ceilf(
367                                                 (m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
368                                 );
369                                 for (u16 y = 1; y <= height; y++) {
370                                         node = map->getNodeNoEx(p + v3s16(0,y,0), &is_valid_position);
371                                         if (!is_valid_position || nodemgr->get(node).walkable) {
372                                                 ok = false;
373                                                 break;
374                                         }
375                                 }
376                         } else {
377                                 // legacy behaviour: check just one node
378                                 node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
379                                 ok = is_valid_position && !nodemgr->get(node).walkable;
380                         }
381                         if (!ok)
382                                 continue;
383
384                         min_distance_f = distance_f;
385                         new_sneak_node = p;
386                 }
387
388                 bool sneak_node_found = (min_distance_f < 100000.0 * BS);
389                 m_sneak_node = new_sneak_node;
390                 m_sneak_node_exists = sneak_node_found;
391
392                 if (sneak_node_found) {
393                         // Update saved top bounding box of sneak node
394                         MapNode n = map->getNodeNoEx(m_sneak_node);
395                         std::vector<aabb3f> nodeboxes;
396                         n.getCollisionBoxes(nodemgr, &nodeboxes);
397                         m_sneak_node_bb_top = getTopBoundingBox(nodeboxes);
398
399                         m_sneak_ladder_detected = physics_override_sneak_glitch &&
400                                         detectSneakLadder(map, nodemgr, floatToInt(position, BS));
401                 } else {
402                         m_sneak_ladder_detected = false;
403                 }
404         }
405
406         /*
407                 Set new position
408         */
409         setPosition(position);
410
411         /*
412                 Report collisions
413         */
414
415         // Dont report if flying
416         if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
417                 for(size_t i=0; i<result.collisions.size(); i++) {
418                         const CollisionInfo &info = result.collisions[i];
419                         collision_info->push_back(info);
420                 }
421         }
422
423         if(!result.standing_on_object && !touching_ground_was && touching_ground) {
424                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
425                 m_client->event()->put(e);
426
427                 // Set camera impact value to be used for view bobbing
428                 camera_impact = getSpeed().Y * -1;
429         }
430
431         {
432                 camera_barely_in_ceiling = false;
433                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
434                 MapNode n = map->getNodeNoEx(camera_np);
435                 if(n.getContent() != CONTENT_IGNORE){
436                         if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
437                                 camera_barely_in_ceiling = true;
438                         }
439                 }
440         }
441
442         /*
443                 Update the node last under the player
444         */
445         m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
446         m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
447
448         /*
449                 Check properties of the node on which the player is standing
450         */
451         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
452         // Determine if jumping is possible
453         m_can_jump = touching_ground && !in_liquid;
454         if(itemgroup_get(f.groups, "disable_jump"))
455                 m_can_jump = false;
456         // Jump key pressed while jumping off from a bouncy block
457         if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
458                 m_speed.Y >= -0.5 * BS) {
459                 float jumpspeed = movement_speed_jump * physics_override_jump;
460                 if (m_speed.Y > 1) {
461                         // Reduce boost when speed already is high
462                         m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
463                 } else {
464                         m_speed.Y += jumpspeed;
465                 }
466                 setSpeed(m_speed);
467                 m_can_jump = false;
468         }
469 }
470
471 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
472 {
473         move(dtime, env, pos_max_d, NULL);
474 }
475
476 void LocalPlayer::applyControl(float dtime)
477 {
478         // Clear stuff
479         swimming_vertical = false;
480
481         setPitch(control.pitch);
482         setYaw(control.yaw);
483
484         // Nullify speed and don't run positioning code if the player is attached
485         if(isAttached)
486         {
487                 setSpeed(v3f(0,0,0));
488                 return;
489         }
490
491         v3f move_direction = v3f(0,0,1);
492         move_direction.rotateXZBy(getYaw());
493
494         v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
495         v3f speedV = v3f(0,0,0); // Vertical (Y)
496
497         bool fly_allowed = m_client->checkLocalPrivilege("fly");
498         bool fast_allowed = m_client->checkLocalPrivilege("fast");
499
500         bool free_move = fly_allowed && g_settings->getBool("free_move");
501         bool fast_move = fast_allowed && g_settings->getBool("fast_move");
502         // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
503         bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
504         bool continuous_forward = g_settings->getBool("continuous_forward");
505         bool always_fly_fast = g_settings->getBool("always_fly_fast");
506
507         // Whether superspeed mode is used or not
508         bool superspeed = false;
509
510         if (always_fly_fast && free_move && fast_move)
511                 superspeed = true;
512
513         // Old descend control
514         if(g_settings->getBool("aux1_descends"))
515         {
516                 // If free movement and fast movement, always move fast
517                 if(free_move && fast_move)
518                         superspeed = true;
519
520                 // Auxiliary button 1 (E)
521                 if(control.aux1)
522                 {
523                         if(free_move)
524                         {
525                                 // In free movement mode, aux1 descends
526                                 if(fast_move)
527                                         speedV.Y = -movement_speed_fast;
528                                 else
529                                         speedV.Y = -movement_speed_walk;
530                         }
531                         else if(in_liquid || in_liquid_stable)
532                         {
533                                 speedV.Y = -movement_speed_walk;
534                                 swimming_vertical = true;
535                         }
536                         else if(is_climbing)
537                         {
538                                 speedV.Y = -movement_speed_climb;
539                         }
540                         else
541                         {
542                                 // If not free movement but fast is allowed, aux1 is
543                                 // "Turbo button"
544                                 if(fast_move)
545                                         superspeed = true;
546                         }
547                 }
548         }
549         // New minecraft-like descend control
550         else
551         {
552                 // Auxiliary button 1 (E)
553                 if(control.aux1)
554                 {
555                         if(!is_climbing)
556                         {
557                                 // aux1 is "Turbo button"
558                                 if(fast_move)
559                                         superspeed = true;
560                         }
561                 }
562
563                 if(control.sneak)
564                 {
565                         if(free_move)
566                         {
567                                 // In free movement mode, sneak descends
568                                 if (fast_move && (control.aux1 || always_fly_fast))
569                                         speedV.Y = -movement_speed_fast;
570                                 else
571                                         speedV.Y = -movement_speed_walk;
572                         }
573                         else if(in_liquid || in_liquid_stable)
574                         {
575                                 if(fast_climb)
576                                         speedV.Y = -movement_speed_fast;
577                                 else
578                                         speedV.Y = -movement_speed_walk;
579                                 swimming_vertical = true;
580                         }
581                         else if(is_climbing)
582                         {
583                                 if(fast_climb)
584                                         speedV.Y = -movement_speed_fast;
585                                 else
586                                         speedV.Y = -movement_speed_climb;
587                         }
588                 }
589         }
590
591         if (continuous_forward)
592                 speedH += move_direction;
593
594         if (control.up) {
595                 if (continuous_forward) {
596                         if (fast_move)
597                                 superspeed = true;
598                 } else {
599                         speedH += move_direction;
600                 }
601         }
602         if (control.down) {
603                 speedH -= move_direction;
604         }
605         if (!control.up && !control.down) {
606                 speedH -= move_direction *
607                         (control.forw_move_joystick_axis / 32767.f);
608         }
609         if (control.left) {
610                 speedH += move_direction.crossProduct(v3f(0,1,0));
611         }
612         if (control.right) {
613                 speedH += move_direction.crossProduct(v3f(0,-1,0));
614         }
615         if (!control.left && !control.right) {
616                 speedH -= move_direction.crossProduct(v3f(0,1,0)) *
617                         (control.sidew_move_joystick_axis / 32767.f);
618         }
619         if(control.jump)
620         {
621                 if (free_move) {
622                         if (g_settings->getBool("aux1_descends") || always_fly_fast) {
623                                 if (fast_move)
624                                         speedV.Y = movement_speed_fast;
625                                 else
626                                         speedV.Y = movement_speed_walk;
627                         } else {
628                                 if(fast_move && control.aux1)
629                                         speedV.Y = movement_speed_fast;
630                                 else
631                                         speedV.Y = movement_speed_walk;
632                         }
633                 }
634                 else if(m_can_jump || (control.sneak && m_sneak_ladder_detected))
635                 {
636                         /*
637                                 NOTE: The d value in move() affects jump height by
638                                 raising the height at which the jump speed is kept
639                                 at its starting value
640                         */
641                         v3f speedJ = getSpeed();
642                         if(speedJ.Y >= -0.5 * BS)
643                         {
644                                 speedJ.Y = movement_speed_jump * physics_override_jump;
645                                 setSpeed(speedJ);
646
647                                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
648                                 m_client->event()->put(e);
649                         }
650                 }
651                 else if(in_liquid)
652                 {
653                         if(fast_climb)
654                                 speedV.Y = movement_speed_fast;
655                         else
656                                 speedV.Y = movement_speed_walk;
657                         swimming_vertical = true;
658                 }
659                 else if(is_climbing)
660                 {
661                         if(fast_climb)
662                                 speedV.Y = movement_speed_fast;
663                         else
664                                 speedV.Y = movement_speed_climb;
665                 }
666         }
667
668         // The speed of the player (Y is ignored)
669         if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
670                 speedH = speedH.normalize() * movement_speed_fast;
671         else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
672                 speedH = speedH.normalize() * movement_speed_crouch;
673         else
674                 speedH = speedH.normalize() * movement_speed_walk;
675
676         // Acceleration increase
677         f32 incH = 0; // Horizontal (X, Z)
678         f32 incV = 0; // Vertical (Y)
679         if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
680         {
681                 // Jumping and falling
682                 if(superspeed || (fast_move && control.aux1))
683                         incH = movement_acceleration_fast * BS * dtime;
684                 else
685                         incH = movement_acceleration_air * BS * dtime;
686                 incV = 0; // No vertical acceleration in air
687         }
688         else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
689                 incH = incV = movement_acceleration_fast * BS * dtime;
690         else
691                 incH = incV = movement_acceleration_default * BS * dtime;
692
693         // Accelerate to target speed with maximum increment
694         accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
695         accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
696 }
697
698 v3s16 LocalPlayer::getStandingNodePos()
699 {
700         if(m_sneak_node_exists)
701                 return m_sneak_node;
702         return floatToInt(getPosition() - v3f(0, BS, 0), BS);
703 }
704
705 v3s16 LocalPlayer::getFootstepNodePos()
706 {
707         if (touching_ground)
708                 // BS * 0.05 below the player's feet ensures a 1/16th height
709                 // nodebox is detected instead of the node below it.
710                 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
711         // A larger distance below is necessary for a footstep sound
712         // when landing after a jump or fall. BS * 0.5 ensures water
713         // sounds when swimming in 1 node deep water.
714         return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
715 }
716
717 v3s16 LocalPlayer::getLightPosition() const
718 {
719         return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
720 }
721
722 v3f LocalPlayer::getEyeOffset() const
723 {
724         float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f;
725         return v3f(0, BS * eye_height, 0);
726 }
727
728 // Horizontal acceleration (X and Z), Y direction is ignored
729 void LocalPlayer::accelerateHorizontal(const v3f &target_speed, const f32 max_increase)
730 {
731         if (max_increase == 0)
732                 return;
733
734         v3f d_wanted = target_speed - m_speed;
735         d_wanted.Y = 0;
736         f32 dl = d_wanted.getLength();
737         if (dl > max_increase)
738                 dl = max_increase;
739
740         v3f d = d_wanted.normalize() * dl;
741
742         m_speed.X += d.X;
743         m_speed.Z += d.Z;
744 }
745
746 // Vertical acceleration (Y), X and Z directions are ignored
747 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
748 {
749         if (max_increase == 0)
750                 return;
751
752         f32 d_wanted = target_speed.Y - m_speed.Y;
753         if (d_wanted > max_increase)
754                 d_wanted = max_increase;
755         else if (d_wanted < -max_increase)
756                 d_wanted = -max_increase;
757
758         m_speed.Y += d_wanted;
759 }
760