Step height: Add as a player object property
[oweals/minetest.git] / src / localplayer.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "localplayer.h"
21
22 #include "event.h"
23 #include "collision.h"
24 #include "nodedef.h"
25 #include "settings.h"
26 #include "environment.h"
27 #include "map.h"
28 #include "client.h"
29 #include "content_cao.h"
30
31 /*
32         LocalPlayer
33 */
34
35 LocalPlayer::LocalPlayer(Client *client, const char *name):
36         Player(name, client->idef()),
37         m_client(client)
38 {
39 }
40
41 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)
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         // Accelerate to target speed with maximum increment
664         accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
665         accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
666 }
667
668 v3s16 LocalPlayer::getStandingNodePos()
669 {
670         if(m_sneak_node_exists)
671                 return m_sneak_node;
672         return floatToInt(getPosition() - v3f(0, BS, 0), BS);
673 }
674
675 v3s16 LocalPlayer::getFootstepNodePos()
676 {
677         if (in_liquid_stable)
678                 // Emit swimming sound if the player is in liquid
679                 return floatToInt(getPosition(), BS);
680         if (touching_ground)
681                 // BS * 0.05 below the player's feet ensures a 1/16th height
682                 // nodebox is detected instead of the node below it.
683                 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
684         // A larger distance below is necessary for a footstep sound
685         // when landing after a jump or fall. BS * 0.5 ensures water
686         // sounds when swimming in 1 node deep water.
687         return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
688 }
689
690 v3s16 LocalPlayer::getLightPosition() const
691 {
692         return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
693 }
694
695 v3f LocalPlayer::getEyeOffset() const
696 {
697         float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f;
698         return v3f(0, BS * eye_height, 0);
699 }
700
701 // Horizontal acceleration (X and Z), Y direction is ignored
702 void LocalPlayer::accelerateHorizontal(const v3f &target_speed, const f32 max_increase)
703 {
704         if (max_increase == 0)
705                 return;
706
707         v3f d_wanted = target_speed - m_speed;
708         d_wanted.Y = 0;
709         f32 dl = d_wanted.getLength();
710         if (dl > max_increase)
711                 dl = max_increase;
712
713         v3f d = d_wanted.normalize() * dl;
714
715         m_speed.X += d.X;
716         m_speed.Z += d.Z;
717 }
718
719 // Vertical acceleration (Y), X and Z directions are ignored
720 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
721 {
722         if (max_increase == 0)
723                 return;
724
725         f32 d_wanted = target_speed.Y - m_speed.Y;
726         if (d_wanted > max_increase)
727                 d_wanted = max_increase;
728         else if (d_wanted < -max_increase)
729                 d_wanted = -max_increase;
730
731         m_speed.Y += d_wanted;
732 }
733
734 // Temporary option for old move code
735 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
736                 std::vector<CollisionInfo> *collision_info)
737 {
738         Map *map = &env->getMap();
739         INodeDefManager *nodemgr = m_client->ndef();
740
741         v3f position = getPosition();
742
743         // Copy parent position if local player is attached
744         if (isAttached) {
745                 setPosition(overridePosition);
746                 m_sneak_node_exists = false;
747                 return;
748         }
749
750         // Skip collision detection if noclip mode is used
751         bool fly_allowed = m_client->checkLocalPrivilege("fly");
752         bool noclip = m_client->checkLocalPrivilege("noclip") &&
753                 g_settings->getBool("noclip");
754         bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
755         if (free_move) {
756                 position += m_speed * dtime;
757                 setPosition(position);
758                 m_sneak_node_exists = false;
759                 return;
760         }
761
762         /*
763                 Collision detection
764         */
765         bool is_valid_position;
766         MapNode node;
767         v3s16 pp;
768
769         /*
770                 Check if player is in liquid (the oscillating value)
771         */
772         if (in_liquid) {
773                 // If in liquid, the threshold of coming out is at higher y
774                 pp = floatToInt(position + v3f(0, BS * 0.1, 0), BS);
775                 node = map->getNodeNoEx(pp, &is_valid_position);
776                 if (is_valid_position) {
777                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
778                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
779                 } else {
780                         in_liquid = false;
781                 }
782         } else {
783                 // If not in liquid, the threshold of going in is at lower y
784                 pp = floatToInt(position + v3f(0, BS * 0.5, 0), BS);
785                 node = map->getNodeNoEx(pp, &is_valid_position);
786                 if (is_valid_position) {
787                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
788                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
789                 } else {
790                         in_liquid = false;
791                 }
792         }
793
794         /*
795                 Check if player is in liquid (the stable value)
796         */
797         pp = floatToInt(position + v3f(0, 0, 0), BS);
798         node = map->getNodeNoEx(pp, &is_valid_position);
799         if (is_valid_position)
800                 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
801         else
802                 in_liquid_stable = false;
803
804         /*
805                 Check if player is climbing
806         */
807         pp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS);
808         v3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS);
809         node = map->getNodeNoEx(pp, &is_valid_position);
810         bool is_valid_position2;
811         MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
812
813         if (!(is_valid_position && is_valid_position2))
814                 is_climbing = false;
815         else
816                 is_climbing = (nodemgr->get(node.getContent()).climbable ||
817                                 nodemgr->get(node2.getContent()).climbable) && !free_move;
818
819         /*
820                 Collision uncertainty radius
821                 Make it a bit larger than the maximum distance of movement
822         */
823         //f32 d = pos_max_d * 1.1;
824         // A fairly large value in here makes moving smoother
825         f32 d = 0.15 * BS;
826         // This should always apply, otherwise there are glitches
827         sanity_check(d > pos_max_d);
828         // Maximum distance over border for sneaking
829         f32 sneak_max = BS * 0.4;
830
831         /*
832                 If sneaking, keep in range from the last walked node and don't
833                 fall off from it
834         */
835         if (control.sneak && m_sneak_node_exists &&
836                         !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
837                         physics_override_sneak) {
838                 f32 maxd = 0.5 * BS + sneak_max;
839                 v3f lwn_f = intToFloat(m_sneak_node, BS);
840                 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
841                 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
842
843                 if (!is_climbing) {
844                         // Move up if necessary
845                         f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
846                         if (position.Y < new_y)
847                                 position.Y = new_y;
848                         /*
849                                 Collision seems broken, since player is sinking when
850                                 sneaking over the edges of current sneaking_node.
851                                 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
852                         */
853                         if (m_speed.Y < 0)
854                                 m_speed.Y = 0;
855                 }
856         }
857
858         // this shouldn't be hardcoded but transmitted from server
859         float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
860
861 #ifdef __ANDROID__
862         player_stepheight += (0.6 * BS);
863 #endif
864
865         v3f accel_f = v3f(0, 0, 0);
866
867         collisionMoveResult result = collisionMoveSimple(env, m_client,
868                 pos_max_d, m_collisionbox, player_stepheight, dtime,
869                 &position, &m_speed, accel_f);
870
871         /*
872                 If the player's feet touch the topside of any node, this is
873                 set to true.
874
875                 Player is allowed to jump when this is true.
876         */
877         bool touching_ground_was = touching_ground;
878         touching_ground = result.touching_ground;
879
880     //bool standing_on_unloaded = result.standing_on_unloaded;
881
882         /*
883                 Check the nodes under the player to see from which node the
884                 player is sneaking from, if any.  If the node from under
885                 the player has been removed, the player falls.
886         */
887         f32 position_y_mod = 0.05 * BS;
888         if (m_sneak_node_bb_ymax > 0)
889                 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
890         v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
891         if (m_sneak_node_exists &&
892                         nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
893                         m_old_node_below_type != "air") {
894                 // Old node appears to have been removed; that is,
895                 // it wasn't air before but now it is
896                 m_need_to_get_new_sneak_node = false;
897                 m_sneak_node_exists = false;
898         } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
899                 // We are on something, so make sure to recalculate the sneak
900                 // node.
901                 m_need_to_get_new_sneak_node = true;
902         }
903
904         if (m_need_to_get_new_sneak_node && physics_override_sneak) {
905                 m_sneak_node_bb_ymax = 0;
906                 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
907                 v2f player_p2df(position.X, position.Z);
908                 f32 min_distance_f = 100000.0 * BS;
909                 // If already seeking from some node, compare to it.
910                 v3s16 new_sneak_node = m_sneak_node;
911                 for (s16 x= -1; x <= 1; x++)
912                 for (s16 z= -1; z <= 1; z++) {
913                         v3s16 p = pos_i_bottom + v3s16(x, 0, z);
914                         v3f pf = intToFloat(p, BS);
915                         v2f node_p2df(pf.X, pf.Z);
916                         f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
917                         f32 max_axis_distance_f = MYMAX(
918                                         fabs(player_p2df.X - node_p2df.X),
919                                         fabs(player_p2df.Y - node_p2df.Y));
920
921                         if (distance_f > min_distance_f ||
922                                         max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)
923                                 continue;
924
925                         // The node to be sneaked on has to be walkable
926                         node = map->getNodeNoEx(p, &is_valid_position);
927                         if (!is_valid_position || nodemgr->get(node).walkable == false)
928                                 continue;
929                         // And the node above it has to be nonwalkable
930                         node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
931                         if (!is_valid_position || nodemgr->get(node).walkable)
932                                 continue;
933                         // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
934                         if (!physics_override_sneak_glitch) {
935                                 node =map->getNodeNoEx(p + v3s16(0, 2, 0), &is_valid_position);
936                                 if (!is_valid_position || nodemgr->get(node).walkable)
937                                         continue;
938                         }
939
940                         min_distance_f = distance_f;
941                         new_sneak_node = p;
942                 }
943
944                 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
945
946                 m_sneak_node = new_sneak_node;
947                 m_sneak_node_exists = sneak_node_found;
948
949                 if (sneak_node_found) {
950                         f32 cb_max = 0;
951                         MapNode n = map->getNodeNoEx(m_sneak_node);
952                         std::vector<aabb3f> nodeboxes;
953                         n.getCollisionBoxes(nodemgr, &nodeboxes);
954                         for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
955                                         it != nodeboxes.end(); ++it) {
956                                 aabb3f box = *it;
957                                 if (box.MaxEdge.Y > cb_max)
958                                         cb_max = box.MaxEdge.Y;
959                         }
960                         m_sneak_node_bb_ymax = cb_max;
961                 }
962
963                 /*
964                         If sneaking, the player's collision box can be in air, so
965                         this has to be set explicitly
966                 */
967                 if (sneak_node_found && control.sneak)
968                         touching_ground = true;
969         }
970
971         /*
972                 Set new position but keep sneak node set
973         */
974         bool sneak_node_exists = m_sneak_node_exists;
975         setPosition(position);
976         m_sneak_node_exists = sneak_node_exists;
977
978         /*
979                 Report collisions
980         */
981         // Dont report if flying
982         if (collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
983                 for (size_t i = 0; i < result.collisions.size(); i++) {
984                         const CollisionInfo &info = result.collisions[i];
985                         collision_info->push_back(info);
986                 }
987         }
988
989         if (!result.standing_on_object && !touching_ground_was && touching_ground) {
990                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
991                 m_client->event()->put(e);
992                 // Set camera impact value to be used for view bobbing
993                 camera_impact = getSpeed().Y * -1;
994         }
995
996         {
997                 camera_barely_in_ceiling = false;
998                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
999                 MapNode n = map->getNodeNoEx(camera_np);
1000                 if (n.getContent() != CONTENT_IGNORE) {
1001                         if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1002                                 camera_barely_in_ceiling = true;
1003                 }
1004         }
1005
1006         /*
1007                 Update the node last under the player
1008         */
1009         m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);
1010         m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
1011
1012         /*
1013                 Check properties of the node on which the player is standing
1014         */
1015         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
1016         // Determine if jumping is possible
1017         m_can_jump = touching_ground && !in_liquid;
1018         if (itemgroup_get(f.groups, "disable_jump"))
1019                 m_can_jump = false;
1020         // Jump key pressed while jumping off from a bouncy block
1021         if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1022                         m_speed.Y >= -0.5 * BS) {
1023                 float jumpspeed = movement_speed_jump * physics_override_jump;
1024                 if (m_speed.Y > 1) {
1025                         // Reduce boost when speed already is high
1026                         m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
1027                 } else {
1028                         m_speed.Y += jumpspeed;
1029                 }
1030                 setSpeed(m_speed);
1031                 m_can_jump = false;
1032         }
1033 }