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