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 const NodeDefManager *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 if (!collision_info || collision_info->empty()) {
173 // Node below the feet, update each ClientEnvironment::step()
174 m_standing_node = floatToInt(m_position, BS) - v3s16(0, 1, 0);
177 // Temporary option for old move code
178 if (!physics_override_new_move) {
179 old_move(dtime, env, pos_max_d, collision_info);
183 Map *map = &env->getMap();
184 const NodeDefManager *nodemgr = m_client->ndef();
186 v3f position = getPosition();
188 // Copy parent position if local player is attached
190 setPosition(overridePosition);
194 PlayerSettings &player_settings = getPlayerSettings();
196 // Skip collision detection if noclip mode is used
197 bool fly_allowed = m_client->checkLocalPrivilege("fly");
198 bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip;
199 bool free_move = player_settings.free_move && fly_allowed;
201 if (noclip && free_move) {
202 position += m_speed * dtime;
203 setPosition(position);
211 bool is_valid_position;
216 Check if player is in liquid (the oscillating value)
219 // If in liquid, the threshold of coming out is at higher y
222 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
223 node = map->getNodeNoEx(pp, &is_valid_position);
224 if (is_valid_position) {
225 in_liquid = nodemgr->get(node.getContent()).isLiquid();
226 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
231 // If not in liquid, the threshold of going in is at lower y
234 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
235 node = map->getNodeNoEx(pp, &is_valid_position);
236 if (is_valid_position) {
237 in_liquid = nodemgr->get(node.getContent()).isLiquid();
238 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
246 Check if player is in liquid (the stable value)
248 pp = floatToInt(position + v3f(0,0,0), BS);
249 node = map->getNodeNoEx(pp, &is_valid_position);
250 if (is_valid_position) {
251 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
253 in_liquid_stable = false;
257 Check if player is climbing
261 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
262 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
263 node = map->getNodeNoEx(pp, &is_valid_position);
264 bool is_valid_position2;
265 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
267 if (!(is_valid_position && is_valid_position2)) {
270 is_climbing = (nodemgr->get(node.getContent()).climbable
271 || nodemgr->get(node2.getContent()).climbable) && !free_move;
275 Collision uncertainty radius
276 Make it a bit larger than the maximum distance of movement
278 //f32 d = pos_max_d * 1.1;
279 // A fairly large value in here makes moving smoother
282 // This should always apply, otherwise there are glitches
283 sanity_check(d > pos_max_d);
285 // Player object property step height is multiplied by BS in
286 // /src/script/common/c_content.cpp and /src/content_sao.cpp
287 float player_stepheight = (m_cao == nullptr) ? 0.0f :
288 (touching_ground ? m_cao->getStepHeight() : (0.2f * BS));
290 // TODO this is a problematic hack.
291 // Use a better implementation for autojump, or apply a custom stepheight
292 // to all players, as this currently creates unintended special movement
293 // abilities and advantages for Android players on a server.
296 player_stepheight += (0.6f * BS);
299 v3f accel_f = v3f(0,0,0);
301 collisionMoveResult result = collisionMoveSimple(env, m_client,
302 pos_max_d, m_collisionbox, player_stepheight, dtime,
303 &position, &m_speed, accel_f);
305 bool could_sneak = control.sneak && !free_move && !in_liquid &&
306 !is_climbing && physics_override_sneak;
308 // Add new collisions to the vector
309 if (collision_info && !free_move) {
310 v3f diff = intToFloat(m_standing_node, BS) - position;
311 f32 distance = diff.getLength();
312 // Force update each ClientEnvironment::step()
313 bool is_first = collision_info->empty();
315 for (const auto &colinfo : result.collisions) {
316 collision_info->push_back(colinfo);
318 if (colinfo.type != COLLISION_NODE ||
319 colinfo.new_speed.Y != 0 ||
320 (could_sneak && m_sneak_node_exists))
323 diff = intToFloat(colinfo.node_p, BS) - position;
325 // Find nearest colliding node
326 f32 len = diff.getLength();
327 if (is_first || len < distance) {
328 m_standing_node = colinfo.node_p;
335 If the player's feet touch the topside of any node, this is
338 Player is allowed to jump when this is true.
340 bool touching_ground_was = touching_ground;
341 touching_ground = result.touching_ground;
342 bool sneak_can_jump = false;
344 // Max. distance (X, Z) over border for sneaking determined by collision box
345 // * 0.49 to keep the center just barely on the node
346 v3f sneak_max = m_collisionbox.getExtent() * 0.49;
348 if (m_sneak_ladder_detected) {
349 // restore legacy behaviour (this makes the m_speed.Y hack necessary)
350 sneak_max = v3f(0.4 * BS, 0, 0.4 * BS);
354 If sneaking, keep on top of last walked node and don't fall off
356 if (could_sneak && m_sneak_node_exists) {
357 const v3f sn_f = intToFloat(m_sneak_node, BS);
358 const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge;
359 const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge;
360 const v3f old_pos = position;
361 const v3f old_speed = m_speed;
362 f32 y_diff = bmax.Y - position.Y;
363 m_standing_node = m_sneak_node;
365 // (BS * 0.6f) is the basic stepheight while standing on ground
366 if (y_diff < BS * 0.6f) {
367 // Only center player when they're on the node
368 position.X = rangelim(position.X,
369 bmin.X - sneak_max.X, bmax.X + sneak_max.X);
370 position.Z = rangelim(position.Z,
371 bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z);
373 if (position.X != old_pos.X)
375 if (position.Z != old_pos.Z)
379 if (y_diff > 0 && m_speed.Y < 0 &&
380 (physics_override_sneak_glitch || y_diff < BS * 0.6f)) {
381 // Move player to the maximal height when falling or when
382 // the ledge is climbed on the next step.
387 // Allow jumping on node edges while sneaking
388 if (m_speed.Y == 0 || m_sneak_ladder_detected)
389 sneak_can_jump = true;
391 if (collision_info &&
392 m_speed.Y - old_speed.Y > BS) {
393 // Collide with sneak node, report fall damage
394 CollisionInfo sn_info;
395 sn_info.node_p = m_sneak_node;
396 sn_info.old_speed = old_speed;
397 sn_info.new_speed = m_speed;
398 collision_info->push_back(sn_info);
403 Find the next sneak node if necessary
405 bool new_sneak_node_exists = false;
408 new_sneak_node_exists = updateSneakNode(map, position, sneak_max);
411 Set new position but keep sneak node set
413 setPosition(position);
414 m_sneak_node_exists = new_sneak_node_exists;
420 if(!result.standing_on_object && !touching_ground_was && touching_ground) {
421 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
423 // Set camera impact value to be used for view bobbing
424 camera_impact = getSpeed().Y * -1;
428 camera_barely_in_ceiling = false;
429 v3s16 camera_np = floatToInt(getEyePosition(), BS);
430 MapNode n = map->getNodeNoEx(camera_np);
431 if(n.getContent() != CONTENT_IGNORE){
432 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
433 camera_barely_in_ceiling = true;
439 Check properties of the node on which the player is standing
441 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(m_standing_node));
442 // Determine if jumping is possible
443 m_can_jump = (touching_ground && !in_liquid && !is_climbing)
445 if (itemgroup_get(f.groups, "disable_jump"))
448 // Jump key pressed while jumping off from a bouncy block
449 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
450 m_speed.Y >= -0.5 * BS) {
451 float jumpspeed = movement_speed_jump * physics_override_jump;
453 // Reduce boost when speed already is high
454 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
456 m_speed.Y += jumpspeed;
463 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
465 move(dtime, env, pos_max_d, NULL);
468 void LocalPlayer::applyControl(float dtime, Environment *env)
471 swimming_vertical = false;
473 setPitch(control.pitch);
476 // Nullify speed and don't run positioning code if the player is attached
479 setSpeed(v3f(0,0,0));
483 PlayerSettings &player_settings = getPlayerSettings();
485 v3f move_direction = v3f(0,0,1);
486 move_direction.rotateXZBy(getYaw());
488 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
489 v3f speedV = v3f(0,0,0); // Vertical (Y)
491 bool fly_allowed = m_client->checkLocalPrivilege("fly");
492 bool fast_allowed = m_client->checkLocalPrivilege("fast");
494 bool free_move = fly_allowed && player_settings.free_move;
495 bool fast_move = fast_allowed && player_settings.fast_move;
496 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
497 bool fast_climb = fast_move && control.aux1 && !player_settings.aux1_descends;
498 bool continuous_forward = player_settings.continuous_forward;
499 bool always_fly_fast = player_settings.always_fly_fast;
501 // Whether superspeed mode is used or not
502 bool superspeed = false;
504 if (always_fly_fast && free_move && fast_move)
507 // Old descend control
508 if (player_settings.aux1_descends)
510 // If free movement and fast movement, always move fast
511 if(free_move && fast_move)
514 // Auxiliary button 1 (E)
519 // In free movement mode, aux1 descends
521 speedV.Y = -movement_speed_fast;
523 speedV.Y = -movement_speed_walk;
525 else if(in_liquid || in_liquid_stable)
527 speedV.Y = -movement_speed_walk;
528 swimming_vertical = true;
532 speedV.Y = -movement_speed_climb;
536 // If not free movement but fast is allowed, aux1 is
543 // New minecraft-like descend control
546 // Auxiliary button 1 (E)
551 // aux1 is "Turbo button"
561 // In free movement mode, sneak descends
562 if (fast_move && (control.aux1 || always_fly_fast))
563 speedV.Y = -movement_speed_fast;
565 speedV.Y = -movement_speed_walk;
567 else if(in_liquid || in_liquid_stable)
570 speedV.Y = -movement_speed_fast;
572 speedV.Y = -movement_speed_walk;
573 swimming_vertical = true;
578 speedV.Y = -movement_speed_fast;
580 speedV.Y = -movement_speed_climb;
585 if (continuous_forward)
586 speedH += move_direction;
589 if (continuous_forward) {
593 speedH += move_direction;
597 speedH -= move_direction;
599 if (!control.up && !control.down) {
600 speedH -= move_direction *
601 (control.forw_move_joystick_axis / 32767.f);
604 speedH += move_direction.crossProduct(v3f(0,1,0));
607 speedH += move_direction.crossProduct(v3f(0,-1,0));
609 if (!control.left && !control.right) {
610 speedH -= move_direction.crossProduct(v3f(0,1,0)) *
611 (control.sidew_move_joystick_axis / 32767.f);
616 if (player_settings.aux1_descends || always_fly_fast) {
618 speedV.Y = movement_speed_fast;
620 speedV.Y = movement_speed_walk;
622 if(fast_move && control.aux1)
623 speedV.Y = movement_speed_fast;
625 speedV.Y = movement_speed_walk;
631 NOTE: The d value in move() affects jump height by
632 raising the height at which the jump speed is kept
633 at its starting value
635 v3f speedJ = getSpeed();
636 if(speedJ.Y >= -0.5 * BS) {
637 speedJ.Y = movement_speed_jump * physics_override_jump;
639 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_JUMP));
645 speedV.Y = movement_speed_fast;
647 speedV.Y = movement_speed_walk;
648 swimming_vertical = true;
653 speedV.Y = movement_speed_fast;
655 speedV.Y = movement_speed_climb;
659 // The speed of the player (Y is ignored)
660 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
661 speedH = speedH.normalize() * movement_speed_fast;
662 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
663 speedH = speedH.normalize() * movement_speed_crouch;
665 speedH = speedH.normalize() * movement_speed_walk;
667 // Acceleration increase
668 f32 incH = 0; // Horizontal (X, Z)
669 f32 incV = 0; // Vertical (Y)
670 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
672 // Jumping and falling
673 if(superspeed || (fast_move && control.aux1))
674 incH = movement_acceleration_fast * BS * dtime;
676 incH = movement_acceleration_air * BS * dtime;
677 incV = 0; // No vertical acceleration in air
679 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
680 incH = incV = movement_acceleration_fast * BS * dtime;
682 incH = incV = movement_acceleration_default * BS * dtime;
684 float slip_factor = 1.0f;
686 slip_factor = getSlipFactor(env, speedH);
688 // Accelerate to target speed with maximum increment
689 accelerateHorizontal(speedH * physics_override_speed,
690 incH * physics_override_speed * slip_factor);
691 accelerateVertical(speedV * physics_override_speed,
692 incV * physics_override_speed);
695 v3s16 LocalPlayer::getStandingNodePos()
697 if(m_sneak_node_exists)
699 return m_standing_node;
702 v3s16 LocalPlayer::getFootstepNodePos()
704 if (in_liquid_stable)
705 // Emit swimming sound if the player is in liquid
706 return floatToInt(getPosition(), BS);
708 // BS * 0.05 below the player's feet ensures a 1/16th height
709 // nodebox is detected instead of the node below it.
710 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
711 // A larger distance below is necessary for a footstep sound
712 // when landing after a jump or fall. BS * 0.5 ensures water
713 // sounds when swimming in 1 node deep water.
714 return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
717 v3s16 LocalPlayer::getLightPosition() const
719 return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
722 v3f LocalPlayer::getEyeOffset() const
724 float eye_height = camera_barely_in_ceiling ?
725 m_eye_height - 0.125f : m_eye_height;
726 return v3f(0, BS * eye_height, 0);
729 // Horizontal acceleration (X and Z), Y direction is ignored
730 void LocalPlayer::accelerateHorizontal(const v3f &target_speed,
731 const f32 max_increase)
733 if (max_increase == 0)
736 v3f d_wanted = target_speed - m_speed;
738 f32 dl = d_wanted.getLength();
739 if (dl > max_increase)
742 v3f d = d_wanted.normalize() * dl;
748 // Vertical acceleration (Y), X and Z directions are ignored
749 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
751 if (max_increase == 0)
754 f32 d_wanted = target_speed.Y - m_speed.Y;
755 if (d_wanted > max_increase)
756 d_wanted = max_increase;
757 else if (d_wanted < -max_increase)
758 d_wanted = -max_increase;
760 m_speed.Y += d_wanted;
763 // Temporary option for old move code
764 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
765 std::vector<CollisionInfo> *collision_info)
767 Map *map = &env->getMap();
768 const NodeDefManager *nodemgr = m_client->ndef();
770 v3f position = getPosition();
772 // Copy parent position if local player is attached
774 setPosition(overridePosition);
775 m_sneak_node_exists = false;
779 PlayerSettings &player_settings = getPlayerSettings();
781 // Skip collision detection if noclip mode is used
782 bool fly_allowed = m_client->checkLocalPrivilege("fly");
783 bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip;
784 bool free_move = noclip && fly_allowed && player_settings.free_move;
786 position += m_speed * dtime;
787 setPosition(position);
788 m_sneak_node_exists = false;
795 bool is_valid_position;
800 Check if player is in liquid (the oscillating value)
803 // If in liquid, the threshold of coming out is at higher y
804 pp = floatToInt(position + v3f(0, BS * 0.1, 0), BS);
805 node = map->getNodeNoEx(pp, &is_valid_position);
806 if (is_valid_position) {
807 in_liquid = nodemgr->get(node.getContent()).isLiquid();
808 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
813 // If not in liquid, the threshold of going in is at lower y
814 pp = floatToInt(position + v3f(0, BS * 0.5, 0), BS);
815 node = map->getNodeNoEx(pp, &is_valid_position);
816 if (is_valid_position) {
817 in_liquid = nodemgr->get(node.getContent()).isLiquid();
818 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
825 Check if player is in liquid (the stable value)
827 pp = floatToInt(position + v3f(0, 0, 0), BS);
828 node = map->getNodeNoEx(pp, &is_valid_position);
829 if (is_valid_position)
830 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
832 in_liquid_stable = false;
835 Check if player is climbing
837 pp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS);
838 v3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS);
839 node = map->getNodeNoEx(pp, &is_valid_position);
840 bool is_valid_position2;
841 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
843 if (!(is_valid_position && is_valid_position2))
846 is_climbing = (nodemgr->get(node.getContent()).climbable ||
847 nodemgr->get(node2.getContent()).climbable) && !free_move;
850 Collision uncertainty radius
851 Make it a bit larger than the maximum distance of movement
853 //f32 d = pos_max_d * 1.1;
854 // A fairly large value in here makes moving smoother
856 // This should always apply, otherwise there are glitches
857 sanity_check(d > pos_max_d);
858 // Maximum distance over border for sneaking
859 f32 sneak_max = BS * 0.4;
862 If sneaking, keep in range from the last walked node and don't
865 if (control.sneak && m_sneak_node_exists &&
866 !(fly_allowed && player_settings.free_move) && !in_liquid &&
867 physics_override_sneak) {
868 f32 maxd = 0.5 * BS + sneak_max;
869 v3f lwn_f = intToFloat(m_sneak_node, BS);
870 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
871 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
874 // Move up if necessary
875 f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
876 if (position.Y < new_y)
879 Collision seems broken, since player is sinking when
880 sneaking over the edges of current sneaking_node.
881 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
888 // this shouldn't be hardcoded but transmitted from server
889 float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
892 player_stepheight += (0.6 * BS);
895 v3f accel_f = v3f(0, 0, 0);
897 collisionMoveResult result = collisionMoveSimple(env, m_client,
898 pos_max_d, m_collisionbox, player_stepheight, dtime,
899 &position, &m_speed, accel_f);
902 If the player's feet touch the topside of any node, this is
905 Player is allowed to jump when this is true.
907 bool touching_ground_was = touching_ground;
908 touching_ground = result.touching_ground;
910 //bool standing_on_unloaded = result.standing_on_unloaded;
913 Check the nodes under the player to see from which node the
914 player is sneaking from, if any. If the node from under
915 the player has been removed, the player falls.
917 f32 position_y_mod = 0.05 * BS;
918 if (m_sneak_node_bb_ymax > 0)
919 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
920 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
921 if (m_sneak_node_exists &&
922 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
923 m_old_node_below_type != "air") {
924 // Old node appears to have been removed; that is,
925 // it wasn't air before but now it is
926 m_need_to_get_new_sneak_node = false;
927 m_sneak_node_exists = false;
928 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
929 // We are on something, so make sure to recalculate the sneak
931 m_need_to_get_new_sneak_node = true;
934 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
935 m_sneak_node_bb_ymax = 0;
936 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
937 v2f player_p2df(position.X, position.Z);
938 f32 min_distance_f = 100000.0 * BS;
939 // If already seeking from some node, compare to it.
940 v3s16 new_sneak_node = m_sneak_node;
941 for (s16 x= -1; x <= 1; x++)
942 for (s16 z= -1; z <= 1; z++) {
943 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
944 v3f pf = intToFloat(p, BS);
945 v2f node_p2df(pf.X, pf.Z);
946 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
947 f32 max_axis_distance_f = MYMAX(
948 std::fabs(player_p2df.X - node_p2df.X),
949 std::fabs(player_p2df.Y - node_p2df.Y));
951 if (distance_f > min_distance_f ||
952 max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)
955 // The node to be sneaked on has to be walkable
956 node = map->getNodeNoEx(p, &is_valid_position);
957 if (!is_valid_position || !nodemgr->get(node).walkable)
959 // And the node above it has to be nonwalkable
960 node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
961 if (!is_valid_position || nodemgr->get(node).walkable)
963 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
964 if (!physics_override_sneak_glitch) {
965 node =map->getNodeNoEx(p + v3s16(0, 2, 0), &is_valid_position);
966 if (!is_valid_position || nodemgr->get(node).walkable)
970 min_distance_f = distance_f;
974 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
976 m_sneak_node = new_sneak_node;
977 m_sneak_node_exists = sneak_node_found;
979 if (sneak_node_found) {
981 MapNode n = map->getNodeNoEx(m_sneak_node);
982 std::vector<aabb3f> nodeboxes;
983 n.getCollisionBoxes(nodemgr, &nodeboxes);
984 for (const auto &box : nodeboxes) {
985 if (box.MaxEdge.Y > cb_max)
986 cb_max = box.MaxEdge.Y;
988 m_sneak_node_bb_ymax = cb_max;
992 If sneaking, the player's collision box can be in air, so
993 this has to be set explicitly
995 if (sneak_node_found && control.sneak)
996 touching_ground = true;
1000 Set new position but keep sneak node set
1002 bool sneak_node_exists = m_sneak_node_exists;
1003 setPosition(position);
1004 m_sneak_node_exists = sneak_node_exists;
1009 // Dont report if flying
1010 if (collision_info && !(player_settings.free_move && fly_allowed)) {
1011 for (const auto &info : result.collisions) {
1012 collision_info->push_back(info);
1016 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1017 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
1018 // Set camera impact value to be used for view bobbing
1019 camera_impact = getSpeed().Y * -1;
1023 camera_barely_in_ceiling = false;
1024 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1025 MapNode n = map->getNodeNoEx(camera_np);
1026 if (n.getContent() != CONTENT_IGNORE) {
1027 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1028 camera_barely_in_ceiling = true;
1033 Update the node last under the player
1035 m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);
1036 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
1039 Check properties of the node on which the player is standing
1041 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
1042 // Determine if jumping is possible
1043 m_can_jump = touching_ground && !in_liquid;
1044 if (itemgroup_get(f.groups, "disable_jump"))
1046 // Jump key pressed while jumping off from a bouncy block
1047 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1048 m_speed.Y >= -0.5 * BS) {
1049 float jumpspeed = movement_speed_jump * physics_override_jump;
1050 if (m_speed.Y > 1) {
1051 // Reduce boost when speed already is high
1052 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
1054 m_speed.Y += jumpspeed;
1061 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1063 // Slip on slippery nodes
1064 const NodeDefManager *nodemgr = env->getGameDef()->ndef();
1065 Map *map = &env->getMap();
1066 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(
1067 getStandingNodePos()));
1070 slippery = itemgroup_get(f.groups, "slippery");
1072 if (slippery >= 1) {
1073 if (speedH == v3f(0.0f)) {
1074 slippery = slippery * 2;
1076 return core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);