3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "localplayer.h"
23 #include "collision.h"
26 #include "environment.h"
29 #include "content_cao.h"
35 LocalPlayer::LocalPlayer(Client *client, const char *name):
36 Player(name, client->idef()),
41 static aabb3f getNodeBoundingBox(const std::vector<aabb3f> &nodeboxes)
43 if (nodeboxes.empty())
44 return aabb3f(0, 0, 0, 0, 0, 0);
48 std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
49 b_max = aabb3f(it->MinEdge, it->MaxEdge);
52 for (; it != nodeboxes.end(); ++it)
53 b_max.addInternalBox(*it);
58 bool LocalPlayer::updateSneakNode(Map *map, const v3f &position,
61 static const v3s16 dir9_center[9] = {
73 const NodeDefManager *nodemgr = m_client->ndef();
75 bool is_valid_position;
76 bool new_sneak_node_exists = m_sneak_node_exists;
78 // We want the top of the sneak node to be below the players feet
79 f32 position_y_mod = 0.05 * BS;
80 if (m_sneak_node_exists)
81 position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - position_y_mod;
83 // Get position of current standing node
84 const v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
86 if (current_node != m_sneak_node) {
87 new_sneak_node_exists = false;
89 node = map->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.0 * BS;
102 for (const auto &d : dir9_center) {
103 const v3s16 p = current_node + d;
104 const v3f pf = intToFloat(p, BS);
105 const v2f diff(position.X - pf.X, position.Z - pf.Z);
106 f32 distance_f = diff.getLength();
108 if (distance_f > min_distance_f ||
109 fabs(diff.X) > (.5 + .1) * BS + sneak_max.X ||
110 fabs(diff.Y) > (.5 + .1) * BS + sneak_max.Z)
114 // The node to be sneaked on has to be walkable
115 node = map->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 (m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
124 for (u16 y = 1; y <= height; y++) {
125 node = map->getNode(p + v3s16(0, y, 0), &is_valid_position);
126 if (!is_valid_position || nodemgr->get(node).walkable) {
132 // legacy behaviour: check just one node
133 node = map->getNode(p + v3s16(0, 1, 0), &is_valid_position);
134 ok = is_valid_position && !nodemgr->get(node).walkable;
139 min_distance_f = distance_f;
141 new_sneak_node_exists = true;
144 if (!new_sneak_node_exists)
147 // Update saved top bounding box of sneak node
148 node = map->getNode(m_sneak_node);
149 std::vector<aabb3f> nodeboxes;
150 node.getCollisionBoxes(nodemgr, &nodeboxes);
151 m_sneak_node_bb_top = getNodeBoundingBox(nodeboxes);
153 if (physics_override_sneak_glitch) {
154 // Detect sneak ladder:
155 // Node two meters above sneak node must be solid
156 node = map->getNode(m_sneak_node + v3s16(0, 2, 0),
158 if (is_valid_position && nodemgr->get(node).walkable) {
159 // Node three meters above: must be non-solid
160 node = map->getNode(m_sneak_node + v3s16(0, 3, 0),
162 m_sneak_ladder_detected = is_valid_position &&
163 !nodemgr->get(node).walkable;
169 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
170 std::vector<CollisionInfo> *collision_info)
172 if (!collision_info || collision_info->empty()) {
173 // Node at feet position, update each ClientEnvironment::step()
174 m_standing_node = floatToInt(m_position, BS);
177 // Temporary option for old move code
178 if (!physics_override_new_move) {
179 old_move(dtime, env, pos_max_d, collision_info);
183 Map *map = &env->getMap();
184 const NodeDefManager *nodemgr = m_client->ndef();
186 v3f position = getPosition();
188 // Copy parent position if local player is attached
190 setPosition(overridePosition);
191 added_velocity = v3f(); // ignored
195 PlayerSettings &player_settings = getPlayerSettings();
197 // Skip collision detection if noclip mode is used
198 bool fly_allowed = m_client->checkLocalPrivilege("fly");
199 bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip;
200 bool free_move = player_settings.free_move && fly_allowed;
202 if (noclip && free_move) {
203 position += m_speed * dtime;
204 setPosition(position);
205 added_velocity = v3f(); // ignored
209 m_speed += added_velocity;
210 added_velocity = v3f();
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,BS*0.1,0), BS);
228 node = map->getNode(pp, &is_valid_position);
229 if (is_valid_position) {
230 in_liquid = nodemgr->get(node.getContent()).isLiquid();
231 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
236 // If not in liquid, the threshold of going in is at lower y
239 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
240 node = map->getNode(pp, &is_valid_position);
241 if (is_valid_position) {
242 in_liquid = nodemgr->get(node.getContent()).isLiquid();
243 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
251 Check if player is in liquid (the stable value)
253 pp = floatToInt(position + v3f(0,0,0), BS);
254 node = map->getNode(pp, &is_valid_position);
255 if (is_valid_position) {
256 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
258 in_liquid_stable = false;
262 Check if player is climbing
266 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
267 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), 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));
295 v3f accel_f = v3f(0,0,0);
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.4 * BS, 0, 0.4 * 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 &&
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 || m_sneak_ladder_detected)
391 sneak_can_jump = true;
393 if (collision_info &&
394 m_speed.Y - old_speed.Y > BS) {
395 // Collide with sneak node, report fall damage
396 CollisionInfo sn_info;
397 sn_info.node_p = m_sneak_node;
398 sn_info.old_speed = old_speed;
399 sn_info.new_speed = m_speed;
400 collision_info->push_back(sn_info);
405 Find the next sneak node if necessary
407 bool new_sneak_node_exists = false;
410 new_sneak_node_exists = updateSneakNode(map, position, sneak_max);
413 Set new position but keep sneak node set
415 setPosition(position);
416 m_sneak_node_exists = new_sneak_node_exists;
422 if(!result.standing_on_object && !touching_ground_was && touching_ground) {
423 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
425 // Set camera impact value to be used for view bobbing
426 camera_impact = getSpeed().Y * -1;
430 camera_barely_in_ceiling = false;
431 v3s16 camera_np = floatToInt(getEyePosition(), BS);
432 MapNode n = map->getNode(camera_np);
433 if(n.getContent() != CONTENT_IGNORE){
434 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
435 camera_barely_in_ceiling = true;
441 Check properties of the node on which the player is standing
443 const ContentFeatures &f = nodemgr->get(map->getNode(m_standing_node));
445 // Determine if jumping is possible
446 m_disable_jump = itemgroup_get(f.groups, "disable_jump");
447 m_can_jump = ((touching_ground && !is_climbing)
448 || sneak_can_jump) && !m_disable_jump;
450 // Jump key pressed while jumping off from a bouncy block
451 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
452 m_speed.Y >= -0.5 * BS) {
453 float jumpspeed = movement_speed_jump * physics_override_jump;
455 // Reduce boost when speed already is high
456 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
458 m_speed.Y += jumpspeed;
465 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
468 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
470 move(dtime, env, pos_max_d, NULL);
473 void LocalPlayer::applyControl(float dtime, Environment *env)
476 swimming_vertical = false;
477 swimming_pitch = false;
479 setPitch(control.pitch);
482 // Nullify speed and don't run positioning code if the player is attached
485 setSpeed(v3f(0,0,0));
489 PlayerSettings &player_settings = getPlayerSettings();
491 // All vectors are relative to the player's yaw,
492 // (and pitch if pitch move mode enabled),
493 // and will be rotated at the end
494 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
495 v3f speedV = v3f(0,0,0); // Vertical (Y)
497 bool fly_allowed = m_client->checkLocalPrivilege("fly");
498 bool fast_allowed = m_client->checkLocalPrivilege("fast");
500 bool free_move = fly_allowed && player_settings.free_move;
501 bool fast_move = fast_allowed && player_settings.fast_move;
502 bool pitch_move = (free_move || in_liquid) && player_settings.pitch_move;
503 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
504 bool fast_climb = fast_move && control.aux1 && !player_settings.aux1_descends;
505 bool always_fly_fast = player_settings.always_fly_fast;
507 // Whether superspeed mode is used or not
508 bool superspeed = false;
510 if (always_fly_fast && free_move && fast_move)
513 // Old descend control
514 if (player_settings.aux1_descends)
516 // If free movement and fast movement, always move fast
517 if(free_move && fast_move)
520 // Auxiliary button 1 (E)
525 // In free movement mode, aux1 descends
527 speedV.Y = -movement_speed_fast;
529 speedV.Y = -movement_speed_walk;
531 else if(in_liquid || in_liquid_stable)
533 speedV.Y = -movement_speed_walk;
534 swimming_vertical = true;
538 speedV.Y = -movement_speed_climb;
542 // If not free movement but fast is allowed, aux1 is
549 // New minecraft-like descend control
552 // Auxiliary button 1 (E)
557 // aux1 is "Turbo button"
567 // In free movement mode, sneak descends
568 if (fast_move && (control.aux1 || always_fly_fast))
569 speedV.Y = -movement_speed_fast;
571 speedV.Y = -movement_speed_walk;
573 else if(in_liquid || in_liquid_stable)
576 speedV.Y = -movement_speed_fast;
578 speedV.Y = -movement_speed_walk;
579 swimming_vertical = true;
584 speedV.Y = -movement_speed_fast;
586 speedV.Y = -movement_speed_climb;
592 speedH += v3f(0,0,1);
595 speedH -= v3f(0,0,1);
597 if (!control.up && !control.down) {
598 speedH -= v3f(0,0,1) *
599 (control.forw_move_joystick_axis / 32767.f);
602 speedH += v3f(-1,0,0);
605 speedH += v3f(1,0,0);
607 if (!control.left && !control.right) {
608 speedH += v3f(1,0,0) *
609 (control.sidew_move_joystick_axis / 32767.f);
612 // release autojump after a given time
613 m_autojump_time -= dtime;
614 if (m_autojump_time <= 0.0f)
620 if (player_settings.aux1_descends || always_fly_fast) {
622 speedV.Y = movement_speed_fast;
624 speedV.Y = movement_speed_walk;
626 if(fast_move && control.aux1)
627 speedV.Y = movement_speed_fast;
629 speedV.Y = movement_speed_walk;
635 NOTE: The d value in move() affects jump height by
636 raising the height at which the jump speed is kept
637 at its starting value
639 v3f speedJ = getSpeed();
640 if(speedJ.Y >= -0.5 * BS) {
641 speedJ.Y = movement_speed_jump * physics_override_jump;
643 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_JUMP));
645 } else if (in_liquid && !m_disable_jump) {
647 speedV.Y = movement_speed_fast;
649 speedV.Y = movement_speed_walk;
650 swimming_vertical = true;
651 } else if (is_climbing && !m_disable_jump) {
653 speedV.Y = movement_speed_fast;
655 speedV.Y = movement_speed_climb;
659 // The speed of the player (Y is ignored)
660 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
661 speedH = speedH.normalize() * movement_speed_fast;
662 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
663 speedH = speedH.normalize() * movement_speed_crouch;
665 speedH = speedH.normalize() * movement_speed_walk;
667 // Acceleration increase
668 f32 incH = 0; // Horizontal (X, Z)
669 f32 incV = 0; // Vertical (Y)
670 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
672 // Jumping and falling
673 if(superspeed || (fast_move && control.aux1))
674 incH = movement_acceleration_fast * BS * dtime;
676 incH = movement_acceleration_air * BS * dtime;
677 incV = 0; // No vertical acceleration in air
679 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
680 incH = incV = movement_acceleration_fast * BS * dtime;
682 incH = incV = movement_acceleration_default * BS * dtime;
684 float slip_factor = 1.0f;
685 if (!free_move && !in_liquid && !in_liquid_stable)
686 slip_factor = getSlipFactor(env, speedH);
688 // Don't sink when swimming in pitch mode
689 if (pitch_move && in_liquid) {
690 v3f controlSpeed = speedH + speedV;
691 if (controlSpeed.getLength() > 0.01f)
692 swimming_pitch = true;
695 // Accelerate to target speed with maximum increment
696 accelerate((speedH + speedV) * physics_override_speed,
697 incH * physics_override_speed * slip_factor, incV * physics_override_speed,
701 v3s16 LocalPlayer::getStandingNodePos()
703 if(m_sneak_node_exists)
705 return m_standing_node;
708 v3s16 LocalPlayer::getFootstepNodePos()
710 if (in_liquid_stable)
711 // Emit swimming sound if the player is in liquid
712 return floatToInt(getPosition(), BS);
714 // BS * 0.05 below the player's feet ensures a 1/16th height
715 // nodebox is detected instead of the node below it.
716 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
717 // A larger distance below is necessary for a footstep sound
718 // when landing after a jump or fall. BS * 0.5 ensures water
719 // sounds when swimming in 1 node deep water.
720 return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
723 v3s16 LocalPlayer::getLightPosition() const
725 return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
728 v3f LocalPlayer::getEyeOffset() const
730 float eye_height = camera_barely_in_ceiling ?
731 m_eye_height - 0.125f : m_eye_height;
732 return v3f(0, BS * eye_height, 0);
736 void LocalPlayer::accelerate(const v3f &target_speed, const f32 max_increase_H,
737 const f32 max_increase_V, const bool use_pitch)
739 const f32 yaw = getYaw();
740 const f32 pitch = getPitch();
741 v3f flat_speed = m_speed;
742 // Rotate speed vector by -yaw and -pitch to make it relative to the player's yaw and pitch
743 flat_speed.rotateXZBy(-yaw);
745 flat_speed.rotateYZBy(-pitch);
747 v3f d_wanted = target_speed - flat_speed;
750 // Then compare the horizontal and vertical components with the wanted speed
751 if (max_increase_H > 0) {
752 v3f d_wanted_H = d_wanted * v3f(1,0,1);
753 if (d_wanted_H.getLength() > max_increase_H)
754 d += d_wanted_H.normalize() * max_increase_H;
759 if (max_increase_V > 0) {
760 f32 d_wanted_V = d_wanted.Y;
761 if (d_wanted_V > max_increase_V)
762 d.Y += max_increase_V;
763 else if (d_wanted_V < -max_increase_V)
764 d.Y -= max_increase_V;
769 // Finally rotate it again
777 // Temporary option for old move code
778 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
779 std::vector<CollisionInfo> *collision_info)
781 Map *map = &env->getMap();
782 const NodeDefManager *nodemgr = m_client->ndef();
784 v3f position = getPosition();
786 // Copy parent position if local player is attached
788 setPosition(overridePosition);
789 m_sneak_node_exists = false;
790 added_velocity = v3f();
794 PlayerSettings &player_settings = getPlayerSettings();
796 // Skip collision detection if noclip mode is used
797 bool fly_allowed = m_client->checkLocalPrivilege("fly");
798 bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip;
799 bool free_move = noclip && fly_allowed && player_settings.free_move;
801 position += m_speed * dtime;
802 setPosition(position);
803 m_sneak_node_exists = false;
804 added_velocity = v3f();
808 m_speed += added_velocity;
809 added_velocity = v3f();
814 bool is_valid_position;
819 Check if player is in liquid (the oscillating value)
822 // If in liquid, the threshold of coming out is at higher y
823 pp = floatToInt(position + v3f(0, BS * 0.1, 0), BS);
824 node = map->getNode(pp, &is_valid_position);
825 if (is_valid_position) {
826 in_liquid = nodemgr->get(node.getContent()).isLiquid();
827 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
832 // If not in liquid, the threshold of going in is at lower y
833 pp = floatToInt(position + v3f(0, BS * 0.5, 0), BS);
834 node = map->getNode(pp, &is_valid_position);
835 if (is_valid_position) {
836 in_liquid = nodemgr->get(node.getContent()).isLiquid();
837 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
844 Check if player is in liquid (the stable value)
846 pp = floatToInt(position + v3f(0, 0, 0), BS);
847 node = map->getNode(pp, &is_valid_position);
848 if (is_valid_position)
849 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
851 in_liquid_stable = false;
854 Check if player is climbing
856 pp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS);
857 v3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS);
858 node = map->getNode(pp, &is_valid_position);
859 bool is_valid_position2;
860 MapNode node2 = map->getNode(pp2, &is_valid_position2);
862 if (!(is_valid_position && is_valid_position2))
865 is_climbing = (nodemgr->get(node.getContent()).climbable ||
866 nodemgr->get(node2.getContent()).climbable) && !free_move;
869 Collision uncertainty radius
870 Make it a bit larger than the maximum distance of movement
872 //f32 d = pos_max_d * 1.1;
873 // A fairly large value in here makes moving smoother
875 // This should always apply, otherwise there are glitches
876 sanity_check(d > pos_max_d);
877 // Maximum distance over border for sneaking
878 f32 sneak_max = BS * 0.4;
881 If sneaking, keep in range from the last walked node and don't
884 if (control.sneak && m_sneak_node_exists &&
885 !(fly_allowed && player_settings.free_move) && !in_liquid &&
886 physics_override_sneak) {
887 f32 maxd = 0.5 * BS + sneak_max;
888 v3f lwn_f = intToFloat(m_sneak_node, BS);
889 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
890 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
893 // Move up if necessary
894 f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
895 if (position.Y < new_y)
898 Collision seems broken, since player is sinking when
899 sneaking over the edges of current sneaking_node.
900 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
907 // this shouldn't be hardcoded but transmitted from server
908 float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
910 v3f accel_f = v3f(0, 0, 0);
911 const v3f initial_position = position;
912 const v3f initial_speed = m_speed;
914 collisionMoveResult result = collisionMoveSimple(env, m_client,
915 pos_max_d, m_collisionbox, player_stepheight, dtime,
916 &position, &m_speed, accel_f);
918 // Positition was slightly changed; update standing node pos
920 m_standing_node = floatToInt(m_position - v3f(0, 0.1f * BS, 0), BS);
922 m_standing_node = floatToInt(m_position, BS);
925 If the player's feet touch the topside of any node, this is
928 Player is allowed to jump when this is true.
930 bool touching_ground_was = touching_ground;
931 touching_ground = result.touching_ground;
933 //bool standing_on_unloaded = result.standing_on_unloaded;
936 Check the nodes under the player to see from which node the
937 player is sneaking from, if any. If the node from under
938 the player has been removed, the player falls.
940 f32 position_y_mod = 0.05 * BS;
941 if (m_sneak_node_bb_ymax > 0)
942 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
943 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
944 if (m_sneak_node_exists &&
945 nodemgr->get(map->getNode(m_old_node_below)).name == "air" &&
946 m_old_node_below_type != "air") {
947 // Old node appears to have been removed; that is,
948 // it wasn't air before but now it is
949 m_need_to_get_new_sneak_node = false;
950 m_sneak_node_exists = false;
951 } else if (nodemgr->get(map->getNode(current_node)).name != "air") {
952 // We are on something, so make sure to recalculate the sneak
954 m_need_to_get_new_sneak_node = true;
957 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
958 m_sneak_node_bb_ymax = 0;
959 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
960 v2f player_p2df(position.X, position.Z);
961 f32 min_distance_f = 100000.0 * BS;
962 // If already seeking from some node, compare to it.
963 v3s16 new_sneak_node = m_sneak_node;
964 for (s16 x= -1; x <= 1; x++)
965 for (s16 z= -1; z <= 1; z++) {
966 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
967 v3f pf = intToFloat(p, BS);
968 v2f node_p2df(pf.X, pf.Z);
969 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
970 f32 max_axis_distance_f = MYMAX(
971 std::fabs(player_p2df.X - node_p2df.X),
972 std::fabs(player_p2df.Y - node_p2df.Y));
974 if (distance_f > min_distance_f ||
975 max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)
978 // The node to be sneaked on has to be walkable
979 node = map->getNode(p, &is_valid_position);
980 if (!is_valid_position || !nodemgr->get(node).walkable)
982 // And the node above it has to be nonwalkable
983 node = map->getNode(p + v3s16(0, 1, 0), &is_valid_position);
984 if (!is_valid_position || nodemgr->get(node).walkable)
986 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
987 if (!physics_override_sneak_glitch) {
988 node =map->getNode(p + v3s16(0, 2, 0), &is_valid_position);
989 if (!is_valid_position || nodemgr->get(node).walkable)
993 min_distance_f = distance_f;
997 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
999 m_sneak_node = new_sneak_node;
1000 m_sneak_node_exists = sneak_node_found;
1002 if (sneak_node_found) {
1004 MapNode n = map->getNode(m_sneak_node);
1005 std::vector<aabb3f> nodeboxes;
1006 n.getCollisionBoxes(nodemgr, &nodeboxes);
1007 for (const auto &box : nodeboxes) {
1008 if (box.MaxEdge.Y > cb_max)
1009 cb_max = box.MaxEdge.Y;
1011 m_sneak_node_bb_ymax = cb_max;
1015 If sneaking, the player's collision box can be in air, so
1016 this has to be set explicitly
1018 if (sneak_node_found && control.sneak)
1019 touching_ground = true;
1023 Set new position but keep sneak node set
1025 bool sneak_node_exists = m_sneak_node_exists;
1026 setPosition(position);
1027 m_sneak_node_exists = sneak_node_exists;
1032 // Dont report if flying
1033 if (collision_info && !(player_settings.free_move && fly_allowed)) {
1034 for (const auto &info : result.collisions) {
1035 collision_info->push_back(info);
1039 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1040 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
1041 // Set camera impact value to be used for view bobbing
1042 camera_impact = getSpeed().Y * -1;
1046 camera_barely_in_ceiling = false;
1047 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1048 MapNode n = map->getNode(camera_np);
1049 if (n.getContent() != CONTENT_IGNORE) {
1050 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1051 camera_barely_in_ceiling = true;
1056 Update the node last under the player
1058 m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);
1059 m_old_node_below_type = nodemgr->get(map->getNode(m_old_node_below)).name;
1062 Check properties of the node on which the player is standing
1064 const ContentFeatures &f = nodemgr->get(map->getNode(
1065 getStandingNodePos()));
1067 // Determine if jumping is possible
1068 m_disable_jump = itemgroup_get(f.groups, "disable_jump");
1069 m_can_jump = touching_ground && !m_disable_jump;
1071 // Jump key pressed while jumping off from a bouncy block
1072 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1073 m_speed.Y >= -0.5 * BS) {
1074 float jumpspeed = movement_speed_jump * physics_override_jump;
1075 if (m_speed.Y > 1) {
1076 // Reduce boost when speed already is high
1077 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
1079 m_speed.Y += jumpspeed;
1086 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
1089 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1091 // Slip on slippery nodes
1092 const NodeDefManager *nodemgr = env->getGameDef()->ndef();
1093 Map *map = &env->getMap();
1094 const ContentFeatures &f = nodemgr->get(map->getNode(
1095 getStandingNodePos()));
1098 slippery = itemgroup_get(f.groups, "slippery");
1100 if (slippery >= 1) {
1101 if (speedH == v3f(0.0f)) {
1102 slippery = slippery * 2;
1104 return core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);
1109 void LocalPlayer::handleAutojump(f32 dtime, Environment *env,
1110 const collisionMoveResult &result, const v3f &initial_position,
1111 const v3f &initial_speed, f32 pos_max_d)
1113 PlayerSettings &player_settings = getPlayerSettings();
1114 if (!player_settings.autojump)
1120 bool control_forward = control.up ||
1121 (!control.up && !control.down &&
1122 control.forw_move_joystick_axis < -0.05);
1123 bool could_autojump =
1124 m_can_jump && !control.jump && !control.sneak && control_forward;
1125 if (!could_autojump)
1128 bool horizontal_collision = false;
1129 for (const auto &colinfo : result.collisions) {
1130 if (colinfo.type == COLLISION_NODE && colinfo.plane != 1) {
1131 horizontal_collision = true;
1132 break; // one is enough
1136 // must be running against something to trigger autojumping
1137 if (!horizontal_collision)
1140 // check for nodes above
1141 v3f headpos_min = m_position + m_collisionbox.MinEdge * 0.99f;
1142 v3f headpos_max = m_position + m_collisionbox.MaxEdge * 0.99f;
1143 headpos_min.Y = headpos_max.Y; // top face of collision box
1144 v3s16 ceilpos_min = floatToInt(headpos_min, BS) + v3s16(0, 1, 0);
1145 v3s16 ceilpos_max = floatToInt(headpos_max, BS) + v3s16(0, 1, 0);
1146 const NodeDefManager *ndef = env->getGameDef()->ndef();
1147 bool is_position_valid;
1148 for (s16 z = ceilpos_min.Z; z <= ceilpos_max.Z; z++) {
1149 for (s16 x = ceilpos_min.X; x <= ceilpos_max.X; x++) {
1150 MapNode n = env->getMap().getNode(v3s16(x, ceilpos_max.Y, z), &is_position_valid);
1152 if (!is_position_valid)
1153 break; // won't collide with the void outside
1154 if (n.getContent() == CONTENT_IGNORE)
1155 return; // players collide with ignore blocks -> same as walkable
1156 const ContentFeatures &f = ndef->get(n);
1158 return; // would bump head, don't jump
1162 float jump_height = 1.1f; // TODO: better than a magic number
1163 v3f jump_pos = initial_position + v3f(0.0f, jump_height * BS, 0.0f);
1164 v3f jump_speed = initial_speed;
1166 // try at peak of jump, zero step height
1167 collisionMoveResult jump_result = collisionMoveSimple(env, m_client, pos_max_d,
1168 m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed,
1171 // see if we can get a little bit farther horizontally if we had
1173 v3f run_delta = m_position - initial_position;
1175 v3f jump_delta = jump_pos - initial_position;
1176 jump_delta.Y = 0.0f;
1177 if (jump_delta.getLengthSQ() > run_delta.getLengthSQ() * 1.01f) {
1179 m_autojump_time = 0.1f;