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