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