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 Check properties of the node on which the player is standing
432 const ContentFeatures &f = nodemgr->get(map->getNode(m_standing_node));
433 const ContentFeatures &f1 = nodemgr->get(map->getNode(m_standing_node + v3s16(0, 1, 0)));
435 // Determine if jumping is possible
436 m_disable_jump = itemgroup_get(f.groups, "disable_jump") ||
437 itemgroup_get(f1.groups, "disable_jump");
438 m_can_jump = ((touching_ground && !is_climbing) || sneak_can_jump) && !m_disable_jump;
440 // Jump key pressed while jumping off from a bouncy block
441 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
442 m_speed.Y >= -0.5f * BS) {
443 float jumpspeed = movement_speed_jump * physics_override_jump;
444 if (m_speed.Y > 1.0f) {
445 // Reduce boost when speed already is high
446 m_speed.Y += jumpspeed / (1.0f + (m_speed.Y / 16.0f));
448 m_speed.Y += jumpspeed;
455 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
458 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
460 move(dtime, env, pos_max_d, NULL);
463 void LocalPlayer::applyControl(float dtime, Environment *env)
466 swimming_vertical = false;
467 swimming_pitch = false;
469 setPitch(control.pitch);
472 // Nullify speed and don't run positioning code if the player is attached
478 PlayerSettings &player_settings = getPlayerSettings();
480 // All vectors are relative to the player's yaw,
481 // (and pitch if pitch move mode enabled),
482 // and will be rotated at the end
483 v3f speedH, speedV; // Horizontal (X, Z) and Vertical (Y)
485 bool fly_allowed = m_client->checkLocalPrivilege("fly");
486 bool fast_allowed = m_client->checkLocalPrivilege("fast");
488 bool free_move = fly_allowed && player_settings.free_move;
489 bool fast_move = fast_allowed && player_settings.fast_move;
490 bool pitch_move = (free_move || in_liquid) && player_settings.pitch_move;
491 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
492 bool fast_climb = fast_move && control.aux1 && !player_settings.aux1_descends;
493 bool always_fly_fast = player_settings.always_fly_fast;
495 // Whether superspeed mode is used or not
496 bool superspeed = false;
498 if (always_fly_fast && free_move && fast_move)
501 // Old descend control
502 if (player_settings.aux1_descends) {
503 // If free movement and fast movement, always move fast
504 if (free_move && fast_move)
507 // Auxiliary button 1 (E)
510 // In free movement mode, aux1 descends
512 speedV.Y = -movement_speed_fast;
514 speedV.Y = -movement_speed_walk;
515 } else if (in_liquid || in_liquid_stable) {
516 speedV.Y = -movement_speed_walk;
517 swimming_vertical = true;
518 } else if (is_climbing) {
519 speedV.Y = -movement_speed_climb;
521 // If not free movement but fast is allowed, aux1 is
528 // New minecraft-like descend control
530 // Auxiliary button 1 (E)
533 // aux1 is "Turbo button"
541 // In free movement mode, sneak descends
542 if (fast_move && (control.aux1 || always_fly_fast))
543 speedV.Y = -movement_speed_fast;
545 speedV.Y = -movement_speed_walk;
546 } else if (in_liquid || in_liquid_stable) {
548 speedV.Y = -movement_speed_fast;
550 speedV.Y = -movement_speed_walk;
551 swimming_vertical = true;
552 } else if (is_climbing) {
554 speedV.Y = -movement_speed_fast;
556 speedV.Y = -movement_speed_climb;
561 speedH = v3f(sin(control.movement_direction), 0.0f, cos(control.movement_direction));
564 // release autojump after a given time
565 m_autojump_time -= dtime;
566 if (m_autojump_time <= 0.0f)
572 if (player_settings.aux1_descends || always_fly_fast) {
574 speedV.Y = movement_speed_fast;
576 speedV.Y = movement_speed_walk;
578 if (fast_move && control.aux1)
579 speedV.Y = movement_speed_fast;
581 speedV.Y = movement_speed_walk;
583 } else if (m_can_jump) {
585 NOTE: The d value in move() affects jump height by
586 raising the height at which the jump speed is kept
587 at its starting value
589 v3f speedJ = getSpeed();
590 if (speedJ.Y >= -0.5f * BS) {
591 speedJ.Y = movement_speed_jump * physics_override_jump;
593 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_JUMP));
595 } else if (in_liquid && !m_disable_jump) {
597 speedV.Y = movement_speed_fast;
599 speedV.Y = movement_speed_walk;
600 swimming_vertical = true;
601 } else if (is_climbing && !m_disable_jump) {
603 speedV.Y = movement_speed_fast;
605 speedV.Y = movement_speed_climb;
609 // The speed of the player (Y is ignored)
610 if (superspeed || (is_climbing && fast_climb) ||
611 ((in_liquid || in_liquid_stable) && fast_climb))
612 speedH = speedH.normalize() * movement_speed_fast;
613 else if (control.sneak && !free_move && !in_liquid && !in_liquid_stable)
614 speedH = speedH.normalize() * movement_speed_crouch;
616 speedH = speedH.normalize() * movement_speed_walk;
618 speedH *= control.movement_speed; /* Apply analog input */
620 // Acceleration increase
621 f32 incH = 0.0f; // Horizontal (X, Z)
622 f32 incV = 0.0f; // Vertical (Y)
623 if ((!touching_ground && !free_move && !is_climbing && !in_liquid) ||
624 (!free_move && m_can_jump && control.jump)) {
625 // Jumping and falling
626 if (superspeed || (fast_move && control.aux1))
627 incH = movement_acceleration_fast * BS * dtime;
629 incH = movement_acceleration_air * BS * dtime;
630 incV = 0.0f; // No vertical acceleration in air
631 } else if (superspeed || (is_climbing && fast_climb) ||
632 ((in_liquid || in_liquid_stable) && fast_climb)) {
633 incH = incV = movement_acceleration_fast * BS * dtime;
635 incH = incV = movement_acceleration_default * BS * dtime;
638 float slip_factor = 1.0f;
639 if (!free_move && !in_liquid && !in_liquid_stable)
640 slip_factor = getSlipFactor(env, speedH);
642 // Don't sink when swimming in pitch mode
643 if (pitch_move && in_liquid) {
644 v3f controlSpeed = speedH + speedV;
645 if (controlSpeed.getLength() > 0.01f)
646 swimming_pitch = true;
649 // Accelerate to target speed with maximum increment
650 accelerate((speedH + speedV) * physics_override_speed,
651 incH * physics_override_speed * slip_factor, incV * physics_override_speed,
655 v3s16 LocalPlayer::getStandingNodePos()
657 if (m_sneak_node_exists)
660 return m_standing_node;
663 v3s16 LocalPlayer::getFootstepNodePos()
665 v3f feet_pos = getPosition() + v3f(0.0f, m_collisionbox.MinEdge.Y, 0.0f);
667 // Emit swimming sound if the player is in liquid
668 if (in_liquid_stable)
669 return floatToInt(feet_pos, BS);
671 // BS * 0.05 below the player's feet ensures a 1/16th height
672 // nodebox is detected instead of the node below it.
674 return floatToInt(feet_pos - v3f(0.0f, BS * 0.05f, 0.0f), BS);
676 // A larger distance below is necessary for a footstep sound
677 // when landing after a jump or fall. BS * 0.5 ensures water
678 // sounds when swimming in 1 node deep water.
679 return floatToInt(feet_pos - v3f(0.0f, BS * 0.5f, 0.0f), BS);
682 v3s16 LocalPlayer::getLightPosition() const
684 return floatToInt(m_position + v3f(0.0f, BS * 1.5f, 0.0f), BS);
687 v3f LocalPlayer::getEyeOffset() const
689 return v3f(0.0f, BS * m_eye_height, 0.0f);
692 ClientActiveObject *LocalPlayer::getParent() const
694 return m_cao ? m_cao->getParent() : nullptr;
697 bool LocalPlayer::isDead() const
699 FATAL_ERROR_IF(!getCAO(), "LocalPlayer's CAO isn't initialized");
700 return !getCAO()->isImmortal() && hp == 0;
704 void LocalPlayer::accelerate(const v3f &target_speed, const f32 max_increase_H,
705 const f32 max_increase_V, const bool use_pitch)
707 const f32 yaw = getYaw();
708 const f32 pitch = getPitch();
709 v3f flat_speed = m_speed;
710 // Rotate speed vector by -yaw and -pitch to make it relative to the player's yaw and pitch
711 flat_speed.rotateXZBy(-yaw);
713 flat_speed.rotateYZBy(-pitch);
715 v3f d_wanted = target_speed - flat_speed;
718 // Then compare the horizontal and vertical components with the wanted speed
719 if (max_increase_H > 0.0f) {
720 v3f d_wanted_H = d_wanted * v3f(1.0f, 0.0f, 1.0f);
721 if (d_wanted_H.getLength() > max_increase_H)
722 d += d_wanted_H.normalize() * max_increase_H;
727 if (max_increase_V > 0.0f) {
728 f32 d_wanted_V = d_wanted.Y;
729 if (d_wanted_V > max_increase_V)
730 d.Y += max_increase_V;
731 else if (d_wanted_V < -max_increase_V)
732 d.Y -= max_increase_V;
737 // Finally rotate it again
745 // Temporary option for old move code
746 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
747 std::vector<CollisionInfo> *collision_info)
749 Map *map = &env->getMap();
750 const NodeDefManager *nodemgr = m_client->ndef();
752 v3f position = getPosition();
754 // Copy parent position if local player is attached
756 setPosition(m_cao->getPosition());
757 m_sneak_node_exists = false;
758 added_velocity = v3f(0.0f);
762 PlayerSettings &player_settings = getPlayerSettings();
764 // Skip collision detection if noclip mode is used
765 bool fly_allowed = m_client->checkLocalPrivilege("fly");
766 bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip;
767 bool free_move = noclip && fly_allowed && player_settings.free_move;
769 position += m_speed * dtime;
770 setPosition(position);
772 touching_ground = false;
773 m_sneak_node_exists = false;
774 added_velocity = v3f(0.0f);
778 m_speed += added_velocity;
779 added_velocity = v3f(0.0f);
784 bool is_valid_position;
789 Check if player is in liquid (the oscillating value)
792 // If in liquid, the threshold of coming out is at higher y
793 pp = floatToInt(position + v3f(0.0f, BS * 0.1f, 0.0f), BS);
794 node = map->getNode(pp, &is_valid_position);
795 if (is_valid_position) {
796 const ContentFeatures &cf = nodemgr->get(node.getContent());
797 in_liquid = cf.liquid_move_physics;
798 move_resistance = cf.move_resistance;
803 // If not in liquid, the threshold of going in is at lower y
804 pp = floatToInt(position + v3f(0.0f, BS * 0.5f, 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;
816 Check if player is in liquid (the stable value)
818 pp = floatToInt(position + v3f(0.0f), BS);
819 node = map->getNode(pp, &is_valid_position);
820 if (is_valid_position)
821 in_liquid_stable = nodemgr->get(node.getContent()).liquid_move_physics;
823 in_liquid_stable = false;
826 Check if player is climbing
828 pp = floatToInt(position + v3f(0.0f, 0.5f * BS, 0.0f), BS);
829 v3s16 pp2 = floatToInt(position + v3f(0.0f, -0.2f * BS, 0.0f), BS);
830 node = map->getNode(pp, &is_valid_position);
831 bool is_valid_position2;
832 MapNode node2 = map->getNode(pp2, &is_valid_position2);
834 if (!(is_valid_position && is_valid_position2))
837 is_climbing = (nodemgr->get(node.getContent()).climbable ||
838 nodemgr->get(node2.getContent()).climbable) && !free_move;
841 Collision uncertainty radius
842 Make it a bit larger than the maximum distance of movement
844 //f32 d = pos_max_d * 1.1;
845 // A fairly large value in here makes moving smoother
847 // This should always apply, otherwise there are glitches
848 sanity_check(d > pos_max_d);
849 // Maximum distance over border for sneaking
850 f32 sneak_max = BS * 0.4f;
853 If sneaking, keep in range from the last walked node and don't
856 if (control.sneak && m_sneak_node_exists &&
857 !(fly_allowed && player_settings.free_move) && !in_liquid &&
858 physics_override_sneak) {
859 f32 maxd = 0.5f * BS + sneak_max;
860 v3f lwn_f = intToFloat(m_sneak_node, BS);
861 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
862 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
865 // Move up if necessary
866 f32 new_y = (lwn_f.Y - 0.5f * BS) + m_sneak_node_bb_ymax;
867 if (position.Y < new_y)
870 Collision seems broken, since player is sinking when
871 sneaking over the edges of current sneaking_node.
872 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
874 if (m_speed.Y < 0.0f)
879 // TODO: This shouldn't be hardcoded but decided by the server
880 float player_stepheight = touching_ground ? (BS * 0.6f) : (BS * 0.2f);
883 const v3f initial_position = position;
884 const v3f initial_speed = m_speed;
886 collisionMoveResult result = collisionMoveSimple(env, m_client,
887 pos_max_d, m_collisionbox, player_stepheight, dtime,
888 &position, &m_speed, accel_f);
890 // Positition was slightly changed; update standing node pos
892 m_standing_node = floatToInt(m_position - v3f(0.0f, 0.1f * BS, 0.0f), BS);
894 m_standing_node = floatToInt(m_position, BS);
897 If the player's feet touch the topside of any node, this is
900 Player is allowed to jump when this is true.
902 bool touching_ground_was = touching_ground;
903 touching_ground = result.touching_ground;
905 //bool standing_on_unloaded = result.standing_on_unloaded;
908 Check the nodes under the player to see from which node the
909 player is sneaking from, if any. If the node from under
910 the player has been removed, the player falls.
912 f32 position_y_mod = 0.05f * BS;
913 if (m_sneak_node_bb_ymax > 0.0f)
914 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
915 v3s16 current_node = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS);
916 if (m_sneak_node_exists &&
917 nodemgr->get(map->getNode(m_old_node_below)).name == "air" &&
918 m_old_node_below_type != "air") {
919 // Old node appears to have been removed; that is,
920 // it wasn't air before but now it is
921 m_need_to_get_new_sneak_node = false;
922 m_sneak_node_exists = false;
923 } else if (nodemgr->get(map->getNode(current_node)).name != "air") {
924 // We are on something, so make sure to recalculate the sneak
926 m_need_to_get_new_sneak_node = true;
929 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
930 m_sneak_node_bb_ymax = 0.0f;
931 v3s16 pos_i_bottom = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS);
932 v2f player_p2df(position.X, position.Z);
933 f32 min_distance_f = 100000.0f * BS;
934 // If already seeking from some node, compare to it.
935 v3s16 new_sneak_node = m_sneak_node;
936 for (s16 x= -1; x <= 1; x++)
937 for (s16 z= -1; z <= 1; z++) {
938 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
939 v3f pf = intToFloat(p, BS);
940 v2f node_p2df(pf.X, pf.Z);
941 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
942 f32 max_axis_distance_f = MYMAX(
943 std::fabs(player_p2df.X - node_p2df.X),
944 std::fabs(player_p2df.Y - node_p2df.Y));
946 if (distance_f > min_distance_f ||
947 max_axis_distance_f > 0.5f * BS + sneak_max + 0.1f * BS)
950 // The node to be sneaked on has to be walkable
951 node = map->getNode(p, &is_valid_position);
952 if (!is_valid_position || !nodemgr->get(node).walkable)
954 // And the node above it has to be nonwalkable
955 node = map->getNode(p + v3s16(0, 1, 0), &is_valid_position);
956 if (!is_valid_position || nodemgr->get(node).walkable)
958 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
959 if (!physics_override_sneak_glitch) {
960 node = map->getNode(p + v3s16(0, 2, 0), &is_valid_position);
961 if (!is_valid_position || nodemgr->get(node).walkable)
965 min_distance_f = distance_f;
969 bool sneak_node_found = (min_distance_f < 100000.0f * BS * 0.9f);
971 m_sneak_node = new_sneak_node;
972 m_sneak_node_exists = sneak_node_found;
974 if (sneak_node_found) {
976 MapNode n = map->getNode(m_sneak_node);
977 std::vector<aabb3f> nodeboxes;
978 n.getCollisionBoxes(nodemgr, &nodeboxes);
979 for (const auto &box : nodeboxes) {
980 if (box.MaxEdge.Y > cb_max)
981 cb_max = box.MaxEdge.Y;
983 m_sneak_node_bb_ymax = cb_max;
987 If sneaking, the player's collision box can be in air, so
988 this has to be set explicitly
990 if (sneak_node_found && control.sneak)
991 touching_ground = true;
995 Set new position but keep sneak node set
997 bool sneak_node_exists = m_sneak_node_exists;
998 setPosition(position);
999 m_sneak_node_exists = sneak_node_exists;
1004 // Don't report if flying
1005 if (collision_info && !(player_settings.free_move && fly_allowed)) {
1006 for (const auto &info : result.collisions) {
1007 collision_info->push_back(info);
1011 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1012 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
1013 // Set camera impact value to be used for view bobbing
1014 camera_impact = getSpeed().Y * -1.0f;
1018 Update the node last under the player
1020 m_old_node_below = floatToInt(position - v3f(0.0f, BS / 2.0f, 0.0f), BS);
1021 m_old_node_below_type = nodemgr->get(map->getNode(m_old_node_below)).name;
1024 Check properties of the node on which the player is standing
1026 const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos()));
1028 // Determine if jumping is possible
1029 m_disable_jump = itemgroup_get(f.groups, "disable_jump");
1030 m_can_jump = touching_ground && !m_disable_jump;
1032 // Jump key pressed while jumping off from a bouncy block
1033 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1034 m_speed.Y >= -0.5f * BS) {
1035 float jumpspeed = movement_speed_jump * physics_override_jump;
1036 if (m_speed.Y > 1.0f) {
1037 // Reduce boost when speed already is high
1038 m_speed.Y += jumpspeed / (1.0f + (m_speed.Y / 16.0f));
1040 m_speed.Y += jumpspeed;
1047 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
1050 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1052 // Slip on slippery nodes
1053 const NodeDefManager *nodemgr = env->getGameDef()->ndef();
1054 Map *map = &env->getMap();
1055 const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos()));
1058 slippery = itemgroup_get(f.groups, "slippery");
1060 if (slippery >= 1) {
1061 if (speedH == v3f(0.0f))
1064 return core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);
1069 void LocalPlayer::handleAutojump(f32 dtime, Environment *env,
1070 const collisionMoveResult &result, const v3f &initial_position,
1071 const v3f &initial_speed, f32 pos_max_d)
1073 PlayerSettings &player_settings = getPlayerSettings();
1074 if (!player_settings.autojump)
1080 bool could_autojump =
1081 m_can_jump && !control.jump && !control.sneak && control.isMoving();
1083 if (!could_autojump)
1086 bool horizontal_collision = false;
1087 for (const auto &colinfo : result.collisions) {
1088 if (colinfo.type == COLLISION_NODE && colinfo.plane != 1) {
1089 horizontal_collision = true;
1090 break; // one is enough
1094 // must be running against something to trigger autojumping
1095 if (!horizontal_collision)
1098 // check for nodes above
1099 v3f headpos_min = m_position + m_collisionbox.MinEdge * 0.99f;
1100 v3f headpos_max = m_position + m_collisionbox.MaxEdge * 0.99f;
1101 headpos_min.Y = headpos_max.Y; // top face of collision box
1102 v3s16 ceilpos_min = floatToInt(headpos_min, BS) + v3s16(0, 1, 0);
1103 v3s16 ceilpos_max = floatToInt(headpos_max, BS) + v3s16(0, 1, 0);
1104 const NodeDefManager *ndef = env->getGameDef()->ndef();
1105 bool is_position_valid;
1106 for (s16 z = ceilpos_min.Z; z <= ceilpos_max.Z; ++z) {
1107 for (s16 x = ceilpos_min.X; x <= ceilpos_max.X; ++x) {
1108 MapNode n = env->getMap().getNode(v3s16(x, ceilpos_max.Y, z), &is_position_valid);
1110 if (!is_position_valid)
1111 break; // won't collide with the void outside
1112 if (n.getContent() == CONTENT_IGNORE)
1113 return; // players collide with ignore blocks -> same as walkable
1114 const ContentFeatures &f = ndef->get(n);
1116 return; // would bump head, don't jump
1120 float jump_height = 1.1f; // TODO: better than a magic number
1121 v3f jump_pos = initial_position + v3f(0.0f, jump_height * BS, 0.0f);
1122 v3f jump_speed = initial_speed;
1124 // try at peak of jump, zero step height
1125 collisionMoveResult jump_result = collisionMoveSimple(env, m_client, pos_max_d,
1126 m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed, v3f(0.0f));
1128 // see if we can get a little bit farther horizontally if we had
1130 v3f run_delta = m_position - initial_position;
1132 v3f jump_delta = jump_pos - initial_position;
1133 jump_delta.Y = 0.0f;
1134 if (jump_delta.getLengthSQ() > run_delta.getLengthSQ() * 1.01f) {
1136 m_autojump_time = 0.1f;