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 v3f accel_f = v3f(0,0,0);
291 const v3f initial_position = position;
292 const v3f initial_speed = m_speed;
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 && !free_move && !in_liquid &&
299 !is_climbing && physics_override_sneak;
301 // Add new collisions to the vector
302 if (collision_info && !free_move) {
303 v3f diff = intToFloat(m_standing_node, BS) - position;
304 f32 distance = diff.getLength();
305 // Force update each ClientEnvironment::step()
306 bool is_first = collision_info->empty();
308 for (const auto &colinfo : result.collisions) {
309 collision_info->push_back(colinfo);
311 if (colinfo.type != COLLISION_NODE ||
312 colinfo.new_speed.Y != 0 ||
313 (could_sneak && m_sneak_node_exists))
316 diff = intToFloat(colinfo.node_p, BS) - position;
318 // Find nearest colliding node
319 f32 len = diff.getLength();
320 if (is_first || len < distance) {
321 m_standing_node = colinfo.node_p;
328 If the player's feet touch the topside of any node, this is
331 Player is allowed to jump when this is true.
333 bool touching_ground_was = touching_ground;
334 touching_ground = result.touching_ground;
335 bool sneak_can_jump = false;
337 // Max. distance (X, Z) over border for sneaking determined by collision box
338 // * 0.49 to keep the center just barely on the node
339 v3f sneak_max = m_collisionbox.getExtent() * 0.49;
341 if (m_sneak_ladder_detected) {
342 // restore legacy behaviour (this makes the m_speed.Y hack necessary)
343 sneak_max = v3f(0.4 * BS, 0, 0.4 * BS);
347 If sneaking, keep on top of last walked node and don't fall off
349 if (could_sneak && m_sneak_node_exists) {
350 const v3f sn_f = intToFloat(m_sneak_node, BS);
351 const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge;
352 const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge;
353 const v3f old_pos = position;
354 const v3f old_speed = m_speed;
355 f32 y_diff = bmax.Y - position.Y;
356 m_standing_node = m_sneak_node;
358 // (BS * 0.6f) is the basic stepheight while standing on ground
359 if (y_diff < BS * 0.6f) {
360 // Only center player when they're on the node
361 position.X = rangelim(position.X,
362 bmin.X - sneak_max.X, bmax.X + sneak_max.X);
363 position.Z = rangelim(position.Z,
364 bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z);
366 if (position.X != old_pos.X)
368 if (position.Z != old_pos.Z)
372 if (y_diff > 0 && m_speed.Y <= 0 &&
373 (physics_override_sneak_glitch || y_diff < BS * 0.6f)) {
374 // Move player to the maximal height when falling or when
375 // the ledge is climbed on the next step.
377 // Smoothen the movement (based on 'position.Y = bmax.Y')
378 position.Y += y_diff * dtime * 22.0f + BS * 0.01f;
379 position.Y = std::min(position.Y, bmax.Y);
383 // Allow jumping on node edges while sneaking
384 if (m_speed.Y == 0 || m_sneak_ladder_detected)
385 sneak_can_jump = true;
387 if (collision_info &&
388 m_speed.Y - old_speed.Y > BS) {
389 // Collide with sneak node, report fall damage
390 CollisionInfo sn_info;
391 sn_info.node_p = m_sneak_node;
392 sn_info.old_speed = old_speed;
393 sn_info.new_speed = m_speed;
394 collision_info->push_back(sn_info);
399 Find the next sneak node if necessary
401 bool new_sneak_node_exists = false;
404 new_sneak_node_exists = updateSneakNode(map, position, sneak_max);
407 Set new position but keep sneak node set
409 setPosition(position);
410 m_sneak_node_exists = new_sneak_node_exists;
416 if(!result.standing_on_object && !touching_ground_was && touching_ground) {
417 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
419 // Set camera impact value to be used for view bobbing
420 camera_impact = getSpeed().Y * -1;
424 camera_barely_in_ceiling = false;
425 v3s16 camera_np = floatToInt(getEyePosition(), BS);
426 MapNode n = map->getNodeNoEx(camera_np);
427 if(n.getContent() != CONTENT_IGNORE){
428 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
429 camera_barely_in_ceiling = true;
435 Check properties of the node on which the player is standing
437 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(m_standing_node));
438 // Determine if jumping is possible
439 m_can_jump = (touching_ground && !in_liquid && !is_climbing)
441 if (itemgroup_get(f.groups, "disable_jump"))
444 // Jump key pressed while jumping off from a bouncy block
445 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
446 m_speed.Y >= -0.5 * BS) {
447 float jumpspeed = movement_speed_jump * physics_override_jump;
449 // Reduce boost when speed already is high
450 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
452 m_speed.Y += jumpspeed;
459 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
462 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
464 move(dtime, env, pos_max_d, NULL);
467 void LocalPlayer::applyControl(float dtime, Environment *env)
470 swimming_vertical = false;
471 swimming_pitch = 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 // All vectors are relative to the player's yaw,
486 // (and pitch if pitch fly mode enabled),
487 // and will be rotated at the end
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 bool pitch_move = (free_move || in_liquid) && player_settings.pitch_move;
497 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
498 bool fast_climb = fast_move && control.aux1 && !player_settings.aux1_descends;
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;
586 speedH += v3f(0,0,1);
589 speedH -= v3f(0,0,1);
591 if (!control.up && !control.down) {
592 speedH -= v3f(0,0,1) *
593 (control.forw_move_joystick_axis / 32767.f);
596 speedH += v3f(-1,0,0);
599 speedH += v3f(1,0,0);
601 if (!control.left && !control.right) {
602 speedH += v3f(1,0,0) *
603 (control.sidew_move_joystick_axis / 32767.f);
606 // release autojump after a given time
607 m_autojump_time -= dtime;
608 if (m_autojump_time <= 0.0f)
614 if (player_settings.aux1_descends || always_fly_fast) {
616 speedV.Y = movement_speed_fast;
618 speedV.Y = movement_speed_walk;
620 if(fast_move && control.aux1)
621 speedV.Y = movement_speed_fast;
623 speedV.Y = movement_speed_walk;
629 NOTE: The d value in move() affects jump height by
630 raising the height at which the jump speed is kept
631 at its starting value
633 v3f speedJ = getSpeed();
634 if(speedJ.Y >= -0.5 * BS) {
635 speedJ.Y = movement_speed_jump * physics_override_jump;
637 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_JUMP));
643 speedV.Y = movement_speed_fast;
645 speedV.Y = movement_speed_walk;
646 swimming_vertical = true;
651 speedV.Y = movement_speed_fast;
653 speedV.Y = movement_speed_climb;
657 // The speed of the player (Y is ignored)
658 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
659 speedH = speedH.normalize() * movement_speed_fast;
660 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
661 speedH = speedH.normalize() * movement_speed_crouch;
663 speedH = speedH.normalize() * movement_speed_walk;
665 // Acceleration increase
666 f32 incH = 0; // Horizontal (X, Z)
667 f32 incV = 0; // Vertical (Y)
668 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
670 // Jumping and falling
671 if(superspeed || (fast_move && control.aux1))
672 incH = movement_acceleration_fast * BS * dtime;
674 incH = movement_acceleration_air * BS * dtime;
675 incV = 0; // No vertical acceleration in air
677 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
678 incH = incV = movement_acceleration_fast * BS * dtime;
680 incH = incV = movement_acceleration_default * BS * dtime;
682 float slip_factor = 1.0f;
683 if (!free_move && !in_liquid && !in_liquid_stable)
684 slip_factor = getSlipFactor(env, speedH);
686 // Don't sink when swimming in pitch mode
687 if (pitch_move && in_liquid) {
688 v3f controlSpeed = speedH + speedV;
689 if (controlSpeed.getLength() > 0.01f)
690 swimming_pitch = true;
693 // Accelerate to target speed with maximum increment
694 accelerate((speedH + speedV) * physics_override_speed,
695 incH * physics_override_speed * slip_factor, incV * physics_override_speed,
699 v3s16 LocalPlayer::getStandingNodePos()
701 if(m_sneak_node_exists)
703 return m_standing_node;
706 v3s16 LocalPlayer::getFootstepNodePos()
708 if (in_liquid_stable)
709 // Emit swimming sound if the player is in liquid
710 return floatToInt(getPosition(), BS);
712 // BS * 0.05 below the player's feet ensures a 1/16th height
713 // nodebox is detected instead of the node below it.
714 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
715 // A larger distance below is necessary for a footstep sound
716 // when landing after a jump or fall. BS * 0.5 ensures water
717 // sounds when swimming in 1 node deep water.
718 return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
721 v3s16 LocalPlayer::getLightPosition() const
723 return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
726 v3f LocalPlayer::getEyeOffset() const
728 float eye_height = camera_barely_in_ceiling ?
729 m_eye_height - 0.125f : m_eye_height;
730 return v3f(0, BS * eye_height, 0);
734 void LocalPlayer::accelerate(const v3f &target_speed, const f32 max_increase_H,
735 const f32 max_increase_V, const bool use_pitch)
737 const f32 yaw = getYaw();
738 const f32 pitch = getPitch();
739 v3f flat_speed = m_speed;
740 // Rotate speed vector by -yaw and -pitch to make it relative to the player's yaw and pitch
741 flat_speed.rotateXZBy(-yaw);
743 flat_speed.rotateYZBy(-pitch);
745 v3f d_wanted = target_speed - flat_speed;
748 // Then compare the horizontal and vertical components with the wanted speed
749 if (max_increase_H > 0) {
750 v3f d_wanted_H = d_wanted * v3f(1,0,1);
751 if (d_wanted_H.getLength() > max_increase_H)
752 d += d_wanted_H.normalize() * max_increase_H;
757 if (max_increase_V > 0) {
758 f32 d_wanted_V = d_wanted.Y;
759 if (d_wanted_V > max_increase_V)
760 d.Y += max_increase_V;
761 else if (d_wanted_V < -max_increase_V)
762 d.Y -= max_increase_V;
767 // Finally rotate it again
775 // Temporary option for old move code
776 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
777 std::vector<CollisionInfo> *collision_info)
779 Map *map = &env->getMap();
780 const NodeDefManager *nodemgr = m_client->ndef();
782 v3f position = getPosition();
784 // Copy parent position if local player is attached
786 setPosition(overridePosition);
787 m_sneak_node_exists = false;
791 PlayerSettings &player_settings = getPlayerSettings();
793 // Skip collision detection if noclip mode is used
794 bool fly_allowed = m_client->checkLocalPrivilege("fly");
795 bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip;
796 bool free_move = noclip && fly_allowed && player_settings.free_move;
798 position += m_speed * dtime;
799 setPosition(position);
800 m_sneak_node_exists = false;
807 bool is_valid_position;
812 Check if player is in liquid (the oscillating value)
815 // If in liquid, the threshold of coming out is at higher y
816 pp = floatToInt(position + v3f(0, BS * 0.1, 0), BS);
817 node = map->getNodeNoEx(pp, &is_valid_position);
818 if (is_valid_position) {
819 in_liquid = nodemgr->get(node.getContent()).isLiquid();
820 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
825 // If not in liquid, the threshold of going in is at lower y
826 pp = floatToInt(position + v3f(0, BS * 0.5, 0), BS);
827 node = map->getNodeNoEx(pp, &is_valid_position);
828 if (is_valid_position) {
829 in_liquid = nodemgr->get(node.getContent()).isLiquid();
830 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
837 Check if player is in liquid (the stable value)
839 pp = floatToInt(position + v3f(0, 0, 0), BS);
840 node = map->getNodeNoEx(pp, &is_valid_position);
841 if (is_valid_position)
842 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
844 in_liquid_stable = false;
847 Check if player is climbing
849 pp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS);
850 v3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS);
851 node = map->getNodeNoEx(pp, &is_valid_position);
852 bool is_valid_position2;
853 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
855 if (!(is_valid_position && is_valid_position2))
858 is_climbing = (nodemgr->get(node.getContent()).climbable ||
859 nodemgr->get(node2.getContent()).climbable) && !free_move;
862 Collision uncertainty radius
863 Make it a bit larger than the maximum distance of movement
865 //f32 d = pos_max_d * 1.1;
866 // A fairly large value in here makes moving smoother
868 // This should always apply, otherwise there are glitches
869 sanity_check(d > pos_max_d);
870 // Maximum distance over border for sneaking
871 f32 sneak_max = BS * 0.4;
874 If sneaking, keep in range from the last walked node and don't
877 if (control.sneak && m_sneak_node_exists &&
878 !(fly_allowed && player_settings.free_move) && !in_liquid &&
879 physics_override_sneak) {
880 f32 maxd = 0.5 * BS + sneak_max;
881 v3f lwn_f = intToFloat(m_sneak_node, BS);
882 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
883 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
886 // Move up if necessary
887 f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
888 if (position.Y < new_y)
891 Collision seems broken, since player is sinking when
892 sneaking over the edges of current sneaking_node.
893 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
900 // this shouldn't be hardcoded but transmitted from server
901 float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
903 v3f accel_f = v3f(0, 0, 0);
904 const v3f initial_position = position;
905 const v3f initial_speed = m_speed;
907 collisionMoveResult result = collisionMoveSimple(env, m_client,
908 pos_max_d, m_collisionbox, player_stepheight, dtime,
909 &position, &m_speed, accel_f);
912 If the player's feet touch the topside of any node, this is
915 Player is allowed to jump when this is true.
917 bool touching_ground_was = touching_ground;
918 touching_ground = result.touching_ground;
920 //bool standing_on_unloaded = result.standing_on_unloaded;
923 Check the nodes under the player to see from which node the
924 player is sneaking from, if any. If the node from under
925 the player has been removed, the player falls.
927 f32 position_y_mod = 0.05 * BS;
928 if (m_sneak_node_bb_ymax > 0)
929 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
930 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
931 if (m_sneak_node_exists &&
932 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
933 m_old_node_below_type != "air") {
934 // Old node appears to have been removed; that is,
935 // it wasn't air before but now it is
936 m_need_to_get_new_sneak_node = false;
937 m_sneak_node_exists = false;
938 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
939 // We are on something, so make sure to recalculate the sneak
941 m_need_to_get_new_sneak_node = true;
944 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
945 m_sneak_node_bb_ymax = 0;
946 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
947 v2f player_p2df(position.X, position.Z);
948 f32 min_distance_f = 100000.0 * BS;
949 // If already seeking from some node, compare to it.
950 v3s16 new_sneak_node = m_sneak_node;
951 for (s16 x= -1; x <= 1; x++)
952 for (s16 z= -1; z <= 1; z++) {
953 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
954 v3f pf = intToFloat(p, BS);
955 v2f node_p2df(pf.X, pf.Z);
956 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
957 f32 max_axis_distance_f = MYMAX(
958 std::fabs(player_p2df.X - node_p2df.X),
959 std::fabs(player_p2df.Y - node_p2df.Y));
961 if (distance_f > min_distance_f ||
962 max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)
965 // The node to be sneaked on has to be walkable
966 node = map->getNodeNoEx(p, &is_valid_position);
967 if (!is_valid_position || !nodemgr->get(node).walkable)
969 // And the node above it has to be nonwalkable
970 node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
971 if (!is_valid_position || nodemgr->get(node).walkable)
973 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
974 if (!physics_override_sneak_glitch) {
975 node =map->getNodeNoEx(p + v3s16(0, 2, 0), &is_valid_position);
976 if (!is_valid_position || nodemgr->get(node).walkable)
980 min_distance_f = distance_f;
984 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
986 m_sneak_node = new_sneak_node;
987 m_sneak_node_exists = sneak_node_found;
989 if (sneak_node_found) {
991 MapNode n = map->getNodeNoEx(m_sneak_node);
992 std::vector<aabb3f> nodeboxes;
993 n.getCollisionBoxes(nodemgr, &nodeboxes);
994 for (const auto &box : nodeboxes) {
995 if (box.MaxEdge.Y > cb_max)
996 cb_max = box.MaxEdge.Y;
998 m_sneak_node_bb_ymax = cb_max;
1002 If sneaking, the player's collision box can be in air, so
1003 this has to be set explicitly
1005 if (sneak_node_found && control.sneak)
1006 touching_ground = true;
1010 Set new position but keep sneak node set
1012 bool sneak_node_exists = m_sneak_node_exists;
1013 setPosition(position);
1014 m_sneak_node_exists = sneak_node_exists;
1019 // Dont report if flying
1020 if (collision_info && !(player_settings.free_move && fly_allowed)) {
1021 for (const auto &info : result.collisions) {
1022 collision_info->push_back(info);
1026 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1027 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
1028 // Set camera impact value to be used for view bobbing
1029 camera_impact = getSpeed().Y * -1;
1033 camera_barely_in_ceiling = false;
1034 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1035 MapNode n = map->getNodeNoEx(camera_np);
1036 if (n.getContent() != CONTENT_IGNORE) {
1037 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1038 camera_barely_in_ceiling = true;
1043 Update the node last under the player
1045 m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);
1046 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
1049 Check properties of the node on which the player is standing
1051 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
1052 // Determine if jumping is possible
1053 m_can_jump = touching_ground && !in_liquid;
1054 if (itemgroup_get(f.groups, "disable_jump"))
1056 // Jump key pressed while jumping off from a bouncy block
1057 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1058 m_speed.Y >= -0.5 * BS) {
1059 float jumpspeed = movement_speed_jump * physics_override_jump;
1060 if (m_speed.Y > 1) {
1061 // Reduce boost when speed already is high
1062 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
1064 m_speed.Y += jumpspeed;
1071 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
1074 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1076 // Slip on slippery nodes
1077 const NodeDefManager *nodemgr = env->getGameDef()->ndef();
1078 Map *map = &env->getMap();
1079 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(
1080 getStandingNodePos()));
1083 slippery = itemgroup_get(f.groups, "slippery");
1085 if (slippery >= 1) {
1086 if (speedH == v3f(0.0f)) {
1087 slippery = slippery * 2;
1089 return core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);
1094 void LocalPlayer::handleAutojump(f32 dtime, Environment *env,
1095 const collisionMoveResult &result, const v3f &initial_position,
1096 const v3f &initial_speed, f32 pos_max_d)
1098 PlayerSettings &player_settings = getPlayerSettings();
1099 if (!player_settings.autojump)
1105 bool control_forward = control.up ||
1106 (!control.up && !control.down &&
1107 control.forw_move_joystick_axis < -0.05);
1108 bool could_autojump =
1109 m_can_jump && !control.jump && !control.sneak && control_forward;
1110 if (!could_autojump)
1113 bool horizontal_collision = false;
1114 for (const auto &colinfo : result.collisions) {
1115 if (colinfo.type == COLLISION_NODE && colinfo.plane != 1) {
1116 horizontal_collision = true;
1117 break; // one is enough
1121 // must be running against something to trigger autojumping
1122 if (!horizontal_collision)
1125 // check for nodes above
1126 v3f headpos_min = m_position + m_collisionbox.MinEdge * 0.99f;
1127 v3f headpos_max = m_position + m_collisionbox.MaxEdge * 0.99f;
1128 headpos_min.Y = headpos_max.Y; // top face of collision box
1129 v3s16 ceilpos_min = floatToInt(headpos_min, BS) + v3s16(0, 1, 0);
1130 v3s16 ceilpos_max = floatToInt(headpos_max, BS) + v3s16(0, 1, 0);
1131 const NodeDefManager *ndef = env->getGameDef()->ndef();
1132 bool is_position_valid;
1133 for (s16 z = ceilpos_min.Z; z <= ceilpos_max.Z; z++) {
1134 for (s16 x = ceilpos_min.X; x <= ceilpos_max.X; x++) {
1135 MapNode n = env->getMap().getNodeNoEx(v3s16(x, ceilpos_max.Y, z), &is_position_valid);
1137 if (!is_position_valid)
1138 break; // won't collide with the void outside
1139 if (n.getContent() == CONTENT_IGNORE)
1140 return; // players collide with ignore blocks -> same as walkable
1141 const ContentFeatures &f = ndef->get(n);
1143 return; // would bump head, don't jump
1147 float jump_height = 1.1f; // TODO: better than a magic number
1148 v3f jump_pos = initial_position + v3f(0.0f, jump_height * BS, 0.0f);
1149 v3f jump_speed = initial_speed;
1151 // try at peak of jump, zero step height
1152 collisionMoveResult jump_result = collisionMoveSimple(env, m_client, pos_max_d,
1153 m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed,
1156 // see if we can get a little bit farther horizontally if we had
1158 v3f run_delta = m_position - initial_position;
1160 v3f jump_delta = jump_pos - initial_position;
1161 jump_delta.Y = 0.0f;
1162 if (jump_delta.getLengthSQ() > run_delta.getLengthSQ() * 1.01f) {
1164 m_autojump_time = 0.1f;