]> git.lizzy.rs Git - dragonfireclient.git/blob - src/localplayer.cpp
Slippery: Simplify, make more efficient (#7086)
[dragonfireclient.git] / src / localplayer.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "localplayer.h"
21
22 #include "event.h"
23 #include "collision.h"
24 #include "nodedef.h"
25 #include "settings.h"
26 #include "environment.h"
27 #include "map.h"
28 #include "client.h"
29 #include "content_cao.h"
30
31 /*
32         LocalPlayer
33 */
34
35 LocalPlayer::LocalPlayer(Client *client, const char *name):
36         Player(name, client->idef()),
37         m_client(client)
38 {
39 }
40
41 static aabb3f getNodeBoundingBox(const std::vector<aabb3f> &nodeboxes)
42 {
43         if (nodeboxes.empty())
44                 return aabb3f(0, 0, 0, 0, 0, 0);
45
46         aabb3f b_max;
47
48         std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
49         b_max = aabb3f(it->MinEdge, it->MaxEdge);
50
51         ++it;
52         for (; it != nodeboxes.end(); ++it)
53                 b_max.addInternalBox(*it);
54
55         return b_max;
56 }
57
58 bool LocalPlayer::updateSneakNode(Map *map, const v3f &position,
59                 const v3f &sneak_max)
60 {
61         static const v3s16 dir9_center[9] = {
62                 v3s16( 0, 0,  0),
63                 v3s16( 1, 0,  0),
64                 v3s16(-1, 0,  0),
65                 v3s16( 0, 0,  1),
66                 v3s16( 0, 0, -1),
67                 v3s16( 1, 0,  1),
68                 v3s16(-1, 0,  1),
69                 v3s16( 1, 0, -1),
70                 v3s16(-1, 0, -1)
71         };
72
73         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                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
421                 m_client->event()->put(e);
422
423                 // Set camera impact value to be used for view bobbing
424                 camera_impact = getSpeed().Y * -1;
425         }
426
427         {
428                 camera_barely_in_ceiling = false;
429                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
430                 MapNode n = map->getNodeNoEx(camera_np);
431                 if(n.getContent() != CONTENT_IGNORE){
432                         if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
433                                 camera_barely_in_ceiling = true;
434                         }
435                 }
436         }
437
438         /*
439                 Check properties of the node on which the player is standing
440         */
441         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(m_standing_node));
442         // Determine if jumping is possible
443         m_can_jump = (touching_ground && !in_liquid && !is_climbing)
444                         || sneak_can_jump;
445         if (itemgroup_get(f.groups, "disable_jump"))
446                 m_can_jump = false;
447
448         // Jump key pressed while jumping off from a bouncy block
449         if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
450                 m_speed.Y >= -0.5 * BS) {
451                 float jumpspeed = movement_speed_jump * physics_override_jump;
452                 if (m_speed.Y > 1) {
453                         // Reduce boost when speed already is high
454                         m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
455                 } else {
456                         m_speed.Y += jumpspeed;
457                 }
458                 setSpeed(m_speed);
459                 m_can_jump = false;
460         }
461 }
462
463 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
464 {
465         move(dtime, env, pos_max_d, NULL);
466 }
467
468 void LocalPlayer::applyControl(float dtime, Environment *env)
469 {
470         // Clear stuff
471         swimming_vertical = false;
472
473         setPitch(control.pitch);
474         setYaw(control.yaw);
475
476         // Nullify speed and don't run positioning code if the player is attached
477         if(isAttached)
478         {
479                 setSpeed(v3f(0,0,0));
480                 return;
481         }
482
483         v3f move_direction = v3f(0,0,1);
484         move_direction.rotateXZBy(getYaw());
485
486         v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
487         v3f speedV = v3f(0,0,0); // Vertical (Y)
488
489         bool fly_allowed = m_client->checkLocalPrivilege("fly");
490         bool fast_allowed = m_client->checkLocalPrivilege("fast");
491
492         bool free_move = fly_allowed && g_settings->getBool("free_move");
493         bool fast_move = fast_allowed && g_settings->getBool("fast_move");
494         // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
495         bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
496         bool continuous_forward = g_settings->getBool("continuous_forward");
497         bool always_fly_fast = g_settings->getBool("always_fly_fast");
498
499         // Whether superspeed mode is used or not
500         bool superspeed = false;
501
502         if (always_fly_fast && free_move && fast_move)
503                 superspeed = true;
504
505         // Old descend control
506         if(g_settings->getBool("aux1_descends"))
507         {
508                 // If free movement and fast movement, always move fast
509                 if(free_move && fast_move)
510                         superspeed = true;
511
512                 // Auxiliary button 1 (E)
513                 if(control.aux1)
514                 {
515                         if(free_move)
516                         {
517                                 // In free movement mode, aux1 descends
518                                 if(fast_move)
519                                         speedV.Y = -movement_speed_fast;
520                                 else
521                                         speedV.Y = -movement_speed_walk;
522                         }
523                         else if(in_liquid || in_liquid_stable)
524                         {
525                                 speedV.Y = -movement_speed_walk;
526                                 swimming_vertical = true;
527                         }
528                         else if(is_climbing)
529                         {
530                                 speedV.Y = -movement_speed_climb;
531                         }
532                         else
533                         {
534                                 // If not free movement but fast is allowed, aux1 is
535                                 // "Turbo button"
536                                 if(fast_move)
537                                         superspeed = true;
538                         }
539                 }
540         }
541         // New minecraft-like descend control
542         else
543         {
544                 // Auxiliary button 1 (E)
545                 if(control.aux1)
546                 {
547                         if(!is_climbing)
548                         {
549                                 // aux1 is "Turbo button"
550                                 if(fast_move)
551                                         superspeed = true;
552                         }
553                 }
554
555                 if(control.sneak)
556                 {
557                         if(free_move)
558                         {
559                                 // In free movement mode, sneak descends
560                                 if (fast_move && (control.aux1 || always_fly_fast))
561                                         speedV.Y = -movement_speed_fast;
562                                 else
563                                         speedV.Y = -movement_speed_walk;
564                         }
565                         else if(in_liquid || in_liquid_stable)
566                         {
567                                 if(fast_climb)
568                                         speedV.Y = -movement_speed_fast;
569                                 else
570                                         speedV.Y = -movement_speed_walk;
571                                 swimming_vertical = true;
572                         }
573                         else if(is_climbing)
574                         {
575                                 if(fast_climb)
576                                         speedV.Y = -movement_speed_fast;
577                                 else
578                                         speedV.Y = -movement_speed_climb;
579                         }
580                 }
581         }
582
583         if (continuous_forward)
584                 speedH += move_direction;
585
586         if (control.up) {
587                 if (continuous_forward) {
588                         if (fast_move)
589                                 superspeed = true;
590                 } else {
591                         speedH += move_direction;
592                 }
593         }
594         if (control.down) {
595                 speedH -= move_direction;
596         }
597         if (!control.up && !control.down) {
598                 speedH -= move_direction *
599                         (control.forw_move_joystick_axis / 32767.f);
600         }
601         if (control.left) {
602                 speedH += move_direction.crossProduct(v3f(0,1,0));
603         }
604         if (control.right) {
605                 speedH += move_direction.crossProduct(v3f(0,-1,0));
606         }
607         if (!control.left && !control.right) {
608                 speedH -= move_direction.crossProduct(v3f(0,1,0)) *
609                         (control.sidew_move_joystick_axis / 32767.f);
610         }
611         if(control.jump)
612         {
613                 if (free_move) {
614                         if (g_settings->getBool("aux1_descends") || always_fly_fast) {
615                                 if (fast_move)
616                                         speedV.Y = movement_speed_fast;
617                                 else
618                                         speedV.Y = movement_speed_walk;
619                         } else {
620                                 if(fast_move && control.aux1)
621                                         speedV.Y = movement_speed_fast;
622                                 else
623                                         speedV.Y = movement_speed_walk;
624                         }
625                 }
626                 else if(m_can_jump)
627                 {
628                         /*
629                                 NOTE: The d value in move() affects jump height by
630                                 raising the height at which the jump speed is kept
631                                 at its starting value
632                         */
633                         v3f speedJ = getSpeed();
634                         if(speedJ.Y >= -0.5 * BS) {
635                                 speedJ.Y = movement_speed_jump * physics_override_jump;
636                                 setSpeed(speedJ);
637
638                                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
639                                 m_client->event()->put(e);
640                         }
641                 }
642                 else if(in_liquid)
643                 {
644                         if(fast_climb)
645                                 speedV.Y = movement_speed_fast;
646                         else
647                                 speedV.Y = movement_speed_walk;
648                         swimming_vertical = true;
649                 }
650                 else if(is_climbing)
651                 {
652                         if(fast_climb)
653                                 speedV.Y = movement_speed_fast;
654                         else
655                                 speedV.Y = movement_speed_climb;
656                 }
657         }
658
659         // The speed of the player (Y is ignored)
660         if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
661                 speedH = speedH.normalize() * movement_speed_fast;
662         else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
663                 speedH = speedH.normalize() * movement_speed_crouch;
664         else
665                 speedH = speedH.normalize() * movement_speed_walk;
666
667         // Acceleration increase
668         f32 incH = 0; // Horizontal (X, Z)
669         f32 incV = 0; // Vertical (Y)
670         if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
671         {
672                 // Jumping and falling
673                 if(superspeed || (fast_move && control.aux1))
674                         incH = movement_acceleration_fast * BS * dtime;
675                 else
676                         incH = movement_acceleration_air * BS * dtime;
677                 incV = 0; // No vertical acceleration in air
678         }
679         else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
680                 incH = incV = movement_acceleration_fast * BS * dtime;
681         else
682                 incH = incV = movement_acceleration_default * BS * dtime;
683
684         float slip_factor = 1.0f;
685         if (!free_move)
686                 slip_factor = getSlipFactor(env, speedH);
687
688         // Accelerate to target speed with maximum increment
689         accelerateHorizontal(speedH * physics_override_speed,
690                         incH * physics_override_speed * slip_factor);
691         accelerateVertical(speedV * physics_override_speed,
692                         incV * physics_override_speed);
693 }
694
695 v3s16 LocalPlayer::getStandingNodePos()
696 {
697         if(m_sneak_node_exists)
698                 return m_sneak_node;
699         return m_standing_node;
700 }
701
702 v3s16 LocalPlayer::getFootstepNodePos()
703 {
704         if (in_liquid_stable)
705                 // Emit swimming sound if the player is in liquid
706                 return floatToInt(getPosition(), BS);
707         if (touching_ground)
708                 // BS * 0.05 below the player's feet ensures a 1/16th height
709                 // nodebox is detected instead of the node below it.
710                 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
711         // A larger distance below is necessary for a footstep sound
712         // when landing after a jump or fall. BS * 0.5 ensures water
713         // sounds when swimming in 1 node deep water.
714         return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
715 }
716
717 v3s16 LocalPlayer::getLightPosition() const
718 {
719         return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
720 }
721
722 v3f LocalPlayer::getEyeOffset() const
723 {
724         float eye_height = camera_barely_in_ceiling ?
725                 m_eye_height - 0.125f : m_eye_height;
726         return v3f(0, BS * eye_height, 0);
727 }
728
729 // Horizontal acceleration (X and Z), Y direction is ignored
730 void LocalPlayer::accelerateHorizontal(const v3f &target_speed,
731         const f32 max_increase)
732 {
733         if (max_increase == 0)
734                 return;
735
736         v3f d_wanted = target_speed - m_speed;
737         d_wanted.Y = 0.0f;
738         f32 dl = d_wanted.getLength();
739         if (dl > max_increase)
740                 dl = max_increase;
741
742         v3f d = d_wanted.normalize() * dl;
743
744         m_speed.X += d.X;
745         m_speed.Z += d.Z;
746 }
747
748 // Vertical acceleration (Y), X and Z directions are ignored
749 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
750 {
751         if (max_increase == 0)
752                 return;
753
754         f32 d_wanted = target_speed.Y - m_speed.Y;
755         if (d_wanted > max_increase)
756                 d_wanted = max_increase;
757         else if (d_wanted < -max_increase)
758                 d_wanted = -max_increase;
759
760         m_speed.Y += d_wanted;
761 }
762
763 // Temporary option for old move code
764 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
765                 std::vector<CollisionInfo> *collision_info)
766 {
767         Map *map = &env->getMap();
768         const NodeDefManager *nodemgr = m_client->ndef();
769
770         v3f position = getPosition();
771
772         // Copy parent position if local player is attached
773         if (isAttached) {
774                 setPosition(overridePosition);
775                 m_sneak_node_exists = false;
776                 return;
777         }
778
779         // Skip collision detection if noclip mode is used
780         bool fly_allowed = m_client->checkLocalPrivilege("fly");
781         bool noclip = m_client->checkLocalPrivilege("noclip") &&
782                 g_settings->getBool("noclip");
783         bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
784         if (free_move) {
785                 position += m_speed * dtime;
786                 setPosition(position);
787                 m_sneak_node_exists = false;
788                 return;
789         }
790
791         /*
792                 Collision detection
793         */
794         bool is_valid_position;
795         MapNode node;
796         v3s16 pp;
797
798         /*
799                 Check if player is in liquid (the oscillating value)
800         */
801         if (in_liquid) {
802                 // If in liquid, the threshold of coming out is at higher y
803                 pp = floatToInt(position + v3f(0, BS * 0.1, 0), BS);
804                 node = map->getNodeNoEx(pp, &is_valid_position);
805                 if (is_valid_position) {
806                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
807                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
808                 } else {
809                         in_liquid = false;
810                 }
811         } else {
812                 // If not in liquid, the threshold of going in is at lower y
813                 pp = floatToInt(position + v3f(0, BS * 0.5, 0), BS);
814                 node = map->getNodeNoEx(pp, &is_valid_position);
815                 if (is_valid_position) {
816                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
817                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
818                 } else {
819                         in_liquid = false;
820                 }
821         }
822
823         /*
824                 Check if player is in liquid (the stable value)
825         */
826         pp = floatToInt(position + v3f(0, 0, 0), BS);
827         node = map->getNodeNoEx(pp, &is_valid_position);
828         if (is_valid_position)
829                 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
830         else
831                 in_liquid_stable = false;
832
833         /*
834                 Check if player is climbing
835         */
836         pp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS);
837         v3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS);
838         node = map->getNodeNoEx(pp, &is_valid_position);
839         bool is_valid_position2;
840         MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
841
842         if (!(is_valid_position && is_valid_position2))
843                 is_climbing = false;
844         else
845                 is_climbing = (nodemgr->get(node.getContent()).climbable ||
846                                 nodemgr->get(node2.getContent()).climbable) && !free_move;
847
848         /*
849                 Collision uncertainty radius
850                 Make it a bit larger than the maximum distance of movement
851         */
852         //f32 d = pos_max_d * 1.1;
853         // A fairly large value in here makes moving smoother
854         f32 d = 0.15 * BS;
855         // This should always apply, otherwise there are glitches
856         sanity_check(d > pos_max_d);
857         // Maximum distance over border for sneaking
858         f32 sneak_max = BS * 0.4;
859
860         /*
861                 If sneaking, keep in range from the last walked node and don't
862                 fall off from it
863         */
864         if (control.sneak && m_sneak_node_exists &&
865                         !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
866                         physics_override_sneak) {
867                 f32 maxd = 0.5 * BS + sneak_max;
868                 v3f lwn_f = intToFloat(m_sneak_node, BS);
869                 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
870                 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
871
872                 if (!is_climbing) {
873                         // Move up if necessary
874                         f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
875                         if (position.Y < new_y)
876                                 position.Y = new_y;
877                         /*
878                                 Collision seems broken, since player is sinking when
879                                 sneaking over the edges of current sneaking_node.
880                                 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
881                         */
882                         if (m_speed.Y < 0)
883                                 m_speed.Y = 0;
884                 }
885         }
886
887         // this shouldn't be hardcoded but transmitted from server
888         float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
889
890 #ifdef __ANDROID__
891         player_stepheight += (0.6 * BS);
892 #endif
893
894         v3f accel_f = v3f(0, 0, 0);
895
896         collisionMoveResult result = collisionMoveSimple(env, m_client,
897                 pos_max_d, m_collisionbox, player_stepheight, dtime,
898                 &position, &m_speed, accel_f);
899
900         /*
901                 If the player's feet touch the topside of any node, this is
902                 set to true.
903
904                 Player is allowed to jump when this is true.
905         */
906         bool touching_ground_was = touching_ground;
907         touching_ground = result.touching_ground;
908
909     //bool standing_on_unloaded = result.standing_on_unloaded;
910
911         /*
912                 Check the nodes under the player to see from which node the
913                 player is sneaking from, if any.  If the node from under
914                 the player has been removed, the player falls.
915         */
916         f32 position_y_mod = 0.05 * BS;
917         if (m_sneak_node_bb_ymax > 0)
918                 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
919         v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
920         if (m_sneak_node_exists &&
921                         nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
922                         m_old_node_below_type != "air") {
923                 // Old node appears to have been removed; that is,
924                 // it wasn't air before but now it is
925                 m_need_to_get_new_sneak_node = false;
926                 m_sneak_node_exists = false;
927         } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
928                 // We are on something, so make sure to recalculate the sneak
929                 // node.
930                 m_need_to_get_new_sneak_node = true;
931         }
932
933         if (m_need_to_get_new_sneak_node && physics_override_sneak) {
934                 m_sneak_node_bb_ymax = 0;
935                 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
936                 v2f player_p2df(position.X, position.Z);
937                 f32 min_distance_f = 100000.0 * BS;
938                 // If already seeking from some node, compare to it.
939                 v3s16 new_sneak_node = m_sneak_node;
940                 for (s16 x= -1; x <= 1; x++)
941                 for (s16 z= -1; z <= 1; z++) {
942                         v3s16 p = pos_i_bottom + v3s16(x, 0, z);
943                         v3f pf = intToFloat(p, BS);
944                         v2f node_p2df(pf.X, pf.Z);
945                         f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
946                         f32 max_axis_distance_f = MYMAX(
947                                         fabs(player_p2df.X - node_p2df.X),
948                                         fabs(player_p2df.Y - node_p2df.Y));
949
950                         if (distance_f > min_distance_f ||
951                                         max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)
952                                 continue;
953
954                         // The node to be sneaked on has to be walkable
955                         node = map->getNodeNoEx(p, &is_valid_position);
956                         if (!is_valid_position || !nodemgr->get(node).walkable)
957                                 continue;
958                         // And the node above it has to be nonwalkable
959                         node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
960                         if (!is_valid_position || nodemgr->get(node).walkable)
961                                 continue;
962                         // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
963                         if (!physics_override_sneak_glitch) {
964                                 node =map->getNodeNoEx(p + v3s16(0, 2, 0), &is_valid_position);
965                                 if (!is_valid_position || nodemgr->get(node).walkable)
966                                         continue;
967                         }
968
969                         min_distance_f = distance_f;
970                         new_sneak_node = p;
971                 }
972
973                 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
974
975                 m_sneak_node = new_sneak_node;
976                 m_sneak_node_exists = sneak_node_found;
977
978                 if (sneak_node_found) {
979                         f32 cb_max = 0;
980                         MapNode n = map->getNodeNoEx(m_sneak_node);
981                         std::vector<aabb3f> nodeboxes;
982                         n.getCollisionBoxes(nodemgr, &nodeboxes);
983                         for (const auto &box : nodeboxes) {
984                                 if (box.MaxEdge.Y > cb_max)
985                                         cb_max = box.MaxEdge.Y;
986                         }
987                         m_sneak_node_bb_ymax = cb_max;
988                 }
989
990                 /*
991                         If sneaking, the player's collision box can be in air, so
992                         this has to be set explicitly
993                 */
994                 if (sneak_node_found && control.sneak)
995                         touching_ground = true;
996         }
997
998         /*
999                 Set new position but keep sneak node set
1000         */
1001         bool sneak_node_exists = m_sneak_node_exists;
1002         setPosition(position);
1003         m_sneak_node_exists = sneak_node_exists;
1004
1005         /*
1006                 Report collisions
1007         */
1008         // Dont report if flying
1009         if (collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
1010                 for (const auto &info : result.collisions) {
1011                         collision_info->push_back(info);
1012                 }
1013         }
1014
1015         if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1016                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
1017                 m_client->event()->put(e);
1018                 // Set camera impact value to be used for view bobbing
1019                 camera_impact = getSpeed().Y * -1;
1020         }
1021
1022         {
1023                 camera_barely_in_ceiling = false;
1024                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1025                 MapNode n = map->getNodeNoEx(camera_np);
1026                 if (n.getContent() != CONTENT_IGNORE) {
1027                         if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1028                                 camera_barely_in_ceiling = true;
1029                 }
1030         }
1031
1032         /*
1033                 Update the node last under the player
1034         */
1035         m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);
1036         m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
1037
1038         /*
1039                 Check properties of the node on which the player is standing
1040         */
1041         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
1042         // Determine if jumping is possible
1043         m_can_jump = touching_ground && !in_liquid;
1044         if (itemgroup_get(f.groups, "disable_jump"))
1045                 m_can_jump = false;
1046         // Jump key pressed while jumping off from a bouncy block
1047         if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1048                         m_speed.Y >= -0.5 * BS) {
1049                 float jumpspeed = movement_speed_jump * physics_override_jump;
1050                 if (m_speed.Y > 1) {
1051                         // Reduce boost when speed already is high
1052                         m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
1053                 } else {
1054                         m_speed.Y += jumpspeed;
1055                 }
1056                 setSpeed(m_speed);
1057                 m_can_jump = false;
1058         }
1059 }
1060
1061 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1062 {
1063         // Slip on slippery nodes
1064         const NodeDefManager *nodemgr = env->getGameDef()->ndef();
1065         Map *map = &env->getMap();
1066         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(
1067                         getStandingNodePos()));
1068         int slippery = 0;
1069         if (f.walkable)
1070                 slippery = itemgroup_get(f.groups, "slippery");
1071
1072         if (slippery >= 1) {
1073                 if (speedH == v3f(0.0f)) {
1074                         slippery = slippery * 2;
1075                 }
1076                 return core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);
1077         }
1078         return 1.0f;
1079 }