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