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.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
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.05f * 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.0f, position_y_mod, 0.0f), BS);
86 if (current_node != m_sneak_node) {
87 new_sneak_node_exists = false;
89 node = map->getNode(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.0f * 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) > (0.5f + 0.1f) * BS + sneak_max.X ||
110 fabs(diff.Y) > (0.5f + 0.1f) * BS + sneak_max.Z)
114 // The node to be sneaked on has to be walkable
115 node = map->getNode(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 ceilf((m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS);
123 for (u16 y = 1; y <= height; y++) {
124 node = map->getNode(p + v3s16(0, y, 0), &is_valid_position);
125 if (!is_valid_position || nodemgr->get(node).walkable) {
131 // legacy behaviour: check just one node
132 node = map->getNode(p + v3s16(0, 1, 0), &is_valid_position);
133 ok = is_valid_position && !nodemgr->get(node).walkable;
138 min_distance_f = distance_f;
140 new_sneak_node_exists = true;
143 if (!new_sneak_node_exists)
146 // Update saved top bounding box of sneak node
147 node = map->getNode(m_sneak_node);
148 std::vector<aabb3f> nodeboxes;
149 node.getCollisionBoxes(nodemgr, &nodeboxes);
150 m_sneak_node_bb_top = getNodeBoundingBox(nodeboxes);
152 if (physics_override_sneak_glitch) {
153 // Detect sneak ladder:
154 // Node two meters above sneak node must be solid
155 node = map->getNode(m_sneak_node + v3s16(0, 2, 0),
157 if (is_valid_position && nodemgr->get(node).walkable) {
158 // Node three meters above: must be non-solid
159 node = map->getNode(m_sneak_node + v3s16(0, 3, 0),
161 m_sneak_ladder_detected = is_valid_position &&
162 !nodemgr->get(node).walkable;
168 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
169 std::vector<CollisionInfo> *collision_info)
171 // Node at feet position, update each ClientEnvironment::step()
172 if (!collision_info || collision_info->empty())
173 m_standing_node = floatToInt(m_position, BS);
175 // Temporary option for old move code
176 if (!physics_override_new_move) {
177 old_move(dtime, env, pos_max_d, collision_info);
181 Map *map = &env->getMap();
182 const NodeDefManager *nodemgr = m_client->ndef();
184 v3f position = getPosition();
186 // Copy parent position if local player is attached
188 setPosition(m_cao->getPosition());
189 added_velocity = v3f(0.0f); // ignored
193 PlayerSettings &player_settings = getPlayerSettings();
195 // Skip collision detection if noclip mode is used
196 bool fly_allowed = m_client->checkLocalPrivilege("fly");
197 bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip;
198 bool free_move = player_settings.free_move && fly_allowed;
200 if (noclip && free_move) {
201 position += m_speed * dtime;
202 setPosition(position);
204 touching_ground = false;
205 added_velocity = v3f(0.0f); // ignored
209 m_speed += added_velocity;
210 added_velocity = v3f(0.0f);
216 bool is_valid_position;
221 Check if player is in liquid (the oscillating value)
224 // If in liquid, the threshold of coming out is at higher y
227 pp = floatToInt(position + v3f(0.0f, BS * 0.1f, 0.0f), BS);
228 node = map->getNode(pp, &is_valid_position);
229 if (is_valid_position) {
230 const ContentFeatures &cf = nodemgr->get(node.getContent());
231 in_liquid = cf.liquid_move_physics;
232 move_resistance = cf.move_resistance;
237 // If not in liquid, the threshold of going in is at lower y
239 pp = floatToInt(position + v3f(0.0f, BS * 0.5f, 0.0f), BS);
240 node = map->getNode(pp, &is_valid_position);
241 if (is_valid_position) {
242 const ContentFeatures &cf = nodemgr->get(node.getContent());
243 in_liquid = cf.liquid_move_physics;
244 move_resistance = cf.move_resistance;
252 Check if player is in liquid (the stable value)
254 pp = floatToInt(position + v3f(0.0f), BS);
255 node = map->getNode(pp, &is_valid_position);
256 if (is_valid_position) {
257 in_liquid_stable = nodemgr->get(node.getContent()).liquid_move_physics;
259 in_liquid_stable = false;
263 Check if player is climbing
266 pp = floatToInt(position + v3f(0.0f, 0.5f * BS, 0.0f), BS);
267 v3s16 pp2 = floatToInt(position + v3f(0.0f, -0.2f * BS, 0.0f), BS);
268 node = map->getNode(pp, &is_valid_position);
269 bool is_valid_position2;
270 MapNode node2 = map->getNode(pp2, &is_valid_position2);
272 if (!(is_valid_position && is_valid_position2)) {
275 is_climbing = (nodemgr->get(node.getContent()).climbable ||
276 nodemgr->get(node2.getContent()).climbable) && !free_move;
280 Collision uncertainty radius
281 Make it a bit larger than the maximum distance of movement
283 //f32 d = pos_max_d * 1.1;
284 // A fairly large value in here makes moving smoother
287 // This should always apply, otherwise there are glitches
288 sanity_check(d > pos_max_d);
290 // Player object property step height is multiplied by BS in
291 // /src/script/common/c_content.cpp and /src/content_sao.cpp
292 float player_stepheight = (m_cao == nullptr) ? 0.0f :
293 (touching_ground ? m_cao->getStepHeight() : (0.2f * BS));
296 const v3f initial_position = position;
297 const v3f initial_speed = m_speed;
299 collisionMoveResult result = collisionMoveSimple(env, m_client,
300 pos_max_d, m_collisionbox, player_stepheight, dtime,
301 &position, &m_speed, accel_f);
303 bool could_sneak = control.sneak && !free_move && !in_liquid &&
304 !is_climbing && physics_override_sneak;
306 // Add new collisions to the vector
307 if (collision_info && !free_move) {
308 v3f diff = intToFloat(m_standing_node, BS) - position;
309 f32 distance = diff.getLength();
310 // Force update each ClientEnvironment::step()
311 bool is_first = collision_info->empty();
313 for (const auto &colinfo : result.collisions) {
314 collision_info->push_back(colinfo);
316 if (colinfo.type != COLLISION_NODE ||
317 colinfo.axis != COLLISION_AXIS_Y ||
318 (could_sneak && m_sneak_node_exists))
321 diff = intToFloat(colinfo.node_p, BS) - position;
323 // Find nearest colliding node
324 f32 len = diff.getLength();
325 if (is_first || len < distance) {
326 m_standing_node = colinfo.node_p;
334 If the player's feet touch the topside of any node, this is
337 Player is allowed to jump when this is true.
339 bool touching_ground_was = touching_ground;
340 touching_ground = result.touching_ground;
341 bool sneak_can_jump = false;
343 // Max. distance (X, Z) over border for sneaking determined by collision box
344 // * 0.49 to keep the center just barely on the node
345 v3f sneak_max = m_collisionbox.getExtent() * 0.49;
347 if (m_sneak_ladder_detected) {
348 // restore legacy behaviour (this makes the m_speed.Y hack necessary)
349 sneak_max = v3f(0.4f * BS, 0.0f, 0.4f * BS);
353 If sneaking, keep on top of last walked node and don't fall off
355 if (could_sneak && m_sneak_node_exists) {
356 const v3f sn_f = intToFloat(m_sneak_node, BS);
357 const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge;
358 const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge;
359 const v3f old_pos = position;
360 const v3f old_speed = m_speed;
361 f32 y_diff = bmax.Y - position.Y;
362 m_standing_node = m_sneak_node;
364 // (BS * 0.6f) is the basic stepheight while standing on ground
365 if (y_diff < BS * 0.6f) {
366 // Only center player when they're on the node
367 position.X = rangelim(position.X,
368 bmin.X - sneak_max.X, bmax.X + sneak_max.X);
369 position.Z = rangelim(position.Z,
370 bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z);
372 if (position.X != old_pos.X)
374 if (position.Z != old_pos.Z)
378 if (y_diff > 0 && m_speed.Y <= 0.0f &&
379 (physics_override_sneak_glitch || y_diff < BS * 0.6f)) {
380 // Move player to the maximal height when falling or when
381 // the ledge is climbed on the next step.
383 // Smoothen the movement (based on 'position.Y = bmax.Y')
384 position.Y += y_diff * dtime * 22.0f + BS * 0.01f;
385 position.Y = std::min(position.Y, bmax.Y);
389 // Allow jumping on node edges while sneaking
390 if (m_speed.Y == 0.0f || m_sneak_ladder_detected)
391 sneak_can_jump = true;
393 if (collision_info &&
394 m_speed.Y - old_speed.Y > BS) {
395 // Collide with sneak node, report fall damage
396 CollisionInfo sn_info;
397 sn_info.node_p = m_sneak_node;
398 sn_info.old_speed = old_speed;
399 sn_info.new_speed = m_speed;
400 collision_info->push_back(sn_info);
405 Find the next sneak node if necessary
407 bool new_sneak_node_exists = false;
410 new_sneak_node_exists = updateSneakNode(map, position, sneak_max);
413 Set new position but keep sneak node set
415 setPosition(position);
416 m_sneak_node_exists = new_sneak_node_exists;
422 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
423 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
425 // Set camera impact value to be used for view bobbing
426 camera_impact = getSpeed().Y * -1;
430 camera_barely_in_ceiling = false;
431 v3s16 camera_np = floatToInt(getEyePosition(), BS);
432 MapNode n = map->getNode(camera_np);
433 if (n.getContent() != CONTENT_IGNORE) {
434 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
435 camera_barely_in_ceiling = true;
440 Check properties of the node on which the player is standing
442 const ContentFeatures &f = nodemgr->get(map->getNode(m_standing_node));
443 const ContentFeatures &f1 = nodemgr->get(map->getNode(m_standing_node + v3s16(0, 1, 0)));
445 // Determine if jumping is possible
446 m_disable_jump = itemgroup_get(f.groups, "disable_jump") ||
447 itemgroup_get(f1.groups, "disable_jump");
448 m_can_jump = ((touching_ground && !is_climbing) || sneak_can_jump) && !m_disable_jump;
450 // Jump key pressed while jumping off from a bouncy block
451 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
452 m_speed.Y >= -0.5f * BS) {
453 float jumpspeed = movement_speed_jump * physics_override_jump;
454 if (m_speed.Y > 1.0f) {
455 // Reduce boost when speed already is high
456 m_speed.Y += jumpspeed / (1.0f + (m_speed.Y / 16.0f));
458 m_speed.Y += jumpspeed;
465 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
468 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
470 move(dtime, env, pos_max_d, NULL);
473 void LocalPlayer::applyControl(float dtime, Environment *env)
476 swimming_vertical = false;
477 swimming_pitch = false;
479 setPitch(control.pitch);
482 // Nullify speed and don't run positioning code if the player is attached
488 PlayerSettings &player_settings = getPlayerSettings();
490 // All vectors are relative to the player's yaw,
491 // (and pitch if pitch move mode enabled),
492 // and will be rotated at the end
493 v3f speedH, speedV; // Horizontal (X, Z) and Vertical (Y)
495 bool fly_allowed = m_client->checkLocalPrivilege("fly");
496 bool fast_allowed = m_client->checkLocalPrivilege("fast");
498 bool free_move = fly_allowed && player_settings.free_move;
499 bool fast_move = fast_allowed && player_settings.fast_move;
500 bool pitch_move = (free_move || in_liquid) && player_settings.pitch_move;
501 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
502 bool fast_climb = fast_move && control.aux1 && !player_settings.aux1_descends;
503 bool always_fly_fast = player_settings.always_fly_fast;
505 // Whether superspeed mode is used or not
506 bool superspeed = false;
508 if (always_fly_fast && free_move && fast_move)
511 // Old descend control
512 if (player_settings.aux1_descends) {
513 // If free movement and fast movement, always move fast
514 if (free_move && fast_move)
517 // Auxiliary button 1 (E)
520 // In free movement mode, aux1 descends
522 speedV.Y = -movement_speed_fast;
524 speedV.Y = -movement_speed_walk;
525 } else if (in_liquid || in_liquid_stable) {
526 speedV.Y = -movement_speed_walk;
527 swimming_vertical = true;
528 } else if (is_climbing) {
529 speedV.Y = -movement_speed_climb;
531 // If not free movement but fast is allowed, aux1 is
538 // New minecraft-like descend control
540 // Auxiliary button 1 (E)
543 // aux1 is "Turbo button"
551 // In free movement mode, sneak descends
552 if (fast_move && (control.aux1 || always_fly_fast))
553 speedV.Y = -movement_speed_fast;
555 speedV.Y = -movement_speed_walk;
556 } else if (in_liquid || in_liquid_stable) {
558 speedV.Y = -movement_speed_fast;
560 speedV.Y = -movement_speed_walk;
561 swimming_vertical = true;
562 } else if (is_climbing) {
564 speedV.Y = -movement_speed_fast;
566 speedV.Y = -movement_speed_climb;
571 speedH = v3f(sin(control.movement_direction), 0.0f, cos(control.movement_direction));
574 // release autojump after a given time
575 m_autojump_time -= dtime;
576 if (m_autojump_time <= 0.0f)
582 if (player_settings.aux1_descends || always_fly_fast) {
584 speedV.Y = movement_speed_fast;
586 speedV.Y = movement_speed_walk;
588 if (fast_move && control.aux1)
589 speedV.Y = movement_speed_fast;
591 speedV.Y = movement_speed_walk;
593 } else if (m_can_jump) {
595 NOTE: The d value in move() affects jump height by
596 raising the height at which the jump speed is kept
597 at its starting value
599 v3f speedJ = getSpeed();
600 if (speedJ.Y >= -0.5f * BS) {
601 speedJ.Y = movement_speed_jump * physics_override_jump;
603 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_JUMP));
605 } else if (in_liquid && !m_disable_jump) {
607 speedV.Y = movement_speed_fast;
609 speedV.Y = movement_speed_walk;
610 swimming_vertical = true;
611 } else if (is_climbing && !m_disable_jump) {
613 speedV.Y = movement_speed_fast;
615 speedV.Y = movement_speed_climb;
619 // The speed of the player (Y is ignored)
620 if (superspeed || (is_climbing && fast_climb) ||
621 ((in_liquid || in_liquid_stable) && fast_climb))
622 speedH = speedH.normalize() * movement_speed_fast;
623 else if (control.sneak && !free_move && !in_liquid && !in_liquid_stable)
624 speedH = speedH.normalize() * movement_speed_crouch;
626 speedH = speedH.normalize() * movement_speed_walk;
628 speedH *= control.movement_speed; /* Apply analog input */
630 // Acceleration increase
631 f32 incH = 0.0f; // Horizontal (X, Z)
632 f32 incV = 0.0f; // Vertical (Y)
633 if ((!touching_ground && !free_move && !is_climbing && !in_liquid) ||
634 (!free_move && m_can_jump && control.jump)) {
635 // Jumping and falling
636 if (superspeed || (fast_move && control.aux1))
637 incH = movement_acceleration_fast * BS * dtime;
639 incH = movement_acceleration_air * BS * dtime;
640 incV = 0.0f; // No vertical acceleration in air
641 } else if (superspeed || (is_climbing && fast_climb) ||
642 ((in_liquid || in_liquid_stable) && fast_climb)) {
643 incH = incV = movement_acceleration_fast * BS * dtime;
645 incH = incV = movement_acceleration_default * BS * dtime;
648 float slip_factor = 1.0f;
649 if (!free_move && !in_liquid && !in_liquid_stable)
650 slip_factor = getSlipFactor(env, speedH);
652 // Don't sink when swimming in pitch mode
653 if (pitch_move && in_liquid) {
654 v3f controlSpeed = speedH + speedV;
655 if (controlSpeed.getLength() > 0.01f)
656 swimming_pitch = true;
659 // Accelerate to target speed with maximum increment
660 accelerate((speedH + speedV) * physics_override_speed,
661 incH * physics_override_speed * slip_factor, incV * physics_override_speed,
665 v3s16 LocalPlayer::getStandingNodePos()
667 if (m_sneak_node_exists)
670 return m_standing_node;
673 v3s16 LocalPlayer::getFootstepNodePos()
675 v3f feet_pos = getPosition() + v3f(0.0f, m_collisionbox.MinEdge.Y, 0.0f);
677 // Emit swimming sound if the player is in liquid
678 if (in_liquid_stable)
679 return floatToInt(feet_pos, BS);
681 // BS * 0.05 below the player's feet ensures a 1/16th height
682 // nodebox is detected instead of the node below it.
684 return floatToInt(feet_pos - v3f(0.0f, BS * 0.05f, 0.0f), BS);
686 // A larger distance below is necessary for a footstep sound
687 // when landing after a jump or fall. BS * 0.5 ensures water
688 // sounds when swimming in 1 node deep water.
689 return floatToInt(feet_pos - v3f(0.0f, BS * 0.5f, 0.0f), BS);
692 v3s16 LocalPlayer::getLightPosition() const
694 return floatToInt(m_position + v3f(0.0f, BS * 1.5f, 0.0f), BS);
697 v3f LocalPlayer::getEyeOffset() const
699 float eye_height = camera_barely_in_ceiling ? m_eye_height - 0.125f : m_eye_height;
700 return v3f(0.0f, BS * eye_height, 0.0f);
703 ClientActiveObject *LocalPlayer::getParent() const
705 return m_cao ? m_cao->getParent() : nullptr;
708 bool LocalPlayer::isDead() const
710 FATAL_ERROR_IF(!getCAO(), "LocalPlayer's CAO isn't initialized");
711 return !getCAO()->isImmortal() && hp == 0;
715 void LocalPlayer::accelerate(const v3f &target_speed, const f32 max_increase_H,
716 const f32 max_increase_V, const bool use_pitch)
718 const f32 yaw = getYaw();
719 const f32 pitch = getPitch();
720 v3f flat_speed = m_speed;
721 // Rotate speed vector by -yaw and -pitch to make it relative to the player's yaw and pitch
722 flat_speed.rotateXZBy(-yaw);
724 flat_speed.rotateYZBy(-pitch);
726 v3f d_wanted = target_speed - flat_speed;
729 // Then compare the horizontal and vertical components with the wanted speed
730 if (max_increase_H > 0.0f) {
731 v3f d_wanted_H = d_wanted * v3f(1.0f, 0.0f, 1.0f);
732 if (d_wanted_H.getLength() > max_increase_H)
733 d += d_wanted_H.normalize() * max_increase_H;
738 if (max_increase_V > 0.0f) {
739 f32 d_wanted_V = d_wanted.Y;
740 if (d_wanted_V > max_increase_V)
741 d.Y += max_increase_V;
742 else if (d_wanted_V < -max_increase_V)
743 d.Y -= max_increase_V;
748 // Finally rotate it again
756 // Temporary option for old move code
757 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
758 std::vector<CollisionInfo> *collision_info)
760 Map *map = &env->getMap();
761 const NodeDefManager *nodemgr = m_client->ndef();
763 v3f position = getPosition();
765 // Copy parent position if local player is attached
767 setPosition(m_cao->getPosition());
768 m_sneak_node_exists = false;
769 added_velocity = v3f(0.0f);
773 PlayerSettings &player_settings = getPlayerSettings();
775 // Skip collision detection if noclip mode is used
776 bool fly_allowed = m_client->checkLocalPrivilege("fly");
777 bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip;
778 bool free_move = noclip && fly_allowed && player_settings.free_move;
780 position += m_speed * dtime;
781 setPosition(position);
783 touching_ground = false;
784 m_sneak_node_exists = false;
785 added_velocity = v3f(0.0f);
789 m_speed += added_velocity;
790 added_velocity = v3f(0.0f);
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.0f, BS * 0.1f, 0.0f), BS);
805 node = map->getNode(pp, &is_valid_position);
806 if (is_valid_position) {
807 const ContentFeatures &cf = nodemgr->get(node.getContent());
808 in_liquid = cf.liquid_move_physics;
809 move_resistance = cf.move_resistance;
814 // If not in liquid, the threshold of going in is at lower y
815 pp = floatToInt(position + v3f(0.0f, BS * 0.5f, 0.0f), BS);
816 node = map->getNode(pp, &is_valid_position);
817 if (is_valid_position) {
818 const ContentFeatures &cf = nodemgr->get(node.getContent());
819 in_liquid = cf.liquid_move_physics;
820 move_resistance = cf.move_resistance;
827 Check if player is in liquid (the stable value)
829 pp = floatToInt(position + v3f(0.0f), BS);
830 node = map->getNode(pp, &is_valid_position);
831 if (is_valid_position)
832 in_liquid_stable = nodemgr->get(node.getContent()).liquid_move_physics;
834 in_liquid_stable = false;
837 Check if player is climbing
839 pp = floatToInt(position + v3f(0.0f, 0.5f * BS, 0.0f), BS);
840 v3s16 pp2 = floatToInt(position + v3f(0.0f, -0.2f * BS, 0.0f), BS);
841 node = map->getNode(pp, &is_valid_position);
842 bool is_valid_position2;
843 MapNode node2 = map->getNode(pp2, &is_valid_position2);
845 if (!(is_valid_position && is_valid_position2))
848 is_climbing = (nodemgr->get(node.getContent()).climbable ||
849 nodemgr->get(node2.getContent()).climbable) && !free_move;
852 Collision uncertainty radius
853 Make it a bit larger than the maximum distance of movement
855 //f32 d = pos_max_d * 1.1;
856 // A fairly large value in here makes moving smoother
858 // This should always apply, otherwise there are glitches
859 sanity_check(d > pos_max_d);
860 // Maximum distance over border for sneaking
861 f32 sneak_max = BS * 0.4f;
864 If sneaking, keep in range from the last walked node and don't
867 if (control.sneak && m_sneak_node_exists &&
868 !(fly_allowed && player_settings.free_move) && !in_liquid &&
869 physics_override_sneak) {
870 f32 maxd = 0.5f * BS + sneak_max;
871 v3f lwn_f = intToFloat(m_sneak_node, BS);
872 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
873 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
876 // Move up if necessary
877 f32 new_y = (lwn_f.Y - 0.5f * BS) + m_sneak_node_bb_ymax;
878 if (position.Y < new_y)
881 Collision seems broken, since player is sinking when
882 sneaking over the edges of current sneaking_node.
883 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
885 if (m_speed.Y < 0.0f)
890 // TODO: This shouldn't be hardcoded but decided by the server
891 float player_stepheight = touching_ground ? (BS * 0.6f) : (BS * 0.2f);
894 const v3f initial_position = position;
895 const v3f initial_speed = m_speed;
897 collisionMoveResult result = collisionMoveSimple(env, m_client,
898 pos_max_d, m_collisionbox, player_stepheight, dtime,
899 &position, &m_speed, accel_f);
901 // Positition was slightly changed; update standing node pos
903 m_standing_node = floatToInt(m_position - v3f(0.0f, 0.1f * BS, 0.0f), BS);
905 m_standing_node = floatToInt(m_position, BS);
908 If the player's feet touch the topside of any node, this is
911 Player is allowed to jump when this is true.
913 bool touching_ground_was = touching_ground;
914 touching_ground = result.touching_ground;
916 //bool standing_on_unloaded = result.standing_on_unloaded;
919 Check the nodes under the player to see from which node the
920 player is sneaking from, if any. If the node from under
921 the player has been removed, the player falls.
923 f32 position_y_mod = 0.05f * BS;
924 if (m_sneak_node_bb_ymax > 0.0f)
925 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
926 v3s16 current_node = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS);
927 if (m_sneak_node_exists &&
928 nodemgr->get(map->getNode(m_old_node_below)).name == "air" &&
929 m_old_node_below_type != "air") {
930 // Old node appears to have been removed; that is,
931 // it wasn't air before but now it is
932 m_need_to_get_new_sneak_node = false;
933 m_sneak_node_exists = false;
934 } else if (nodemgr->get(map->getNode(current_node)).name != "air") {
935 // We are on something, so make sure to recalculate the sneak
937 m_need_to_get_new_sneak_node = true;
940 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
941 m_sneak_node_bb_ymax = 0.0f;
942 v3s16 pos_i_bottom = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS);
943 v2f player_p2df(position.X, position.Z);
944 f32 min_distance_f = 100000.0f * BS;
945 // If already seeking from some node, compare to it.
946 v3s16 new_sneak_node = m_sneak_node;
947 for (s16 x= -1; x <= 1; x++)
948 for (s16 z= -1; z <= 1; z++) {
949 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
950 v3f pf = intToFloat(p, BS);
951 v2f node_p2df(pf.X, pf.Z);
952 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
953 f32 max_axis_distance_f = MYMAX(
954 std::fabs(player_p2df.X - node_p2df.X),
955 std::fabs(player_p2df.Y - node_p2df.Y));
957 if (distance_f > min_distance_f ||
958 max_axis_distance_f > 0.5f * BS + sneak_max + 0.1f * BS)
961 // The node to be sneaked on has to be walkable
962 node = map->getNode(p, &is_valid_position);
963 if (!is_valid_position || !nodemgr->get(node).walkable)
965 // And the node above it has to be nonwalkable
966 node = map->getNode(p + v3s16(0, 1, 0), &is_valid_position);
967 if (!is_valid_position || nodemgr->get(node).walkable)
969 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
970 if (!physics_override_sneak_glitch) {
971 node = map->getNode(p + v3s16(0, 2, 0), &is_valid_position);
972 if (!is_valid_position || nodemgr->get(node).walkable)
976 min_distance_f = distance_f;
980 bool sneak_node_found = (min_distance_f < 100000.0f * BS * 0.9f);
982 m_sneak_node = new_sneak_node;
983 m_sneak_node_exists = sneak_node_found;
985 if (sneak_node_found) {
987 MapNode n = map->getNode(m_sneak_node);
988 std::vector<aabb3f> nodeboxes;
989 n.getCollisionBoxes(nodemgr, &nodeboxes);
990 for (const auto &box : nodeboxes) {
991 if (box.MaxEdge.Y > cb_max)
992 cb_max = box.MaxEdge.Y;
994 m_sneak_node_bb_ymax = cb_max;
998 If sneaking, the player's collision box can be in air, so
999 this has to be set explicitly
1001 if (sneak_node_found && control.sneak)
1002 touching_ground = true;
1006 Set new position but keep sneak node set
1008 bool sneak_node_exists = m_sneak_node_exists;
1009 setPosition(position);
1010 m_sneak_node_exists = sneak_node_exists;
1015 // Don't report if flying
1016 if (collision_info && !(player_settings.free_move && fly_allowed)) {
1017 for (const auto &info : result.collisions) {
1018 collision_info->push_back(info);
1022 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1023 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
1024 // Set camera impact value to be used for view bobbing
1025 camera_impact = getSpeed().Y * -1.0f;
1029 camera_barely_in_ceiling = false;
1030 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1031 MapNode n = map->getNode(camera_np);
1032 if (n.getContent() != CONTENT_IGNORE) {
1033 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1034 camera_barely_in_ceiling = true;
1039 Update the node last under the player
1041 m_old_node_below = floatToInt(position - v3f(0.0f, BS / 2.0f, 0.0f), BS);
1042 m_old_node_below_type = nodemgr->get(map->getNode(m_old_node_below)).name;
1045 Check properties of the node on which the player is standing
1047 const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos()));
1049 // Determine if jumping is possible
1050 m_disable_jump = itemgroup_get(f.groups, "disable_jump");
1051 m_can_jump = touching_ground && !m_disable_jump;
1053 // Jump key pressed while jumping off from a bouncy block
1054 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1055 m_speed.Y >= -0.5f * BS) {
1056 float jumpspeed = movement_speed_jump * physics_override_jump;
1057 if (m_speed.Y > 1.0f) {
1058 // Reduce boost when speed already is high
1059 m_speed.Y += jumpspeed / (1.0f + (m_speed.Y / 16.0f));
1061 m_speed.Y += jumpspeed;
1068 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
1071 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1073 // Slip on slippery nodes
1074 const NodeDefManager *nodemgr = env->getGameDef()->ndef();
1075 Map *map = &env->getMap();
1076 const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos()));
1079 slippery = itemgroup_get(f.groups, "slippery");
1081 if (slippery >= 1) {
1082 if (speedH == v3f(0.0f))
1085 return core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);
1090 void LocalPlayer::handleAutojump(f32 dtime, Environment *env,
1091 const collisionMoveResult &result, const v3f &initial_position,
1092 const v3f &initial_speed, f32 pos_max_d)
1094 PlayerSettings &player_settings = getPlayerSettings();
1095 if (!player_settings.autojump)
1101 bool could_autojump =
1102 m_can_jump && !control.jump && !control.sneak && control.isMoving();
1104 if (!could_autojump)
1107 bool horizontal_collision = false;
1108 for (const auto &colinfo : result.collisions) {
1109 if (colinfo.type == COLLISION_NODE && colinfo.plane != 1) {
1110 horizontal_collision = true;
1111 break; // one is enough
1115 // must be running against something to trigger autojumping
1116 if (!horizontal_collision)
1119 // check for nodes above
1120 v3f headpos_min = m_position + m_collisionbox.MinEdge * 0.99f;
1121 v3f headpos_max = m_position + m_collisionbox.MaxEdge * 0.99f;
1122 headpos_min.Y = headpos_max.Y; // top face of collision box
1123 v3s16 ceilpos_min = floatToInt(headpos_min, BS) + v3s16(0, 1, 0);
1124 v3s16 ceilpos_max = floatToInt(headpos_max, BS) + v3s16(0, 1, 0);
1125 const NodeDefManager *ndef = env->getGameDef()->ndef();
1126 bool is_position_valid;
1127 for (s16 z = ceilpos_min.Z; z <= ceilpos_max.Z; ++z) {
1128 for (s16 x = ceilpos_min.X; x <= ceilpos_max.X; ++x) {
1129 MapNode n = env->getMap().getNode(v3s16(x, ceilpos_max.Y, z), &is_position_valid);
1131 if (!is_position_valid)
1132 break; // won't collide with the void outside
1133 if (n.getContent() == CONTENT_IGNORE)
1134 return; // players collide with ignore blocks -> same as walkable
1135 const ContentFeatures &f = ndef->get(n);
1137 return; // would bump head, don't jump
1141 float jump_height = 1.1f; // TODO: better than a magic number
1142 v3f jump_pos = initial_position + v3f(0.0f, jump_height * BS, 0.0f);
1143 v3f jump_speed = initial_speed;
1145 // try at peak of jump, zero step height
1146 collisionMoveResult jump_result = collisionMoveSimple(env, m_client, pos_max_d,
1147 m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed, v3f(0.0f));
1149 // see if we can get a little bit farther horizontally if we had
1151 v3f run_delta = m_position - initial_position;
1153 v3f jump_delta = jump_pos - initial_position;
1154 jump_delta.Y = 0.0f;
1155 if (jump_delta.getLengthSQ() > run_delta.getLengthSQ() * 1.01f) {
1157 m_autojump_time = 0.1f;