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