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"
30 #include "util/pointedthing.h"
31 #include "client/game.h"
37 LocalPlayer::LocalPlayer(Client *client, const char *name):
38 Player(name, client->idef()),
43 static aabb3f getNodeBoundingBox(const std::vector<aabb3f> &nodeboxes)
45 if (nodeboxes.empty())
46 return aabb3f(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
50 std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
51 b_max = aabb3f(it->MinEdge, it->MaxEdge);
54 for (; it != nodeboxes.end(); ++it)
55 b_max.addInternalBox(*it);
60 bool LocalPlayer::updateSneakNode(Map *map, const v3f &position,
63 static const v3s16 dir9_center[9] = {
75 const NodeDefManager *nodemgr = m_client->ndef();
77 bool is_valid_position;
78 bool new_sneak_node_exists = m_sneak_node_exists;
80 // We want the top of the sneak node to be below the players feet
81 f32 position_y_mod = 0.05f * BS;
82 if (m_sneak_node_exists)
83 position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - position_y_mod;
85 // Get position of current standing node
86 const v3s16 current_node = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS);
88 if (current_node != m_sneak_node) {
89 new_sneak_node_exists = false;
91 node = map->getNode(current_node, &is_valid_position);
92 if (!is_valid_position || nodemgr->get(node).walkable)
93 new_sneak_node_exists = false;
96 // Keep old sneak node
97 if (new_sneak_node_exists)
100 // Get new sneak node
101 m_sneak_ladder_detected = false;
102 f32 min_distance_f = 100000.0f * BS;
104 for (const auto &d : dir9_center) {
105 const v3s16 p = current_node + d;
106 const v3f pf = intToFloat(p, BS);
107 const v2f diff(position.X - pf.X, position.Z - pf.Z);
108 f32 distance_f = diff.getLength();
110 if (distance_f > min_distance_f ||
111 fabs(diff.X) > (0.5f + 0.1f) * BS + sneak_max.X ||
112 fabs(diff.Y) > (0.5f + 0.1f) * BS + sneak_max.Z)
116 // The node to be sneaked on has to be walkable
117 node = map->getNode(p, &is_valid_position);
118 if (!is_valid_position || ! nodemgr->get(node).walkable)
120 // And the node(s) above have to be nonwalkable
122 if (!physics_override_sneak_glitch) {
124 ceilf((m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS);
125 for (u16 y = 1; y <= height; y++) {
126 node = map->getNode(p + v3s16(0, y, 0), &is_valid_position);
127 if (!is_valid_position || nodemgr->get(node).walkable) {
133 // legacy behaviour: check just one node
134 node = map->getNode(p + v3s16(0, 1, 0), &is_valid_position);
135 ok = is_valid_position && ! nodemgr->get(node).walkable;
140 min_distance_f = distance_f;
142 new_sneak_node_exists = true;
145 if (!new_sneak_node_exists)
148 // Update saved top bounding box of sneak node
149 node = map->getNode(m_sneak_node);
150 std::vector<aabb3f> nodeboxes;
151 node.getCollisionBoxes(nodemgr, &nodeboxes);
152 m_sneak_node_bb_top = getNodeBoundingBox(nodeboxes);
154 if (physics_override_sneak_glitch) {
155 // Detect sneak ladder:
156 // Node two meters above sneak node must be solid
157 node = map->getNode(m_sneak_node + v3s16(0, 2, 0),
159 if (is_valid_position && nodemgr->get(node).walkable) {
160 // Node three meters above: must be non-solid
161 node = map->getNode(m_sneak_node + v3s16(0, 3, 0),
163 m_sneak_ladder_detected = is_valid_position &&
164 ! nodemgr->get(node).walkable;
170 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
171 std::vector<CollisionInfo> *collision_info)
173 if (m_cao && m_cao->m_waiting_for_reattach > 0)
174 m_cao->m_waiting_for_reattach -= dtime;
176 // Node at feet position, update each ClientEnvironment::step()
177 if (!collision_info || collision_info->empty())
178 m_standing_node = floatToInt(m_position, BS);
180 // Temporary option for old move code
181 if (!physics_override_new_move) {
182 old_move(dtime, env, pos_max_d, collision_info);
186 Map *map = &env->getMap();
187 const NodeDefManager *nodemgr = m_client->ndef();
189 v3f position = getPosition();
191 // Copy parent position if local player is attached
193 setPosition(m_cao->getPosition());
194 added_velocity = v3f(0.0f); // ignored
198 PlayerSettings &player_settings = getPlayerSettings();
200 // Skip collision detection if noclip mode is used
201 bool fly_allowed = m_client->checkLocalPrivilege("fly") || g_settings->getBool("freecam");
202 bool noclip = (m_client->checkLocalPrivilege("noclip") && player_settings.noclip) || g_settings->getBool("freecam");
203 bool free_move = (player_settings.free_move && fly_allowed) || g_settings->getBool("freecam");
205 if (noclip && free_move) {
206 position += m_speed * dtime;
207 setPosition(position);
209 touching_ground = false;
210 added_velocity = v3f(0.0f); // ignored
214 m_speed += added_velocity;
215 added_velocity = v3f(0.0f);
221 bool is_valid_position;
226 Check if player is in liquid (the oscillating value)
229 // If in liquid, the threshold of coming out is at higher y
232 pp = floatToInt(position + v3f(0.0f, BS * 0.1f, 0.0f), BS);
233 node = map->getNode(pp, &is_valid_position);
234 if (is_valid_position) {
235 const ContentFeatures &cf = nodemgr->get(node.getContent());
236 in_liquid = cf.liquid_move_physics;
237 move_resistance = cf.move_resistance;
242 // If not in liquid, the threshold of going in is at lower y
244 pp = floatToInt(position + v3f(0.0f, BS * 0.5f, 0.0f), BS);
245 node = map->getNode(pp, &is_valid_position);
246 if (is_valid_position) {
247 const ContentFeatures &cf = nodemgr->get(node.getContent());
248 in_liquid = cf.liquid_move_physics;
249 move_resistance = cf.move_resistance;
257 Check if player is in liquid (the stable value)
259 pp = floatToInt(position + v3f(0.0f), BS);
260 node = map->getNode(pp, &is_valid_position);
261 if (is_valid_position) {
262 in_liquid_stable = nodemgr->get(node.getContent()).liquid_move_physics;
264 in_liquid_stable = false;
268 Check if player is climbing
271 pp = floatToInt(position + v3f(0.0f, 0.5f * BS, 0.0f), BS);
272 v3s16 pp2 = floatToInt(position + v3f(0.0f, -0.2f * BS, 0.0f), BS);
273 node = map->getNode(pp, &is_valid_position);
274 bool is_valid_position2;
275 MapNode node2 = map->getNode(pp2, &is_valid_position2);
277 if (!(is_valid_position && is_valid_position2)) {
280 is_climbing = (nodemgr->get(node.getContent()).climbable ||
281 nodemgr->get(node2.getContent()).climbable) && !free_move;
284 if (!is_climbing && !free_move && g_settings->getBool("spider")) {
285 v3s16 spider_positions[4] = {
286 floatToInt(position + v3f(+1.0f, +0.0f, 0.0f) * BS, BS),
287 floatToInt(position + v3f(-1.0f, +0.0f, 0.0f) * BS, BS),
288 floatToInt(position + v3f( 0.0f, +0.0f, +1.0f) * BS, BS),
289 floatToInt(position + v3f( 0.0f, +0.0f, -1.0f) * BS, BS),
292 for (v3s16 sp : spider_positions) {
294 MapNode node = map->getNode(sp, &is_valid);
296 if (is_valid && nodemgr->get(node.getContent()).walkable) {
304 Collision uncertainty radius
305 Make it a bit larger than the maximum distance of movement
307 //f32 d = pos_max_d * 1.1;
308 // A fairly large value in here makes moving smoother
311 // This should always apply, otherwise there are glitches
312 sanity_check(d > pos_max_d);
314 // Player object property step height is multiplied by BS in
315 // /src/script/common/c_content.cpp and /src/content_sao.cpp
316 float player_stepheight = (m_cao == nullptr) ? 0.0f :
317 (touching_ground ? m_cao->getStepHeight() : (0.2f * BS));
320 const v3f initial_position = position;
321 const v3f initial_speed = m_speed;
323 collisionMoveResult result = collisionMoveSimple(env, m_client,
324 pos_max_d, m_collisionbox, player_stepheight, dtime,
325 &position, &m_speed, accel_f, NULL, true, true);
327 bool could_sneak = control.sneak && !free_move && !in_liquid &&
328 !is_climbing && physics_override_sneak;
330 // Add new collisions to the vector
331 if (collision_info && !free_move) {
332 v3f diff = intToFloat(m_standing_node, BS) - position;
333 f32 distance = diff.getLength();
334 // Force update each ClientEnvironment::step()
335 bool is_first = collision_info->empty();
337 for (const auto &colinfo : result.collisions) {
338 collision_info->push_back(colinfo);
340 if (colinfo.type != COLLISION_NODE ||
341 colinfo.axis != COLLISION_AXIS_Y ||
342 (could_sneak && m_sneak_node_exists))
345 diff = intToFloat(colinfo.node_p, BS) - position;
347 // Find nearest colliding node
348 f32 len = diff.getLength();
349 if (is_first || len < distance) {
350 m_standing_node = colinfo.node_p;
358 If the player's feet touch the topside of any node, this is
361 Player is allowed to jump when this is true.
363 bool touching_ground_was = touching_ground;
364 touching_ground = result.touching_ground || g_settings->getBool("airjump");
365 bool sneak_can_jump = false;
367 // Max. distance (X, Z) over border for sneaking determined by collision box
368 // * 0.49 to keep the center just barely on the node
369 v3f sneak_max = m_collisionbox.getExtent() * 0.49;
371 if (m_sneak_ladder_detected) {
372 // restore legacy behaviour (this makes the m_speed.Y hack necessary)
373 sneak_max = v3f(0.4f * BS, 0.0f, 0.4f * BS);
377 If sneaking, keep on top of last walked node and don't fall off
379 if (could_sneak && m_sneak_node_exists) {
380 const v3f sn_f = intToFloat(m_sneak_node, BS);
381 const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge;
382 const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge;
383 const v3f old_pos = position;
384 const v3f old_speed = m_speed;
385 f32 y_diff = bmax.Y - position.Y;
386 m_standing_node = m_sneak_node;
388 // (BS * 0.6f) is the basic stepheight while standing on ground
389 if (y_diff < BS * 0.6f) {
390 // Only center player when they're on the node
391 position.X = rangelim(position.X,
392 bmin.X - sneak_max.X, bmax.X + sneak_max.X);
393 position.Z = rangelim(position.Z,
394 bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z);
396 if (position.X != old_pos.X)
398 if (position.Z != old_pos.Z)
402 if (y_diff > 0 && m_speed.Y <= 0.0f &&
403 (physics_override_sneak_glitch || y_diff < BS * 0.6f)) {
404 // Move player to the maximal height when falling or when
405 // the ledge is climbed on the next step.
407 // Smoothen the movement (based on 'position.Y = bmax.Y')
408 position.Y += y_diff * dtime * 22.0f + BS * 0.01f;
409 position.Y = std::min(position.Y, bmax.Y);
413 // Allow jumping on node edges while sneaking
414 if (m_speed.Y == 0.0f || m_sneak_ladder_detected)
415 sneak_can_jump = true;
417 if (collision_info &&
418 m_speed.Y - old_speed.Y > BS) {
419 // Collide with sneak node, report fall damage
420 CollisionInfo sn_info;
421 sn_info.node_p = m_sneak_node;
422 sn_info.old_speed = old_speed;
423 sn_info.new_speed = m_speed;
424 collision_info->push_back(sn_info);
429 Find the next sneak node if necessary
431 bool new_sneak_node_exists = false;
434 new_sneak_node_exists = updateSneakNode(map, position, sneak_max);
437 Set new position but keep sneak node set
439 setPosition(position);
440 m_sneak_node_exists = new_sneak_node_exists;
446 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
447 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
449 // Set camera impact value to be used for view bobbing
450 camera_impact = getSpeed().Y * -1;
454 camera_barely_in_ceiling = false;
455 v3s16 camera_np = floatToInt(getEyePosition(), BS);
456 MapNode n = map->getNode(camera_np);
457 if (n.getContent() != CONTENT_IGNORE) {
458 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
459 camera_barely_in_ceiling = true;
464 Check properties of the node on which the player is standing
466 const ContentFeatures &f = nodemgr->get(map->getNode(m_standing_node));
467 const ContentFeatures &f1 = nodemgr->get(map->getNode(m_standing_node + v3s16(0, 1, 0)));
469 // Determine if jumping is possible
470 m_disable_jump = itemgroup_get(f.groups, "disable_jump") ||
471 itemgroup_get(f1.groups, "disable_jump");
472 m_can_jump = ((touching_ground && !is_climbing) || sneak_can_jump) && !m_disable_jump;
474 // Jump key pressed while jumping off from a bouncy block
475 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
476 m_speed.Y >= -0.5f * BS) {
477 float jumpspeed = movement_speed_jump * physics_override_jump;
478 if (m_speed.Y > 1.0f) {
479 // Reduce boost when speed already is high
480 m_speed.Y += jumpspeed / (1.0f + (m_speed.Y / 16.0f));
482 m_speed.Y += jumpspeed;
489 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
492 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
494 move(dtime, env, pos_max_d, NULL);
497 void LocalPlayer::applyControl(float dtime, Environment *env)
500 swimming_vertical = false;
501 swimming_pitch = false;
503 setPitch(control.pitch);
506 // Nullify speed and don't run positioning code if the player is attached
512 PlayerSettings &player_settings = getPlayerSettings();
514 // All vectors are relative to the player's yaw,
515 // (and pitch if pitch move mode enabled),
516 // and will be rotated at the end
517 v3f speedH, speedV; // Horizontal (X, Z) and Vertical (Y)
519 bool fly_allowed = m_client->checkLocalPrivilege("fly");
520 bool fast_allowed = m_client->checkLocalPrivilege("fast");
522 bool free_move = (fly_allowed && player_settings.free_move) || g_settings->getBool("freecam");
523 bool fast_move = (fast_allowed && player_settings.fast_move) || g_settings->getBool("freecam");
524 bool pitch_move = (free_move || in_liquid) && player_settings.pitch_move;
525 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
526 bool fast_climb = fast_move && control.aux1 && !player_settings.aux1_descends;
527 bool always_fly_fast = player_settings.always_fly_fast;
529 // Whether superspeed mode is used or not
530 bool superspeed = false;
532 if (always_fly_fast && free_move && fast_move)
535 // Old descend control
536 if (player_settings.aux1_descends) {
537 // If free movement and fast movement, always move fast
538 if (free_move && fast_move)
541 // Auxiliary button 1 (E)
544 // In free movement mode, aux1 descends
546 speedV.Y = -movement_speed_fast;
548 speedV.Y = -movement_speed_walk;
549 } else if (in_liquid || in_liquid_stable) {
550 speedV.Y = -movement_speed_walk;
551 swimming_vertical = true;
552 } else if (is_climbing) {
553 speedV.Y = -movement_speed_climb;
555 // If not free movement but fast is allowed, aux1 is
562 // New minecraft-like descend control
564 // Auxiliary button 1 (E)
567 // aux1 is "Turbo button"
575 // In free movement mode, sneak descends
576 if (fast_move && (control.aux1 || always_fly_fast))
577 speedV.Y = -movement_speed_fast;
579 speedV.Y = -movement_speed_walk;
580 } else if (in_liquid || in_liquid_stable) {
582 speedV.Y = -movement_speed_fast;
584 speedV.Y = -movement_speed_walk;
585 swimming_vertical = true;
586 } else if (is_climbing) {
588 speedV.Y = -movement_speed_fast;
590 speedV.Y = -movement_speed_climb;
595 speedH = v3f(sin(control.movement_direction), 0.0f, cos(control.movement_direction));
598 // release autojump after a given time
599 m_autojump_time -= dtime;
600 if (m_autojump_time <= 0.0f)
606 if (player_settings.aux1_descends || always_fly_fast) {
608 speedV.Y = movement_speed_fast;
610 speedV.Y = movement_speed_walk;
612 if (fast_move && control.aux1)
613 speedV.Y = movement_speed_fast;
615 speedV.Y = movement_speed_walk;
617 } else if (m_can_jump || g_settings->getBool("jetpack")) {
619 NOTE: The d value in move() affects jump height by
620 raising the height at which the jump speed is kept
621 at its starting value
623 v3f speedJ = getSpeed();
624 if (speedJ.Y >= -0.5f * BS || g_settings->getBool("jetpack")) {
625 speedJ.Y = movement_speed_jump * physics_override_jump;
627 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_JUMP));
629 } else if (in_liquid && !m_disable_jump) {
631 speedV.Y = movement_speed_fast;
633 speedV.Y = movement_speed_walk;
634 swimming_vertical = true;
635 } else if (is_climbing && !m_disable_jump) {
637 speedV.Y = movement_speed_fast;
639 speedV.Y = movement_speed_climb;
643 // The speed of the player (Y is ignored)
644 if (superspeed || (is_climbing && fast_climb) ||
645 ((in_liquid || in_liquid_stable) && fast_climb))
646 speedH = speedH.normalize() * movement_speed_fast;
647 else if (control.sneak && !free_move && !in_liquid && !in_liquid_stable && !g_settings->getBool("no_slow"))
648 speedH = speedH.normalize() * movement_speed_crouch;
650 speedH = speedH.normalize() * movement_speed_walk;
652 speedH *= control.movement_speed; /* Apply analog input */
654 // Acceleration increase
655 f32 incH = 0.0f; // Horizontal (X, Z)
656 f32 incV = 0.0f; // Vertical (Y)
657 if ((!touching_ground && !free_move && !is_climbing && !in_liquid) ||
658 (!free_move && m_can_jump && control.jump)) {
659 // Jumping and falling
660 if (superspeed || (fast_move && control.aux1))
661 incH = movement_acceleration_fast * BS * dtime;
663 incH = movement_acceleration_air * BS * dtime;
664 incV = 0.0f; // No vertical acceleration in air
665 } else if (superspeed || (is_climbing && fast_climb) ||
666 ((in_liquid || in_liquid_stable) && fast_climb)) {
667 incH = incV = movement_acceleration_fast * BS * dtime;
669 incH = incV = movement_acceleration_default * BS * dtime;
672 float slip_factor = 1.0f;
673 if (!free_move && !in_liquid && !in_liquid_stable)
674 slip_factor = getSlipFactor(env, speedH);
676 // Don't sink when swimming in pitch mode
677 if (pitch_move && in_liquid) {
678 v3f controlSpeed = speedH + speedV;
679 if (controlSpeed.getLength() > 0.01f)
680 swimming_pitch = true;
683 // Accelerate to target speed with maximum increment
684 accelerate((speedH + speedV) * physics_override_speed,
685 incH * physics_override_speed * slip_factor, incV * physics_override_speed,
689 v3s16 LocalPlayer::getStandingNodePos()
691 if (m_sneak_node_exists)
694 return m_standing_node;
697 v3s16 LocalPlayer::getFootstepNodePos()
699 v3f feet_pos = getPosition() + v3f(0.0f, m_collisionbox.MinEdge.Y, 0.0f);
701 // Emit swimming sound if the player is in liquid
702 if (in_liquid_stable)
703 return floatToInt(feet_pos, BS);
705 // BS * 0.05 below the player's feet ensures a 1/16th height
706 // nodebox is detected instead of the node below it.
708 return floatToInt(feet_pos - v3f(0.0f, BS * 0.05f, 0.0f), 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(feet_pos - v3f(0.0f, BS * 0.5f, 0.0f), BS);
716 v3s16 LocalPlayer::getLightPosition() const
718 return floatToInt(m_position + v3f(0.0f, BS * 1.5f, 0.0f), BS);
721 v3f LocalPlayer::getSendSpeed()
723 v3f speed = getLegitSpeed();
725 if (m_client->modsLoaded())
726 speed = m_client->getScript()->get_send_speed(speed);
731 v3f LocalPlayer::getEyeOffset() const
733 float eye_height = camera_barely_in_ceiling ? m_eye_height - 0.125f : m_eye_height;
734 return v3f(0.0f, BS * eye_height, 0.0f);
737 ClientActiveObject *LocalPlayer::getParent() const
739 return (m_cao && ! g_settings->getBool("entity_speed")) ? m_cao->getParent() : nullptr;
742 bool LocalPlayer::isDead() const
744 FATAL_ERROR_IF(!getCAO(), "LocalPlayer's CAO isn't initialized");
745 return !getCAO()->isImmortal() && hp == 0;
748 void LocalPlayer::tryReattach(int id)
750 PointedThing pointed(id, v3f(0, 0, 0), v3s16(0, 0, 0), 0);
751 m_client->interact(INTERACT_PLACE, pointed);
752 m_cao->m_waiting_for_reattach = 10;
755 bool LocalPlayer::isWaitingForReattach() const
757 return g_settings->getBool("entity_speed") && m_cao && ! m_cao->getParent() && m_cao->m_waiting_for_reattach > 0;
761 void LocalPlayer::accelerate(const v3f &target_speed, const f32 max_increase_H,
762 const f32 max_increase_V, const bool use_pitch)
764 const f32 yaw = getYaw();
765 const f32 pitch = getPitch();
766 v3f flat_speed = m_speed;
767 // Rotate speed vector by -yaw and -pitch to make it relative to the player's yaw and pitch
768 flat_speed.rotateXZBy(-yaw);
770 flat_speed.rotateYZBy(-pitch);
772 v3f d_wanted = target_speed - flat_speed;
775 // Then compare the horizontal and vertical components with the wanted speed
776 if (max_increase_H > 0.0f) {
777 v3f d_wanted_H = d_wanted * v3f(1.0f, 0.0f, 1.0f);
778 if (d_wanted_H.getLength() > max_increase_H)
779 d += d_wanted_H.normalize() * max_increase_H;
784 if (max_increase_V > 0.0f) {
785 f32 d_wanted_V = d_wanted.Y;
786 if (d_wanted_V > max_increase_V)
787 d.Y += max_increase_V;
788 else if (d_wanted_V < -max_increase_V)
789 d.Y -= max_increase_V;
794 // Finally rotate it again
802 // Temporary option for old move code
803 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
804 std::vector<CollisionInfo> *collision_info)
806 Map *map = &env->getMap();
807 const NodeDefManager *nodemgr = m_client->ndef();
809 v3f position = getPosition();
811 // Copy parent position if local player is attached
813 setPosition(m_cao->getPosition());
814 m_sneak_node_exists = false;
815 added_velocity = v3f(0.0f);
819 PlayerSettings &player_settings = getPlayerSettings();
821 // Skip collision detection if noclip mode is used
822 bool fly_allowed = m_client->checkLocalPrivilege("fly") || g_settings->getBool("freecam");
823 bool noclip = (m_client->checkLocalPrivilege("noclip") && player_settings.noclip) || g_settings->getBool("freecam");
824 bool free_move = (noclip && fly_allowed && player_settings.free_move) || g_settings->getBool("freecam");
826 position += m_speed * dtime;
827 setPosition(position);
829 touching_ground = false;
830 m_sneak_node_exists = false;
831 added_velocity = v3f(0.0f);
835 m_speed += added_velocity;
836 added_velocity = v3f(0.0f);
841 bool is_valid_position;
846 Check if player is in liquid (the oscillating value)
849 // If in liquid, the threshold of coming out is at higher y
850 pp = floatToInt(position + v3f(0.0f, BS * 0.1f, 0.0f), BS);
851 node = map->getNode(pp, &is_valid_position);
852 if (is_valid_position) {
853 const ContentFeatures &cf = nodemgr->get(node.getContent());
854 in_liquid = cf.liquid_move_physics;
855 move_resistance = cf.move_resistance;
860 // If not in liquid, the threshold of going in is at lower y
861 pp = floatToInt(position + v3f(0.0f, BS * 0.5f, 0.0f), BS);
862 node = map->getNode(pp, &is_valid_position);
863 if (is_valid_position) {
864 const ContentFeatures &cf = nodemgr->get(node.getContent());
865 in_liquid = cf.liquid_move_physics;
866 move_resistance = cf.move_resistance;
873 Check if player is in liquid (the stable value)
875 pp = floatToInt(position + v3f(0.0f), BS);
876 node = map->getNode(pp, &is_valid_position);
877 if (is_valid_position)
878 in_liquid_stable = nodemgr->get(node.getContent()).liquid_move_physics;
880 in_liquid_stable = false;
883 Check if player is climbing
885 pp = floatToInt(position + v3f(0.0f, 0.5f * BS, 0.0f), BS);
886 v3s16 pp2 = floatToInt(position + v3f(0.0f, -0.2f * BS, 0.0f), BS);
887 node = map->getNode(pp, &is_valid_position);
888 bool is_valid_position2;
889 MapNode node2 = map->getNode(pp2, &is_valid_position2);
891 if (!(is_valid_position && is_valid_position2))
894 is_climbing = (nodemgr->get(node.getContent()).climbable ||
895 nodemgr->get(node2.getContent()).climbable) && !free_move;
898 Collision uncertainty radius
899 Make it a bit larger than the maximum distance of movement
901 //f32 d = pos_max_d * 1.1;
902 // A fairly large value in here makes moving smoother
904 // This should always apply, otherwise there are glitches
905 sanity_check(d > pos_max_d);
906 // Maximum distance over border for sneaking
907 f32 sneak_max = BS * 0.4f;
910 If sneaking, keep in range from the last walked node and don't
913 if (control.sneak && m_sneak_node_exists &&
914 !(fly_allowed && player_settings.free_move) && !in_liquid &&
915 physics_override_sneak) {
916 f32 maxd = 0.5f * BS + sneak_max;
917 v3f lwn_f = intToFloat(m_sneak_node, BS);
918 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
919 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
922 // Move up if necessary
923 f32 new_y = (lwn_f.Y - 0.5f * BS) + m_sneak_node_bb_ymax;
924 if (position.Y < new_y)
927 Collision seems broken, since player is sinking when
928 sneaking over the edges of current sneaking_node.
929 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
931 if (m_speed.Y < 0.0f)
936 // TODO: This shouldn't be hardcoded but decided by the server
937 float player_stepheight = touching_ground ? (BS * 0.6f) : (BS * 0.2f);
940 const v3f initial_position = position;
941 const v3f initial_speed = m_speed;
943 collisionMoveResult result = collisionMoveSimple(env, m_client,
944 pos_max_d, m_collisionbox, player_stepheight, dtime,
945 &position, &m_speed, accel_f, NULL, true, true);
947 // Positition was slightly changed; update standing node pos
949 m_standing_node = floatToInt(m_position - v3f(0.0f, 0.1f * BS, 0.0f), BS);
951 m_standing_node = floatToInt(m_position, BS);
954 If the player's feet touch the topside of any node, this is
957 Player is allowed to jump when this is true.
959 bool touching_ground_was = touching_ground;
960 touching_ground = result.touching_ground;
962 //bool standing_on_unloaded = result.standing_on_unloaded;
965 Check the nodes under the player to see from which node the
966 player is sneaking from, if any. If the node from under
967 the player has been removed, the player falls.
969 f32 position_y_mod = 0.05f * BS;
970 if (m_sneak_node_bb_ymax > 0.0f)
971 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
972 v3s16 current_node = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS);
973 if (m_sneak_node_exists &&
974 nodemgr->get(map->getNode(m_old_node_below)).name == "air" &&
975 m_old_node_below_type != "air") {
976 // Old node appears to have been removed; that is,
977 // it wasn't air before but now it is
978 m_need_to_get_new_sneak_node = false;
979 m_sneak_node_exists = false;
980 } else if (nodemgr->get(map->getNode(current_node)).name != "air") {
981 // We are on something, so make sure to recalculate the sneak
983 m_need_to_get_new_sneak_node = true;
986 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
987 m_sneak_node_bb_ymax = 0.0f;
988 v3s16 pos_i_bottom = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS);
989 v2f player_p2df(position.X, position.Z);
990 f32 min_distance_f = 100000.0f * BS;
991 // If already seeking from some node, compare to it.
992 v3s16 new_sneak_node = m_sneak_node;
993 for (s16 x= -1; x <= 1; x++)
994 for (s16 z= -1; z <= 1; z++) {
995 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
996 v3f pf = intToFloat(p, BS);
997 v2f node_p2df(pf.X, pf.Z);
998 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
999 f32 max_axis_distance_f = MYMAX(
1000 std::fabs(player_p2df.X - node_p2df.X),
1001 std::fabs(player_p2df.Y - node_p2df.Y));
1003 if (distance_f > min_distance_f ||
1004 max_axis_distance_f > 0.5f * BS + sneak_max + 0.1f * BS)
1007 // The node to be sneaked on has to be walkable
1008 node = map->getNode(p, &is_valid_position);
1009 if (!is_valid_position || !nodemgr->get(node).walkable)
1011 // And the node above it has to be nonwalkable
1012 node = map->getNode(p + v3s16(0, 1, 0), &is_valid_position);
1013 if (!is_valid_position || nodemgr->get(node).walkable)
1015 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
1016 if (!physics_override_sneak_glitch) {
1017 node = map->getNode(p + v3s16(0, 2, 0), &is_valid_position);
1018 if (!is_valid_position || nodemgr->get(node).walkable)
1022 min_distance_f = distance_f;
1026 bool sneak_node_found = (min_distance_f < 100000.0f * BS * 0.9f);
1028 m_sneak_node = new_sneak_node;
1029 m_sneak_node_exists = sneak_node_found;
1031 if (sneak_node_found) {
1033 MapNode n = map->getNode(m_sneak_node);
1034 std::vector<aabb3f> nodeboxes;
1035 n.getCollisionBoxes(nodemgr, &nodeboxes);
1036 for (const auto &box : nodeboxes) {
1037 if (box.MaxEdge.Y > cb_max)
1038 cb_max = box.MaxEdge.Y;
1040 m_sneak_node_bb_ymax = cb_max;
1044 If sneaking, the player's collision box can be in air, so
1045 this has to be set explicitly
1047 if (sneak_node_found && control.sneak)
1048 touching_ground = true;
1052 Set new position but keep sneak node set
1054 bool sneak_node_exists = m_sneak_node_exists;
1055 setPosition(position);
1056 m_sneak_node_exists = sneak_node_exists;
1061 // Don't report if flying
1062 if (collision_info && !(player_settings.free_move && fly_allowed)) {
1063 for (const auto &info : result.collisions) {
1064 collision_info->push_back(info);
1068 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1069 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
1070 // Set camera impact value to be used for view bobbing
1071 camera_impact = getSpeed().Y * -1.0f;
1075 camera_barely_in_ceiling = false;
1076 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1077 MapNode n = map->getNode(camera_np);
1078 if (n.getContent() != CONTENT_IGNORE) {
1079 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1080 camera_barely_in_ceiling = true;
1085 Update the node last under the player
1087 m_old_node_below = floatToInt(position - v3f(0.0f, BS / 2.0f, 0.0f), BS);
1088 m_old_node_below_type = nodemgr->get(map->getNode(m_old_node_below)).name;
1091 Check properties of the node on which the player is standing
1093 const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos()));
1095 // Determine if jumping is possible
1096 m_disable_jump = itemgroup_get(f.groups, "disable_jump");
1097 m_can_jump = touching_ground && !m_disable_jump;
1099 // Jump key pressed while jumping off from a bouncy block
1100 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1101 m_speed.Y >= -0.5f * BS) {
1102 float jumpspeed = movement_speed_jump * physics_override_jump;
1103 if (m_speed.Y > 1.0f) {
1104 // Reduce boost when speed already is high
1105 m_speed.Y += jumpspeed / (1.0f + (m_speed.Y / 16.0f));
1107 m_speed.Y += jumpspeed;
1114 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
1117 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1119 // Slip on slippery nodes
1120 const NodeDefManager *nodemgr = env->getGameDef()->ndef();
1121 Map *map = &env->getMap();
1122 const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos()));
1124 if (f.walkable && ! g_settings->getBool("antislip"))
1125 slippery = itemgroup_get(f.groups, "slippery");
1127 if (slippery >= 1) {
1128 if (speedH == v3f(0.0f))
1131 return core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);
1136 void LocalPlayer::handleAutojump(f32 dtime, Environment *env,
1137 const collisionMoveResult &result, const v3f &initial_position,
1138 const v3f &initial_speed, f32 pos_max_d)
1140 PlayerSettings &player_settings = getPlayerSettings();
1141 if (!player_settings.autojump)
1147 bool could_autojump =
1148 m_can_jump && !control.jump && !control.sneak && control.isMoving();
1150 if (!could_autojump)
1153 bool horizontal_collision = false;
1154 for (const auto &colinfo : result.collisions) {
1155 if (colinfo.type == COLLISION_NODE && colinfo.plane != 1) {
1156 horizontal_collision = true;
1157 break; // one is enough
1161 // must be running against something to trigger autojumping
1162 if (!horizontal_collision)
1165 // check for nodes above
1166 v3f headpos_min = m_position + m_collisionbox.MinEdge * 0.99f;
1167 v3f headpos_max = m_position + m_collisionbox.MaxEdge * 0.99f;
1168 headpos_min.Y = headpos_max.Y; // top face of collision box
1169 v3s16 ceilpos_min = floatToInt(headpos_min, BS) + v3s16(0, 1, 0);
1170 v3s16 ceilpos_max = floatToInt(headpos_max, BS) + v3s16(0, 1, 0);
1171 const NodeDefManager *ndef = env->getGameDef()->ndef();
1172 bool is_position_valid;
1173 for (s16 z = ceilpos_min.Z; z <= ceilpos_max.Z; ++z) {
1174 for (s16 x = ceilpos_min.X; x <= ceilpos_max.X; ++x) {
1175 MapNode n = env->getMap().getNode(v3s16(x, ceilpos_max.Y, z), &is_position_valid);
1177 if (!is_position_valid)
1178 break; // won't collide with the void outside
1179 if (n.getContent() == CONTENT_IGNORE)
1180 return; // players collide with ignore blocks -> same as walkable
1181 const ContentFeatures &f = ndef->get(n);
1183 return; // would bump head, don't jump
1187 float jump_height = 1.1f; // TODO: better than a magic number
1188 v3f jump_pos = initial_position + v3f(0.0f, jump_height * BS, 0.0f);
1189 v3f jump_speed = initial_speed;
1191 // try at peak of jump, zero step height
1192 collisionMoveResult jump_result = collisionMoveSimple(env, m_client, pos_max_d,
1193 m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed, v3f(0.0f), NULL, true, true);
1195 // see if we can get a little bit farther horizontally if we had
1197 v3f run_delta = m_position - initial_position;
1199 v3f jump_delta = jump_pos - initial_position;
1200 jump_delta.Y = 0.0f;
1201 if (jump_delta.getLengthSQ() > run_delta.getLengthSQ() * 1.01f) {
1203 m_autojump_time = 0.1f;