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