Fix player rotations (#7941)
[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 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
472         setPitch(control.pitch);
473         setYaw(control.yaw);
474
475         // Nullify speed and don't run positioning code if the player is attached
476         if(isAttached)
477         {
478                 setSpeed(v3f(0,0,0));
479                 return;
480         }
481
482         PlayerSettings &player_settings = getPlayerSettings();
483
484         // All vectors are relative to the player's yaw,
485         // (and pitch if pitch fly mode enabled),
486         // and will be rotated at the end
487         v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
488         v3f speedV = v3f(0,0,0); // Vertical (Y)
489
490         bool fly_allowed = m_client->checkLocalPrivilege("fly");
491         bool fast_allowed = m_client->checkLocalPrivilege("fast");
492
493         bool free_move = fly_allowed && player_settings.free_move;
494         bool fast_move = fast_allowed && player_settings.fast_move;
495         bool pitch_fly = free_move && player_settings.pitch_fly;
496         // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
497         bool fast_climb = fast_move && control.aux1 && !player_settings.aux1_descends;
498         bool continuous_forward = player_settings.continuous_forward;
499         bool always_fly_fast = player_settings.always_fly_fast;
500
501         // Whether superspeed mode is used or not
502         bool superspeed = false;
503
504         if (always_fly_fast && free_move && fast_move)
505                 superspeed = true;
506
507         // Old descend control
508         if (player_settings.aux1_descends)
509         {
510                 // If free movement and fast movement, always move fast
511                 if(free_move && fast_move)
512                         superspeed = true;
513
514                 // Auxiliary button 1 (E)
515                 if(control.aux1)
516                 {
517                         if(free_move)
518                         {
519                                 // In free movement mode, aux1 descends
520                                 if(fast_move)
521                                         speedV.Y = -movement_speed_fast;
522                                 else
523                                         speedV.Y = -movement_speed_walk;
524                         }
525                         else if(in_liquid || in_liquid_stable)
526                         {
527                                 speedV.Y = -movement_speed_walk;
528                                 swimming_vertical = true;
529                         }
530                         else if(is_climbing)
531                         {
532                                 speedV.Y = -movement_speed_climb;
533                         }
534                         else
535                         {
536                                 // If not free movement but fast is allowed, aux1 is
537                                 // "Turbo button"
538                                 if(fast_move)
539                                         superspeed = true;
540                         }
541                 }
542         }
543         // New minecraft-like descend control
544         else
545         {
546                 // Auxiliary button 1 (E)
547                 if(control.aux1)
548                 {
549                         if(!is_climbing)
550                         {
551                                 // aux1 is "Turbo button"
552                                 if(fast_move)
553                                         superspeed = true;
554                         }
555                 }
556
557                 if(control.sneak)
558                 {
559                         if(free_move)
560                         {
561                                 // In free movement mode, sneak descends
562                                 if (fast_move && (control.aux1 || always_fly_fast))
563                                         speedV.Y = -movement_speed_fast;
564                                 else
565                                         speedV.Y = -movement_speed_walk;
566                         }
567                         else if(in_liquid || in_liquid_stable)
568                         {
569                                 if(fast_climb)
570                                         speedV.Y = -movement_speed_fast;
571                                 else
572                                         speedV.Y = -movement_speed_walk;
573                                 swimming_vertical = true;
574                         }
575                         else if(is_climbing)
576                         {
577                                 if(fast_climb)
578                                         speedV.Y = -movement_speed_fast;
579                                 else
580                                         speedV.Y = -movement_speed_climb;
581                         }
582                 }
583         }
584
585         if (continuous_forward)
586                 speedH += v3f(0,0,1);
587
588         if (control.up) {
589                 if (continuous_forward) {
590                         if (fast_move)
591                                 superspeed = true;
592                 } else {
593                         speedH += v3f(0,0,1);
594                 }
595         }
596         if (control.down) {
597                 speedH -= v3f(0,0,1);
598         }
599         if (!control.up && !control.down) {
600                 speedH -= v3f(0,0,1) *
601                         (control.forw_move_joystick_axis / 32767.f);
602         }
603         if (control.left) {
604                 speedH += v3f(-1,0,0);
605         }
606         if (control.right) {
607                 speedH += v3f(1,0,0);
608         }
609         if (!control.left && !control.right) {
610                 speedH += v3f(1,0,0) *
611                         (control.sidew_move_joystick_axis / 32767.f);
612         }
613         if(control.jump)
614         {
615                 if (free_move) {
616                         if (player_settings.aux1_descends || always_fly_fast) {
617                                 if (fast_move)
618                                         speedV.Y = movement_speed_fast;
619                                 else
620                                         speedV.Y = movement_speed_walk;
621                         } else {
622                                 if(fast_move && control.aux1)
623                                         speedV.Y = movement_speed_fast;
624                                 else
625                                         speedV.Y = movement_speed_walk;
626                         }
627                 }
628                 else if(m_can_jump)
629                 {
630                         /*
631                                 NOTE: The d value in move() affects jump height by
632                                 raising the height at which the jump speed is kept
633                                 at its starting value
634                         */
635                         v3f speedJ = getSpeed();
636                         if(speedJ.Y >= -0.5 * BS) {
637                                 speedJ.Y = movement_speed_jump * physics_override_jump;
638                                 setSpeed(speedJ);
639                                 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_JUMP));
640                         }
641                 }
642                 else if(in_liquid)
643                 {
644                         if(fast_climb)
645                                 speedV.Y = movement_speed_fast;
646                         else
647                                 speedV.Y = movement_speed_walk;
648                         swimming_vertical = true;
649                 }
650                 else if(is_climbing)
651                 {
652                         if(fast_climb)
653                                 speedV.Y = movement_speed_fast;
654                         else
655                                 speedV.Y = movement_speed_climb;
656                 }
657         }
658
659         // The speed of the player (Y is ignored)
660         if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
661                 speedH = speedH.normalize() * movement_speed_fast;
662         else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
663                 speedH = speedH.normalize() * movement_speed_crouch;
664         else
665                 speedH = speedH.normalize() * movement_speed_walk;
666
667         // Acceleration increase
668         f32 incH = 0; // Horizontal (X, Z)
669         f32 incV = 0; // Vertical (Y)
670         if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
671         {
672                 // Jumping and falling
673                 if(superspeed || (fast_move && control.aux1))
674                         incH = movement_acceleration_fast * BS * dtime;
675                 else
676                         incH = movement_acceleration_air * BS * dtime;
677                 incV = 0; // No vertical acceleration in air
678         }
679         else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
680                 incH = incV = movement_acceleration_fast * BS * dtime;
681         else
682                 incH = incV = movement_acceleration_default * BS * dtime;
683
684         float slip_factor = 1.0f;
685         if (!free_move)
686                 slip_factor = getSlipFactor(env, speedH);
687
688         // Accelerate to target speed with maximum increment
689         accelerate((speedH + speedV) * physics_override_speed,
690                         incH * physics_override_speed * slip_factor, incV * physics_override_speed,
691                         pitch_fly);
692 }
693
694 v3s16 LocalPlayer::getStandingNodePos()
695 {
696         if(m_sneak_node_exists)
697                 return m_sneak_node;
698         return m_standing_node;
699 }
700
701 v3s16 LocalPlayer::getFootstepNodePos()
702 {
703         if (in_liquid_stable)
704                 // Emit swimming sound if the player is in liquid
705                 return floatToInt(getPosition(), BS);
706         if (touching_ground)
707                 // BS * 0.05 below the player's feet ensures a 1/16th height
708                 // nodebox is detected instead of the node below it.
709                 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
710         // A larger distance below is necessary for a footstep sound
711         // when landing after a jump or fall. BS * 0.5 ensures water
712         // sounds when swimming in 1 node deep water.
713         return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
714 }
715
716 v3s16 LocalPlayer::getLightPosition() const
717 {
718         return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
719 }
720
721 v3f LocalPlayer::getEyeOffset() const
722 {
723         float eye_height = camera_barely_in_ceiling ?
724                 m_eye_height - 0.125f : m_eye_height;
725         return v3f(0, BS * eye_height, 0);
726 }
727
728 // 3D acceleration
729 void LocalPlayer::accelerate(const v3f &target_speed, const f32 max_increase_H,
730                 const f32 max_increase_V, const bool use_pitch)
731 {
732         const f32 yaw = getYaw();
733         const f32 pitch = getPitch();
734         v3f flat_speed = m_speed;
735         // Rotate speed vector by -yaw and -pitch to make it relative to the player's yaw and pitch
736         flat_speed.rotateXZBy(-yaw);
737         if (use_pitch)
738                 flat_speed.rotateYZBy(-pitch);
739
740         v3f d_wanted = target_speed - flat_speed;
741         v3f d = v3f(0,0,0);
742
743         // Then compare the horizontal and vertical components with the wanted speed
744         if (max_increase_H > 0) {
745                 v3f d_wanted_H = d_wanted * v3f(1,0,1);
746                 if (d_wanted_H.getLength() > max_increase_H)
747                         d += d_wanted_H.normalize() * max_increase_H;
748                 else
749                         d += d_wanted_H;
750         }
751
752         if (max_increase_V > 0) {
753                 f32 d_wanted_V = d_wanted.Y;
754                 if (d_wanted_V > max_increase_V)
755                         d.Y += max_increase_V;
756                 else if (d_wanted_V < -max_increase_V)
757                         d.Y -= max_increase_V;
758                 else
759                         d.Y += d_wanted_V;
760         }
761
762         // Finally rotate it again
763         if (use_pitch)
764                 d.rotateYZBy(pitch);
765         d.rotateXZBy(yaw);
766
767         m_speed += d;
768 }
769
770 // Temporary option for old move code
771 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
772                 std::vector<CollisionInfo> *collision_info)
773 {
774         Map *map = &env->getMap();
775         const NodeDefManager *nodemgr = m_client->ndef();
776
777         v3f position = getPosition();
778
779         // Copy parent position if local player is attached
780         if (isAttached) {
781                 setPosition(overridePosition);
782                 m_sneak_node_exists = false;
783                 return;
784         }
785
786         PlayerSettings &player_settings = getPlayerSettings();
787
788         // Skip collision detection if noclip mode is used
789         bool fly_allowed = m_client->checkLocalPrivilege("fly");
790         bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip;
791         bool free_move = noclip && fly_allowed && player_settings.free_move;
792         if (free_move) {
793                 position += m_speed * dtime;
794                 setPosition(position);
795                 m_sneak_node_exists = false;
796                 return;
797         }
798
799         /*
800                 Collision detection
801         */
802         bool is_valid_position;
803         MapNode node;
804         v3s16 pp;
805
806         /*
807                 Check if player is in liquid (the oscillating value)
808         */
809         if (in_liquid) {
810                 // If in liquid, the threshold of coming out is at higher y
811                 pp = floatToInt(position + v3f(0, BS * 0.1, 0), BS);
812                 node = map->getNodeNoEx(pp, &is_valid_position);
813                 if (is_valid_position) {
814                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
815                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
816                 } else {
817                         in_liquid = false;
818                 }
819         } else {
820                 // If not in liquid, the threshold of going in is at lower y
821                 pp = floatToInt(position + v3f(0, BS * 0.5, 0), BS);
822                 node = map->getNodeNoEx(pp, &is_valid_position);
823                 if (is_valid_position) {
824                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
825                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
826                 } else {
827                         in_liquid = false;
828                 }
829         }
830
831         /*
832                 Check if player is in liquid (the stable value)
833         */
834         pp = floatToInt(position + v3f(0, 0, 0), BS);
835         node = map->getNodeNoEx(pp, &is_valid_position);
836         if (is_valid_position)
837                 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
838         else
839                 in_liquid_stable = false;
840
841         /*
842                 Check if player is climbing
843         */
844         pp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS);
845         v3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS);
846         node = map->getNodeNoEx(pp, &is_valid_position);
847         bool is_valid_position2;
848         MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
849
850         if (!(is_valid_position && is_valid_position2))
851                 is_climbing = false;
852         else
853                 is_climbing = (nodemgr->get(node.getContent()).climbable ||
854                                 nodemgr->get(node2.getContent()).climbable) && !free_move;
855
856         /*
857                 Collision uncertainty radius
858                 Make it a bit larger than the maximum distance of movement
859         */
860         //f32 d = pos_max_d * 1.1;
861         // A fairly large value in here makes moving smoother
862         f32 d = 0.15 * BS;
863         // This should always apply, otherwise there are glitches
864         sanity_check(d > pos_max_d);
865         // Maximum distance over border for sneaking
866         f32 sneak_max = BS * 0.4;
867
868         /*
869                 If sneaking, keep in range from the last walked node and don't
870                 fall off from it
871         */
872         if (control.sneak && m_sneak_node_exists &&
873                         !(fly_allowed && player_settings.free_move) && !in_liquid &&
874                         physics_override_sneak) {
875                 f32 maxd = 0.5 * BS + sneak_max;
876                 v3f lwn_f = intToFloat(m_sneak_node, BS);
877                 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
878                 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
879
880                 if (!is_climbing) {
881                         // Move up if necessary
882                         f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
883                         if (position.Y < new_y)
884                                 position.Y = new_y;
885                         /*
886                                 Collision seems broken, since player is sinking when
887                                 sneaking over the edges of current sneaking_node.
888                                 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
889                         */
890                         if (m_speed.Y < 0)
891                                 m_speed.Y = 0;
892                 }
893         }
894
895         // this shouldn't be hardcoded but transmitted from server
896         float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
897
898         v3f accel_f = v3f(0, 0, 0);
899         const v3f initial_position = position;
900         const v3f initial_speed = m_speed;
901
902         collisionMoveResult result = collisionMoveSimple(env, m_client,
903                 pos_max_d, m_collisionbox, player_stepheight, dtime,
904                 &position, &m_speed, accel_f);
905
906         /*
907                 If the player's feet touch the topside of any node, this is
908                 set to true.
909
910                 Player is allowed to jump when this is true.
911         */
912         bool touching_ground_was = touching_ground;
913         touching_ground = result.touching_ground;
914
915     //bool standing_on_unloaded = result.standing_on_unloaded;
916
917         /*
918                 Check the nodes under the player to see from which node the
919                 player is sneaking from, if any.  If the node from under
920                 the player has been removed, the player falls.
921         */
922         f32 position_y_mod = 0.05 * BS;
923         if (m_sneak_node_bb_ymax > 0)
924                 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
925         v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
926         if (m_sneak_node_exists &&
927                         nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
928                         m_old_node_below_type != "air") {
929                 // Old node appears to have been removed; that is,
930                 // it wasn't air before but now it is
931                 m_need_to_get_new_sneak_node = false;
932                 m_sneak_node_exists = false;
933         } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
934                 // We are on something, so make sure to recalculate the sneak
935                 // node.
936                 m_need_to_get_new_sneak_node = true;
937         }
938
939         if (m_need_to_get_new_sneak_node && physics_override_sneak) {
940                 m_sneak_node_bb_ymax = 0;
941                 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
942                 v2f player_p2df(position.X, position.Z);
943                 f32 min_distance_f = 100000.0 * BS;
944                 // If already seeking from some node, compare to it.
945                 v3s16 new_sneak_node = m_sneak_node;
946                 for (s16 x= -1; x <= 1; x++)
947                 for (s16 z= -1; z <= 1; z++) {
948                         v3s16 p = pos_i_bottom + v3s16(x, 0, z);
949                         v3f pf = intToFloat(p, BS);
950                         v2f node_p2df(pf.X, pf.Z);
951                         f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
952                         f32 max_axis_distance_f = MYMAX(
953                                         std::fabs(player_p2df.X - node_p2df.X),
954                                         std::fabs(player_p2df.Y - node_p2df.Y));
955
956                         if (distance_f > min_distance_f ||
957                                         max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)
958                                 continue;
959
960                         // The node to be sneaked on has to be walkable
961                         node = map->getNodeNoEx(p, &is_valid_position);
962                         if (!is_valid_position || !nodemgr->get(node).walkable)
963                                 continue;
964                         // And the node above it has to be nonwalkable
965                         node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
966                         if (!is_valid_position || nodemgr->get(node).walkable)
967                                 continue;
968                         // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
969                         if (!physics_override_sneak_glitch) {
970                                 node =map->getNodeNoEx(p + v3s16(0, 2, 0), &is_valid_position);
971                                 if (!is_valid_position || nodemgr->get(node).walkable)
972                                         continue;
973                         }
974
975                         min_distance_f = distance_f;
976                         new_sneak_node = p;
977                 }
978
979                 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
980
981                 m_sneak_node = new_sneak_node;
982                 m_sneak_node_exists = sneak_node_found;
983
984                 if (sneak_node_found) {
985                         f32 cb_max = 0;
986                         MapNode n = map->getNodeNoEx(m_sneak_node);
987                         std::vector<aabb3f> nodeboxes;
988                         n.getCollisionBoxes(nodemgr, &nodeboxes);
989                         for (const auto &box : nodeboxes) {
990                                 if (box.MaxEdge.Y > cb_max)
991                                         cb_max = box.MaxEdge.Y;
992                         }
993                         m_sneak_node_bb_ymax = cb_max;
994                 }
995
996                 /*
997                         If sneaking, the player's collision box can be in air, so
998                         this has to be set explicitly
999                 */
1000                 if (sneak_node_found && control.sneak)
1001                         touching_ground = true;
1002         }
1003
1004         /*
1005                 Set new position but keep sneak node set
1006         */
1007         bool sneak_node_exists = m_sneak_node_exists;
1008         setPosition(position);
1009         m_sneak_node_exists = sneak_node_exists;
1010
1011         /*
1012                 Report collisions
1013         */
1014         // Dont report if flying
1015         if (collision_info && !(player_settings.free_move && fly_allowed)) {
1016                 for (const auto &info : result.collisions) {
1017                         collision_info->push_back(info);
1018                 }
1019         }
1020
1021         if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1022                 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
1023                 // Set camera impact value to be used for view bobbing
1024                 camera_impact = getSpeed().Y * -1;
1025         }
1026
1027         {
1028                 camera_barely_in_ceiling = false;
1029                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1030                 MapNode n = map->getNodeNoEx(camera_np);
1031                 if (n.getContent() != CONTENT_IGNORE) {
1032                         if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1033                                 camera_barely_in_ceiling = true;
1034                 }
1035         }
1036
1037         /*
1038                 Update the node last under the player
1039         */
1040         m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);
1041         m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
1042
1043         /*
1044                 Check properties of the node on which the player is standing
1045         */
1046         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
1047         // Determine if jumping is possible
1048         m_can_jump = touching_ground && !in_liquid;
1049         if (itemgroup_get(f.groups, "disable_jump"))
1050                 m_can_jump = false;
1051         // Jump key pressed while jumping off from a bouncy block
1052         if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1053                         m_speed.Y >= -0.5 * BS) {
1054                 float jumpspeed = movement_speed_jump * physics_override_jump;
1055                 if (m_speed.Y > 1) {
1056                         // Reduce boost when speed already is high
1057                         m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
1058                 } else {
1059                         m_speed.Y += jumpspeed;
1060                 }
1061                 setSpeed(m_speed);
1062                 m_can_jump = false;
1063         }
1064
1065         // Autojump
1066         handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
1067 }
1068
1069 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1070 {
1071         // Slip on slippery nodes
1072         const NodeDefManager *nodemgr = env->getGameDef()->ndef();
1073         Map *map = &env->getMap();
1074         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(
1075                         getStandingNodePos()));
1076         int slippery = 0;
1077         if (f.walkable)
1078                 slippery = itemgroup_get(f.groups, "slippery");
1079
1080         if (slippery >= 1) {
1081                 if (speedH == v3f(0.0f)) {
1082                         slippery = slippery * 2;
1083                 }
1084                 return core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);
1085         }
1086         return 1.0f;
1087 }
1088
1089 void LocalPlayer::handleAutojump(f32 dtime, Environment *env,
1090                 const collisionMoveResult &result, const v3f &initial_position,
1091                 const v3f &initial_speed, f32 pos_max_d)
1092 {
1093         PlayerSettings &player_settings = getPlayerSettings();
1094         if (!player_settings.autojump)
1095                 return;
1096
1097         if (m_autojump) {
1098                 // release autojump after a given time
1099                 m_autojump_time -= dtime;
1100                 if (m_autojump_time <= 0.0f)
1101                         m_autojump = false;
1102                 return;
1103         }
1104
1105         bool control_forward = control.up ||
1106                                (!control.up && !control.down &&
1107                                                control.forw_move_joystick_axis < -0.05);
1108         bool could_autojump =
1109                         m_can_jump && !control.jump && !control.sneak && control_forward;
1110         if (!could_autojump)
1111                 return;
1112
1113         bool horizontal_collision = false;
1114         for (const auto &colinfo : result.collisions) {
1115                 if (colinfo.type == COLLISION_NODE && colinfo.plane != 1) {
1116                         horizontal_collision = true;
1117                         break; // one is enough
1118                 }
1119         }
1120
1121         // must be running against something to trigger autojumping
1122         if (!horizontal_collision)
1123                 return;
1124
1125         // check for nodes above
1126         v3f headpos_min = m_position + m_collisionbox.MinEdge * 0.99f;
1127         v3f headpos_max = m_position + m_collisionbox.MaxEdge * 0.99f;
1128         headpos_min.Y = headpos_max.Y; // top face of collision box
1129         v3s16 ceilpos_min = floatToInt(headpos_min, BS) + v3s16(0, 1, 0);
1130         v3s16 ceilpos_max = floatToInt(headpos_max, BS) + v3s16(0, 1, 0);
1131         const NodeDefManager *ndef = env->getGameDef()->ndef();
1132         bool is_position_valid;
1133         for (s16 z = ceilpos_min.Z; z <= ceilpos_max.Z; z++) {
1134                 for (s16 x = ceilpos_min.X; x <= ceilpos_max.X; x++) {
1135                         MapNode n = env->getMap().getNodeNoEx(v3s16(x, ceilpos_max.Y, z), &is_position_valid);
1136
1137                         if (!is_position_valid)
1138                                 break;  // won't collide with the void outside
1139                         if (n.getContent() == CONTENT_IGNORE)
1140                                 return; // players collide with ignore blocks -> same as walkable
1141                         const ContentFeatures &f = ndef->get(n);
1142                         if (f.walkable)
1143                                 return; // would bump head, don't jump
1144                 }
1145         }
1146
1147         float jump_height = 1.1f; // TODO: better than a magic number
1148         v3f jump_pos = initial_position + v3f(0.0f, jump_height * BS, 0.0f);
1149         v3f jump_speed = initial_speed;
1150
1151         // try at peak of jump, zero step height
1152         collisionMoveResult jump_result = collisionMoveSimple(env, m_client, pos_max_d,
1153                         m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed,
1154                         v3f(0, 0, 0));
1155
1156         // see if we can get a little bit farther horizontally if we had
1157         // jumped
1158         v3f run_delta = m_position - initial_position;
1159         run_delta.Y = 0.0f;
1160         v3f jump_delta = jump_pos - initial_position;
1161         jump_delta.Y = 0.0f;
1162         if (jump_delta.getLengthSQ() > run_delta.getLengthSQ() * 1.01f) {
1163                 m_autojump = true;
1164                 m_autojump_time = 0.1f;
1165         }
1166 }