]> git.lizzy.rs Git - dragonfireclient.git/blob - src/localplayer.cpp
Modernize various files (src/k*, src/l*)
[dragonfireclient.git] / src / 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
22 #include "event.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, 0, 0, 0, 0, 0);
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         INodeDefManager *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.05 * 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, position_y_mod, 0), BS);
85
86         if (current_node != m_sneak_node) {
87                 new_sneak_node_exists = false;
88         } else {
89                 node = map->getNodeNoEx(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.0 * 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) > (.5 + .1) * BS + sneak_max.X ||
110                                 fabs(diff.Y) > (.5 + .1) * BS + sneak_max.Z)
111                         continue;
112
113
114                 // The node to be sneaked on has to be walkable
115                 node = map->getNodeNoEx(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 = ceilf(
122                                         (m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
123                         );
124                         for (u16 y = 1; y <= height; y++) {
125                                 node = map->getNodeNoEx(p + v3s16(0, y, 0), &is_valid_position);
126                                 if (!is_valid_position || nodemgr->get(node).walkable) {
127                                         ok = false;
128                                         break;
129                                 }
130                         }
131                 } else {
132                         // legacy behaviour: check just one node
133                         node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
134                         ok = is_valid_position && !nodemgr->get(node).walkable;
135                 }
136                 if (!ok)
137                         continue;
138
139                 min_distance_f = distance_f;
140                 m_sneak_node = p;
141                 new_sneak_node_exists = true;
142         }
143
144         if (!new_sneak_node_exists)
145                 return false;
146
147         // Update saved top bounding box of sneak node
148         node = map->getNodeNoEx(m_sneak_node);
149         std::vector<aabb3f> nodeboxes;
150         node.getCollisionBoxes(nodemgr, &nodeboxes);
151         m_sneak_node_bb_top = getNodeBoundingBox(nodeboxes);
152
153         if (physics_override_sneak_glitch) {
154                 // Detect sneak ladder:
155                 // Node two meters above sneak node must be solid
156                 node = map->getNodeNoEx(m_sneak_node + v3s16(0, 2, 0),
157                         &is_valid_position);
158                 if (is_valid_position && nodemgr->get(node).walkable) {
159                         // Node three meters above: must be non-solid
160                         node = map->getNodeNoEx(m_sneak_node + v3s16(0, 3, 0),
161                                 &is_valid_position);
162                         m_sneak_ladder_detected = is_valid_position &&
163                                 !nodemgr->get(node).walkable;
164                 }
165         }
166         return true;
167 }
168
169 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
170                 std::vector<CollisionInfo> *collision_info)
171 {
172         // Temporary option for old move code
173         if (!physics_override_new_move) {
174                 old_move(dtime, env, pos_max_d, collision_info);
175                 return;
176         }
177
178         Map *map = &env->getMap();
179         INodeDefManager *nodemgr = m_client->ndef();
180
181         v3f position = getPosition();
182
183         // Copy parent position if local player is attached
184         if (isAttached) {
185                 setPosition(overridePosition);
186                 return;
187         }
188
189         // Skip collision detection if noclip mode is used
190         bool fly_allowed = m_client->checkLocalPrivilege("fly");
191         bool noclip = m_client->checkLocalPrivilege("noclip") &&
192                 g_settings->getBool("noclip");
193         bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
194         if (free_move) {
195                 position += m_speed * dtime;
196                 setPosition(position);
197                 return;
198         }
199
200         /*
201                 Collision detection
202         */
203
204         bool is_valid_position;
205         MapNode node;
206         v3s16 pp;
207
208         /*
209                 Check if player is in liquid (the oscillating value)
210         */
211
212         // If in liquid, the threshold of coming out is at higher y
213         if (in_liquid)
214         {
215                 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
216                 node = map->getNodeNoEx(pp, &is_valid_position);
217                 if (is_valid_position) {
218                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
219                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
220                 } else {
221                         in_liquid = false;
222                 }
223         }
224         // If not in liquid, the threshold of going in is at lower y
225         else
226         {
227                 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
228                 node = map->getNodeNoEx(pp, &is_valid_position);
229                 if (is_valid_position) {
230                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
231                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
232                 } else {
233                         in_liquid = false;
234                 }
235         }
236
237
238         /*
239                 Check if player is in liquid (the stable value)
240         */
241         pp = floatToInt(position + v3f(0,0,0), BS);
242         node = map->getNodeNoEx(pp, &is_valid_position);
243         if (is_valid_position) {
244                 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
245         } else {
246                 in_liquid_stable = false;
247         }
248
249         /*
250                 Check if player is climbing
251         */
252
253
254         pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
255         v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
256         node = map->getNodeNoEx(pp, &is_valid_position);
257         bool is_valid_position2;
258         MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
259
260         if (!(is_valid_position && is_valid_position2)) {
261                 is_climbing = false;
262         } else {
263                 is_climbing = (nodemgr->get(node.getContent()).climbable
264                                 || nodemgr->get(node2.getContent()).climbable) && !free_move;
265         }
266
267         /*
268                 Collision uncertainty radius
269                 Make it a bit larger than the maximum distance of movement
270         */
271         //f32 d = pos_max_d * 1.1;
272         // A fairly large value in here makes moving smoother
273         f32 d = 0.15*BS;
274
275         // This should always apply, otherwise there are glitches
276         sanity_check(d > pos_max_d);
277
278         // Player object property step height is multiplied by BS in
279         // /src/script/common/c_content.cpp and /src/content_sao.cpp
280         float player_stepheight = (m_cao == nullptr) ? 0.0f :
281                 (touching_ground ? m_cao->getStepHeight() : (0.2f * BS));
282
283         // TODO this is a problematic hack.
284         // Use a better implementation for autojump, or apply a custom stepheight
285         // to all players, as this currently creates unintended special movement
286         // abilities and advantages for Android players on a server.
287 #ifdef __ANDROID__
288         player_stepheight += (0.6f * BS);
289 #endif
290
291         v3f accel_f = v3f(0,0,0);
292
293         collisionMoveResult result = collisionMoveSimple(env, m_client,
294                 pos_max_d, m_collisionbox, player_stepheight, dtime,
295                 &position, &m_speed, accel_f);
296
297         bool could_sneak = control.sneak &&
298                 !(fly_allowed && g_settings->getBool("free_move")) &&
299                 !in_liquid && !is_climbing &&
300                 physics_override_sneak;
301         /*
302                 If the player's feet touch the topside of any node, this is
303                 set to true.
304
305                 Player is allowed to jump when this is true.
306         */
307         bool touching_ground_was = touching_ground;
308         touching_ground = result.touching_ground;
309         bool sneak_can_jump = false;
310
311         // Max. distance (X, Z) over border for sneaking determined by collision box
312         // * 0.49 to keep the center just barely on the node
313         v3f sneak_max = m_collisionbox.getExtent() * 0.49;
314
315         if (m_sneak_ladder_detected) {
316                 // restore legacy behaviour (this makes the m_speed.Y hack necessary)
317                 sneak_max = v3f(0.4 * BS, 0, 0.4 * BS);
318         }
319
320         /*
321                 If sneaking, keep on top of last walked node and don't fall off
322         */
323         if (could_sneak && m_sneak_node_exists) {
324                 const v3f sn_f = intToFloat(m_sneak_node, BS);
325                 const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge;
326                 const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge;
327                 const v3f old_pos = position;
328                 const v3f old_speed = m_speed;
329                 f32 y_diff = bmax.Y - position.Y;
330
331                 // (BS * 0.6f) is the basic stepheight while standing on ground
332                 if (y_diff < BS * 0.6f) {
333                         // Only center player when they're on the node
334                         position.X = rangelim(position.X,
335                                 bmin.X - sneak_max.X, bmax.X + sneak_max.X);
336                         position.Z = rangelim(position.Z,
337                                 bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z);
338
339                         if (position.X != old_pos.X)
340                                 m_speed.X = 0;
341                         if (position.Z != old_pos.Z)
342                                 m_speed.Z = 0;
343                 }
344
345                 if (y_diff > 0 && m_speed.Y < 0 &&
346                                 (physics_override_sneak_glitch || y_diff < BS * 0.6f)) {
347                         // Move player to the maximal height when falling or when
348                         // the ledge is climbed on the next step.
349                         position.Y = bmax.Y;
350                         m_speed.Y = 0;
351                 }
352
353                 // Allow jumping on node edges while sneaking
354                 if (m_speed.Y == 0 || m_sneak_ladder_detected)
355                         sneak_can_jump = true;
356
357                 if (collision_info != NULL &&
358                                 m_speed.Y - old_speed.Y > BS) {
359                         // Collide with sneak node, report fall damage
360                         CollisionInfo sn_info;
361                         sn_info.node_p = m_sneak_node;
362                         sn_info.old_speed = old_speed;
363                         sn_info.new_speed = m_speed;
364                         collision_info->push_back(sn_info);
365                 }
366         }
367
368         /*
369                 Find the next sneak node if necessary
370         */
371         bool new_sneak_node_exists = false;
372
373         if (could_sneak)
374                 new_sneak_node_exists = updateSneakNode(map, position, sneak_max);
375
376         /*
377                 Set new position but keep sneak node set
378         */
379         setPosition(position);
380         m_sneak_node_exists = new_sneak_node_exists;
381
382         /*
383                 Report collisions
384         */
385
386         // Dont report if flying
387         if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
388                 for (const auto &colinfo : result.collisions) {
389                         collision_info->push_back(colinfo);
390                 }
391         }
392
393         if(!result.standing_on_object && !touching_ground_was && touching_ground) {
394                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
395                 m_client->event()->put(e);
396
397                 // Set camera impact value to be used for view bobbing
398                 camera_impact = getSpeed().Y * -1;
399         }
400
401         {
402                 camera_barely_in_ceiling = false;
403                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
404                 MapNode n = map->getNodeNoEx(camera_np);
405                 if(n.getContent() != CONTENT_IGNORE){
406                         if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
407                                 camera_barely_in_ceiling = true;
408                         }
409                 }
410         }
411
412         /*
413                 Check properties of the node on which the player is standing
414         */
415         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
416         // Determine if jumping is possible
417         m_can_jump = (touching_ground && !in_liquid && !is_climbing)
418                         || sneak_can_jump;
419         if (itemgroup_get(f.groups, "disable_jump"))
420                 m_can_jump = false;
421
422         // Jump key pressed while jumping off from a bouncy block
423         if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
424                 m_speed.Y >= -0.5 * BS) {
425                 float jumpspeed = movement_speed_jump * physics_override_jump;
426                 if (m_speed.Y > 1) {
427                         // Reduce boost when speed already is high
428                         m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
429                 } else {
430                         m_speed.Y += jumpspeed;
431                 }
432                 setSpeed(m_speed);
433                 m_can_jump = false;
434         }
435 }
436
437 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
438 {
439         move(dtime, env, pos_max_d, NULL);
440 }
441
442 void LocalPlayer::applyControl(float dtime, Environment *env)
443 {
444         // Clear stuff
445         swimming_vertical = false;
446
447         setPitch(control.pitch);
448         setYaw(control.yaw);
449
450         // Nullify speed and don't run positioning code if the player is attached
451         if(isAttached)
452         {
453                 setSpeed(v3f(0,0,0));
454                 return;
455         }
456
457         v3f move_direction = v3f(0,0,1);
458         move_direction.rotateXZBy(getYaw());
459
460         v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
461         v3f speedV = v3f(0,0,0); // Vertical (Y)
462
463         bool fly_allowed = m_client->checkLocalPrivilege("fly");
464         bool fast_allowed = m_client->checkLocalPrivilege("fast");
465
466         bool free_move = fly_allowed && g_settings->getBool("free_move");
467         bool fast_move = fast_allowed && g_settings->getBool("fast_move");
468         // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
469         bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
470         bool continuous_forward = g_settings->getBool("continuous_forward");
471         bool always_fly_fast = g_settings->getBool("always_fly_fast");
472
473         // Whether superspeed mode is used or not
474         bool superspeed = false;
475
476         if (always_fly_fast && free_move && fast_move)
477                 superspeed = true;
478
479         // Old descend control
480         if(g_settings->getBool("aux1_descends"))
481         {
482                 // If free movement and fast movement, always move fast
483                 if(free_move && fast_move)
484                         superspeed = true;
485
486                 // Auxiliary button 1 (E)
487                 if(control.aux1)
488                 {
489                         if(free_move)
490                         {
491                                 // In free movement mode, aux1 descends
492                                 if(fast_move)
493                                         speedV.Y = -movement_speed_fast;
494                                 else
495                                         speedV.Y = -movement_speed_walk;
496                         }
497                         else if(in_liquid || in_liquid_stable)
498                         {
499                                 speedV.Y = -movement_speed_walk;
500                                 swimming_vertical = true;
501                         }
502                         else if(is_climbing)
503                         {
504                                 speedV.Y = -movement_speed_climb;
505                         }
506                         else
507                         {
508                                 // If not free movement but fast is allowed, aux1 is
509                                 // "Turbo button"
510                                 if(fast_move)
511                                         superspeed = true;
512                         }
513                 }
514         }
515         // New minecraft-like descend control
516         else
517         {
518                 // Auxiliary button 1 (E)
519                 if(control.aux1)
520                 {
521                         if(!is_climbing)
522                         {
523                                 // aux1 is "Turbo button"
524                                 if(fast_move)
525                                         superspeed = true;
526                         }
527                 }
528
529                 if(control.sneak)
530                 {
531                         if(free_move)
532                         {
533                                 // In free movement mode, sneak descends
534                                 if (fast_move && (control.aux1 || always_fly_fast))
535                                         speedV.Y = -movement_speed_fast;
536                                 else
537                                         speedV.Y = -movement_speed_walk;
538                         }
539                         else if(in_liquid || in_liquid_stable)
540                         {
541                                 if(fast_climb)
542                                         speedV.Y = -movement_speed_fast;
543                                 else
544                                         speedV.Y = -movement_speed_walk;
545                                 swimming_vertical = true;
546                         }
547                         else if(is_climbing)
548                         {
549                                 if(fast_climb)
550                                         speedV.Y = -movement_speed_fast;
551                                 else
552                                         speedV.Y = -movement_speed_climb;
553                         }
554                 }
555         }
556
557         if (continuous_forward)
558                 speedH += move_direction;
559
560         if (control.up) {
561                 if (continuous_forward) {
562                         if (fast_move)
563                                 superspeed = true;
564                 } else {
565                         speedH += move_direction;
566                 }
567         }
568         if (control.down) {
569                 speedH -= move_direction;
570         }
571         if (!control.up && !control.down) {
572                 speedH -= move_direction *
573                         (control.forw_move_joystick_axis / 32767.f);
574         }
575         if (control.left) {
576                 speedH += move_direction.crossProduct(v3f(0,1,0));
577         }
578         if (control.right) {
579                 speedH += move_direction.crossProduct(v3f(0,-1,0));
580         }
581         if (!control.left && !control.right) {
582                 speedH -= move_direction.crossProduct(v3f(0,1,0)) *
583                         (control.sidew_move_joystick_axis / 32767.f);
584         }
585         if(control.jump)
586         {
587                 if (free_move) {
588                         if (g_settings->getBool("aux1_descends") || always_fly_fast) {
589                                 if (fast_move)
590                                         speedV.Y = movement_speed_fast;
591                                 else
592                                         speedV.Y = movement_speed_walk;
593                         } else {
594                                 if(fast_move && control.aux1)
595                                         speedV.Y = movement_speed_fast;
596                                 else
597                                         speedV.Y = movement_speed_walk;
598                         }
599                 }
600                 else if(m_can_jump)
601                 {
602                         /*
603                                 NOTE: The d value in move() affects jump height by
604                                 raising the height at which the jump speed is kept
605                                 at its starting value
606                         */
607                         v3f speedJ = getSpeed();
608                         if(speedJ.Y >= -0.5 * BS) {
609                                 speedJ.Y = movement_speed_jump * physics_override_jump;
610                                 setSpeed(speedJ);
611
612                                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
613                                 m_client->event()->put(e);
614                         }
615                 }
616                 else if(in_liquid)
617                 {
618                         if(fast_climb)
619                                 speedV.Y = movement_speed_fast;
620                         else
621                                 speedV.Y = movement_speed_walk;
622                         swimming_vertical = true;
623                 }
624                 else if(is_climbing)
625                 {
626                         if(fast_climb)
627                                 speedV.Y = movement_speed_fast;
628                         else
629                                 speedV.Y = movement_speed_climb;
630                 }
631         }
632
633         // The speed of the player (Y is ignored)
634         if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
635                 speedH = speedH.normalize() * movement_speed_fast;
636         else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
637                 speedH = speedH.normalize() * movement_speed_crouch;
638         else
639                 speedH = speedH.normalize() * movement_speed_walk;
640
641         // Acceleration increase
642         f32 incH = 0; // Horizontal (X, Z)
643         f32 incV = 0; // Vertical (Y)
644         if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
645         {
646                 // Jumping and falling
647                 if(superspeed || (fast_move && control.aux1))
648                         incH = movement_acceleration_fast * BS * dtime;
649                 else
650                         incH = movement_acceleration_air * BS * dtime;
651                 incV = 0; // No vertical acceleration in air
652         }
653         else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
654                 incH = incV = movement_acceleration_fast * BS * dtime;
655         else
656                 incH = incV = movement_acceleration_default * BS * dtime;
657
658         const INodeDefManager *nodemgr = env->getGameDef()->ndef();
659         Map *map = &env->getMap();
660         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
661         bool slippery = (itemgroup_get(f.groups, "slippery") != 0);
662         // Accelerate to target speed with maximum increment
663         accelerateHorizontal(speedH * physics_override_speed,
664                         incH * physics_override_speed, slippery);
665         accelerateVertical(speedV * physics_override_speed,
666                         incV * physics_override_speed);
667 }
668
669 v3s16 LocalPlayer::getStandingNodePos()
670 {
671         if(m_sneak_node_exists)
672                 return m_sneak_node;
673         return floatToInt(getPosition() - v3f(0, BS, 0), BS);
674 }
675
676 v3s16 LocalPlayer::getFootstepNodePos()
677 {
678         if (in_liquid_stable)
679                 // Emit swimming sound if the player is in liquid
680                 return floatToInt(getPosition(), BS);
681         if (touching_ground)
682                 // BS * 0.05 below the player's feet ensures a 1/16th height
683                 // nodebox is detected instead of the node below it.
684                 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
685         // A larger distance below is necessary for a footstep sound
686         // when landing after a jump or fall. BS * 0.5 ensures water
687         // sounds when swimming in 1 node deep water.
688         return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
689 }
690
691 v3s16 LocalPlayer::getLightPosition() const
692 {
693         return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
694 }
695
696 v3f LocalPlayer::getEyeOffset() const
697 {
698         float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f;
699         return v3f(0, BS * eye_height, 0);
700 }
701
702 // Horizontal acceleration (X and Z), Y direction is ignored
703 void LocalPlayer::accelerateHorizontal(const v3f &target_speed,
704         const f32 max_increase, bool slippery)
705 {
706         if (max_increase == 0)
707                 return;
708
709         v3f d_wanted = target_speed - m_speed;
710         if (slippery) {
711                 if (target_speed == v3f())
712                         d_wanted = -m_speed * 0.05f;
713                 else
714                         d_wanted *= 0.1f;
715         }
716
717         d_wanted.Y = 0.0f;
718         f32 dl = d_wanted.getLength();
719         if (dl > max_increase)
720                 dl = max_increase;
721
722         v3f d = d_wanted.normalize() * dl;
723
724         m_speed.X += d.X;
725         m_speed.Z += d.Z;
726 }
727
728 // Vertical acceleration (Y), X and Z directions are ignored
729 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
730 {
731         if (max_increase == 0)
732                 return;
733
734         f32 d_wanted = target_speed.Y - m_speed.Y;
735         if (d_wanted > max_increase)
736                 d_wanted = max_increase;
737         else if (d_wanted < -max_increase)
738                 d_wanted = -max_increase;
739
740         m_speed.Y += d_wanted;
741 }
742
743 // Temporary option for old move code
744 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
745                 std::vector<CollisionInfo> *collision_info)
746 {
747         Map *map = &env->getMap();
748         INodeDefManager *nodemgr = m_client->ndef();
749
750         v3f position = getPosition();
751
752         // Copy parent position if local player is attached
753         if (isAttached) {
754                 setPosition(overridePosition);
755                 m_sneak_node_exists = false;
756                 return;
757         }
758
759         // Skip collision detection if noclip mode is used
760         bool fly_allowed = m_client->checkLocalPrivilege("fly");
761         bool noclip = m_client->checkLocalPrivilege("noclip") &&
762                 g_settings->getBool("noclip");
763         bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
764         if (free_move) {
765                 position += m_speed * dtime;
766                 setPosition(position);
767                 m_sneak_node_exists = false;
768                 return;
769         }
770
771         /*
772                 Collision detection
773         */
774         bool is_valid_position;
775         MapNode node;
776         v3s16 pp;
777
778         /*
779                 Check if player is in liquid (the oscillating value)
780         */
781         if (in_liquid) {
782                 // If in liquid, the threshold of coming out is at higher y
783                 pp = floatToInt(position + v3f(0, BS * 0.1, 0), BS);
784                 node = map->getNodeNoEx(pp, &is_valid_position);
785                 if (is_valid_position) {
786                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
787                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
788                 } else {
789                         in_liquid = false;
790                 }
791         } else {
792                 // If not in liquid, the threshold of going in is at lower y
793                 pp = floatToInt(position + v3f(0, BS * 0.5, 0), BS);
794                 node = map->getNodeNoEx(pp, &is_valid_position);
795                 if (is_valid_position) {
796                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
797                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
798                 } else {
799                         in_liquid = false;
800                 }
801         }
802
803         /*
804                 Check if player is in liquid (the stable value)
805         */
806         pp = floatToInt(position + v3f(0, 0, 0), BS);
807         node = map->getNodeNoEx(pp, &is_valid_position);
808         if (is_valid_position)
809                 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
810         else
811                 in_liquid_stable = false;
812
813         /*
814                 Check if player is climbing
815         */
816         pp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS);
817         v3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS);
818         node = map->getNodeNoEx(pp, &is_valid_position);
819         bool is_valid_position2;
820         MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
821
822         if (!(is_valid_position && is_valid_position2))
823                 is_climbing = false;
824         else
825                 is_climbing = (nodemgr->get(node.getContent()).climbable ||
826                                 nodemgr->get(node2.getContent()).climbable) && !free_move;
827
828         /*
829                 Collision uncertainty radius
830                 Make it a bit larger than the maximum distance of movement
831         */
832         //f32 d = pos_max_d * 1.1;
833         // A fairly large value in here makes moving smoother
834         f32 d = 0.15 * BS;
835         // This should always apply, otherwise there are glitches
836         sanity_check(d > pos_max_d);
837         // Maximum distance over border for sneaking
838         f32 sneak_max = BS * 0.4;
839
840         /*
841                 If sneaking, keep in range from the last walked node and don't
842                 fall off from it
843         */
844         if (control.sneak && m_sneak_node_exists &&
845                         !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
846                         physics_override_sneak) {
847                 f32 maxd = 0.5 * BS + sneak_max;
848                 v3f lwn_f = intToFloat(m_sneak_node, BS);
849                 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
850                 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
851
852                 if (!is_climbing) {
853                         // Move up if necessary
854                         f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
855                         if (position.Y < new_y)
856                                 position.Y = new_y;
857                         /*
858                                 Collision seems broken, since player is sinking when
859                                 sneaking over the edges of current sneaking_node.
860                                 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
861                         */
862                         if (m_speed.Y < 0)
863                                 m_speed.Y = 0;
864                 }
865         }
866
867         // this shouldn't be hardcoded but transmitted from server
868         float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
869
870 #ifdef __ANDROID__
871         player_stepheight += (0.6 * BS);
872 #endif
873
874         v3f accel_f = v3f(0, 0, 0);
875
876         collisionMoveResult result = collisionMoveSimple(env, m_client,
877                 pos_max_d, m_collisionbox, player_stepheight, dtime,
878                 &position, &m_speed, accel_f);
879
880         /*
881                 If the player's feet touch the topside of any node, this is
882                 set to true.
883
884                 Player is allowed to jump when this is true.
885         */
886         bool touching_ground_was = touching_ground;
887         touching_ground = result.touching_ground;
888
889     //bool standing_on_unloaded = result.standing_on_unloaded;
890
891         /*
892                 Check the nodes under the player to see from which node the
893                 player is sneaking from, if any.  If the node from under
894                 the player has been removed, the player falls.
895         */
896         f32 position_y_mod = 0.05 * BS;
897         if (m_sneak_node_bb_ymax > 0)
898                 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
899         v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
900         if (m_sneak_node_exists &&
901                         nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
902                         m_old_node_below_type != "air") {
903                 // Old node appears to have been removed; that is,
904                 // it wasn't air before but now it is
905                 m_need_to_get_new_sneak_node = false;
906                 m_sneak_node_exists = false;
907         } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
908                 // We are on something, so make sure to recalculate the sneak
909                 // node.
910                 m_need_to_get_new_sneak_node = true;
911         }
912
913         if (m_need_to_get_new_sneak_node && physics_override_sneak) {
914                 m_sneak_node_bb_ymax = 0;
915                 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
916                 v2f player_p2df(position.X, position.Z);
917                 f32 min_distance_f = 100000.0 * BS;
918                 // If already seeking from some node, compare to it.
919                 v3s16 new_sneak_node = m_sneak_node;
920                 for (s16 x= -1; x <= 1; x++)
921                 for (s16 z= -1; z <= 1; z++) {
922                         v3s16 p = pos_i_bottom + v3s16(x, 0, z);
923                         v3f pf = intToFloat(p, BS);
924                         v2f node_p2df(pf.X, pf.Z);
925                         f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
926                         f32 max_axis_distance_f = MYMAX(
927                                         fabs(player_p2df.X - node_p2df.X),
928                                         fabs(player_p2df.Y - node_p2df.Y));
929
930                         if (distance_f > min_distance_f ||
931                                         max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)
932                                 continue;
933
934                         // The node to be sneaked on has to be walkable
935                         node = map->getNodeNoEx(p, &is_valid_position);
936                         if (!is_valid_position || !nodemgr->get(node).walkable)
937                                 continue;
938                         // And the node above it has to be nonwalkable
939                         node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
940                         if (!is_valid_position || nodemgr->get(node).walkable)
941                                 continue;
942                         // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
943                         if (!physics_override_sneak_glitch) {
944                                 node =map->getNodeNoEx(p + v3s16(0, 2, 0), &is_valid_position);
945                                 if (!is_valid_position || nodemgr->get(node).walkable)
946                                         continue;
947                         }
948
949                         min_distance_f = distance_f;
950                         new_sneak_node = p;
951                 }
952
953                 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
954
955                 m_sneak_node = new_sneak_node;
956                 m_sneak_node_exists = sneak_node_found;
957
958                 if (sneak_node_found) {
959                         f32 cb_max = 0;
960                         MapNode n = map->getNodeNoEx(m_sneak_node);
961                         std::vector<aabb3f> nodeboxes;
962                         n.getCollisionBoxes(nodemgr, &nodeboxes);
963                         for (const auto &box : nodeboxes) {
964                                 if (box.MaxEdge.Y > cb_max)
965                                         cb_max = box.MaxEdge.Y;
966                         }
967                         m_sneak_node_bb_ymax = cb_max;
968                 }
969
970                 /*
971                         If sneaking, the player's collision box can be in air, so
972                         this has to be set explicitly
973                 */
974                 if (sneak_node_found && control.sneak)
975                         touching_ground = true;
976         }
977
978         /*
979                 Set new position but keep sneak node set
980         */
981         bool sneak_node_exists = m_sneak_node_exists;
982         setPosition(position);
983         m_sneak_node_exists = sneak_node_exists;
984
985         /*
986                 Report collisions
987         */
988         // Dont report if flying
989         if (collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
990                 for (const auto &info : result.collisions) {
991                         collision_info->push_back(info);
992                 }
993         }
994
995         if (!result.standing_on_object && !touching_ground_was && touching_ground) {
996                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
997                 m_client->event()->put(e);
998                 // Set camera impact value to be used for view bobbing
999                 camera_impact = getSpeed().Y * -1;
1000         }
1001
1002         {
1003                 camera_barely_in_ceiling = false;
1004                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1005                 MapNode n = map->getNodeNoEx(camera_np);
1006                 if (n.getContent() != CONTENT_IGNORE) {
1007                         if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1008                                 camera_barely_in_ceiling = true;
1009                 }
1010         }
1011
1012         /*
1013                 Update the node last under the player
1014         */
1015         m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);
1016         m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
1017
1018         /*
1019                 Check properties of the node on which the player is standing
1020         */
1021         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
1022         // Determine if jumping is possible
1023         m_can_jump = touching_ground && !in_liquid;
1024         if (itemgroup_get(f.groups, "disable_jump"))
1025                 m_can_jump = false;
1026         // Jump key pressed while jumping off from a bouncy block
1027         if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1028                         m_speed.Y >= -0.5 * BS) {
1029                 float jumpspeed = movement_speed_jump * physics_override_jump;
1030                 if (m_speed.Y > 1) {
1031                         // Reduce boost when speed already is high
1032                         m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
1033                 } else {
1034                         m_speed.Y += jumpspeed;
1035                 }
1036                 setSpeed(m_speed);
1037                 m_can_jump = false;
1038         }
1039 }