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"
29 #include "content_cao.h"
35 LocalPlayer::LocalPlayer(Client *client, const char *name):
36 Player(name, client->idef()),
41 static aabb3f getNodeBoundingBox(const std::vector<aabb3f> &nodeboxes)
43 if (nodeboxes.empty())
44 return aabb3f(0, 0, 0, 0, 0, 0);
48 std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
49 b_max = aabb3f(it->MinEdge, it->MaxEdge);
52 for (; it != nodeboxes.end(); ++it)
53 b_max.addInternalBox(*it);
58 bool LocalPlayer::updateSneakNode(Map *map, const v3f &position,
61 static const v3s16 dir9_center[9] = {
73 INodeDefManager *nodemgr = m_client->ndef();
75 bool is_valid_position;
76 bool new_sneak_node_exists = m_sneak_node_exists;
78 // We want the top of the sneak node to be below the players feet
79 f32 position_y_mod = 0.05 * BS;
80 if (m_sneak_node_exists)
81 position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - position_y_mod;
83 // Get position of current standing node
84 const v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
86 if (current_node != m_sneak_node) {
87 new_sneak_node_exists = false;
89 node = map->getNodeNoEx(current_node, &is_valid_position);
90 if (!is_valid_position || !nodemgr->get(node).walkable)
91 new_sneak_node_exists = false;
94 // Keep old sneak node
95 if (new_sneak_node_exists)
99 m_sneak_ladder_detected = false;
100 f32 min_distance_f = 100000.0 * BS;
102 for (const auto &d : dir9_center) {
103 const v3s16 p = current_node + d;
104 const v3f pf = intToFloat(p, BS);
105 const v2f diff(position.X - pf.X, position.Z - pf.Z);
106 f32 distance_f = diff.getLength();
108 if (distance_f > min_distance_f ||
109 fabs(diff.X) > (.5 + .1) * BS + sneak_max.X ||
110 fabs(diff.Y) > (.5 + .1) * BS + sneak_max.Z)
114 // The node to be sneaked on has to be walkable
115 node = map->getNodeNoEx(p, &is_valid_position);
116 if (!is_valid_position || !nodemgr->get(node).walkable)
118 // And the node(s) above have to be nonwalkable
120 if (!physics_override_sneak_glitch) {
122 (m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
124 for (u16 y = 1; y <= height; y++) {
125 node = map->getNodeNoEx(p + v3s16(0, y, 0), &is_valid_position);
126 if (!is_valid_position || nodemgr->get(node).walkable) {
132 // legacy behaviour: check just one node
133 node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
134 ok = is_valid_position && !nodemgr->get(node).walkable;
139 min_distance_f = distance_f;
141 new_sneak_node_exists = true;
144 if (!new_sneak_node_exists)
147 // Update saved top bounding box of sneak node
148 node = map->getNodeNoEx(m_sneak_node);
149 std::vector<aabb3f> nodeboxes;
150 node.getCollisionBoxes(nodemgr, &nodeboxes);
151 m_sneak_node_bb_top = getNodeBoundingBox(nodeboxes);
153 if (physics_override_sneak_glitch) {
154 // Detect sneak ladder:
155 // Node two meters above sneak node must be solid
156 node = map->getNodeNoEx(m_sneak_node + v3s16(0, 2, 0),
158 if (is_valid_position && nodemgr->get(node).walkable) {
159 // Node three meters above: must be non-solid
160 node = map->getNodeNoEx(m_sneak_node + v3s16(0, 3, 0),
162 m_sneak_ladder_detected = is_valid_position &&
163 !nodemgr->get(node).walkable;
169 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
170 std::vector<CollisionInfo> *collision_info)
172 // Temporary option for old move code
173 if (!physics_override_new_move) {
174 old_move(dtime, env, pos_max_d, collision_info);
178 Map *map = &env->getMap();
179 INodeDefManager *nodemgr = m_client->ndef();
181 v3f position = getPosition();
183 // Copy parent position if local player is attached
185 setPosition(overridePosition);
189 // Skip collision detection if noclip mode is used
190 bool fly_allowed = m_client->checkLocalPrivilege("fly");
191 bool noclip = m_client->checkLocalPrivilege("noclip") &&
192 g_settings->getBool("noclip");
193 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
195 position += m_speed * dtime;
196 setPosition(position);
204 bool is_valid_position;
209 Check if player is in liquid (the oscillating value)
212 // If in liquid, the threshold of coming out is at higher y
215 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
216 node = map->getNodeNoEx(pp, &is_valid_position);
217 if (is_valid_position) {
218 in_liquid = nodemgr->get(node.getContent()).isLiquid();
219 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
224 // If not in liquid, the threshold of going in is at lower y
227 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
228 node = map->getNodeNoEx(pp, &is_valid_position);
229 if (is_valid_position) {
230 in_liquid = nodemgr->get(node.getContent()).isLiquid();
231 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
239 Check if player is in liquid (the stable value)
241 pp = floatToInt(position + v3f(0,0,0), BS);
242 node = map->getNodeNoEx(pp, &is_valid_position);
243 if (is_valid_position) {
244 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
246 in_liquid_stable = false;
250 Check if player is climbing
254 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
255 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
256 node = map->getNodeNoEx(pp, &is_valid_position);
257 bool is_valid_position2;
258 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
260 if (!(is_valid_position && is_valid_position2)) {
263 is_climbing = (nodemgr->get(node.getContent()).climbable
264 || nodemgr->get(node2.getContent()).climbable) && !free_move;
268 Collision uncertainty radius
269 Make it a bit larger than the maximum distance of movement
271 //f32 d = pos_max_d * 1.1;
272 // A fairly large value in here makes moving smoother
275 // This should always apply, otherwise there are glitches
276 sanity_check(d > pos_max_d);
278 // Player object property step height is multiplied by BS in
279 // /src/script/common/c_content.cpp and /src/content_sao.cpp
280 float player_stepheight = (m_cao == nullptr) ? 0.0f :
281 (touching_ground ? m_cao->getStepHeight() : (0.2f * BS));
283 // TODO this is a problematic hack.
284 // Use a better implementation for autojump, or apply a custom stepheight
285 // to all players, as this currently creates unintended special movement
286 // abilities and advantages for Android players on a server.
289 player_stepheight += (0.6f * BS);
292 v3f accel_f = v3f(0,0,0);
294 collisionMoveResult result = collisionMoveSimple(env, m_client,
295 pos_max_d, m_collisionbox, player_stepheight, dtime,
296 &position, &m_speed, accel_f);
298 bool could_sneak = control.sneak &&
299 !(fly_allowed && g_settings->getBool("free_move")) &&
300 !in_liquid && !is_climbing &&
301 physics_override_sneak;
303 If the player's feet touch the topside of any node, this is
306 Player is allowed to jump when this is true.
308 bool touching_ground_was = touching_ground;
309 touching_ground = result.touching_ground;
310 bool sneak_can_jump = false;
312 // Max. distance (X, Z) over border for sneaking determined by collision box
313 // * 0.49 to keep the center just barely on the node
314 v3f sneak_max = m_collisionbox.getExtent() * 0.49;
316 if (m_sneak_ladder_detected) {
317 // restore legacy behaviour (this makes the m_speed.Y hack necessary)
318 sneak_max = v3f(0.4 * BS, 0, 0.4 * BS);
322 If sneaking, keep on top of last walked node and don't fall off
324 if (could_sneak && m_sneak_node_exists) {
325 const v3f sn_f = intToFloat(m_sneak_node, BS);
326 const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge;
327 const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge;
328 const v3f old_pos = position;
329 const v3f old_speed = m_speed;
330 f32 y_diff = bmax.Y - position.Y;
332 // (BS * 0.6f) is the basic stepheight while standing on ground
333 if (y_diff < BS * 0.6f) {
334 // Only center player when they're on the node
335 position.X = rangelim(position.X,
336 bmin.X - sneak_max.X, bmax.X + sneak_max.X);
337 position.Z = rangelim(position.Z,
338 bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z);
340 if (position.X != old_pos.X)
342 if (position.Z != old_pos.Z)
346 if (y_diff > 0 && m_speed.Y < 0 &&
347 (physics_override_sneak_glitch || y_diff < BS * 0.6f)) {
348 // Move player to the maximal height when falling or when
349 // the ledge is climbed on the next step.
354 // Allow jumping on node edges while sneaking
355 if (m_speed.Y == 0 || m_sneak_ladder_detected)
356 sneak_can_jump = true;
358 if (collision_info != NULL &&
359 m_speed.Y - old_speed.Y > BS) {
360 // Collide with sneak node, report fall damage
361 CollisionInfo sn_info;
362 sn_info.node_p = m_sneak_node;
363 sn_info.old_speed = old_speed;
364 sn_info.new_speed = m_speed;
365 collision_info->push_back(sn_info);
370 Find the next sneak node if necessary
372 bool new_sneak_node_exists = false;
375 new_sneak_node_exists = updateSneakNode(map, position, sneak_max);
378 Set new position but keep sneak node set
380 setPosition(position);
381 m_sneak_node_exists = new_sneak_node_exists;
387 // Dont report if flying
388 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
389 for (const auto &colinfo : result.collisions) {
390 collision_info->push_back(colinfo);
394 if(!result.standing_on_object && !touching_ground_was && touching_ground) {
395 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
396 m_client->event()->put(e);
398 // Set camera impact value to be used for view bobbing
399 camera_impact = getSpeed().Y * -1;
403 camera_barely_in_ceiling = false;
404 v3s16 camera_np = floatToInt(getEyePosition(), BS);
405 MapNode n = map->getNodeNoEx(camera_np);
406 if(n.getContent() != CONTENT_IGNORE){
407 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
408 camera_barely_in_ceiling = true;
414 Check properties of the node on which the player is standing
416 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
417 // Determine if jumping is possible
418 m_can_jump = (touching_ground && !in_liquid && !is_climbing)
420 if (itemgroup_get(f.groups, "disable_jump"))
423 // Jump key pressed while jumping off from a bouncy block
424 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
425 m_speed.Y >= -0.5 * BS) {
426 float jumpspeed = movement_speed_jump * physics_override_jump;
428 // Reduce boost when speed already is high
429 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
431 m_speed.Y += jumpspeed;
438 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
440 move(dtime, env, pos_max_d, NULL);
443 void LocalPlayer::applyControl(float dtime, Environment *env)
446 swimming_vertical = false;
448 setPitch(control.pitch);
451 // Nullify speed and don't run positioning code if the player is attached
454 setSpeed(v3f(0,0,0));
458 v3f move_direction = v3f(0,0,1);
459 move_direction.rotateXZBy(getYaw());
461 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
462 v3f speedV = v3f(0,0,0); // Vertical (Y)
464 bool fly_allowed = m_client->checkLocalPrivilege("fly");
465 bool fast_allowed = m_client->checkLocalPrivilege("fast");
467 bool free_move = fly_allowed && g_settings->getBool("free_move");
468 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
469 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
470 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
471 bool continuous_forward = g_settings->getBool("continuous_forward");
472 bool always_fly_fast = g_settings->getBool("always_fly_fast");
474 // Whether superspeed mode is used or not
475 bool superspeed = false;
477 if (always_fly_fast && free_move && fast_move)
480 // Old descend control
481 if(g_settings->getBool("aux1_descends"))
483 // If free movement and fast movement, always move fast
484 if(free_move && fast_move)
487 // Auxiliary button 1 (E)
492 // In free movement mode, aux1 descends
494 speedV.Y = -movement_speed_fast;
496 speedV.Y = -movement_speed_walk;
498 else if(in_liquid || in_liquid_stable)
500 speedV.Y = -movement_speed_walk;
501 swimming_vertical = true;
505 speedV.Y = -movement_speed_climb;
509 // If not free movement but fast is allowed, aux1 is
516 // New minecraft-like descend control
519 // Auxiliary button 1 (E)
524 // aux1 is "Turbo button"
534 // In free movement mode, sneak descends
535 if (fast_move && (control.aux1 || always_fly_fast))
536 speedV.Y = -movement_speed_fast;
538 speedV.Y = -movement_speed_walk;
540 else if(in_liquid || in_liquid_stable)
543 speedV.Y = -movement_speed_fast;
545 speedV.Y = -movement_speed_walk;
546 swimming_vertical = true;
551 speedV.Y = -movement_speed_fast;
553 speedV.Y = -movement_speed_climb;
558 if (continuous_forward)
559 speedH += move_direction;
562 if (continuous_forward) {
566 speedH += move_direction;
570 speedH -= move_direction;
572 if (!control.up && !control.down) {
573 speedH -= move_direction *
574 (control.forw_move_joystick_axis / 32767.f);
577 speedH += move_direction.crossProduct(v3f(0,1,0));
580 speedH += move_direction.crossProduct(v3f(0,-1,0));
582 if (!control.left && !control.right) {
583 speedH -= move_direction.crossProduct(v3f(0,1,0)) *
584 (control.sidew_move_joystick_axis / 32767.f);
589 if (g_settings->getBool("aux1_descends") || always_fly_fast) {
591 speedV.Y = movement_speed_fast;
593 speedV.Y = movement_speed_walk;
595 if(fast_move && control.aux1)
596 speedV.Y = movement_speed_fast;
598 speedV.Y = movement_speed_walk;
604 NOTE: The d value in move() affects jump height by
605 raising the height at which the jump speed is kept
606 at its starting value
608 v3f speedJ = getSpeed();
609 if(speedJ.Y >= -0.5 * BS) {
610 speedJ.Y = movement_speed_jump * physics_override_jump;
613 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
614 m_client->event()->put(e);
620 speedV.Y = movement_speed_fast;
622 speedV.Y = movement_speed_walk;
623 swimming_vertical = true;
628 speedV.Y = movement_speed_fast;
630 speedV.Y = movement_speed_climb;
634 // The speed of the player (Y is ignored)
635 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
636 speedH = speedH.normalize() * movement_speed_fast;
637 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
638 speedH = speedH.normalize() * movement_speed_crouch;
640 speedH = speedH.normalize() * movement_speed_walk;
642 // Acceleration increase
643 f32 incH = 0; // Horizontal (X, Z)
644 f32 incV = 0; // Vertical (Y)
645 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
647 // Jumping and falling
648 if(superspeed || (fast_move && control.aux1))
649 incH = movement_acceleration_fast * BS * dtime;
651 incH = movement_acceleration_air * BS * dtime;
652 incV = 0; // No vertical acceleration in air
654 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
655 incH = incV = movement_acceleration_fast * BS * dtime;
657 incH = incV = movement_acceleration_default * BS * dtime;
659 float slip_factor = getSlipFactor(env, speedH);
660 // Accelerate to target speed with maximum increment
661 accelerateHorizontal(speedH * physics_override_speed,
662 incH * physics_override_speed * slip_factor);
663 accelerateVertical(speedV * physics_override_speed,
664 incV * physics_override_speed);
667 v3s16 LocalPlayer::getStandingNodePos()
669 if(m_sneak_node_exists)
671 return floatToInt(getPosition() - v3f(0, BS, 0), BS);
674 v3s16 LocalPlayer::getFootstepNodePos()
676 if (in_liquid_stable)
677 // Emit swimming sound if the player is in liquid
678 return floatToInt(getPosition(), BS);
680 // BS * 0.05 below the player's feet ensures a 1/16th height
681 // nodebox is detected instead of the node below it.
682 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
683 // A larger distance below is necessary for a footstep sound
684 // when landing after a jump or fall. BS * 0.5 ensures water
685 // sounds when swimming in 1 node deep water.
686 return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
689 v3s16 LocalPlayer::getLightPosition() const
691 return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
694 v3f LocalPlayer::getEyeOffset() const
696 float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f;
697 return v3f(0, BS * eye_height, 0);
700 // Horizontal acceleration (X and Z), Y direction is ignored
701 void LocalPlayer::accelerateHorizontal(const v3f &target_speed,
702 const f32 max_increase)
704 if (max_increase == 0)
707 v3f d_wanted = target_speed - m_speed;
709 f32 dl = d_wanted.getLength();
710 if (dl > max_increase)
713 v3f d = d_wanted.normalize() * dl;
719 // Vertical acceleration (Y), X and Z directions are ignored
720 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
722 if (max_increase == 0)
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;
731 m_speed.Y += d_wanted;
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)
738 Map *map = &env->getMap();
739 INodeDefManager *nodemgr = m_client->ndef();
741 v3f position = getPosition();
743 // Copy parent position if local player is attached
745 setPosition(overridePosition);
746 m_sneak_node_exists = false;
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");
756 position += m_speed * dtime;
757 setPosition(position);
758 m_sneak_node_exists = false;
765 bool is_valid_position;
770 Check if player is in liquid (the oscillating value)
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;
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;
795 Check if player is in liquid (the stable value)
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();
802 in_liquid_stable = false;
805 Check if player is climbing
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);
813 if (!(is_valid_position && is_valid_position2))
816 is_climbing = (nodemgr->get(node.getContent()).climbable ||
817 nodemgr->get(node2.getContent()).climbable) && !free_move;
820 Collision uncertainty radius
821 Make it a bit larger than the maximum distance of movement
823 //f32 d = pos_max_d * 1.1;
824 // A fairly large value in here makes moving smoother
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;
832 If sneaking, keep in range from the last walked node and don't
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);
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)
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.
858 // this shouldn't be hardcoded but transmitted from server
859 float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
862 player_stepheight += (0.6 * BS);
865 v3f accel_f = v3f(0, 0, 0);
867 collisionMoveResult result = collisionMoveSimple(env, m_client,
868 pos_max_d, m_collisionbox, player_stepheight, dtime,
869 &position, &m_speed, accel_f);
872 If the player's feet touch the topside of any node, this is
875 Player is allowed to jump when this is true.
877 bool touching_ground_was = touching_ground;
878 touching_ground = result.touching_ground;
880 //bool standing_on_unloaded = result.standing_on_unloaded;
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.
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
901 m_need_to_get_new_sneak_node = true;
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));
921 if (distance_f > min_distance_f ||
922 max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)
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)
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)
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)
940 min_distance_f = distance_f;
944 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
946 m_sneak_node = new_sneak_node;
947 m_sneak_node_exists = sneak_node_found;
949 if (sneak_node_found) {
951 MapNode n = map->getNodeNoEx(m_sneak_node);
952 std::vector<aabb3f> nodeboxes;
953 n.getCollisionBoxes(nodemgr, &nodeboxes);
954 for (const auto &box : nodeboxes) {
955 if (box.MaxEdge.Y > cb_max)
956 cb_max = box.MaxEdge.Y;
958 m_sneak_node_bb_ymax = cb_max;
962 If sneaking, the player's collision box can be in air, so
963 this has to be set explicitly
965 if (sneak_node_found && control.sneak)
966 touching_ground = true;
970 Set new position but keep sneak node set
972 bool sneak_node_exists = m_sneak_node_exists;
973 setPosition(position);
974 m_sneak_node_exists = sneak_node_exists;
979 // Dont report if flying
980 if (collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
981 for (const auto &info : result.collisions) {
982 collision_info->push_back(info);
986 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
987 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
988 m_client->event()->put(e);
989 // Set camera impact value to be used for view bobbing
990 camera_impact = getSpeed().Y * -1;
994 camera_barely_in_ceiling = false;
995 v3s16 camera_np = floatToInt(getEyePosition(), BS);
996 MapNode n = map->getNodeNoEx(camera_np);
997 if (n.getContent() != CONTENT_IGNORE) {
998 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
999 camera_barely_in_ceiling = true;
1004 Update the node last under the player
1006 m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);
1007 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
1010 Check properties of the node on which the player is standing
1012 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
1013 // Determine if jumping is possible
1014 m_can_jump = touching_ground && !in_liquid;
1015 if (itemgroup_get(f.groups, "disable_jump"))
1017 // Jump key pressed while jumping off from a bouncy block
1018 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1019 m_speed.Y >= -0.5 * BS) {
1020 float jumpspeed = movement_speed_jump * physics_override_jump;
1021 if (m_speed.Y > 1) {
1022 // Reduce boost when speed already is high
1023 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
1025 m_speed.Y += jumpspeed;
1032 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1035 if (!touching_ground)
1038 float slip_factor = 1.0f;
1039 // Slip on slippery nodes
1040 const INodeDefManager *nodemgr = env->getGameDef()->ndef();
1041 Map *map = &env->getMap();
1042 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(
1043 floatToInt(getPosition() - v3f(0, 0.05f * BS, 0), BS)));
1046 slippery = itemgroup_get(f.groups, "slippery");
1047 } else if (is_slipping) {
1048 // slipping over an edge? Check surroundings for slippery nodes
1049 slippery = 2 << 16; // guard value, bigger than all realistic ones
1050 for (int z = 0; z <= 1; z++) {
1051 for (int x = 0; x <= 1; x++) {
1052 // this should cover all nodes surrounding player position
1053 v3f offset((x - 0.5f) * BS, 0.05f * BS, (z - 0.5f) * BS);
1054 const ContentFeatures &f2 = nodemgr->get(map->getNodeNoEx(
1055 floatToInt(getPosition() - offset, BS)));
1057 // find least slippery node we might be standing on
1058 int s = itemgroup_get(f2.groups, "slippery");
1064 // without any hits, ignore slippery
1065 if (slippery >= (2 << 16))
1068 if (slippery >= 1) {
1069 if (speedH == v3f(0.0f)) {
1070 slippery = slippery * 2;
1072 slip_factor = core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);
1075 // remember this to avoid checking the edge case above too often
1076 is_slipping = false;