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