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);
203 added_velocity = v3f(0.0f); // ignored
207 m_speed += added_velocity;
208 added_velocity = v3f(0.0f);
214 bool is_valid_position;
219 Check if player is in liquid (the oscillating value)
222 // If in liquid, the threshold of coming out is at higher y
225 pp = floatToInt(position + v3f(0.0f, BS * 0.1f, 0.0f), BS);
226 node = map->getNode(pp, &is_valid_position);
227 if (is_valid_position) {
228 in_liquid = nodemgr->get(node.getContent()).isLiquid();
229 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
234 // If not in liquid, the threshold of going in is at lower y
236 pp = floatToInt(position + v3f(0.0f, BS * 0.5f, 0.0f), BS);
237 node = map->getNode(pp, &is_valid_position);
238 if (is_valid_position) {
239 in_liquid = nodemgr->get(node.getContent()).isLiquid();
240 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
248 Check if player is in liquid (the stable value)
250 pp = floatToInt(position + v3f(0.0f), BS);
251 node = map->getNode(pp, &is_valid_position);
252 if (is_valid_position) {
253 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
255 in_liquid_stable = false;
259 Check if player is climbing
262 pp = floatToInt(position + v3f(0.0f, 0.5f * BS, 0.0f), BS);
263 v3s16 pp2 = floatToInt(position + v3f(0.0f, -0.2f * BS, 0.0f), BS);
264 node = map->getNode(pp, &is_valid_position);
265 bool is_valid_position2;
266 MapNode node2 = map->getNode(pp2, &is_valid_position2);
268 if (!(is_valid_position && is_valid_position2)) {
271 is_climbing = (nodemgr->get(node.getContent()).climbable ||
272 nodemgr->get(node2.getContent()).climbable) && !free_move;
276 Collision uncertainty radius
277 Make it a bit larger than the maximum distance of movement
279 //f32 d = pos_max_d * 1.1;
280 // A fairly large value in here makes moving smoother
283 // This should always apply, otherwise there are glitches
284 sanity_check(d > pos_max_d);
286 // Player object property step height is multiplied by BS in
287 // /src/script/common/c_content.cpp and /src/content_sao.cpp
288 float player_stepheight = (m_cao == nullptr) ? 0.0f :
289 (touching_ground ? m_cao->getStepHeight() : (0.2f * BS));
292 const v3f initial_position = position;
293 const v3f initial_speed = m_speed;
295 collisionMoveResult result = collisionMoveSimple(env, m_client,
296 pos_max_d, m_collisionbox, player_stepheight, dtime,
297 &position, &m_speed, accel_f);
299 bool could_sneak = control.sneak && !free_move && !in_liquid &&
300 !is_climbing && physics_override_sneak;
302 // Add new collisions to the vector
303 if (collision_info && !free_move) {
304 v3f diff = intToFloat(m_standing_node, BS) - position;
305 f32 distance = diff.getLength();
306 // Force update each ClientEnvironment::step()
307 bool is_first = collision_info->empty();
309 for (const auto &colinfo : result.collisions) {
310 collision_info->push_back(colinfo);
312 if (colinfo.type != COLLISION_NODE ||
313 colinfo.axis != COLLISION_AXIS_Y ||
314 (could_sneak && m_sneak_node_exists))
317 diff = intToFloat(colinfo.node_p, BS) - position;
319 // Find nearest colliding node
320 f32 len = diff.getLength();
321 if (is_first || len < distance) {
322 m_standing_node = colinfo.node_p;
330 If the player's feet touch the topside of any node, this is
333 Player is allowed to jump when this is true.
335 bool touching_ground_was = touching_ground;
336 touching_ground = result.touching_ground;
337 bool sneak_can_jump = false;
339 // Max. distance (X, Z) over border for sneaking determined by collision box
340 // * 0.49 to keep the center just barely on the node
341 v3f sneak_max = m_collisionbox.getExtent() * 0.49;
343 if (m_sneak_ladder_detected) {
344 // restore legacy behaviour (this makes the m_speed.Y hack necessary)
345 sneak_max = v3f(0.4f * BS, 0.0f, 0.4f * BS);
349 If sneaking, keep on top of last walked node and don't fall off
351 if (could_sneak && m_sneak_node_exists) {
352 const v3f sn_f = intToFloat(m_sneak_node, BS);
353 const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge;
354 const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge;
355 const v3f old_pos = position;
356 const v3f old_speed = m_speed;
357 f32 y_diff = bmax.Y - position.Y;
358 m_standing_node = m_sneak_node;
360 // (BS * 0.6f) is the basic stepheight while standing on ground
361 if (y_diff < BS * 0.6f) {
362 // Only center player when they're on the node
363 position.X = rangelim(position.X,
364 bmin.X - sneak_max.X, bmax.X + sneak_max.X);
365 position.Z = rangelim(position.Z,
366 bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z);
368 if (position.X != old_pos.X)
370 if (position.Z != old_pos.Z)
374 if (y_diff > 0 && m_speed.Y <= 0.0f &&
375 (physics_override_sneak_glitch || y_diff < BS * 0.6f)) {
376 // Move player to the maximal height when falling or when
377 // the ledge is climbed on the next step.
379 // Smoothen the movement (based on 'position.Y = bmax.Y')
380 position.Y += y_diff * dtime * 22.0f + BS * 0.01f;
381 position.Y = std::min(position.Y, bmax.Y);
385 // Allow jumping on node edges while sneaking
386 if (m_speed.Y == 0.0f || m_sneak_ladder_detected)
387 sneak_can_jump = true;
389 if (collision_info &&
390 m_speed.Y - old_speed.Y > BS) {
391 // Collide with sneak node, report fall damage
392 CollisionInfo sn_info;
393 sn_info.node_p = m_sneak_node;
394 sn_info.old_speed = old_speed;
395 sn_info.new_speed = m_speed;
396 collision_info->push_back(sn_info);
401 Find the next sneak node if necessary
403 bool new_sneak_node_exists = false;
406 new_sneak_node_exists = updateSneakNode(map, position, sneak_max);
409 Set new position but keep sneak node set
411 setPosition(position);
412 m_sneak_node_exists = new_sneak_node_exists;
418 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
419 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
421 // Set camera impact value to be used for view bobbing
422 camera_impact = getSpeed().Y * -1;
426 camera_barely_in_ceiling = false;
427 v3s16 camera_np = floatToInt(getEyePosition(), BS);
428 MapNode n = map->getNode(camera_np);
429 if (n.getContent() != CONTENT_IGNORE) {
430 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
431 camera_barely_in_ceiling = true;
436 Check properties of the node on which the player is standing
438 const ContentFeatures &f = nodemgr->get(map->getNode(m_standing_node));
440 // Determine if jumping is possible
441 m_disable_jump = itemgroup_get(f.groups, "disable_jump");
442 m_can_jump = ((touching_ground && !is_climbing) || sneak_can_jump) && !m_disable_jump;
444 // Jump key pressed while jumping off from a bouncy block
445 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
446 m_speed.Y >= -0.5f * BS) {
447 float jumpspeed = movement_speed_jump * physics_override_jump;
448 if (m_speed.Y > 1.0f) {
449 // Reduce boost when speed already is high
450 m_speed.Y += jumpspeed / (1.0f + (m_speed.Y / 16.0f));
452 m_speed.Y += jumpspeed;
459 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
462 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
464 move(dtime, env, pos_max_d, NULL);
467 void LocalPlayer::applyControl(float dtime, Environment *env)
470 swimming_vertical = false;
471 swimming_pitch = false;
473 setPitch(control.pitch);
476 // Nullify speed and don't run positioning code if the player is attached
482 PlayerSettings &player_settings = getPlayerSettings();
484 // All vectors are relative to the player's yaw,
485 // (and pitch if pitch move mode enabled),
486 // and will be rotated at the end
487 v3f speedH, speedV; // Horizontal (X, Z) and Vertical (Y)
489 bool fly_allowed = m_client->checkLocalPrivilege("fly");
490 bool fast_allowed = m_client->checkLocalPrivilege("fast");
492 bool free_move = fly_allowed && player_settings.free_move;
493 bool fast_move = fast_allowed && player_settings.fast_move;
494 bool pitch_move = (free_move || in_liquid) && player_settings.pitch_move;
495 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
496 bool fast_climb = fast_move && control.aux1 && !player_settings.aux1_descends;
497 bool always_fly_fast = player_settings.always_fly_fast;
499 // Whether superspeed mode is used or not
500 bool superspeed = false;
502 if (always_fly_fast && free_move && fast_move)
505 // Old descend control
506 if (player_settings.aux1_descends) {
507 // If free movement and fast movement, always move fast
508 if (free_move && fast_move)
511 // Auxiliary button 1 (E)
514 // In free movement mode, aux1 descends
516 speedV.Y = -movement_speed_fast;
518 speedV.Y = -movement_speed_walk;
519 } else if (in_liquid || in_liquid_stable) {
520 speedV.Y = -movement_speed_walk;
521 swimming_vertical = true;
522 } else if (is_climbing) {
523 speedV.Y = -movement_speed_climb;
525 // If not free movement but fast is allowed, aux1 is
532 // New minecraft-like descend control
534 // Auxiliary button 1 (E)
537 // aux1 is "Turbo button"
545 // In free movement mode, sneak descends
546 if (fast_move && (control.aux1 || always_fly_fast))
547 speedV.Y = -movement_speed_fast;
549 speedV.Y = -movement_speed_walk;
550 } else if (in_liquid || in_liquid_stable) {
552 speedV.Y = -movement_speed_fast;
554 speedV.Y = -movement_speed_walk;
555 swimming_vertical = true;
556 } else if (is_climbing) {
558 speedV.Y = -movement_speed_fast;
560 speedV.Y = -movement_speed_climb;
566 speedH += v3f(0.0f, 0.0f, 1.0f);
569 speedH -= v3f(0.0f, 0.0f, 1.0f);
571 if (!control.up && !control.down)
572 speedH -= v3f(0.0f, 0.0f, 1.0f) * (control.forw_move_joystick_axis / 32767.f);
575 speedH += v3f(-1.0f, 0.0f, 0.0f);
578 speedH += v3f(1.0f, 0.0f, 0.0f);
580 if (!control.left && !control.right)
581 speedH += v3f(1.0f, 0.0f, 0.0f) * (control.sidew_move_joystick_axis / 32767.f);
584 // release autojump after a given time
585 m_autojump_time -= dtime;
586 if (m_autojump_time <= 0.0f)
592 if (player_settings.aux1_descends || always_fly_fast) {
594 speedV.Y = movement_speed_fast;
596 speedV.Y = movement_speed_walk;
598 if (fast_move && control.aux1)
599 speedV.Y = movement_speed_fast;
601 speedV.Y = movement_speed_walk;
603 } else if (m_can_jump) {
605 NOTE: The d value in move() affects jump height by
606 raising the height at which the jump speed is kept
607 at its starting value
609 v3f speedJ = getSpeed();
610 if (speedJ.Y >= -0.5f * BS) {
611 speedJ.Y = movement_speed_jump * physics_override_jump;
613 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_JUMP));
615 } else if (in_liquid && !m_disable_jump) {
617 speedV.Y = movement_speed_fast;
619 speedV.Y = movement_speed_walk;
620 swimming_vertical = true;
621 } else if (is_climbing && !m_disable_jump) {
623 speedV.Y = movement_speed_fast;
625 speedV.Y = movement_speed_climb;
629 // The speed of the player (Y is ignored)
630 if (superspeed || (is_climbing && fast_climb) ||
631 ((in_liquid || in_liquid_stable) && fast_climb))
632 speedH = speedH.normalize() * movement_speed_fast;
633 else if (control.sneak && !free_move && !in_liquid && !in_liquid_stable)
634 speedH = speedH.normalize() * movement_speed_crouch;
636 speedH = speedH.normalize() * movement_speed_walk;
638 // Acceleration increase
639 f32 incH = 0.0f; // Horizontal (X, Z)
640 f32 incV = 0.0f; // Vertical (Y)
641 if ((!touching_ground && !free_move && !is_climbing && !in_liquid) ||
642 (!free_move && m_can_jump && control.jump)) {
643 // Jumping and falling
644 if (superspeed || (fast_move && control.aux1))
645 incH = movement_acceleration_fast * BS * dtime;
647 incH = movement_acceleration_air * BS * dtime;
648 incV = 0.0f; // No vertical acceleration in air
649 } else if (superspeed || (is_climbing && fast_climb) ||
650 ((in_liquid || in_liquid_stable) && fast_climb)) {
651 incH = incV = movement_acceleration_fast * BS * dtime;
653 incH = incV = movement_acceleration_default * BS * dtime;
656 float slip_factor = 1.0f;
657 if (!free_move && !in_liquid && !in_liquid_stable)
658 slip_factor = getSlipFactor(env, speedH);
660 // Don't sink when swimming in pitch mode
661 if (pitch_move && in_liquid) {
662 v3f controlSpeed = speedH + speedV;
663 if (controlSpeed.getLength() > 0.01f)
664 swimming_pitch = true;
667 // Accelerate to target speed with maximum increment
668 accelerate((speedH + speedV) * physics_override_speed,
669 incH * physics_override_speed * slip_factor, incV * physics_override_speed,
673 v3s16 LocalPlayer::getStandingNodePos()
675 if (m_sneak_node_exists)
678 return m_standing_node;
681 v3s16 LocalPlayer::getFootstepNodePos()
683 // Emit swimming sound if the player is in liquid
684 if (in_liquid_stable)
685 return floatToInt(getPosition(), BS);
687 // BS * 0.05 below the player's feet ensures a 1/16th height
688 // nodebox is detected instead of the node below it.
690 return floatToInt(getPosition() - v3f(0.0f, BS * 0.05f, 0.0f), BS);
692 // A larger distance below is necessary for a footstep sound
693 // when landing after a jump or fall. BS * 0.5 ensures water
694 // sounds when swimming in 1 node deep water.
695 return floatToInt(getPosition() - v3f(0.0f, BS * 0.5f, 0.0f), BS);
698 v3s16 LocalPlayer::getLightPosition() const
700 return floatToInt(m_position + v3f(0.0f, BS * 1.5f, 0.0f), BS);
703 v3f LocalPlayer::getEyeOffset() const
705 float eye_height = camera_barely_in_ceiling ? m_eye_height - 0.125f : m_eye_height;
706 return v3f(0.0f, BS * eye_height, 0.0f);
709 ClientActiveObject *LocalPlayer::getParent() const
711 return m_cao ? m_cao->getParent() : nullptr;
714 bool LocalPlayer::isDead() const
716 FATAL_ERROR_IF(!getCAO(), "LocalPlayer's CAO isn't initialized");
717 return !getCAO()->isImmortal() && hp == 0;
721 void LocalPlayer::accelerate(const v3f &target_speed, const f32 max_increase_H,
722 const f32 max_increase_V, const bool use_pitch)
724 const f32 yaw = getYaw();
725 const f32 pitch = getPitch();
726 v3f flat_speed = m_speed;
727 // Rotate speed vector by -yaw and -pitch to make it relative to the player's yaw and pitch
728 flat_speed.rotateXZBy(-yaw);
730 flat_speed.rotateYZBy(-pitch);
732 v3f d_wanted = target_speed - flat_speed;
735 // Then compare the horizontal and vertical components with the wanted speed
736 if (max_increase_H > 0.0f) {
737 v3f d_wanted_H = d_wanted * v3f(1.0f, 0.0f, 1.0f);
738 if (d_wanted_H.getLength() > max_increase_H)
739 d += d_wanted_H.normalize() * max_increase_H;
744 if (max_increase_V > 0.0f) {
745 f32 d_wanted_V = d_wanted.Y;
746 if (d_wanted_V > max_increase_V)
747 d.Y += max_increase_V;
748 else if (d_wanted_V < -max_increase_V)
749 d.Y -= max_increase_V;
754 // Finally rotate it again
762 // Temporary option for old move code
763 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
764 std::vector<CollisionInfo> *collision_info)
766 Map *map = &env->getMap();
767 const NodeDefManager *nodemgr = m_client->ndef();
769 v3f position = getPosition();
771 // Copy parent position if local player is attached
773 setPosition(m_cao->getPosition());
774 m_sneak_node_exists = false;
775 added_velocity = v3f(0.0f);
779 PlayerSettings &player_settings = getPlayerSettings();
781 // Skip collision detection if noclip mode is used
782 bool fly_allowed = m_client->checkLocalPrivilege("fly");
783 bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip;
784 bool free_move = noclip && fly_allowed && player_settings.free_move;
786 position += m_speed * dtime;
787 setPosition(position);
788 m_sneak_node_exists = false;
789 added_velocity = v3f(0.0f);
793 m_speed += added_velocity;
794 added_velocity = v3f(0.0f);
799 bool is_valid_position;
804 Check if player is in liquid (the oscillating value)
807 // If in liquid, the threshold of coming out is at higher y
808 pp = floatToInt(position + v3f(0.0f, BS * 0.1f, 0.0f), BS);
809 node = map->getNode(pp, &is_valid_position);
810 if (is_valid_position) {
811 in_liquid = nodemgr->get(node.getContent()).isLiquid();
812 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
817 // If not in liquid, the threshold of going in is at lower y
818 pp = floatToInt(position + v3f(0.0f, BS * 0.5f, 0.0f), BS);
819 node = map->getNode(pp, &is_valid_position);
820 if (is_valid_position) {
821 in_liquid = nodemgr->get(node.getContent()).isLiquid();
822 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
829 Check if player is in liquid (the stable value)
831 pp = floatToInt(position + v3f(0.0f), BS);
832 node = map->getNode(pp, &is_valid_position);
833 if (is_valid_position)
834 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
836 in_liquid_stable = false;
839 Check if player is climbing
841 pp = floatToInt(position + v3f(0.0f, 0.5f * BS, 0.0f), BS);
842 v3s16 pp2 = floatToInt(position + v3f(0.0f, -0.2f * BS, 0.0f), BS);
843 node = map->getNode(pp, &is_valid_position);
844 bool is_valid_position2;
845 MapNode node2 = map->getNode(pp2, &is_valid_position2);
847 if (!(is_valid_position && is_valid_position2))
850 is_climbing = (nodemgr->get(node.getContent()).climbable ||
851 nodemgr->get(node2.getContent()).climbable) && !free_move;
854 Collision uncertainty radius
855 Make it a bit larger than the maximum distance of movement
857 //f32 d = pos_max_d * 1.1;
858 // A fairly large value in here makes moving smoother
860 // This should always apply, otherwise there are glitches
861 sanity_check(d > pos_max_d);
862 // Maximum distance over border for sneaking
863 f32 sneak_max = BS * 0.4f;
866 If sneaking, keep in range from the last walked node and don't
869 if (control.sneak && m_sneak_node_exists &&
870 !(fly_allowed && player_settings.free_move) && !in_liquid &&
871 physics_override_sneak) {
872 f32 maxd = 0.5f * BS + sneak_max;
873 v3f lwn_f = intToFloat(m_sneak_node, BS);
874 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
875 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
878 // Move up if necessary
879 f32 new_y = (lwn_f.Y - 0.5f * BS) + m_sneak_node_bb_ymax;
880 if (position.Y < new_y)
883 Collision seems broken, since player is sinking when
884 sneaking over the edges of current sneaking_node.
885 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
887 if (m_speed.Y < 0.0f)
892 // TODO: This shouldn't be hardcoded but decided by the server
893 float player_stepheight = touching_ground ? (BS * 0.6f) : (BS * 0.2f);
896 const v3f initial_position = position;
897 const v3f initial_speed = m_speed;
899 collisionMoveResult result = collisionMoveSimple(env, m_client,
900 pos_max_d, m_collisionbox, player_stepheight, dtime,
901 &position, &m_speed, accel_f);
903 // Positition was slightly changed; update standing node pos
905 m_standing_node = floatToInt(m_position - v3f(0.0f, 0.1f * BS, 0.0f), BS);
907 m_standing_node = floatToInt(m_position, BS);
910 If the player's feet touch the topside of any node, this is
913 Player is allowed to jump when this is true.
915 bool touching_ground_was = touching_ground;
916 touching_ground = result.touching_ground;
918 //bool standing_on_unloaded = result.standing_on_unloaded;
921 Check the nodes under the player to see from which node the
922 player is sneaking from, if any. If the node from under
923 the player has been removed, the player falls.
925 f32 position_y_mod = 0.05f * BS;
926 if (m_sneak_node_bb_ymax > 0.0f)
927 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
928 v3s16 current_node = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS);
929 if (m_sneak_node_exists &&
930 nodemgr->get(map->getNode(m_old_node_below)).name == "air" &&
931 m_old_node_below_type != "air") {
932 // Old node appears to have been removed; that is,
933 // it wasn't air before but now it is
934 m_need_to_get_new_sneak_node = false;
935 m_sneak_node_exists = false;
936 } else if (nodemgr->get(map->getNode(current_node)).name != "air") {
937 // We are on something, so make sure to recalculate the sneak
939 m_need_to_get_new_sneak_node = true;
942 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
943 m_sneak_node_bb_ymax = 0.0f;
944 v3s16 pos_i_bottom = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS);
945 v2f player_p2df(position.X, position.Z);
946 f32 min_distance_f = 100000.0f * BS;
947 // If already seeking from some node, compare to it.
948 v3s16 new_sneak_node = m_sneak_node;
949 for (s16 x= -1; x <= 1; x++)
950 for (s16 z= -1; z <= 1; z++) {
951 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
952 v3f pf = intToFloat(p, BS);
953 v2f node_p2df(pf.X, pf.Z);
954 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
955 f32 max_axis_distance_f = MYMAX(
956 std::fabs(player_p2df.X - node_p2df.X),
957 std::fabs(player_p2df.Y - node_p2df.Y));
959 if (distance_f > min_distance_f ||
960 max_axis_distance_f > 0.5f * BS + sneak_max + 0.1f * BS)
963 // The node to be sneaked on has to be walkable
964 node = map->getNode(p, &is_valid_position);
965 if (!is_valid_position || !nodemgr->get(node).walkable)
967 // And the node above it has to be nonwalkable
968 node = map->getNode(p + v3s16(0, 1, 0), &is_valid_position);
969 if (!is_valid_position || nodemgr->get(node).walkable)
971 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
972 if (!physics_override_sneak_glitch) {
973 node = map->getNode(p + v3s16(0, 2, 0), &is_valid_position);
974 if (!is_valid_position || nodemgr->get(node).walkable)
978 min_distance_f = distance_f;
982 bool sneak_node_found = (min_distance_f < 100000.0f * BS * 0.9f);
984 m_sneak_node = new_sneak_node;
985 m_sneak_node_exists = sneak_node_found;
987 if (sneak_node_found) {
989 MapNode n = map->getNode(m_sneak_node);
990 std::vector<aabb3f> nodeboxes;
991 n.getCollisionBoxes(nodemgr, &nodeboxes);
992 for (const auto &box : nodeboxes) {
993 if (box.MaxEdge.Y > cb_max)
994 cb_max = box.MaxEdge.Y;
996 m_sneak_node_bb_ymax = cb_max;
1000 If sneaking, the player's collision box can be in air, so
1001 this has to be set explicitly
1003 if (sneak_node_found && control.sneak)
1004 touching_ground = true;
1008 Set new position but keep sneak node set
1010 bool sneak_node_exists = m_sneak_node_exists;
1011 setPosition(position);
1012 m_sneak_node_exists = sneak_node_exists;
1017 // Don't report if flying
1018 if (collision_info && !(player_settings.free_move && fly_allowed)) {
1019 for (const auto &info : result.collisions) {
1020 collision_info->push_back(info);
1024 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1025 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
1026 // Set camera impact value to be used for view bobbing
1027 camera_impact = getSpeed().Y * -1.0f;
1031 camera_barely_in_ceiling = false;
1032 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1033 MapNode n = map->getNode(camera_np);
1034 if (n.getContent() != CONTENT_IGNORE) {
1035 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1036 camera_barely_in_ceiling = true;
1041 Update the node last under the player
1043 m_old_node_below = floatToInt(position - v3f(0.0f, BS / 2.0f, 0.0f), BS);
1044 m_old_node_below_type = nodemgr->get(map->getNode(m_old_node_below)).name;
1047 Check properties of the node on which the player is standing
1049 const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos()));
1051 // Determine if jumping is possible
1052 m_disable_jump = itemgroup_get(f.groups, "disable_jump");
1053 m_can_jump = touching_ground && !m_disable_jump;
1055 // Jump key pressed while jumping off from a bouncy block
1056 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1057 m_speed.Y >= -0.5f * BS) {
1058 float jumpspeed = movement_speed_jump * physics_override_jump;
1059 if (m_speed.Y > 1.0f) {
1060 // Reduce boost when speed already is high
1061 m_speed.Y += jumpspeed / (1.0f + (m_speed.Y / 16.0f));
1063 m_speed.Y += jumpspeed;
1070 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
1073 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1075 // Slip on slippery nodes
1076 const NodeDefManager *nodemgr = env->getGameDef()->ndef();
1077 Map *map = &env->getMap();
1078 const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos()));
1081 slippery = itemgroup_get(f.groups, "slippery");
1083 if (slippery >= 1) {
1084 if (speedH == v3f(0.0f))
1087 return core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);
1092 void LocalPlayer::handleAutojump(f32 dtime, Environment *env,
1093 const collisionMoveResult &result, const v3f &initial_position,
1094 const v3f &initial_speed, f32 pos_max_d)
1096 PlayerSettings &player_settings = getPlayerSettings();
1097 if (!player_settings.autojump)
1103 bool control_forward = control.up ||
1104 (!control.up && !control.down &&
1105 control.forw_move_joystick_axis < -0.05f);
1107 bool could_autojump =
1108 m_can_jump && !control.jump && !control.sneak && control_forward;
1110 if (!could_autojump)
1113 bool horizontal_collision = false;
1114 for (const auto &colinfo : result.collisions) {
1115 if (colinfo.type == COLLISION_NODE && colinfo.plane != 1) {
1116 horizontal_collision = true;
1117 break; // one is enough
1121 // must be running against something to trigger autojumping
1122 if (!horizontal_collision)
1125 // check for nodes above
1126 v3f headpos_min = m_position + m_collisionbox.MinEdge * 0.99f;
1127 v3f headpos_max = m_position + m_collisionbox.MaxEdge * 0.99f;
1128 headpos_min.Y = headpos_max.Y; // top face of collision box
1129 v3s16 ceilpos_min = floatToInt(headpos_min, BS) + v3s16(0, 1, 0);
1130 v3s16 ceilpos_max = floatToInt(headpos_max, BS) + v3s16(0, 1, 0);
1131 const NodeDefManager *ndef = env->getGameDef()->ndef();
1132 bool is_position_valid;
1133 for (s16 z = ceilpos_min.Z; z <= ceilpos_max.Z; ++z) {
1134 for (s16 x = ceilpos_min.X; x <= ceilpos_max.X; ++x) {
1135 MapNode n = env->getMap().getNode(v3s16(x, ceilpos_max.Y, z), &is_position_valid);
1137 if (!is_position_valid)
1138 break; // won't collide with the void outside
1139 if (n.getContent() == CONTENT_IGNORE)
1140 return; // players collide with ignore blocks -> same as walkable
1141 const ContentFeatures &f = ndef->get(n);
1143 return; // would bump head, don't jump
1147 float jump_height = 1.1f; // TODO: better than a magic number
1148 v3f jump_pos = initial_position + v3f(0.0f, jump_height * BS, 0.0f);
1149 v3f jump_speed = initial_speed;
1151 // try at peak of jump, zero step height
1152 collisionMoveResult jump_result = collisionMoveSimple(env, m_client, pos_max_d,
1153 m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed, v3f(0.0f));
1155 // see if we can get a little bit farther horizontally if we had
1157 v3f run_delta = m_position - initial_position;
1159 v3f jump_delta = jump_pos - initial_position;
1160 jump_delta.Y = 0.0f;
1161 if (jump_delta.getLengthSQ() > run_delta.getLengthSQ() * 1.01f) {
1163 m_autojump_time = 0.1f;