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