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