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