Add slippery group for nodes (players/items slide)
[oweals/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 #include "content_cao.h"
30
31 /*
32         LocalPlayer
33 */
34
35 LocalPlayer::LocalPlayer(Client *client, const char *name):
36         Player(name, client->idef()),
37         m_client(client)
38 {
39 }
40
41 LocalPlayer::~LocalPlayer()
42 {
43 }
44
45 static aabb3f getNodeBoundingBox(const std::vector<aabb3f> &nodeboxes)
46 {
47         if (nodeboxes.size() == 0)
48                 return aabb3f(0, 0, 0, 0, 0, 0);
49
50         aabb3f b_max;
51
52         std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
53         b_max = aabb3f(it->MinEdge, it->MaxEdge);
54
55         ++it;
56         for (; it != nodeboxes.end(); ++it)
57                 b_max.addInternalBox(*it);
58
59         return b_max;
60 }
61
62 bool LocalPlayer::updateSneakNode(Map *map, const v3f &position,
63                 const v3f &sneak_max)
64 {
65         static const v3s16 dir9_center[9] = {
66                 v3s16( 0, 0,  0),
67                 v3s16( 1, 0,  0),
68                 v3s16(-1, 0,  0),
69                 v3s16( 0, 0,  1),
70                 v3s16( 0, 0, -1),
71                 v3s16( 1, 0,  1),
72                 v3s16(-1, 0,  1),
73                 v3s16( 1, 0, -1),
74                 v3s16(-1, 0, -1)
75         };
76
77         INodeDefManager *nodemgr = m_client->ndef();
78         MapNode node;
79         bool is_valid_position;
80         bool new_sneak_node_exists = m_sneak_node_exists;
81
82         // We want the top of the sneak node to be below the players feet
83         f32 position_y_mod = 0.05 * BS;
84         if (m_sneak_node_exists)
85                 position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - position_y_mod;
86
87         // Get position of current standing node
88         const v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
89
90         if (current_node != m_sneak_node) {
91                 new_sneak_node_exists = false;
92         } else {
93                 node = map->getNodeNoEx(current_node, &is_valid_position);
94                 if (!is_valid_position || !nodemgr->get(node).walkable)
95                         new_sneak_node_exists = false;
96         }
97
98         // Keep old sneak node
99         if (new_sneak_node_exists)
100                 return true;
101
102         // Get new sneak node
103         m_sneak_ladder_detected = false;
104         f32 min_distance_f = 100000.0 * BS;
105
106         for (s16 d = 0; d < 9; d++) {
107                 const v3s16 p = current_node + dir9_center[d];
108                 const v3f pf = intToFloat(p, BS);
109                 const v2f diff(position.X - pf.X, position.Z - pf.Z);
110                 f32 distance_f = diff.getLength();
111
112                 if (distance_f > min_distance_f ||
113                                 fabs(diff.X) > (.5 + .1) * BS + sneak_max.X ||
114                                 fabs(diff.Y) > (.5 + .1) * BS + sneak_max.Z)
115                         continue;
116
117
118                 // The node to be sneaked on has to be walkable
119                 node = map->getNodeNoEx(p, &is_valid_position);
120                 if (!is_valid_position || !nodemgr->get(node).walkable)
121                         continue;
122                 // And the node(s) above have to be nonwalkable
123                 bool ok = true;
124                 if (!physics_override_sneak_glitch) {
125                         u16 height = ceilf(
126                                         (m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
127                         );
128                         for (u16 y = 1; y <= height; y++) {
129                                 node = map->getNodeNoEx(p + v3s16(0, y, 0), &is_valid_position);
130                                 if (!is_valid_position || nodemgr->get(node).walkable) {
131                                         ok = false;
132                                         break;
133                                 }
134                         }
135                 } else {
136                         // legacy behaviour: check just one node
137                         node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
138                         ok = is_valid_position && !nodemgr->get(node).walkable;
139                 }
140                 if (!ok)
141                         continue;
142
143                 min_distance_f = distance_f;
144                 m_sneak_node = p;
145                 new_sneak_node_exists = true;
146         }
147
148         if (!new_sneak_node_exists)
149                 return false;
150
151         // Update saved top bounding box of sneak node
152         node = map->getNodeNoEx(m_sneak_node);
153         std::vector<aabb3f> nodeboxes;
154         node.getCollisionBoxes(nodemgr, &nodeboxes);
155         m_sneak_node_bb_top = getNodeBoundingBox(nodeboxes);
156
157         if (physics_override_sneak_glitch) {
158                 // Detect sneak ladder:
159                 // Node two meters above sneak node must be solid
160                 node = map->getNodeNoEx(m_sneak_node + v3s16(0, 2, 0),
161                         &is_valid_position);
162                 if (is_valid_position && nodemgr->get(node).walkable) {
163                         // Node three meters above: must be non-solid
164                         node = map->getNodeNoEx(m_sneak_node + v3s16(0, 3, 0),
165                                 &is_valid_position);
166                         m_sneak_ladder_detected = is_valid_position &&
167                                 !nodemgr->get(node).walkable;
168                 }
169         }
170         return true;
171 }
172
173 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
174                 std::vector<CollisionInfo> *collision_info)
175 {
176         // Temporary option for old move code
177         if (!physics_override_new_move) {
178                 old_move(dtime, env, pos_max_d, collision_info);
179                 return;
180         }
181
182         Map *map = &env->getMap();
183         INodeDefManager *nodemgr = m_client->ndef();
184
185         v3f position = getPosition();
186
187         // Copy parent position if local player is attached
188         if (isAttached) {
189                 setPosition(overridePosition);
190                 return;
191         }
192
193         // Skip collision detection if noclip mode is used
194         bool fly_allowed = m_client->checkLocalPrivilege("fly");
195         bool noclip = m_client->checkLocalPrivilege("noclip") &&
196                 g_settings->getBool("noclip");
197         bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
198         if (free_move) {
199                 position += m_speed * dtime;
200                 setPosition(position);
201                 return;
202         }
203
204         /*
205                 Collision detection
206         */
207
208         bool is_valid_position;
209         MapNode node;
210         v3s16 pp;
211
212         /*
213                 Check if player is in liquid (the oscillating value)
214         */
215
216         // If in liquid, the threshold of coming out is at higher y
217         if (in_liquid)
218         {
219                 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
220                 node = map->getNodeNoEx(pp, &is_valid_position);
221                 if (is_valid_position) {
222                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
223                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
224                 } else {
225                         in_liquid = false;
226                 }
227         }
228         // If not in liquid, the threshold of going in is at lower y
229         else
230         {
231                 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
232                 node = map->getNodeNoEx(pp, &is_valid_position);
233                 if (is_valid_position) {
234                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
235                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
236                 } else {
237                         in_liquid = false;
238                 }
239         }
240
241
242         /*
243                 Check if player is in liquid (the stable value)
244         */
245         pp = floatToInt(position + v3f(0,0,0), BS);
246         node = map->getNodeNoEx(pp, &is_valid_position);
247         if (is_valid_position) {
248                 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
249         } else {
250                 in_liquid_stable = false;
251         }
252
253         /*
254                 Check if player is climbing
255         */
256
257
258         pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
259         v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
260         node = map->getNodeNoEx(pp, &is_valid_position);
261         bool is_valid_position2;
262         MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
263
264         if (!(is_valid_position && is_valid_position2)) {
265                 is_climbing = false;
266         } else {
267                 is_climbing = (nodemgr->get(node.getContent()).climbable
268                                 || nodemgr->get(node2.getContent()).climbable) && !free_move;
269         }
270
271         /*
272                 Collision uncertainty radius
273                 Make it a bit larger than the maximum distance of movement
274         */
275         //f32 d = pos_max_d * 1.1;
276         // A fairly large value in here makes moving smoother
277         f32 d = 0.15*BS;
278
279         // This should always apply, otherwise there are glitches
280         sanity_check(d > pos_max_d);
281
282         // Player object property step height is multiplied by BS in
283         // /src/script/common/c_content.cpp and /src/content_sao.cpp
284         float player_stepheight = (m_cao == nullptr) ? 0.0f :
285                 (touching_ground ? m_cao->getStepHeight() : (0.2f * BS));
286
287         // TODO this is a problematic hack.
288         // Use a better implementation for autojump, or apply a custom stepheight
289         // to all players, as this currently creates unintended special movement
290         // abilities and advantages for Android players on a server.
291 #ifdef __ANDROID__
292         player_stepheight += (0.6f * BS);
293 #endif
294
295         v3f accel_f = v3f(0,0,0);
296
297         collisionMoveResult result = collisionMoveSimple(env, m_client,
298                 pos_max_d, m_collisionbox, player_stepheight, dtime,
299                 &position, &m_speed, accel_f);
300
301         bool could_sneak = control.sneak &&
302                 !(fly_allowed && g_settings->getBool("free_move")) &&
303                 !in_liquid && !is_climbing &&
304                 physics_override_sneak;
305         /*
306                 If the player's feet touch the topside of any node, this is
307                 set to true.
308
309                 Player is allowed to jump when this is true.
310         */
311         bool touching_ground_was = touching_ground;
312         touching_ground = result.touching_ground;
313         bool sneak_can_jump = false;
314
315         // Max. distance (X, Z) over border for sneaking determined by collision box
316         // * 0.49 to keep the center just barely on the node
317         v3f sneak_max = m_collisionbox.getExtent() * 0.49;
318
319         if (m_sneak_ladder_detected) {
320                 // restore legacy behaviour (this makes the m_speed.Y hack necessary)
321                 sneak_max = v3f(0.4 * BS, 0, 0.4 * BS);
322         }
323
324         /*
325                 If sneaking, keep on top of last walked node and don't fall off
326         */
327         if (could_sneak && m_sneak_node_exists) {
328                 const v3f sn_f = intToFloat(m_sneak_node, BS);
329                 const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge;
330                 const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge;
331                 const v3f old_pos = position;
332                 const v3f old_speed = m_speed;
333                 f32 y_diff = bmax.Y - position.Y;
334
335                 // (BS * 0.6f) is the basic stepheight while standing on ground
336                 if (y_diff < BS * 0.6f) {
337                         // Only center player when they're on the node
338                         position.X = rangelim(position.X,
339                                 bmin.X - sneak_max.X, bmax.X + sneak_max.X);
340                         position.Z = rangelim(position.Z,
341                                 bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z);
342
343                         if (position.X != old_pos.X)
344                                 m_speed.X = 0;
345                         if (position.Z != old_pos.Z)
346                                 m_speed.Z = 0;
347                 }
348
349                 if (y_diff > 0 && m_speed.Y < 0 &&
350                                 (physics_override_sneak_glitch || y_diff < BS * 0.6f)) {
351                         // Move player to the maximal height when falling or when
352                         // the ledge is climbed on the next step.
353                         position.Y = bmax.Y;
354                         m_speed.Y = 0;
355                 }
356
357                 // Allow jumping on node edges while sneaking
358                 if (m_speed.Y == 0 || m_sneak_ladder_detected)
359                         sneak_can_jump = true;
360
361                 if (collision_info != NULL &&
362                                 m_speed.Y - old_speed.Y > BS) {
363                         // Collide with sneak node, report fall damage
364                         CollisionInfo sn_info;
365                         sn_info.node_p = m_sneak_node;
366                         sn_info.old_speed = old_speed;
367                         sn_info.new_speed = m_speed;
368                         collision_info->push_back(sn_info);
369                 }
370         }
371
372         /*
373                 Find the next sneak node if necessary
374         */
375         bool new_sneak_node_exists = false;
376
377         if (could_sneak)
378                 new_sneak_node_exists = updateSneakNode(map, position, sneak_max);
379
380         /*
381                 Set new position but keep sneak node set
382         */
383         setPosition(position);
384         m_sneak_node_exists = new_sneak_node_exists;
385
386         /*
387                 Report collisions
388         */
389
390         // Dont report if flying
391         if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
392                 for(size_t i=0; i<result.collisions.size(); i++) {
393                         const CollisionInfo &info = result.collisions[i];
394                         collision_info->push_back(info);
395                 }
396         }
397
398         if(!result.standing_on_object && !touching_ground_was && touching_ground) {
399                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
400                 m_client->event()->put(e);
401
402                 // Set camera impact value to be used for view bobbing
403                 camera_impact = getSpeed().Y * -1;
404         }
405
406         {
407                 camera_barely_in_ceiling = false;
408                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
409                 MapNode n = map->getNodeNoEx(camera_np);
410                 if(n.getContent() != CONTENT_IGNORE){
411                         if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
412                                 camera_barely_in_ceiling = true;
413                         }
414                 }
415         }
416
417         /*
418                 Check properties of the node on which the player is standing
419         */
420         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
421         // Determine if jumping is possible
422         m_can_jump = (touching_ground && !in_liquid && !is_climbing)
423                         || sneak_can_jump;
424         if (itemgroup_get(f.groups, "disable_jump"))
425                 m_can_jump = false;
426
427         // Jump key pressed while jumping off from a bouncy block
428         if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
429                 m_speed.Y >= -0.5 * BS) {
430                 float jumpspeed = movement_speed_jump * physics_override_jump;
431                 if (m_speed.Y > 1) {
432                         // Reduce boost when speed already is high
433                         m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
434                 } else {
435                         m_speed.Y += jumpspeed;
436                 }
437                 setSpeed(m_speed);
438                 m_can_jump = false;
439         }
440 }
441
442 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
443 {
444         move(dtime, env, pos_max_d, NULL);
445 }
446
447 void LocalPlayer::applyControl(float dtime, ClientEnvironment *env)
448 {
449         // Clear stuff
450         swimming_vertical = false;
451
452         setPitch(control.pitch);
453         setYaw(control.yaw);
454
455         // Nullify speed and don't run positioning code if the player is attached
456         if(isAttached)
457         {
458                 setSpeed(v3f(0,0,0));
459                 return;
460         }
461
462         v3f move_direction = v3f(0,0,1);
463         move_direction.rotateXZBy(getYaw());
464
465         v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
466         v3f speedV = v3f(0,0,0); // Vertical (Y)
467
468         bool fly_allowed = m_client->checkLocalPrivilege("fly");
469         bool fast_allowed = m_client->checkLocalPrivilege("fast");
470
471         bool free_move = fly_allowed && g_settings->getBool("free_move");
472         bool fast_move = fast_allowed && g_settings->getBool("fast_move");
473         // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
474         bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
475         bool continuous_forward = g_settings->getBool("continuous_forward");
476         bool always_fly_fast = g_settings->getBool("always_fly_fast");
477
478         // Whether superspeed mode is used or not
479         bool superspeed = false;
480
481         if (always_fly_fast && free_move && fast_move)
482                 superspeed = true;
483
484         // Old descend control
485         if(g_settings->getBool("aux1_descends"))
486         {
487                 // If free movement and fast movement, always move fast
488                 if(free_move && fast_move)
489                         superspeed = true;
490
491                 // Auxiliary button 1 (E)
492                 if(control.aux1)
493                 {
494                         if(free_move)
495                         {
496                                 // In free movement mode, aux1 descends
497                                 if(fast_move)
498                                         speedV.Y = -movement_speed_fast;
499                                 else
500                                         speedV.Y = -movement_speed_walk;
501                         }
502                         else if(in_liquid || in_liquid_stable)
503                         {
504                                 speedV.Y = -movement_speed_walk;
505                                 swimming_vertical = true;
506                         }
507                         else if(is_climbing)
508                         {
509                                 speedV.Y = -movement_speed_climb;
510                         }
511                         else
512                         {
513                                 // If not free movement but fast is allowed, aux1 is
514                                 // "Turbo button"
515                                 if(fast_move)
516                                         superspeed = true;
517                         }
518                 }
519         }
520         // New minecraft-like descend control
521         else
522         {
523                 // Auxiliary button 1 (E)
524                 if(control.aux1)
525                 {
526                         if(!is_climbing)
527                         {
528                                 // aux1 is "Turbo button"
529                                 if(fast_move)
530                                         superspeed = true;
531                         }
532                 }
533
534                 if(control.sneak)
535                 {
536                         if(free_move)
537                         {
538                                 // In free movement mode, sneak descends
539                                 if (fast_move && (control.aux1 || always_fly_fast))
540                                         speedV.Y = -movement_speed_fast;
541                                 else
542                                         speedV.Y = -movement_speed_walk;
543                         }
544                         else if(in_liquid || in_liquid_stable)
545                         {
546                                 if(fast_climb)
547                                         speedV.Y = -movement_speed_fast;
548                                 else
549                                         speedV.Y = -movement_speed_walk;
550                                 swimming_vertical = true;
551                         }
552                         else if(is_climbing)
553                         {
554                                 if(fast_climb)
555                                         speedV.Y = -movement_speed_fast;
556                                 else
557                                         speedV.Y = -movement_speed_climb;
558                         }
559                 }
560         }
561
562         if (continuous_forward)
563                 speedH += move_direction;
564
565         if (control.up) {
566                 if (continuous_forward) {
567                         if (fast_move)
568                                 superspeed = true;
569                 } else {
570                         speedH += move_direction;
571                 }
572         }
573         if (control.down) {
574                 speedH -= move_direction;
575         }
576         if (!control.up && !control.down) {
577                 speedH -= move_direction *
578                         (control.forw_move_joystick_axis / 32767.f);
579         }
580         if (control.left) {
581                 speedH += move_direction.crossProduct(v3f(0,1,0));
582         }
583         if (control.right) {
584                 speedH += move_direction.crossProduct(v3f(0,-1,0));
585         }
586         if (!control.left && !control.right) {
587                 speedH -= move_direction.crossProduct(v3f(0,1,0)) *
588                         (control.sidew_move_joystick_axis / 32767.f);
589         }
590         if(control.jump)
591         {
592                 if (free_move) {
593                         if (g_settings->getBool("aux1_descends") || always_fly_fast) {
594                                 if (fast_move)
595                                         speedV.Y = movement_speed_fast;
596                                 else
597                                         speedV.Y = movement_speed_walk;
598                         } else {
599                                 if(fast_move && control.aux1)
600                                         speedV.Y = movement_speed_fast;
601                                 else
602                                         speedV.Y = movement_speed_walk;
603                         }
604                 }
605                 else if(m_can_jump)
606                 {
607                         /*
608                                 NOTE: The d value in move() affects jump height by
609                                 raising the height at which the jump speed is kept
610                                 at its starting value
611                         */
612                         v3f speedJ = getSpeed();
613                         if(speedJ.Y >= -0.5 * BS) {
614                                 speedJ.Y = movement_speed_jump * physics_override_jump;
615                                 setSpeed(speedJ);
616
617                                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
618                                 m_client->event()->put(e);
619                         }
620                 }
621                 else if(in_liquid)
622                 {
623                         if(fast_climb)
624                                 speedV.Y = movement_speed_fast;
625                         else
626                                 speedV.Y = movement_speed_walk;
627                         swimming_vertical = true;
628                 }
629                 else if(is_climbing)
630                 {
631                         if(fast_climb)
632                                 speedV.Y = movement_speed_fast;
633                         else
634                                 speedV.Y = movement_speed_climb;
635                 }
636         }
637
638         // The speed of the player (Y is ignored)
639         if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
640                 speedH = speedH.normalize() * movement_speed_fast;
641         else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
642                 speedH = speedH.normalize() * movement_speed_crouch;
643         else
644                 speedH = speedH.normalize() * movement_speed_walk;
645
646         // Acceleration increase
647         f32 incH = 0; // Horizontal (X, Z)
648         f32 incV = 0; // Vertical (Y)
649         if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
650         {
651                 // Jumping and falling
652                 if(superspeed || (fast_move && control.aux1))
653                         incH = movement_acceleration_fast * BS * dtime;
654                 else
655                         incH = movement_acceleration_air * BS * dtime;
656                 incV = 0; // No vertical acceleration in air
657         }
658         else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
659                 incH = incV = movement_acceleration_fast * BS * dtime;
660         else
661                 incH = incV = movement_acceleration_default * BS * dtime;
662
663         INodeDefManager *nodemgr = env->getGameDef()->ndef();
664         Map *map = &env->getMap();
665         bool slippery = false;
666         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
667         slippery = itemgroup_get(f.groups, "slippery");
668         // Accelerate to target speed with maximum increment
669         accelerateHorizontal(speedH * physics_override_speed,
670                         incH * physics_override_speed, slippery);
671         accelerateVertical(speedV * physics_override_speed,
672                         incV * physics_override_speed);
673 }
674
675 v3s16 LocalPlayer::getStandingNodePos()
676 {
677         if(m_sneak_node_exists)
678                 return m_sneak_node;
679         return floatToInt(getPosition() - v3f(0, BS, 0), BS);
680 }
681
682 v3s16 LocalPlayer::getFootstepNodePos()
683 {
684         if (in_liquid_stable)
685                 // Emit swimming sound if the player is in liquid
686                 return floatToInt(getPosition(), BS);
687         if (touching_ground)
688                 // BS * 0.05 below the player's feet ensures a 1/16th height
689                 // nodebox is detected instead of the node below it.
690                 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
691         // A larger distance below is necessary for a footstep sound
692         // when landing after a jump or fall. BS * 0.5 ensures water
693         // sounds when swimming in 1 node deep water.
694         return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
695 }
696
697 v3s16 LocalPlayer::getLightPosition() const
698 {
699         return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
700 }
701
702 v3f LocalPlayer::getEyeOffset() const
703 {
704         float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f;
705         return v3f(0, BS * eye_height, 0);
706 }
707
708 // Horizontal acceleration (X and Z), Y direction is ignored
709 void LocalPlayer::accelerateHorizontal(const v3f &target_speed,
710         const f32 max_increase, bool slippery)
711 {
712         if (max_increase == 0)
713                 return;
714
715         v3f d_wanted = target_speed - m_speed;
716         if (slippery) {
717                 if (target_speed == v3f(0))
718                         d_wanted = -m_speed * 0.05f;
719                 else
720                         d_wanted = target_speed * 0.1f - m_speed * 0.1f;
721         }
722
723         d_wanted.Y = 0;
724         f32 dl = d_wanted.getLength();
725         if (dl > max_increase)
726                 dl = max_increase;
727
728         v3f d = d_wanted.normalize() * dl;
729
730         m_speed.X += d.X;
731         m_speed.Z += d.Z;
732 }
733
734 // Vertical acceleration (Y), X and Z directions are ignored
735 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
736 {
737         if (max_increase == 0)
738                 return;
739
740         f32 d_wanted = target_speed.Y - m_speed.Y;
741         if (d_wanted > max_increase)
742                 d_wanted = max_increase;
743         else if (d_wanted < -max_increase)
744                 d_wanted = -max_increase;
745
746         m_speed.Y += d_wanted;
747 }
748
749 // Temporary option for old move code
750 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
751                 std::vector<CollisionInfo> *collision_info)
752 {
753         Map *map = &env->getMap();
754         INodeDefManager *nodemgr = m_client->ndef();
755
756         v3f position = getPosition();
757
758         // Copy parent position if local player is attached
759         if (isAttached) {
760                 setPosition(overridePosition);
761                 m_sneak_node_exists = false;
762                 return;
763         }
764
765         // Skip collision detection if noclip mode is used
766         bool fly_allowed = m_client->checkLocalPrivilege("fly");
767         bool noclip = m_client->checkLocalPrivilege("noclip") &&
768                 g_settings->getBool("noclip");
769         bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
770         if (free_move) {
771                 position += m_speed * dtime;
772                 setPosition(position);
773                 m_sneak_node_exists = false;
774                 return;
775         }
776
777         /*
778                 Collision detection
779         */
780         bool is_valid_position;
781         MapNode node;
782         v3s16 pp;
783
784         /*
785                 Check if player is in liquid (the oscillating value)
786         */
787         if (in_liquid) {
788                 // If in liquid, the threshold of coming out is at higher y
789                 pp = floatToInt(position + v3f(0, BS * 0.1, 0), BS);
790                 node = map->getNodeNoEx(pp, &is_valid_position);
791                 if (is_valid_position) {
792                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
793                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
794                 } else {
795                         in_liquid = false;
796                 }
797         } else {
798                 // If not in liquid, the threshold of going in is at lower y
799                 pp = floatToInt(position + v3f(0, BS * 0.5, 0), BS);
800                 node = map->getNodeNoEx(pp, &is_valid_position);
801                 if (is_valid_position) {
802                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
803                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
804                 } else {
805                         in_liquid = false;
806                 }
807         }
808
809         /*
810                 Check if player is in liquid (the stable value)
811         */
812         pp = floatToInt(position + v3f(0, 0, 0), BS);
813         node = map->getNodeNoEx(pp, &is_valid_position);
814         if (is_valid_position)
815                 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
816         else
817                 in_liquid_stable = false;
818
819         /*
820                 Check if player is climbing
821         */
822         pp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS);
823         v3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS);
824         node = map->getNodeNoEx(pp, &is_valid_position);
825         bool is_valid_position2;
826         MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
827
828         if (!(is_valid_position && is_valid_position2))
829                 is_climbing = false;
830         else
831                 is_climbing = (nodemgr->get(node.getContent()).climbable ||
832                                 nodemgr->get(node2.getContent()).climbable) && !free_move;
833
834         /*
835                 Collision uncertainty radius
836                 Make it a bit larger than the maximum distance of movement
837         */
838         //f32 d = pos_max_d * 1.1;
839         // A fairly large value in here makes moving smoother
840         f32 d = 0.15 * BS;
841         // This should always apply, otherwise there are glitches
842         sanity_check(d > pos_max_d);
843         // Maximum distance over border for sneaking
844         f32 sneak_max = BS * 0.4;
845
846         /*
847                 If sneaking, keep in range from the last walked node and don't
848                 fall off from it
849         */
850         if (control.sneak && m_sneak_node_exists &&
851                         !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
852                         physics_override_sneak) {
853                 f32 maxd = 0.5 * BS + sneak_max;
854                 v3f lwn_f = intToFloat(m_sneak_node, BS);
855                 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
856                 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
857
858                 if (!is_climbing) {
859                         // Move up if necessary
860                         f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
861                         if (position.Y < new_y)
862                                 position.Y = new_y;
863                         /*
864                                 Collision seems broken, since player is sinking when
865                                 sneaking over the edges of current sneaking_node.
866                                 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
867                         */
868                         if (m_speed.Y < 0)
869                                 m_speed.Y = 0;
870                 }
871         }
872
873         // this shouldn't be hardcoded but transmitted from server
874         float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
875
876 #ifdef __ANDROID__
877         player_stepheight += (0.6 * BS);
878 #endif
879
880         v3f accel_f = v3f(0, 0, 0);
881
882         collisionMoveResult result = collisionMoveSimple(env, m_client,
883                 pos_max_d, m_collisionbox, player_stepheight, dtime,
884                 &position, &m_speed, accel_f);
885
886         /*
887                 If the player's feet touch the topside of any node, this is
888                 set to true.
889
890                 Player is allowed to jump when this is true.
891         */
892         bool touching_ground_was = touching_ground;
893         touching_ground = result.touching_ground;
894
895     //bool standing_on_unloaded = result.standing_on_unloaded;
896
897         /*
898                 Check the nodes under the player to see from which node the
899                 player is sneaking from, if any.  If the node from under
900                 the player has been removed, the player falls.
901         */
902         f32 position_y_mod = 0.05 * BS;
903         if (m_sneak_node_bb_ymax > 0)
904                 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
905         v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
906         if (m_sneak_node_exists &&
907                         nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
908                         m_old_node_below_type != "air") {
909                 // Old node appears to have been removed; that is,
910                 // it wasn't air before but now it is
911                 m_need_to_get_new_sneak_node = false;
912                 m_sneak_node_exists = false;
913         } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
914                 // We are on something, so make sure to recalculate the sneak
915                 // node.
916                 m_need_to_get_new_sneak_node = true;
917         }
918
919         if (m_need_to_get_new_sneak_node && physics_override_sneak) {
920                 m_sneak_node_bb_ymax = 0;
921                 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
922                 v2f player_p2df(position.X, position.Z);
923                 f32 min_distance_f = 100000.0 * BS;
924                 // If already seeking from some node, compare to it.
925                 v3s16 new_sneak_node = m_sneak_node;
926                 for (s16 x= -1; x <= 1; x++)
927                 for (s16 z= -1; z <= 1; z++) {
928                         v3s16 p = pos_i_bottom + v3s16(x, 0, z);
929                         v3f pf = intToFloat(p, BS);
930                         v2f node_p2df(pf.X, pf.Z);
931                         f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
932                         f32 max_axis_distance_f = MYMAX(
933                                         fabs(player_p2df.X - node_p2df.X),
934                                         fabs(player_p2df.Y - node_p2df.Y));
935
936                         if (distance_f > min_distance_f ||
937                                         max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)
938                                 continue;
939
940                         // The node to be sneaked on has to be walkable
941                         node = map->getNodeNoEx(p, &is_valid_position);
942                         if (!is_valid_position || nodemgr->get(node).walkable == false)
943                                 continue;
944                         // And the node above it has to be nonwalkable
945                         node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
946                         if (!is_valid_position || nodemgr->get(node).walkable)
947                                 continue;
948                         // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
949                         if (!physics_override_sneak_glitch) {
950                                 node =map->getNodeNoEx(p + v3s16(0, 2, 0), &is_valid_position);
951                                 if (!is_valid_position || nodemgr->get(node).walkable)
952                                         continue;
953                         }
954
955                         min_distance_f = distance_f;
956                         new_sneak_node = p;
957                 }
958
959                 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
960
961                 m_sneak_node = new_sneak_node;
962                 m_sneak_node_exists = sneak_node_found;
963
964                 if (sneak_node_found) {
965                         f32 cb_max = 0;
966                         MapNode n = map->getNodeNoEx(m_sneak_node);
967                         std::vector<aabb3f> nodeboxes;
968                         n.getCollisionBoxes(nodemgr, &nodeboxes);
969                         for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
970                                         it != nodeboxes.end(); ++it) {
971                                 aabb3f box = *it;
972                                 if (box.MaxEdge.Y > cb_max)
973                                         cb_max = box.MaxEdge.Y;
974                         }
975                         m_sneak_node_bb_ymax = cb_max;
976                 }
977
978                 /*
979                         If sneaking, the player's collision box can be in air, so
980                         this has to be set explicitly
981                 */
982                 if (sneak_node_found && control.sneak)
983                         touching_ground = true;
984         }
985
986         /*
987                 Set new position but keep sneak node set
988         */
989         bool sneak_node_exists = m_sneak_node_exists;
990         setPosition(position);
991         m_sneak_node_exists = sneak_node_exists;
992
993         /*
994                 Report collisions
995         */
996         // Dont report if flying
997         if (collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
998                 for (size_t i = 0; i < result.collisions.size(); i++) {
999                         const CollisionInfo &info = result.collisions[i];
1000                         collision_info->push_back(info);
1001                 }
1002         }
1003
1004         if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1005                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
1006                 m_client->event()->put(e);
1007                 // Set camera impact value to be used for view bobbing
1008                 camera_impact = getSpeed().Y * -1;
1009         }
1010
1011         {
1012                 camera_barely_in_ceiling = false;
1013                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1014                 MapNode n = map->getNodeNoEx(camera_np);
1015                 if (n.getContent() != CONTENT_IGNORE) {
1016                         if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1017                                 camera_barely_in_ceiling = true;
1018                 }
1019         }
1020
1021         /*
1022                 Update the node last under the player
1023         */
1024         m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);
1025         m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
1026
1027         /*
1028                 Check properties of the node on which the player is standing
1029         */
1030         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
1031         // Determine if jumping is possible
1032         m_can_jump = touching_ground && !in_liquid;
1033         if (itemgroup_get(f.groups, "disable_jump"))
1034                 m_can_jump = false;
1035         // Jump key pressed while jumping off from a bouncy block
1036         if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1037                         m_speed.Y >= -0.5 * BS) {
1038                 float jumpspeed = movement_speed_jump * physics_override_jump;
1039                 if (m_speed.Y > 1) {
1040                         // Reduce boost when speed already is high
1041                         m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
1042                 } else {
1043                         m_speed.Y += jumpspeed;
1044                 }
1045                 setSpeed(m_speed);
1046                 m_can_jump = false;
1047         }
1048 }