]> git.lizzy.rs Git - minetest.git/blob - src/client/localplayer.cpp
7e7b1a867bf3722800782610563f01e078730e6a
[minetest.git] / src / client / 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 #include <cmath>
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         const NodeDefManager *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         const NodeDefManager *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         PlayerSettings &player_settings = getPlayerSettings();
195
196         // Skip collision detection if noclip mode is used
197         bool fly_allowed = m_client->checkLocalPrivilege("fly");
198         bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip;
199         bool free_move = player_settings.free_move && fly_allowed;
200
201         if (noclip && free_move) {
202                 position += m_speed * dtime;
203                 setPosition(position);
204                 return;
205         }
206
207         /*
208                 Collision detection
209         */
210
211         bool is_valid_position;
212         MapNode node;
213         v3s16 pp;
214
215         /*
216                 Check if player is in liquid (the oscillating value)
217         */
218
219         // If in liquid, the threshold of coming out is at higher y
220         if (in_liquid)
221         {
222                 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
223                 node = map->getNodeNoEx(pp, &is_valid_position);
224                 if (is_valid_position) {
225                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
226                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
227                 } else {
228                         in_liquid = false;
229                 }
230         }
231         // If not in liquid, the threshold of going in is at lower y
232         else
233         {
234                 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
235                 node = map->getNodeNoEx(pp, &is_valid_position);
236                 if (is_valid_position) {
237                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
238                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
239                 } else {
240                         in_liquid = false;
241                 }
242         }
243
244
245         /*
246                 Check if player is in liquid (the stable value)
247         */
248         pp = floatToInt(position + v3f(0,0,0), BS);
249         node = map->getNodeNoEx(pp, &is_valid_position);
250         if (is_valid_position) {
251                 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
252         } else {
253                 in_liquid_stable = false;
254         }
255
256         /*
257                 Check if player is climbing
258         */
259
260
261         pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
262         v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
263         node = map->getNodeNoEx(pp, &is_valid_position);
264         bool is_valid_position2;
265         MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
266
267         if (!(is_valid_position && is_valid_position2)) {
268                 is_climbing = false;
269         } else {
270                 is_climbing = (nodemgr->get(node.getContent()).climbable
271                                 || nodemgr->get(node2.getContent()).climbable) && !free_move;
272         }
273
274         /*
275                 Collision uncertainty radius
276                 Make it a bit larger than the maximum distance of movement
277         */
278         //f32 d = pos_max_d * 1.1;
279         // A fairly large value in here makes moving smoother
280         f32 d = 0.15*BS;
281
282         // This should always apply, otherwise there are glitches
283         sanity_check(d > pos_max_d);
284
285         // Player object property step height is multiplied by BS in
286         // /src/script/common/c_content.cpp and /src/content_sao.cpp
287         float player_stepheight = (m_cao == nullptr) ? 0.0f :
288                 (touching_ground ? m_cao->getStepHeight() : (0.2f * BS));
289
290         v3f accel_f = v3f(0,0,0);
291         const v3f initial_position = position;
292         const v3f initial_speed = m_speed;
293
294         collisionMoveResult result = collisionMoveSimple(env, m_client,
295                 pos_max_d, m_collisionbox, player_stepheight, dtime,
296                 &position, &m_speed, accel_f);
297
298         bool could_sneak = control.sneak && !free_move && !in_liquid &&
299                 !is_climbing && physics_override_sneak;
300
301         // Add new collisions to the vector
302         if (collision_info && !free_move) {
303                 v3f diff = intToFloat(m_standing_node, BS) - position;
304                 f32 distance = diff.getLength();
305                 // Force update each ClientEnvironment::step()
306                 bool is_first = collision_info->empty();
307
308                 for (const auto &colinfo : result.collisions) {
309                         collision_info->push_back(colinfo);
310
311                         if (colinfo.type != COLLISION_NODE ||
312                                         colinfo.new_speed.Y != 0 ||
313                                         (could_sneak && m_sneak_node_exists))
314                                 continue;
315
316                         diff = intToFloat(colinfo.node_p, BS) - position;
317
318                         // Find nearest colliding node
319                         f32 len = diff.getLength();
320                         if (is_first || len < distance) {
321                                 m_standing_node = colinfo.node_p;
322                                 distance = len;
323                         }
324                 }
325         }
326
327         /*
328                 If the player's feet touch the topside of any node, this is
329                 set to true.
330
331                 Player is allowed to jump when this is true.
332         */
333         bool touching_ground_was = touching_ground;
334         touching_ground = result.touching_ground;
335         bool sneak_can_jump = false;
336
337         // Max. distance (X, Z) over border for sneaking determined by collision box
338         // * 0.49 to keep the center just barely on the node
339         v3f sneak_max = m_collisionbox.getExtent() * 0.49;
340
341         if (m_sneak_ladder_detected) {
342                 // restore legacy behaviour (this makes the m_speed.Y hack necessary)
343                 sneak_max = v3f(0.4 * BS, 0, 0.4 * BS);
344         }
345
346         /*
347                 If sneaking, keep on top of last walked node and don't fall off
348         */
349         if (could_sneak && m_sneak_node_exists) {
350                 const v3f sn_f = intToFloat(m_sneak_node, BS);
351                 const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge;
352                 const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge;
353                 const v3f old_pos = position;
354                 const v3f old_speed = m_speed;
355                 f32 y_diff = bmax.Y - position.Y;
356                 m_standing_node = m_sneak_node;
357
358                 // (BS * 0.6f) is the basic stepheight while standing on ground
359                 if (y_diff < BS * 0.6f) {
360                         // Only center player when they're on the node
361                         position.X = rangelim(position.X,
362                                 bmin.X - sneak_max.X, bmax.X + sneak_max.X);
363                         position.Z = rangelim(position.Z,
364                                 bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z);
365
366                         if (position.X != old_pos.X)
367                                 m_speed.X = 0;
368                         if (position.Z != old_pos.Z)
369                                 m_speed.Z = 0;
370                 }
371
372                 if (y_diff > 0 && m_speed.Y <= 0 &&
373                                 (physics_override_sneak_glitch || y_diff < BS * 0.6f)) {
374                         // Move player to the maximal height when falling or when
375                         // the ledge is climbed on the next step.
376
377                         // Smoothen the movement (based on 'position.Y = bmax.Y')
378                         position.Y += y_diff * dtime * 22.0f + BS * 0.01f;
379                         position.Y = std::min(position.Y, bmax.Y);
380                         m_speed.Y = 0;
381                 }
382
383                 // Allow jumping on node edges while sneaking
384                 if (m_speed.Y == 0 || m_sneak_ladder_detected)
385                         sneak_can_jump = true;
386
387                 if (collision_info &&
388                                 m_speed.Y - old_speed.Y > BS) {
389                         // Collide with sneak node, report fall damage
390                         CollisionInfo sn_info;
391                         sn_info.node_p = m_sneak_node;
392                         sn_info.old_speed = old_speed;
393                         sn_info.new_speed = m_speed;
394                         collision_info->push_back(sn_info);
395                 }
396         }
397
398         /*
399                 Find the next sneak node if necessary
400         */
401         bool new_sneak_node_exists = false;
402
403         if (could_sneak)
404                 new_sneak_node_exists = updateSneakNode(map, position, sneak_max);
405
406         /*
407                 Set new position but keep sneak node set
408         */
409         setPosition(position);
410         m_sneak_node_exists = new_sneak_node_exists;
411
412         /*
413                 Report collisions
414         */
415
416         if(!result.standing_on_object && !touching_ground_was && touching_ground) {
417                 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
418
419                 // Set camera impact value to be used for view bobbing
420                 camera_impact = getSpeed().Y * -1;
421         }
422
423         {
424                 camera_barely_in_ceiling = false;
425                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
426                 MapNode n = map->getNodeNoEx(camera_np);
427                 if(n.getContent() != CONTENT_IGNORE){
428                         if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
429                                 camera_barely_in_ceiling = true;
430                         }
431                 }
432         }
433
434         /*
435                 Check properties of the node on which the player is standing
436         */
437         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(m_standing_node));
438         // Determine if jumping is possible
439         m_can_jump = (touching_ground && !in_liquid && !is_climbing)
440                         || sneak_can_jump;
441         if (itemgroup_get(f.groups, "disable_jump"))
442                 m_can_jump = false;
443
444         // Jump key pressed while jumping off from a bouncy block
445         if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
446                 m_speed.Y >= -0.5 * BS) {
447                 float jumpspeed = movement_speed_jump * physics_override_jump;
448                 if (m_speed.Y > 1) {
449                         // Reduce boost when speed already is high
450                         m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
451                 } else {
452                         m_speed.Y += jumpspeed;
453                 }
454                 setSpeed(m_speed);
455                 m_can_jump = false;
456         }
457
458         // Autojump
459         handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
460 }
461
462 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
463 {
464         move(dtime, env, pos_max_d, NULL);
465 }
466
467 void LocalPlayer::applyControl(float dtime, Environment *env)
468 {
469         // Clear stuff
470         swimming_vertical = false;
471         swimming_pitch = 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         PlayerSettings &player_settings = getPlayerSettings();
484
485         // All vectors are relative to the player's yaw,
486         // (and pitch if pitch fly mode enabled),
487         // and will be rotated at the end
488         v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
489         v3f speedV = v3f(0,0,0); // Vertical (Y)
490
491         bool fly_allowed = m_client->checkLocalPrivilege("fly");
492         bool fast_allowed = m_client->checkLocalPrivilege("fast");
493
494         bool free_move = fly_allowed && player_settings.free_move;
495         bool fast_move = fast_allowed && player_settings.fast_move;
496         bool pitch_move = (free_move || in_liquid) && player_settings.pitch_move;
497         // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
498         bool fast_climb = fast_move && control.aux1 && !player_settings.aux1_descends;
499         bool continuous_forward = player_settings.continuous_forward;
500         bool always_fly_fast = player_settings.always_fly_fast;
501
502         // Whether superspeed mode is used or not
503         bool superspeed = false;
504
505         if (always_fly_fast && free_move && fast_move)
506                 superspeed = true;
507
508         // Old descend control
509         if (player_settings.aux1_descends)
510         {
511                 // If free movement and fast movement, always move fast
512                 if(free_move && fast_move)
513                         superspeed = true;
514
515                 // Auxiliary button 1 (E)
516                 if(control.aux1)
517                 {
518                         if(free_move)
519                         {
520                                 // In free movement mode, aux1 descends
521                                 if(fast_move)
522                                         speedV.Y = -movement_speed_fast;
523                                 else
524                                         speedV.Y = -movement_speed_walk;
525                         }
526                         else if(in_liquid || in_liquid_stable)
527                         {
528                                 speedV.Y = -movement_speed_walk;
529                                 swimming_vertical = true;
530                         }
531                         else if(is_climbing)
532                         {
533                                 speedV.Y = -movement_speed_climb;
534                         }
535                         else
536                         {
537                                 // If not free movement but fast is allowed, aux1 is
538                                 // "Turbo button"
539                                 if(fast_move)
540                                         superspeed = true;
541                         }
542                 }
543         }
544         // New minecraft-like descend control
545         else
546         {
547                 // Auxiliary button 1 (E)
548                 if(control.aux1)
549                 {
550                         if(!is_climbing)
551                         {
552                                 // aux1 is "Turbo button"
553                                 if(fast_move)
554                                         superspeed = true;
555                         }
556                 }
557
558                 if(control.sneak)
559                 {
560                         if(free_move)
561                         {
562                                 // In free movement mode, sneak descends
563                                 if (fast_move && (control.aux1 || always_fly_fast))
564                                         speedV.Y = -movement_speed_fast;
565                                 else
566                                         speedV.Y = -movement_speed_walk;
567                         }
568                         else if(in_liquid || in_liquid_stable)
569                         {
570                                 if(fast_climb)
571                                         speedV.Y = -movement_speed_fast;
572                                 else
573                                         speedV.Y = -movement_speed_walk;
574                                 swimming_vertical = true;
575                         }
576                         else if(is_climbing)
577                         {
578                                 if(fast_climb)
579                                         speedV.Y = -movement_speed_fast;
580                                 else
581                                         speedV.Y = -movement_speed_climb;
582                         }
583                 }
584         }
585
586         if (continuous_forward)
587                 speedH += v3f(0,0,1);
588
589         if (control.up) {
590                 if (continuous_forward) {
591                         if (fast_move)
592                                 superspeed = true;
593                 } else {
594                         speedH += v3f(0,0,1);
595                 }
596         }
597         if (control.down) {
598                 speedH -= v3f(0,0,1);
599         }
600         if (!control.up && !control.down) {
601                 speedH -= v3f(0,0,1) *
602                         (control.forw_move_joystick_axis / 32767.f);
603         }
604         if (control.left) {
605                 speedH += v3f(-1,0,0);
606         }
607         if (control.right) {
608                 speedH += v3f(1,0,0);
609         }
610         if (!control.left && !control.right) {
611                 speedH += v3f(1,0,0) *
612                         (control.sidew_move_joystick_axis / 32767.f);
613         }
614         if (m_autojump) {
615                 // release autojump after a given time
616                 m_autojump_time -= dtime;
617                 if (m_autojump_time <= 0.0f)
618                         m_autojump = false;
619         }
620         if(control.jump)
621         {
622                 if (free_move) {
623                         if (player_settings.aux1_descends || always_fly_fast) {
624                                 if (fast_move)
625                                         speedV.Y = movement_speed_fast;
626                                 else
627                                         speedV.Y = movement_speed_walk;
628                         } else {
629                                 if(fast_move && control.aux1)
630                                         speedV.Y = movement_speed_fast;
631                                 else
632                                         speedV.Y = movement_speed_walk;
633                         }
634                 }
635                 else if(m_can_jump)
636                 {
637                         /*
638                                 NOTE: The d value in move() affects jump height by
639                                 raising the height at which the jump speed is kept
640                                 at its starting value
641                         */
642                         v3f speedJ = getSpeed();
643                         if(speedJ.Y >= -0.5 * BS) {
644                                 speedJ.Y = movement_speed_jump * physics_override_jump;
645                                 setSpeed(speedJ);
646                                 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_JUMP));
647                         }
648                 }
649                 else if(in_liquid)
650                 {
651                         if(fast_climb)
652                                 speedV.Y = movement_speed_fast;
653                         else
654                                 speedV.Y = movement_speed_walk;
655                         swimming_vertical = true;
656                 }
657                 else if(is_climbing)
658                 {
659                         if(fast_climb)
660                                 speedV.Y = movement_speed_fast;
661                         else
662                                 speedV.Y = movement_speed_climb;
663                 }
664         }
665
666         // The speed of the player (Y is ignored)
667         if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
668                 speedH = speedH.normalize() * movement_speed_fast;
669         else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
670                 speedH = speedH.normalize() * movement_speed_crouch;
671         else
672                 speedH = speedH.normalize() * movement_speed_walk;
673
674         // Acceleration increase
675         f32 incH = 0; // Horizontal (X, Z)
676         f32 incV = 0; // Vertical (Y)
677         if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
678         {
679                 // Jumping and falling
680                 if(superspeed || (fast_move && control.aux1))
681                         incH = movement_acceleration_fast * BS * dtime;
682                 else
683                         incH = movement_acceleration_air * BS * dtime;
684                 incV = 0; // No vertical acceleration in air
685         }
686         else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
687                 incH = incV = movement_acceleration_fast * BS * dtime;
688         else
689                 incH = incV = movement_acceleration_default * BS * dtime;
690
691         float slip_factor = 1.0f;
692         if (!free_move && !in_liquid && !in_liquid_stable)
693                 slip_factor = getSlipFactor(env, speedH);
694
695         // Don't sink when swimming in pitch mode
696         if (pitch_move && in_liquid) {
697                 v3f controlSpeed = speedH + speedV;
698                 if (controlSpeed.getLength() > 0.01f)
699                         swimming_pitch = true;
700         }
701
702         // Accelerate to target speed with maximum increment
703         accelerate((speedH + speedV) * physics_override_speed,
704                         incH * physics_override_speed * slip_factor, incV * physics_override_speed,
705                         pitch_move);
706 }
707
708 v3s16 LocalPlayer::getStandingNodePos()
709 {
710         if(m_sneak_node_exists)
711                 return m_sneak_node;
712         return m_standing_node;
713 }
714
715 v3s16 LocalPlayer::getFootstepNodePos()
716 {
717         if (in_liquid_stable)
718                 // Emit swimming sound if the player is in liquid
719                 return floatToInt(getPosition(), BS);
720         if (touching_ground)
721                 // BS * 0.05 below the player's feet ensures a 1/16th height
722                 // nodebox is detected instead of the node below it.
723                 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
724         // A larger distance below is necessary for a footstep sound
725         // when landing after a jump or fall. BS * 0.5 ensures water
726         // sounds when swimming in 1 node deep water.
727         return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
728 }
729
730 v3s16 LocalPlayer::getLightPosition() const
731 {
732         return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
733 }
734
735 v3f LocalPlayer::getEyeOffset() const
736 {
737         float eye_height = camera_barely_in_ceiling ?
738                 m_eye_height - 0.125f : m_eye_height;
739         return v3f(0, BS * eye_height, 0);
740 }
741
742 // 3D acceleration
743 void LocalPlayer::accelerate(const v3f &target_speed, const f32 max_increase_H,
744                 const f32 max_increase_V, const bool use_pitch)
745 {
746         const f32 yaw = getYaw();
747         const f32 pitch = getPitch();
748         v3f flat_speed = m_speed;
749         // Rotate speed vector by -yaw and -pitch to make it relative to the player's yaw and pitch
750         flat_speed.rotateXZBy(-yaw);
751         if (use_pitch)
752                 flat_speed.rotateYZBy(-pitch);
753
754         v3f d_wanted = target_speed - flat_speed;
755         v3f d = v3f(0,0,0);
756
757         // Then compare the horizontal and vertical components with the wanted speed
758         if (max_increase_H > 0) {
759                 v3f d_wanted_H = d_wanted * v3f(1,0,1);
760                 if (d_wanted_H.getLength() > max_increase_H)
761                         d += d_wanted_H.normalize() * max_increase_H;
762                 else
763                         d += d_wanted_H;
764         }
765
766         if (max_increase_V > 0) {
767                 f32 d_wanted_V = d_wanted.Y;
768                 if (d_wanted_V > max_increase_V)
769                         d.Y += max_increase_V;
770                 else if (d_wanted_V < -max_increase_V)
771                         d.Y -= max_increase_V;
772                 else
773                         d.Y += d_wanted_V;
774         }
775
776         // Finally rotate it again
777         if (use_pitch)
778                 d.rotateYZBy(pitch);
779         d.rotateXZBy(yaw);
780
781         m_speed += d;
782 }
783
784 // Temporary option for old move code
785 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
786                 std::vector<CollisionInfo> *collision_info)
787 {
788         Map *map = &env->getMap();
789         const NodeDefManager *nodemgr = m_client->ndef();
790
791         v3f position = getPosition();
792
793         // Copy parent position if local player is attached
794         if (isAttached) {
795                 setPosition(overridePosition);
796                 m_sneak_node_exists = false;
797                 return;
798         }
799
800         PlayerSettings &player_settings = getPlayerSettings();
801
802         // Skip collision detection if noclip mode is used
803         bool fly_allowed = m_client->checkLocalPrivilege("fly");
804         bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip;
805         bool free_move = noclip && fly_allowed && player_settings.free_move;
806         if (free_move) {
807                 position += m_speed * dtime;
808                 setPosition(position);
809                 m_sneak_node_exists = false;
810                 return;
811         }
812
813         /*
814                 Collision detection
815         */
816         bool is_valid_position;
817         MapNode node;
818         v3s16 pp;
819
820         /*
821                 Check if player is in liquid (the oscillating value)
822         */
823         if (in_liquid) {
824                 // If in liquid, the threshold of coming out is at higher y
825                 pp = floatToInt(position + v3f(0, BS * 0.1, 0), BS);
826                 node = map->getNodeNoEx(pp, &is_valid_position);
827                 if (is_valid_position) {
828                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
829                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
830                 } else {
831                         in_liquid = false;
832                 }
833         } else {
834                 // If not in liquid, the threshold of going in is at lower y
835                 pp = floatToInt(position + v3f(0, BS * 0.5, 0), BS);
836                 node = map->getNodeNoEx(pp, &is_valid_position);
837                 if (is_valid_position) {
838                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
839                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
840                 } else {
841                         in_liquid = false;
842                 }
843         }
844
845         /*
846                 Check if player is in liquid (the stable value)
847         */
848         pp = floatToInt(position + v3f(0, 0, 0), BS);
849         node = map->getNodeNoEx(pp, &is_valid_position);
850         if (is_valid_position)
851                 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
852         else
853                 in_liquid_stable = false;
854
855         /*
856                 Check if player is climbing
857         */
858         pp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS);
859         v3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS);
860         node = map->getNodeNoEx(pp, &is_valid_position);
861         bool is_valid_position2;
862         MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
863
864         if (!(is_valid_position && is_valid_position2))
865                 is_climbing = false;
866         else
867                 is_climbing = (nodemgr->get(node.getContent()).climbable ||
868                                 nodemgr->get(node2.getContent()).climbable) && !free_move;
869
870         /*
871                 Collision uncertainty radius
872                 Make it a bit larger than the maximum distance of movement
873         */
874         //f32 d = pos_max_d * 1.1;
875         // A fairly large value in here makes moving smoother
876         f32 d = 0.15 * BS;
877         // This should always apply, otherwise there are glitches
878         sanity_check(d > pos_max_d);
879         // Maximum distance over border for sneaking
880         f32 sneak_max = BS * 0.4;
881
882         /*
883                 If sneaking, keep in range from the last walked node and don't
884                 fall off from it
885         */
886         if (control.sneak && m_sneak_node_exists &&
887                         !(fly_allowed && player_settings.free_move) && !in_liquid &&
888                         physics_override_sneak) {
889                 f32 maxd = 0.5 * BS + sneak_max;
890                 v3f lwn_f = intToFloat(m_sneak_node, BS);
891                 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
892                 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
893
894                 if (!is_climbing) {
895                         // Move up if necessary
896                         f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
897                         if (position.Y < new_y)
898                                 position.Y = new_y;
899                         /*
900                                 Collision seems broken, since player is sinking when
901                                 sneaking over the edges of current sneaking_node.
902                                 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
903                         */
904                         if (m_speed.Y < 0)
905                                 m_speed.Y = 0;
906                 }
907         }
908
909         // this shouldn't be hardcoded but transmitted from server
910         float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
911
912         v3f accel_f = v3f(0, 0, 0);
913         const v3f initial_position = position;
914         const v3f initial_speed = m_speed;
915
916         collisionMoveResult result = collisionMoveSimple(env, m_client,
917                 pos_max_d, m_collisionbox, player_stepheight, dtime,
918                 &position, &m_speed, accel_f);
919
920         /*
921                 If the player's feet touch the topside of any node, this is
922                 set to true.
923
924                 Player is allowed to jump when this is true.
925         */
926         bool touching_ground_was = touching_ground;
927         touching_ground = result.touching_ground;
928
929     //bool standing_on_unloaded = result.standing_on_unloaded;
930
931         /*
932                 Check the nodes under the player to see from which node the
933                 player is sneaking from, if any.  If the node from under
934                 the player has been removed, the player falls.
935         */
936         f32 position_y_mod = 0.05 * BS;
937         if (m_sneak_node_bb_ymax > 0)
938                 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
939         v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
940         if (m_sneak_node_exists &&
941                         nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
942                         m_old_node_below_type != "air") {
943                 // Old node appears to have been removed; that is,
944                 // it wasn't air before but now it is
945                 m_need_to_get_new_sneak_node = false;
946                 m_sneak_node_exists = false;
947         } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
948                 // We are on something, so make sure to recalculate the sneak
949                 // node.
950                 m_need_to_get_new_sneak_node = true;
951         }
952
953         if (m_need_to_get_new_sneak_node && physics_override_sneak) {
954                 m_sneak_node_bb_ymax = 0;
955                 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
956                 v2f player_p2df(position.X, position.Z);
957                 f32 min_distance_f = 100000.0 * BS;
958                 // If already seeking from some node, compare to it.
959                 v3s16 new_sneak_node = m_sneak_node;
960                 for (s16 x= -1; x <= 1; x++)
961                 for (s16 z= -1; z <= 1; z++) {
962                         v3s16 p = pos_i_bottom + v3s16(x, 0, z);
963                         v3f pf = intToFloat(p, BS);
964                         v2f node_p2df(pf.X, pf.Z);
965                         f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
966                         f32 max_axis_distance_f = MYMAX(
967                                         std::fabs(player_p2df.X - node_p2df.X),
968                                         std::fabs(player_p2df.Y - node_p2df.Y));
969
970                         if (distance_f > min_distance_f ||
971                                         max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)
972                                 continue;
973
974                         // The node to be sneaked on has to be walkable
975                         node = map->getNodeNoEx(p, &is_valid_position);
976                         if (!is_valid_position || !nodemgr->get(node).walkable)
977                                 continue;
978                         // And the node above it has to be nonwalkable
979                         node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
980                         if (!is_valid_position || nodemgr->get(node).walkable)
981                                 continue;
982                         // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
983                         if (!physics_override_sneak_glitch) {
984                                 node =map->getNodeNoEx(p + v3s16(0, 2, 0), &is_valid_position);
985                                 if (!is_valid_position || nodemgr->get(node).walkable)
986                                         continue;
987                         }
988
989                         min_distance_f = distance_f;
990                         new_sneak_node = p;
991                 }
992
993                 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
994
995                 m_sneak_node = new_sneak_node;
996                 m_sneak_node_exists = sneak_node_found;
997
998                 if (sneak_node_found) {
999                         f32 cb_max = 0;
1000                         MapNode n = map->getNodeNoEx(m_sneak_node);
1001                         std::vector<aabb3f> nodeboxes;
1002                         n.getCollisionBoxes(nodemgr, &nodeboxes);
1003                         for (const auto &box : nodeboxes) {
1004                                 if (box.MaxEdge.Y > cb_max)
1005                                         cb_max = box.MaxEdge.Y;
1006                         }
1007                         m_sneak_node_bb_ymax = cb_max;
1008                 }
1009
1010                 /*
1011                         If sneaking, the player's collision box can be in air, so
1012                         this has to be set explicitly
1013                 */
1014                 if (sneak_node_found && control.sneak)
1015                         touching_ground = true;
1016         }
1017
1018         /*
1019                 Set new position but keep sneak node set
1020         */
1021         bool sneak_node_exists = m_sneak_node_exists;
1022         setPosition(position);
1023         m_sneak_node_exists = sneak_node_exists;
1024
1025         /*
1026                 Report collisions
1027         */
1028         // Dont report if flying
1029         if (collision_info && !(player_settings.free_move && fly_allowed)) {
1030                 for (const auto &info : result.collisions) {
1031                         collision_info->push_back(info);
1032                 }
1033         }
1034
1035         if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1036                 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
1037                 // Set camera impact value to be used for view bobbing
1038                 camera_impact = getSpeed().Y * -1;
1039         }
1040
1041         {
1042                 camera_barely_in_ceiling = false;
1043                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1044                 MapNode n = map->getNodeNoEx(camera_np);
1045                 if (n.getContent() != CONTENT_IGNORE) {
1046                         if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1047                                 camera_barely_in_ceiling = true;
1048                 }
1049         }
1050
1051         /*
1052                 Update the node last under the player
1053         */
1054         m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);
1055         m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
1056
1057         /*
1058                 Check properties of the node on which the player is standing
1059         */
1060         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
1061         // Determine if jumping is possible
1062         m_can_jump = touching_ground && !in_liquid;
1063         if (itemgroup_get(f.groups, "disable_jump"))
1064                 m_can_jump = false;
1065         // Jump key pressed while jumping off from a bouncy block
1066         if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1067                         m_speed.Y >= -0.5 * BS) {
1068                 float jumpspeed = movement_speed_jump * physics_override_jump;
1069                 if (m_speed.Y > 1) {
1070                         // Reduce boost when speed already is high
1071                         m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
1072                 } else {
1073                         m_speed.Y += jumpspeed;
1074                 }
1075                 setSpeed(m_speed);
1076                 m_can_jump = false;
1077         }
1078
1079         // Autojump
1080         handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
1081 }
1082
1083 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1084 {
1085         // Slip on slippery nodes
1086         const NodeDefManager *nodemgr = env->getGameDef()->ndef();
1087         Map *map = &env->getMap();
1088         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(
1089                         getStandingNodePos()));
1090         int slippery = 0;
1091         if (f.walkable)
1092                 slippery = itemgroup_get(f.groups, "slippery");
1093
1094         if (slippery >= 1) {
1095                 if (speedH == v3f(0.0f)) {
1096                         slippery = slippery * 2;
1097                 }
1098                 return core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);
1099         }
1100         return 1.0f;
1101 }
1102
1103 void LocalPlayer::handleAutojump(f32 dtime, Environment *env,
1104                 const collisionMoveResult &result, const v3f &initial_position,
1105                 const v3f &initial_speed, f32 pos_max_d)
1106 {
1107         PlayerSettings &player_settings = getPlayerSettings();
1108         if (!player_settings.autojump)
1109                 return;
1110
1111         if (m_autojump)
1112                 return;
1113
1114         bool control_forward = control.up || player_settings.continuous_forward ||
1115                         (!control.up && !control.down &&
1116                         control.forw_move_joystick_axis < -0.05);
1117         bool could_autojump =
1118                         m_can_jump && !control.jump && !control.sneak && control_forward;
1119         if (!could_autojump)
1120                 return;
1121
1122         bool horizontal_collision = false;
1123         for (const auto &colinfo : result.collisions) {
1124                 if (colinfo.type == COLLISION_NODE && colinfo.plane != 1) {
1125                         horizontal_collision = true;
1126                         break; // one is enough
1127                 }
1128         }
1129
1130         // must be running against something to trigger autojumping
1131         if (!horizontal_collision)
1132                 return;
1133
1134         // check for nodes above
1135         v3f headpos_min = m_position + m_collisionbox.MinEdge * 0.99f;
1136         v3f headpos_max = m_position + m_collisionbox.MaxEdge * 0.99f;
1137         headpos_min.Y = headpos_max.Y; // top face of collision box
1138         v3s16 ceilpos_min = floatToInt(headpos_min, BS) + v3s16(0, 1, 0);
1139         v3s16 ceilpos_max = floatToInt(headpos_max, BS) + v3s16(0, 1, 0);
1140         const NodeDefManager *ndef = env->getGameDef()->ndef();
1141         bool is_position_valid;
1142         for (s16 z = ceilpos_min.Z; z <= ceilpos_max.Z; z++) {
1143                 for (s16 x = ceilpos_min.X; x <= ceilpos_max.X; x++) {
1144                         MapNode n = env->getMap().getNodeNoEx(v3s16(x, ceilpos_max.Y, z), &is_position_valid);
1145
1146                         if (!is_position_valid)
1147                                 break;  // won't collide with the void outside
1148                         if (n.getContent() == CONTENT_IGNORE)
1149                                 return; // players collide with ignore blocks -> same as walkable
1150                         const ContentFeatures &f = ndef->get(n);
1151                         if (f.walkable)
1152                                 return; // would bump head, don't jump
1153                 }
1154         }
1155
1156         float jump_height = 1.1f; // TODO: better than a magic number
1157         v3f jump_pos = initial_position + v3f(0.0f, jump_height * BS, 0.0f);
1158         v3f jump_speed = initial_speed;
1159
1160         // try at peak of jump, zero step height
1161         collisionMoveResult jump_result = collisionMoveSimple(env, m_client, pos_max_d,
1162                         m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed,
1163                         v3f(0, 0, 0));
1164
1165         // see if we can get a little bit farther horizontally if we had
1166         // jumped
1167         v3f run_delta = m_position - initial_position;
1168         run_delta.Y = 0.0f;
1169         v3f jump_delta = jump_pos - initial_position;
1170         jump_delta.Y = 0.0f;
1171         if (jump_delta.getLengthSQ() > run_delta.getLengthSQ() * 1.01f) {
1172                 m_autojump = true;
1173                 m_autojump_time = 0.1f;
1174         }
1175 }