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;
472 setPitch(control.pitch);
475 // Nullify speed and don't run positioning code if the player is attached
478 setSpeed(v3f(0,0,0));
482 PlayerSettings &player_settings = getPlayerSettings();
484 // All vectors are relative to the player's yaw,
485 // (and pitch if pitch fly mode enabled),
486 // and will be rotated at the end
487 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
488 v3f speedV = v3f(0,0,0); // Vertical (Y)
490 bool fly_allowed = m_client->checkLocalPrivilege("fly");
491 bool fast_allowed = m_client->checkLocalPrivilege("fast");
493 bool free_move = fly_allowed && player_settings.free_move;
494 bool fast_move = fast_allowed && player_settings.fast_move;
495 bool pitch_fly = free_move && player_settings.pitch_fly;
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 += v3f(0,0,1);
589 if (continuous_forward) {
593 speedH += v3f(0,0,1);
597 speedH -= v3f(0,0,1);
599 if (!control.up && !control.down) {
600 speedH -= v3f(0,0,1) *
601 (control.forw_move_joystick_axis / 32767.f);
604 speedH += v3f(-1,0,0);
607 speedH += v3f(1,0,0);
609 if (!control.left && !control.right) {
610 speedH += v3f(1,0,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 accelerate((speedH + speedV) * physics_override_speed,
690 incH * physics_override_speed * slip_factor, incV * physics_override_speed,
694 v3s16 LocalPlayer::getStandingNodePos()
696 if(m_sneak_node_exists)
698 return m_standing_node;
701 v3s16 LocalPlayer::getFootstepNodePos()
703 if (in_liquid_stable)
704 // Emit swimming sound if the player is in liquid
705 return floatToInt(getPosition(), BS);
707 // BS * 0.05 below the player's feet ensures a 1/16th height
708 // nodebox is detected instead of the node below it.
709 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
710 // A larger distance below is necessary for a footstep sound
711 // when landing after a jump or fall. BS * 0.5 ensures water
712 // sounds when swimming in 1 node deep water.
713 return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
716 v3s16 LocalPlayer::getLightPosition() const
718 return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
721 v3f LocalPlayer::getEyeOffset() const
723 float eye_height = camera_barely_in_ceiling ?
724 m_eye_height - 0.125f : m_eye_height;
725 return v3f(0, BS * eye_height, 0);
729 void LocalPlayer::accelerate(const v3f &target_speed, const f32 max_increase_H,
730 const f32 max_increase_V, const bool use_pitch)
732 const f32 yaw = getYaw();
733 const f32 pitch = getPitch();
734 v3f flat_speed = m_speed;
735 // Rotate speed vector by -yaw and -pitch to make it relative to the player's yaw and pitch
736 flat_speed.rotateXZBy(-yaw);
738 flat_speed.rotateYZBy(-pitch);
740 v3f d_wanted = target_speed - flat_speed;
743 // Then compare the horizontal and vertical components with the wanted speed
744 if (max_increase_H > 0) {
745 v3f d_wanted_H = d_wanted * v3f(1,0,1);
746 if (d_wanted_H.getLength() > max_increase_H)
747 d += d_wanted_H.normalize() * max_increase_H;
752 if (max_increase_V > 0) {
753 f32 d_wanted_V = d_wanted.Y;
754 if (d_wanted_V > max_increase_V)
755 d.Y += max_increase_V;
756 else if (d_wanted_V < -max_increase_V)
757 d.Y -= max_increase_V;
762 // Finally rotate it again
770 // Temporary option for old move code
771 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
772 std::vector<CollisionInfo> *collision_info)
774 Map *map = &env->getMap();
775 const NodeDefManager *nodemgr = m_client->ndef();
777 v3f position = getPosition();
779 // Copy parent position if local player is attached
781 setPosition(overridePosition);
782 m_sneak_node_exists = false;
786 PlayerSettings &player_settings = getPlayerSettings();
788 // Skip collision detection if noclip mode is used
789 bool fly_allowed = m_client->checkLocalPrivilege("fly");
790 bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip;
791 bool free_move = noclip && fly_allowed && player_settings.free_move;
793 position += m_speed * dtime;
794 setPosition(position);
795 m_sneak_node_exists = false;
802 bool is_valid_position;
807 Check if player is in liquid (the oscillating value)
810 // If in liquid, the threshold of coming out is at higher y
811 pp = floatToInt(position + v3f(0, BS * 0.1, 0), BS);
812 node = map->getNodeNoEx(pp, &is_valid_position);
813 if (is_valid_position) {
814 in_liquid = nodemgr->get(node.getContent()).isLiquid();
815 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
820 // If not in liquid, the threshold of going in is at lower y
821 pp = floatToInt(position + v3f(0, BS * 0.5, 0), BS);
822 node = map->getNodeNoEx(pp, &is_valid_position);
823 if (is_valid_position) {
824 in_liquid = nodemgr->get(node.getContent()).isLiquid();
825 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
832 Check if player is in liquid (the stable value)
834 pp = floatToInt(position + v3f(0, 0, 0), BS);
835 node = map->getNodeNoEx(pp, &is_valid_position);
836 if (is_valid_position)
837 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
839 in_liquid_stable = false;
842 Check if player is climbing
844 pp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS);
845 v3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS);
846 node = map->getNodeNoEx(pp, &is_valid_position);
847 bool is_valid_position2;
848 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
850 if (!(is_valid_position && is_valid_position2))
853 is_climbing = (nodemgr->get(node.getContent()).climbable ||
854 nodemgr->get(node2.getContent()).climbable) && !free_move;
857 Collision uncertainty radius
858 Make it a bit larger than the maximum distance of movement
860 //f32 d = pos_max_d * 1.1;
861 // A fairly large value in here makes moving smoother
863 // This should always apply, otherwise there are glitches
864 sanity_check(d > pos_max_d);
865 // Maximum distance over border for sneaking
866 f32 sneak_max = BS * 0.4;
869 If sneaking, keep in range from the last walked node and don't
872 if (control.sneak && m_sneak_node_exists &&
873 !(fly_allowed && player_settings.free_move) && !in_liquid &&
874 physics_override_sneak) {
875 f32 maxd = 0.5 * BS + sneak_max;
876 v3f lwn_f = intToFloat(m_sneak_node, BS);
877 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
878 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
881 // Move up if necessary
882 f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
883 if (position.Y < new_y)
886 Collision seems broken, since player is sinking when
887 sneaking over the edges of current sneaking_node.
888 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
895 // this shouldn't be hardcoded but transmitted from server
896 float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
898 v3f accel_f = v3f(0, 0, 0);
899 const v3f initial_position = position;
900 const v3f initial_speed = m_speed;
902 collisionMoveResult result = collisionMoveSimple(env, m_client,
903 pos_max_d, m_collisionbox, player_stepheight, dtime,
904 &position, &m_speed, accel_f);
907 If the player's feet touch the topside of any node, this is
910 Player is allowed to jump when this is true.
912 bool touching_ground_was = touching_ground;
913 touching_ground = result.touching_ground;
915 //bool standing_on_unloaded = result.standing_on_unloaded;
918 Check the nodes under the player to see from which node the
919 player is sneaking from, if any. If the node from under
920 the player has been removed, the player falls.
922 f32 position_y_mod = 0.05 * BS;
923 if (m_sneak_node_bb_ymax > 0)
924 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
925 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
926 if (m_sneak_node_exists &&
927 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
928 m_old_node_below_type != "air") {
929 // Old node appears to have been removed; that is,
930 // it wasn't air before but now it is
931 m_need_to_get_new_sneak_node = false;
932 m_sneak_node_exists = false;
933 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
934 // We are on something, so make sure to recalculate the sneak
936 m_need_to_get_new_sneak_node = true;
939 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
940 m_sneak_node_bb_ymax = 0;
941 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
942 v2f player_p2df(position.X, position.Z);
943 f32 min_distance_f = 100000.0 * BS;
944 // If already seeking from some node, compare to it.
945 v3s16 new_sneak_node = m_sneak_node;
946 for (s16 x= -1; x <= 1; x++)
947 for (s16 z= -1; z <= 1; z++) {
948 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
949 v3f pf = intToFloat(p, BS);
950 v2f node_p2df(pf.X, pf.Z);
951 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
952 f32 max_axis_distance_f = MYMAX(
953 std::fabs(player_p2df.X - node_p2df.X),
954 std::fabs(player_p2df.Y - node_p2df.Y));
956 if (distance_f > min_distance_f ||
957 max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)
960 // The node to be sneaked on has to be walkable
961 node = map->getNodeNoEx(p, &is_valid_position);
962 if (!is_valid_position || !nodemgr->get(node).walkable)
964 // And the node above it has to be nonwalkable
965 node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
966 if (!is_valid_position || nodemgr->get(node).walkable)
968 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
969 if (!physics_override_sneak_glitch) {
970 node =map->getNodeNoEx(p + v3s16(0, 2, 0), &is_valid_position);
971 if (!is_valid_position || nodemgr->get(node).walkable)
975 min_distance_f = distance_f;
979 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
981 m_sneak_node = new_sneak_node;
982 m_sneak_node_exists = sneak_node_found;
984 if (sneak_node_found) {
986 MapNode n = map->getNodeNoEx(m_sneak_node);
987 std::vector<aabb3f> nodeboxes;
988 n.getCollisionBoxes(nodemgr, &nodeboxes);
989 for (const auto &box : nodeboxes) {
990 if (box.MaxEdge.Y > cb_max)
991 cb_max = box.MaxEdge.Y;
993 m_sneak_node_bb_ymax = cb_max;
997 If sneaking, the player's collision box can be in air, so
998 this has to be set explicitly
1000 if (sneak_node_found && control.sneak)
1001 touching_ground = true;
1005 Set new position but keep sneak node set
1007 bool sneak_node_exists = m_sneak_node_exists;
1008 setPosition(position);
1009 m_sneak_node_exists = sneak_node_exists;
1014 // Dont report if flying
1015 if (collision_info && !(player_settings.free_move && fly_allowed)) {
1016 for (const auto &info : result.collisions) {
1017 collision_info->push_back(info);
1021 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1022 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
1023 // Set camera impact value to be used for view bobbing
1024 camera_impact = getSpeed().Y * -1;
1028 camera_barely_in_ceiling = false;
1029 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1030 MapNode n = map->getNodeNoEx(camera_np);
1031 if (n.getContent() != CONTENT_IGNORE) {
1032 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1033 camera_barely_in_ceiling = true;
1038 Update the node last under the player
1040 m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);
1041 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
1044 Check properties of the node on which the player is standing
1046 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
1047 // Determine if jumping is possible
1048 m_can_jump = touching_ground && !in_liquid;
1049 if (itemgroup_get(f.groups, "disable_jump"))
1051 // Jump key pressed while jumping off from a bouncy block
1052 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1053 m_speed.Y >= -0.5 * BS) {
1054 float jumpspeed = movement_speed_jump * physics_override_jump;
1055 if (m_speed.Y > 1) {
1056 // Reduce boost when speed already is high
1057 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
1059 m_speed.Y += jumpspeed;
1066 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
1069 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1071 // Slip on slippery nodes
1072 const NodeDefManager *nodemgr = env->getGameDef()->ndef();
1073 Map *map = &env->getMap();
1074 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(
1075 getStandingNodePos()));
1078 slippery = itemgroup_get(f.groups, "slippery");
1080 if (slippery >= 1) {
1081 if (speedH == v3f(0.0f)) {
1082 slippery = slippery * 2;
1084 return core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);
1089 void LocalPlayer::handleAutojump(f32 dtime, Environment *env,
1090 const collisionMoveResult &result, const v3f &initial_position,
1091 const v3f &initial_speed, f32 pos_max_d)
1093 PlayerSettings &player_settings = getPlayerSettings();
1094 if (!player_settings.autojump)
1098 // release autojump after a given time
1099 m_autojump_time -= dtime;
1100 if (m_autojump_time <= 0.0f)
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;