3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
20 #include "localplayer.h"
23 #include "collision.h"
26 #include "environment.h"
34 LocalPlayer::LocalPlayer(Client *client, const char *name):
35 Player(name, client->idef()),
39 touching_ground(false),
41 in_liquid_stable(false),
44 swimming_vertical(false),
45 // Movement overrides are multipliers and must be 1 by default
46 physics_override_speed(1.0f),
47 physics_override_jump(1.0f),
48 physics_override_gravity(1.0f),
49 physics_override_sneak(true),
50 physics_override_sneak_glitch(false),
51 physics_override_new_move(true), // Temporary option for old move code
52 overridePosition(v3f(0,0,0)),
53 last_position(v3f(0,0,0)),
54 last_speed(v3f(0,0,0)),
61 makes_footstep_sound(true),
62 last_animation(NO_ANIM),
64 hotbar_selected_image(""),
65 light_color(255,255,255,255),
66 hurt_tilt_timer(0.0f),
67 hurt_tilt_strength(0.0f),
69 m_sneak_node(32767,32767,32767),
70 m_sneak_node_bb_ymax(0), // To support temporary option for old move code
71 m_sneak_node_bb_top(0,0,0,0,0,0),
72 m_sneak_node_exists(false),
73 m_need_to_get_new_sneak_node(true),
74 m_sneak_ladder_detected(false),
75 m_ledge_detected(false),
76 m_old_node_below(32767,32767,32767),
77 m_old_node_below_type("air"),
79 m_breath(PLAYER_MAX_BREATH),
82 camera_barely_in_ceiling(false),
83 m_collisionbox(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30),
87 // Initialize hp to 0, so that no hearts will be shown if server
88 // doesn't support health points
90 eye_offset_first = v3f(0,0,0);
91 eye_offset_third = v3f(0,0,0);
94 LocalPlayer::~LocalPlayer()
98 static aabb3f getTopBoundingBox(const std::vector<aabb3f> &nodeboxes)
101 b_max.reset(-BS, -BS, -BS);
102 for (std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
103 it != nodeboxes.end(); ++it) {
105 if (box.MaxEdge.Y > b_max.MaxEdge.Y)
107 else if (box.MaxEdge.Y == b_max.MaxEdge.Y)
108 b_max.addInternalBox(box);
110 return aabb3f(v3f(b_max.MinEdge.X, b_max.MaxEdge.Y, b_max.MinEdge.Z), b_max.MaxEdge);
113 #define GETNODE(map, p3, v2, y, valid) \
114 (map)->getNodeNoEx((p3) + v3s16((v2).X, y, (v2).Y), valid)
116 // pos is the node the player is standing inside(!)
117 static bool detectSneakLadder(Map *map, INodeDefManager *nodemgr, v3s16 pos)
119 // Detects a structure known as "sneak ladder" or "sneak elevator"
120 // that relies on bugs to provide a fast means of vertical transportation,
121 // the bugs have since been fixed but this function remains to keep it working.
122 // NOTE: This is just entirely a huge hack and causes way too many problems.
123 bool is_valid_position;
125 // X/Z vectors for 4 neighboring nodes
126 static const v2s16 vecs[] = { v2s16(-1, 0), v2s16(1, 0), v2s16(0, -1), v2s16(0, 1) };
128 for (u16 i = 0; i < ARRLEN(vecs); i++) {
129 const v2s16 vec = vecs[i];
131 // walkability of bottom & top node should differ
132 node = GETNODE(map, pos, vec, 0, &is_valid_position);
133 if (!is_valid_position)
135 bool w = nodemgr->get(node).walkable;
136 node = GETNODE(map, pos, vec, 1, &is_valid_position);
137 if (!is_valid_position || w == nodemgr->get(node).walkable)
140 // check one more node above OR below with corresponding walkability
141 node = GETNODE(map, pos, vec, -1, &is_valid_position);
142 bool ok = is_valid_position && w != nodemgr->get(node).walkable;
144 node = GETNODE(map, pos, vec, 2, &is_valid_position);
145 ok = is_valid_position && w == nodemgr->get(node).walkable;
155 static bool detectLedge(Map *map, INodeDefManager *nodemgr, v3s16 pos)
157 bool is_valid_position;
159 // X/Z vectors for 4 neighboring nodes
160 static const v2s16 vecs[] = {v2s16(-1, 0), v2s16(1, 0), v2s16(0, -1), v2s16(0, 1)};
162 for (u16 i = 0; i < ARRLEN(vecs); i++) {
163 const v2s16 vec = vecs[i];
165 node = GETNODE(map, pos, vec, 1, &is_valid_position);
166 if (is_valid_position && nodemgr->get(node).walkable) {
168 node = GETNODE(map, pos, vec, 2, &is_valid_position);
169 if (is_valid_position && !nodemgr->get(node).walkable)
170 // Space above ledge exists
180 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
181 std::vector<CollisionInfo> *collision_info)
183 // Temporary option for old move code
184 if (!physics_override_new_move) {
185 old_move(dtime, env, pos_max_d, collision_info);
189 Map *map = &env->getMap();
190 INodeDefManager *nodemgr = m_client->ndef();
192 v3f position = getPosition();
194 // Copy parent position if local player is attached
197 setPosition(overridePosition);
198 m_sneak_node_exists = false;
202 // Skip collision detection if noclip mode is used
203 bool fly_allowed = m_client->checkLocalPrivilege("fly");
204 bool noclip = m_client->checkLocalPrivilege("noclip") &&
205 g_settings->getBool("noclip");
206 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
208 position += m_speed * dtime;
209 setPosition(position);
210 m_sneak_node_exists = false;
218 bool is_valid_position;
223 Check if player is in liquid (the oscillating value)
226 // If in liquid, the threshold of coming out is at higher y
229 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
230 node = map->getNodeNoEx(pp, &is_valid_position);
231 if (is_valid_position) {
232 in_liquid = nodemgr->get(node.getContent()).isLiquid();
233 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
238 // If not in liquid, the threshold of going in is at lower y
241 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
242 node = map->getNodeNoEx(pp, &is_valid_position);
243 if (is_valid_position) {
244 in_liquid = nodemgr->get(node.getContent()).isLiquid();
245 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
253 Check if player is in liquid (the stable value)
255 pp = floatToInt(position + v3f(0,0,0), BS);
256 node = map->getNodeNoEx(pp, &is_valid_position);
257 if (is_valid_position) {
258 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
260 in_liquid_stable = false;
264 Check if player is climbing
268 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
269 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
270 node = map->getNodeNoEx(pp, &is_valid_position);
271 bool is_valid_position2;
272 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
274 if (!(is_valid_position && is_valid_position2)) {
277 is_climbing = (nodemgr->get(node.getContent()).climbable
278 || nodemgr->get(node2.getContent()).climbable) && !free_move;
283 Collision uncertainty radius
284 Make it a bit larger than the maximum distance of movement
286 //f32 d = pos_max_d * 1.1;
287 // A fairly large value in here makes moving smoother
290 // This should always apply, otherwise there are glitches
291 sanity_check(d > pos_max_d);
293 // Max. distance (X, Z) over border for sneaking determined by collision box
294 // * 0.49 to keep the center just barely on the node
295 v3f sneak_max = m_collisionbox.getExtent() * 0.49;
296 if (m_sneak_ladder_detected) {
297 // restore legacy behaviour (this makes the m_speed.Y hack necessary)
298 sneak_max = v3f(0.4 * BS, 0, 0.4 * BS);
302 If sneaking, keep in range from the last walked node and don't
305 if (control.sneak && m_sneak_node_exists &&
306 !(fly_allowed && g_settings->getBool("free_move")) &&
307 !in_liquid && !is_climbing &&
308 physics_override_sneak) {
309 const v3f sn_f = intToFloat(m_sneak_node, BS);
310 const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge;
311 const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge;
312 const v3f old_pos = position;
313 const v3f old_speed = m_speed;
315 position.X = rangelim(position.X,
316 bmin.X - sneak_max.X, bmax.X + sneak_max.X);
317 position.Z = rangelim(position.Z,
318 bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z);
320 if (position.X != old_pos.X)
322 if (position.Z != old_pos.Z)
325 // Because we keep the player collision box on the node, limiting
326 // position.Y is not necessary but useful to prevent players from
327 // being inside a node if sneaking on e.g. the lower part of a stair
328 if (!m_sneak_ladder_detected) {
329 position.Y = MYMAX(position.Y, bmax.Y);
331 // legacy behaviour that sometimes causes some weird slow sinking
332 m_speed.Y = MYMAX(m_speed.Y, 0);
335 if (collision_info != NULL &&
336 m_speed.Y - old_speed.Y > BS) {
337 // Collide with sneak node, report fall damage
338 CollisionInfo sn_info;
339 sn_info.node_p = m_sneak_node;
340 sn_info.old_speed = old_speed;
341 sn_info.new_speed = m_speed;
342 collision_info->push_back(sn_info);
346 // TODO: this shouldn't be hardcoded but transmitted from server
347 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
350 player_stepheight += (0.6 * BS);
353 v3f accel_f = v3f(0,0,0);
355 collisionMoveResult result = collisionMoveSimple(env, m_client,
356 pos_max_d, m_collisionbox, player_stepheight, dtime,
357 &position, &m_speed, accel_f);
360 If the player's feet touch the topside of any node, this is
363 Player is allowed to jump when this is true.
365 bool touching_ground_was = touching_ground;
366 touching_ground = result.touching_ground;
368 // We want the top of the sneak node to be below the players feet
370 if (m_sneak_node_exists)
371 position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - 0.05 * BS;
373 position_y_mod = (0.5 - 0.05) * BS;
374 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
376 Check the nodes under the player to see from which node the
377 player is sneaking from, if any. If the node from under
378 the player has been removed, the player falls.
380 if (m_sneak_node_exists &&
381 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
382 m_old_node_below_type != "air") {
383 // Old node appears to have been removed; that is,
384 // it wasn't air before but now it is
385 m_need_to_get_new_sneak_node = false;
386 m_sneak_node_exists = false;
387 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
388 // We are on something, so make sure to recalculate the sneak
390 m_need_to_get_new_sneak_node = true;
393 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
394 v2f player_p2df(position.X, position.Z);
395 f32 min_distance_f = 100000.0 * BS;
396 v3s16 new_sneak_node = m_sneak_node;
397 for(s16 x=-1; x<=1; x++)
398 for(s16 z=-1; z<=1; z++)
400 v3s16 p = current_node + v3s16(x,0,z);
401 v3f pf = intToFloat(p, BS);
402 v2f node_p2df(pf.X, pf.Z);
403 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
405 if (distance_f > min_distance_f ||
406 fabs(player_p2df.X-node_p2df.X) > (.5+.1)*BS + sneak_max.X ||
407 fabs(player_p2df.Y-node_p2df.Y) > (.5+.1)*BS + sneak_max.Z)
411 // The node to be sneaked on has to be walkable
412 node = map->getNodeNoEx(p, &is_valid_position);
413 if (!is_valid_position || !nodemgr->get(node).walkable)
415 // And the node(s) above have to be nonwalkable
417 if (!physics_override_sneak_glitch) {
419 (m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
421 for (u16 y = 1; y <= height; y++) {
422 node = map->getNodeNoEx(p + v3s16(0,y,0), &is_valid_position);
423 if (!is_valid_position || nodemgr->get(node).walkable) {
429 // legacy behaviour: check just one node
430 node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
431 ok = is_valid_position && !nodemgr->get(node).walkable;
436 min_distance_f = distance_f;
440 bool sneak_node_found = (min_distance_f < 100000.0 * BS);
441 m_sneak_node = new_sneak_node;
442 m_sneak_node_exists = sneak_node_found;
444 if (sneak_node_found) {
445 // Update saved top bounding box of sneak node
446 MapNode n = map->getNodeNoEx(m_sneak_node);
447 std::vector<aabb3f> nodeboxes;
448 n.getCollisionBoxes(nodemgr, &nodeboxes);
449 m_sneak_node_bb_top = getTopBoundingBox(nodeboxes);
451 m_sneak_ladder_detected = physics_override_sneak_glitch &&
452 detectSneakLadder(map, nodemgr, floatToInt(position, BS));
454 m_sneak_ladder_detected = false;
459 If 'sneak glitch' enabled detect ledge for old sneak-jump
460 behaviour of climbing onto a ledge 2 nodes up.
462 if (physics_override_sneak_glitch && control.sneak && control.jump)
463 m_ledge_detected = detectLedge(map, nodemgr, floatToInt(position, BS));
466 Set new position but keep sneak node set
468 bool sneak_node_exists = m_sneak_node_exists;
469 setPosition(position);
470 m_sneak_node_exists = sneak_node_exists;
476 // Dont report if flying
477 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
478 for(size_t i=0; i<result.collisions.size(); i++) {
479 const CollisionInfo &info = result.collisions[i];
480 collision_info->push_back(info);
484 if(!result.standing_on_object && !touching_ground_was && touching_ground) {
485 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
486 m_client->event()->put(e);
488 // Set camera impact value to be used for view bobbing
489 camera_impact = getSpeed().Y * -1;
493 camera_barely_in_ceiling = false;
494 v3s16 camera_np = floatToInt(getEyePosition(), BS);
495 MapNode n = map->getNodeNoEx(camera_np);
496 if(n.getContent() != CONTENT_IGNORE){
497 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
498 camera_barely_in_ceiling = true;
504 Update the node last under the player
506 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
507 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
510 Check properties of the node on which the player is standing
512 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
513 // Determine if jumping is possible
514 m_can_jump = touching_ground && !in_liquid;
515 if (itemgroup_get(f.groups, "disable_jump"))
517 else if (control.sneak && m_sneak_ladder_detected && !in_liquid && !is_climbing)
520 // Jump key pressed while jumping off from a bouncy block
521 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
522 m_speed.Y >= -0.5 * BS) {
523 float jumpspeed = movement_speed_jump * physics_override_jump;
525 // Reduce boost when speed already is high
526 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
528 m_speed.Y += jumpspeed;
535 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
537 move(dtime, env, pos_max_d, NULL);
540 void LocalPlayer::applyControl(float dtime)
543 swimming_vertical = false;
545 setPitch(control.pitch);
548 // Nullify speed and don't run positioning code if the player is attached
551 setSpeed(v3f(0,0,0));
555 v3f move_direction = v3f(0,0,1);
556 move_direction.rotateXZBy(getYaw());
558 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
559 v3f speedV = v3f(0,0,0); // Vertical (Y)
561 bool fly_allowed = m_client->checkLocalPrivilege("fly");
562 bool fast_allowed = m_client->checkLocalPrivilege("fast");
564 bool free_move = fly_allowed && g_settings->getBool("free_move");
565 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
566 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
567 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
568 bool continuous_forward = g_settings->getBool("continuous_forward");
569 bool always_fly_fast = g_settings->getBool("always_fly_fast");
571 // Whether superspeed mode is used or not
572 bool superspeed = false;
574 if (always_fly_fast && free_move && fast_move)
577 // Old descend control
578 if(g_settings->getBool("aux1_descends"))
580 // If free movement and fast movement, always move fast
581 if(free_move && fast_move)
584 // Auxiliary button 1 (E)
589 // In free movement mode, aux1 descends
591 speedV.Y = -movement_speed_fast;
593 speedV.Y = -movement_speed_walk;
595 else if(in_liquid || in_liquid_stable)
597 speedV.Y = -movement_speed_walk;
598 swimming_vertical = true;
602 speedV.Y = -movement_speed_climb;
606 // If not free movement but fast is allowed, aux1 is
613 // New minecraft-like descend control
616 // Auxiliary button 1 (E)
621 // aux1 is "Turbo button"
631 // In free movement mode, sneak descends
632 if (fast_move && (control.aux1 || always_fly_fast))
633 speedV.Y = -movement_speed_fast;
635 speedV.Y = -movement_speed_walk;
637 else if(in_liquid || in_liquid_stable)
640 speedV.Y = -movement_speed_fast;
642 speedV.Y = -movement_speed_walk;
643 swimming_vertical = true;
648 speedV.Y = -movement_speed_fast;
650 speedV.Y = -movement_speed_climb;
655 if (continuous_forward)
656 speedH += move_direction;
659 if (continuous_forward) {
663 speedH += move_direction;
667 speedH -= move_direction;
669 if (!control.up && !control.down) {
670 speedH -= move_direction *
671 (control.forw_move_joystick_axis / 32767.f);
674 speedH += move_direction.crossProduct(v3f(0,1,0));
677 speedH += move_direction.crossProduct(v3f(0,-1,0));
679 if (!control.left && !control.right) {
680 speedH -= move_direction.crossProduct(v3f(0,1,0)) *
681 (control.sidew_move_joystick_axis / 32767.f);
686 if (g_settings->getBool("aux1_descends") || always_fly_fast) {
688 speedV.Y = movement_speed_fast;
690 speedV.Y = movement_speed_walk;
692 if(fast_move && control.aux1)
693 speedV.Y = movement_speed_fast;
695 speedV.Y = movement_speed_walk;
701 NOTE: The d value in move() affects jump height by
702 raising the height at which the jump speed is kept
703 at its starting value
705 v3f speedJ = getSpeed();
706 if(speedJ.Y >= -0.5 * BS) {
707 if (m_ledge_detected) {
708 // Limit jump speed to a minimum that allows
709 // jumping up onto a ledge 2 nodes up.
710 speedJ.Y = movement_speed_jump *
711 MYMAX(physics_override_jump, 1.3f);
713 m_ledge_detected = false;
715 speedJ.Y = movement_speed_jump * physics_override_jump;
719 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
720 m_client->event()->put(e);
726 speedV.Y = movement_speed_fast;
728 speedV.Y = movement_speed_walk;
729 swimming_vertical = true;
734 speedV.Y = movement_speed_fast;
736 speedV.Y = movement_speed_climb;
740 // The speed of the player (Y is ignored)
741 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
742 speedH = speedH.normalize() * movement_speed_fast;
743 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
744 speedH = speedH.normalize() * movement_speed_crouch;
746 speedH = speedH.normalize() * movement_speed_walk;
748 // Acceleration increase
749 f32 incH = 0; // Horizontal (X, Z)
750 f32 incV = 0; // Vertical (Y)
751 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
753 // Jumping and falling
754 if(superspeed || (fast_move && control.aux1))
755 incH = movement_acceleration_fast * BS * dtime;
757 incH = movement_acceleration_air * BS * dtime;
758 incV = 0; // No vertical acceleration in air
760 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
761 incH = incV = movement_acceleration_fast * BS * dtime;
763 incH = incV = movement_acceleration_default * BS * dtime;
765 // Accelerate to target speed with maximum increment
766 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
767 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
770 v3s16 LocalPlayer::getStandingNodePos()
772 if(m_sneak_node_exists)
774 return floatToInt(getPosition() - v3f(0, BS, 0), BS);
777 v3s16 LocalPlayer::getFootstepNodePos()
780 // BS * 0.05 below the player's feet ensures a 1/16th height
781 // nodebox is detected instead of the node below it.
782 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
783 // A larger distance below is necessary for a footstep sound
784 // when landing after a jump or fall. BS * 0.5 ensures water
785 // sounds when swimming in 1 node deep water.
786 return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
789 v3s16 LocalPlayer::getLightPosition() const
791 return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
794 v3f LocalPlayer::getEyeOffset() const
796 float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f;
797 return v3f(0, BS * eye_height, 0);
800 // Horizontal acceleration (X and Z), Y direction is ignored
801 void LocalPlayer::accelerateHorizontal(const v3f &target_speed, const f32 max_increase)
803 if (max_increase == 0)
806 v3f d_wanted = target_speed - m_speed;
808 f32 dl = d_wanted.getLength();
809 if (dl > max_increase)
812 v3f d = d_wanted.normalize() * dl;
818 // Vertical acceleration (Y), X and Z directions are ignored
819 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
821 if (max_increase == 0)
824 f32 d_wanted = target_speed.Y - m_speed.Y;
825 if (d_wanted > max_increase)
826 d_wanted = max_increase;
827 else if (d_wanted < -max_increase)
828 d_wanted = -max_increase;
830 m_speed.Y += d_wanted;
833 // Temporary option for old move code
834 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
835 std::vector<CollisionInfo> *collision_info)
837 Map *map = &env->getMap();
838 INodeDefManager *nodemgr = m_client->ndef();
840 v3f position = getPosition();
842 // Copy parent position if local player is attached
844 setPosition(overridePosition);
845 m_sneak_node_exists = false;
849 // Skip collision detection if noclip mode is used
850 bool fly_allowed = m_client->checkLocalPrivilege("fly");
851 bool noclip = m_client->checkLocalPrivilege("noclip") &&
852 g_settings->getBool("noclip");
853 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
855 position += m_speed * dtime;
856 setPosition(position);
857 m_sneak_node_exists = false;
864 bool is_valid_position;
869 Check if player is in liquid (the oscillating value)
872 // If in liquid, the threshold of coming out is at higher y
873 pp = floatToInt(position + v3f(0, BS * 0.1, 0), BS);
874 node = map->getNodeNoEx(pp, &is_valid_position);
875 if (is_valid_position) {
876 in_liquid = nodemgr->get(node.getContent()).isLiquid();
877 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
882 // If not in liquid, the threshold of going in is at lower y
883 pp = floatToInt(position + v3f(0, BS * 0.5, 0), BS);
884 node = map->getNodeNoEx(pp, &is_valid_position);
885 if (is_valid_position) {
886 in_liquid = nodemgr->get(node.getContent()).isLiquid();
887 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
894 Check if player is in liquid (the stable value)
896 pp = floatToInt(position + v3f(0, 0, 0), BS);
897 node = map->getNodeNoEx(pp, &is_valid_position);
898 if (is_valid_position)
899 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
901 in_liquid_stable = false;
904 Check if player is climbing
906 pp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS);
907 v3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS);
908 node = map->getNodeNoEx(pp, &is_valid_position);
909 bool is_valid_position2;
910 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
912 if (!(is_valid_position && is_valid_position2))
915 is_climbing = (nodemgr->get(node.getContent()).climbable ||
916 nodemgr->get(node2.getContent()).climbable) && !free_move;
919 Collision uncertainty radius
920 Make it a bit larger than the maximum distance of movement
922 //f32 d = pos_max_d * 1.1;
923 // A fairly large value in here makes moving smoother
925 // This should always apply, otherwise there are glitches
926 sanity_check(d > pos_max_d);
927 // Maximum distance over border for sneaking
928 f32 sneak_max = BS * 0.4;
931 If sneaking, keep in range from the last walked node and don't
934 if (control.sneak && m_sneak_node_exists &&
935 !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
936 physics_override_sneak) {
937 f32 maxd = 0.5 * BS + sneak_max;
938 v3f lwn_f = intToFloat(m_sneak_node, BS);
939 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
940 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
943 // Move up if necessary
944 f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
945 if (position.Y < new_y)
948 Collision seems broken, since player is sinking when
949 sneaking over the edges of current sneaking_node.
950 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
957 // this shouldn't be hardcoded but transmitted from server
958 float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
961 player_stepheight += (0.6 * BS);
964 v3f accel_f = v3f(0, 0, 0);
966 collisionMoveResult result = collisionMoveSimple(env, m_client,
967 pos_max_d, m_collisionbox, player_stepheight, dtime,
968 &position, &m_speed, accel_f);
971 If the player's feet touch the topside of any node, this is
974 Player is allowed to jump when this is true.
976 bool touching_ground_was = touching_ground;
977 touching_ground = result.touching_ground;
979 //bool standing_on_unloaded = result.standing_on_unloaded;
982 Check the nodes under the player to see from which node the
983 player is sneaking from, if any. If the node from under
984 the player has been removed, the player falls.
986 f32 position_y_mod = 0.05 * BS;
987 if (m_sneak_node_bb_ymax > 0)
988 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
989 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
990 if (m_sneak_node_exists &&
991 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
992 m_old_node_below_type != "air") {
993 // Old node appears to have been removed; that is,
994 // it wasn't air before but now it is
995 m_need_to_get_new_sneak_node = false;
996 m_sneak_node_exists = false;
997 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
998 // We are on something, so make sure to recalculate the sneak
1000 m_need_to_get_new_sneak_node = true;
1003 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
1004 m_sneak_node_bb_ymax = 0;
1005 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
1006 v2f player_p2df(position.X, position.Z);
1007 f32 min_distance_f = 100000.0 * BS;
1008 // If already seeking from some node, compare to it.
1009 v3s16 new_sneak_node = m_sneak_node;
1010 for (s16 x= -1; x <= 1; x++)
1011 for (s16 z= -1; z <= 1; z++) {
1012 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
1013 v3f pf = intToFloat(p, BS);
1014 v2f node_p2df(pf.X, pf.Z);
1015 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
1016 f32 max_axis_distance_f = MYMAX(
1017 fabs(player_p2df.X - node_p2df.X),
1018 fabs(player_p2df.Y - node_p2df.Y));
1020 if (distance_f > min_distance_f ||
1021 max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)
1024 // The node to be sneaked on has to be walkable
1025 node = map->getNodeNoEx(p, &is_valid_position);
1026 if (!is_valid_position || nodemgr->get(node).walkable == false)
1028 // And the node above it has to be nonwalkable
1029 node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
1030 if (!is_valid_position || nodemgr->get(node).walkable)
1032 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
1033 if (!physics_override_sneak_glitch) {
1034 node =map->getNodeNoEx(p + v3s16(0, 2, 0), &is_valid_position);
1035 if (!is_valid_position || nodemgr->get(node).walkable)
1039 min_distance_f = distance_f;
1043 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
1045 m_sneak_node = new_sneak_node;
1046 m_sneak_node_exists = sneak_node_found;
1048 if (sneak_node_found) {
1050 MapNode n = map->getNodeNoEx(m_sneak_node);
1051 std::vector<aabb3f> nodeboxes;
1052 n.getCollisionBoxes(nodemgr, &nodeboxes);
1053 for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
1054 it != nodeboxes.end(); ++it) {
1056 if (box.MaxEdge.Y > cb_max)
1057 cb_max = box.MaxEdge.Y;
1059 m_sneak_node_bb_ymax = cb_max;
1063 If sneaking, the player's collision box can be in air, so
1064 this has to be set explicitly
1066 if (sneak_node_found && control.sneak)
1067 touching_ground = true;
1071 Set new position but keep sneak node set
1073 bool sneak_node_exists = m_sneak_node_exists;
1074 setPosition(position);
1075 m_sneak_node_exists = sneak_node_exists;
1080 // Dont report if flying
1081 if (collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
1082 for (size_t i = 0; i < result.collisions.size(); i++) {
1083 const CollisionInfo &info = result.collisions[i];
1084 collision_info->push_back(info);
1088 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1089 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
1090 m_client->event()->put(e);
1091 // Set camera impact value to be used for view bobbing
1092 camera_impact = getSpeed().Y * -1;
1096 camera_barely_in_ceiling = false;
1097 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1098 MapNode n = map->getNodeNoEx(camera_np);
1099 if (n.getContent() != CONTENT_IGNORE) {
1100 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1101 camera_barely_in_ceiling = true;
1106 Update the node last under the player
1108 m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);
1109 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
1112 Check properties of the node on which the player is standing
1114 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
1115 // Determine if jumping is possible
1116 m_can_jump = touching_ground && !in_liquid;
1117 if (itemgroup_get(f.groups, "disable_jump"))
1119 // Jump key pressed while jumping off from a bouncy block
1120 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1121 m_speed.Y >= -0.5 * BS) {
1122 float jumpspeed = movement_speed_jump * physics_override_jump;
1123 if (m_speed.Y > 1) {
1124 // Reduce boost when speed already is high
1125 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
1127 m_speed.Y += jumpspeed;