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