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