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->getNodeNoEx(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->getNodeNoEx(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->getNodeNoEx(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->getNodeNoEx(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->getNodeNoEx(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->getNodeNoEx(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->getNodeNoEx(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 below the feet, update each ClientEnvironment::step()
174 m_standing_node = floatToInt(m_position, BS) - v3s16(0, 1, 0);
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);
194 PlayerSettings &player_settings = getPlayerSettings();
196 // Skip collision detection if noclip mode is used
197 bool fly_allowed = m_client->checkLocalPrivilege("fly");
198 bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip;
199 bool free_move = player_settings.free_move && fly_allowed;
201 if (noclip && free_move) {
202 position += m_speed * dtime;
203 setPosition(position);
211 bool is_valid_position;
216 Check if player is in liquid (the oscillating value)
219 // If in liquid, the threshold of coming out is at higher y
222 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
223 node = map->getNodeNoEx(pp, &is_valid_position);
224 if (is_valid_position) {
225 in_liquid = nodemgr->get(node.getContent()).isLiquid();
226 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
231 // If not in liquid, the threshold of going in is at lower y
234 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
235 node = map->getNodeNoEx(pp, &is_valid_position);
236 if (is_valid_position) {
237 in_liquid = nodemgr->get(node.getContent()).isLiquid();
238 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
246 Check if player is in liquid (the stable value)
248 pp = floatToInt(position + v3f(0,0,0), BS);
249 node = map->getNodeNoEx(pp, &is_valid_position);
250 if (is_valid_position) {
251 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
253 in_liquid_stable = false;
257 Check if player is climbing
261 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
262 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
263 node = map->getNodeNoEx(pp, &is_valid_position);
264 bool is_valid_position2;
265 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
267 if (!(is_valid_position && is_valid_position2)) {
270 is_climbing = (nodemgr->get(node.getContent()).climbable
271 || nodemgr->get(node2.getContent()).climbable) && !free_move;
275 Collision uncertainty radius
276 Make it a bit larger than the maximum distance of movement
278 //f32 d = pos_max_d * 1.1;
279 // A fairly large value in here makes moving smoother
282 // This should always apply, otherwise there are glitches
283 sanity_check(d > pos_max_d);
285 // Player object property step height is multiplied by BS in
286 // /src/script/common/c_content.cpp and /src/content_sao.cpp
287 float player_stepheight = (m_cao == nullptr) ? 0.0f :
288 (touching_ground ? m_cao->getStepHeight() : (0.2f * BS));
290 v3f accel_f = v3f(0,0,0);
291 const v3f initial_position = position;
292 const v3f initial_speed = m_speed;
294 collisionMoveResult result = collisionMoveSimple(env, m_client,
295 pos_max_d, m_collisionbox, player_stepheight, dtime,
296 &position, &m_speed, accel_f);
298 bool could_sneak = control.sneak && !free_move && !in_liquid &&
299 !is_climbing && physics_override_sneak;
301 // Add new collisions to the vector
302 if (collision_info && !free_move) {
303 v3f diff = intToFloat(m_standing_node, BS) - position;
304 f32 distance = diff.getLength();
305 // Force update each ClientEnvironment::step()
306 bool is_first = collision_info->empty();
308 for (const auto &colinfo : result.collisions) {
309 collision_info->push_back(colinfo);
311 if (colinfo.type != COLLISION_NODE ||
312 colinfo.new_speed.Y != 0 ||
313 (could_sneak && m_sneak_node_exists))
316 diff = intToFloat(colinfo.node_p, BS) - position;
318 // Find nearest colliding node
319 f32 len = diff.getLength();
320 if (is_first || len < distance) {
321 m_standing_node = colinfo.node_p;
328 If the player's feet touch the topside of any node, this is
331 Player is allowed to jump when this is true.
333 bool touching_ground_was = touching_ground;
334 touching_ground = result.touching_ground;
335 bool sneak_can_jump = false;
337 // Max. distance (X, Z) over border for sneaking determined by collision box
338 // * 0.49 to keep the center just barely on the node
339 v3f sneak_max = m_collisionbox.getExtent() * 0.49;
341 if (m_sneak_ladder_detected) {
342 // restore legacy behaviour (this makes the m_speed.Y hack necessary)
343 sneak_max = v3f(0.4 * BS, 0, 0.4 * BS);
347 If sneaking, keep on top of last walked node and don't fall off
349 if (could_sneak && m_sneak_node_exists) {
350 const v3f sn_f = intToFloat(m_sneak_node, BS);
351 const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge;
352 const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge;
353 const v3f old_pos = position;
354 const v3f old_speed = m_speed;
355 f32 y_diff = bmax.Y - position.Y;
356 m_standing_node = m_sneak_node;
358 // (BS * 0.6f) is the basic stepheight while standing on ground
359 if (y_diff < BS * 0.6f) {
360 // Only center player when they're on the node
361 position.X = rangelim(position.X,
362 bmin.X - sneak_max.X, bmax.X + sneak_max.X);
363 position.Z = rangelim(position.Z,
364 bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z);
366 if (position.X != old_pos.X)
368 if (position.Z != old_pos.Z)
372 if (y_diff > 0 && m_speed.Y <= 0 &&
373 (physics_override_sneak_glitch || y_diff < BS * 0.6f)) {
374 // Move player to the maximal height when falling or when
375 // the ledge is climbed on the next step.
377 // Smoothen the movement (based on 'position.Y = bmax.Y')
378 position.Y += y_diff * dtime * 22.0f + BS * 0.01f;
379 position.Y = std::min(position.Y, bmax.Y);
383 // Allow jumping on node edges while sneaking
384 if (m_speed.Y == 0 || m_sneak_ladder_detected)
385 sneak_can_jump = true;
387 if (collision_info &&
388 m_speed.Y - old_speed.Y > BS) {
389 // Collide with sneak node, report fall damage
390 CollisionInfo sn_info;
391 sn_info.node_p = m_sneak_node;
392 sn_info.old_speed = old_speed;
393 sn_info.new_speed = m_speed;
394 collision_info->push_back(sn_info);
399 Find the next sneak node if necessary
401 bool new_sneak_node_exists = false;
404 new_sneak_node_exists = updateSneakNode(map, position, sneak_max);
407 Set new position but keep sneak node set
409 setPosition(position);
410 m_sneak_node_exists = new_sneak_node_exists;
416 if(!result.standing_on_object && !touching_ground_was && touching_ground) {
417 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
419 // Set camera impact value to be used for view bobbing
420 camera_impact = getSpeed().Y * -1;
424 camera_barely_in_ceiling = false;
425 v3s16 camera_np = floatToInt(getEyePosition(), BS);
426 MapNode n = map->getNodeNoEx(camera_np);
427 if(n.getContent() != CONTENT_IGNORE){
428 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
429 camera_barely_in_ceiling = true;
435 Check properties of the node on which the player is standing
437 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(m_standing_node));
438 // Determine if jumping is possible
439 m_can_jump = (touching_ground && !in_liquid && !is_climbing)
441 if (itemgroup_get(f.groups, "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.5 * BS) {
447 float jumpspeed = movement_speed_jump * physics_override_jump;
449 // Reduce boost when speed already is high
450 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
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
479 setSpeed(v3f(0,0,0));
483 PlayerSettings &player_settings = getPlayerSettings();
485 // All vectors are relative to the player's yaw,
486 // (and pitch if pitch fly mode enabled),
487 // and will be rotated at the end
488 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
489 v3f speedV = v3f(0,0,0); // Vertical (Y)
491 bool fly_allowed = m_client->checkLocalPrivilege("fly");
492 bool fast_allowed = m_client->checkLocalPrivilege("fast");
494 bool free_move = fly_allowed && player_settings.free_move;
495 bool fast_move = fast_allowed && player_settings.fast_move;
496 bool pitch_move = (free_move || in_liquid) && player_settings.pitch_move;
497 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
498 bool fast_climb = fast_move && control.aux1 && !player_settings.aux1_descends;
499 bool continuous_forward = player_settings.continuous_forward;
500 bool always_fly_fast = player_settings.always_fly_fast;
502 // Whether superspeed mode is used or not
503 bool superspeed = false;
505 if (always_fly_fast && free_move && fast_move)
508 // Old descend control
509 if (player_settings.aux1_descends)
511 // If free movement and fast movement, always move fast
512 if(free_move && fast_move)
515 // Auxiliary button 1 (E)
520 // In free movement mode, aux1 descends
522 speedV.Y = -movement_speed_fast;
524 speedV.Y = -movement_speed_walk;
526 else if(in_liquid || in_liquid_stable)
528 speedV.Y = -movement_speed_walk;
529 swimming_vertical = true;
533 speedV.Y = -movement_speed_climb;
537 // If not free movement but fast is allowed, aux1 is
544 // New minecraft-like descend control
547 // Auxiliary button 1 (E)
552 // aux1 is "Turbo button"
562 // In free movement mode, sneak descends
563 if (fast_move && (control.aux1 || always_fly_fast))
564 speedV.Y = -movement_speed_fast;
566 speedV.Y = -movement_speed_walk;
568 else if(in_liquid || in_liquid_stable)
571 speedV.Y = -movement_speed_fast;
573 speedV.Y = -movement_speed_walk;
574 swimming_vertical = true;
579 speedV.Y = -movement_speed_fast;
581 speedV.Y = -movement_speed_climb;
586 if (continuous_forward)
587 speedH += v3f(0,0,1);
590 if (continuous_forward) {
594 speedH += v3f(0,0,1);
598 speedH -= v3f(0,0,1);
600 if (!control.up && !control.down) {
601 speedH -= v3f(0,0,1) *
602 (control.forw_move_joystick_axis / 32767.f);
605 speedH += v3f(-1,0,0);
608 speedH += v3f(1,0,0);
610 if (!control.left && !control.right) {
611 speedH += v3f(1,0,0) *
612 (control.sidew_move_joystick_axis / 32767.f);
615 // release autojump after a given time
616 m_autojump_time -= dtime;
617 if (m_autojump_time <= 0.0f)
623 if (player_settings.aux1_descends || always_fly_fast) {
625 speedV.Y = movement_speed_fast;
627 speedV.Y = movement_speed_walk;
629 if(fast_move && control.aux1)
630 speedV.Y = movement_speed_fast;
632 speedV.Y = movement_speed_walk;
638 NOTE: The d value in move() affects jump height by
639 raising the height at which the jump speed is kept
640 at its starting value
642 v3f speedJ = getSpeed();
643 if(speedJ.Y >= -0.5 * BS) {
644 speedJ.Y = movement_speed_jump * physics_override_jump;
646 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_JUMP));
652 speedV.Y = movement_speed_fast;
654 speedV.Y = movement_speed_walk;
655 swimming_vertical = true;
660 speedV.Y = movement_speed_fast;
662 speedV.Y = movement_speed_climb;
666 // The speed of the player (Y is ignored)
667 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
668 speedH = speedH.normalize() * movement_speed_fast;
669 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
670 speedH = speedH.normalize() * movement_speed_crouch;
672 speedH = speedH.normalize() * movement_speed_walk;
674 // Acceleration increase
675 f32 incH = 0; // Horizontal (X, Z)
676 f32 incV = 0; // Vertical (Y)
677 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
679 // Jumping and falling
680 if(superspeed || (fast_move && control.aux1))
681 incH = movement_acceleration_fast * BS * dtime;
683 incH = movement_acceleration_air * BS * dtime;
684 incV = 0; // No vertical acceleration in air
686 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
687 incH = incV = movement_acceleration_fast * BS * dtime;
689 incH = incV = movement_acceleration_default * BS * dtime;
691 float slip_factor = 1.0f;
692 if (!free_move && !in_liquid && !in_liquid_stable)
693 slip_factor = getSlipFactor(env, speedH);
695 // Don't sink when swimming in pitch mode
696 if (pitch_move && in_liquid) {
697 v3f controlSpeed = speedH + speedV;
698 if (controlSpeed.getLength() > 0.01f)
699 swimming_pitch = true;
702 // Accelerate to target speed with maximum increment
703 accelerate((speedH + speedV) * physics_override_speed,
704 incH * physics_override_speed * slip_factor, incV * physics_override_speed,
708 v3s16 LocalPlayer::getStandingNodePos()
710 if(m_sneak_node_exists)
712 return m_standing_node;
715 v3s16 LocalPlayer::getFootstepNodePos()
717 if (in_liquid_stable)
718 // Emit swimming sound if the player is in liquid
719 return floatToInt(getPosition(), BS);
721 // BS * 0.05 below the player's feet ensures a 1/16th height
722 // nodebox is detected instead of the node below it.
723 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
724 // A larger distance below is necessary for a footstep sound
725 // when landing after a jump or fall. BS * 0.5 ensures water
726 // sounds when swimming in 1 node deep water.
727 return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
730 v3s16 LocalPlayer::getLightPosition() const
732 return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
735 v3f LocalPlayer::getEyeOffset() const
737 float eye_height = camera_barely_in_ceiling ?
738 m_eye_height - 0.125f : m_eye_height;
739 return v3f(0, BS * eye_height, 0);
743 void LocalPlayer::accelerate(const v3f &target_speed, const f32 max_increase_H,
744 const f32 max_increase_V, const bool use_pitch)
746 const f32 yaw = getYaw();
747 const f32 pitch = getPitch();
748 v3f flat_speed = m_speed;
749 // Rotate speed vector by -yaw and -pitch to make it relative to the player's yaw and pitch
750 flat_speed.rotateXZBy(-yaw);
752 flat_speed.rotateYZBy(-pitch);
754 v3f d_wanted = target_speed - flat_speed;
757 // Then compare the horizontal and vertical components with the wanted speed
758 if (max_increase_H > 0) {
759 v3f d_wanted_H = d_wanted * v3f(1,0,1);
760 if (d_wanted_H.getLength() > max_increase_H)
761 d += d_wanted_H.normalize() * max_increase_H;
766 if (max_increase_V > 0) {
767 f32 d_wanted_V = d_wanted.Y;
768 if (d_wanted_V > max_increase_V)
769 d.Y += max_increase_V;
770 else if (d_wanted_V < -max_increase_V)
771 d.Y -= max_increase_V;
776 // Finally rotate it again
784 // Temporary option for old move code
785 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
786 std::vector<CollisionInfo> *collision_info)
788 Map *map = &env->getMap();
789 const NodeDefManager *nodemgr = m_client->ndef();
791 v3f position = getPosition();
793 // Copy parent position if local player is attached
795 setPosition(overridePosition);
796 m_sneak_node_exists = false;
800 PlayerSettings &player_settings = getPlayerSettings();
802 // Skip collision detection if noclip mode is used
803 bool fly_allowed = m_client->checkLocalPrivilege("fly");
804 bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip;
805 bool free_move = noclip && fly_allowed && player_settings.free_move;
807 position += m_speed * dtime;
808 setPosition(position);
809 m_sneak_node_exists = false;
816 bool is_valid_position;
821 Check if player is in liquid (the oscillating value)
824 // If in liquid, the threshold of coming out is at higher y
825 pp = floatToInt(position + v3f(0, BS * 0.1, 0), BS);
826 node = map->getNodeNoEx(pp, &is_valid_position);
827 if (is_valid_position) {
828 in_liquid = nodemgr->get(node.getContent()).isLiquid();
829 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
834 // If not in liquid, the threshold of going in is at lower y
835 pp = floatToInt(position + v3f(0, BS * 0.5, 0), BS);
836 node = map->getNodeNoEx(pp, &is_valid_position);
837 if (is_valid_position) {
838 in_liquid = nodemgr->get(node.getContent()).isLiquid();
839 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
846 Check if player is in liquid (the stable value)
848 pp = floatToInt(position + v3f(0, 0, 0), BS);
849 node = map->getNodeNoEx(pp, &is_valid_position);
850 if (is_valid_position)
851 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
853 in_liquid_stable = false;
856 Check if player is climbing
858 pp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS);
859 v3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS);
860 node = map->getNodeNoEx(pp, &is_valid_position);
861 bool is_valid_position2;
862 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
864 if (!(is_valid_position && is_valid_position2))
867 is_climbing = (nodemgr->get(node.getContent()).climbable ||
868 nodemgr->get(node2.getContent()).climbable) && !free_move;
871 Collision uncertainty radius
872 Make it a bit larger than the maximum distance of movement
874 //f32 d = pos_max_d * 1.1;
875 // A fairly large value in here makes moving smoother
877 // This should always apply, otherwise there are glitches
878 sanity_check(d > pos_max_d);
879 // Maximum distance over border for sneaking
880 f32 sneak_max = BS * 0.4;
883 If sneaking, keep in range from the last walked node and don't
886 if (control.sneak && m_sneak_node_exists &&
887 !(fly_allowed && player_settings.free_move) && !in_liquid &&
888 physics_override_sneak) {
889 f32 maxd = 0.5 * BS + sneak_max;
890 v3f lwn_f = intToFloat(m_sneak_node, BS);
891 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
892 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
895 // Move up if necessary
896 f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
897 if (position.Y < new_y)
900 Collision seems broken, since player is sinking when
901 sneaking over the edges of current sneaking_node.
902 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
909 // this shouldn't be hardcoded but transmitted from server
910 float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
912 v3f accel_f = v3f(0, 0, 0);
913 const v3f initial_position = position;
914 const v3f initial_speed = m_speed;
916 collisionMoveResult result = collisionMoveSimple(env, m_client,
917 pos_max_d, m_collisionbox, player_stepheight, dtime,
918 &position, &m_speed, accel_f);
921 If the player's feet touch the topside of any node, this is
924 Player is allowed to jump when this is true.
926 bool touching_ground_was = touching_ground;
927 touching_ground = result.touching_ground;
929 //bool standing_on_unloaded = result.standing_on_unloaded;
932 Check the nodes under the player to see from which node the
933 player is sneaking from, if any. If the node from under
934 the player has been removed, the player falls.
936 f32 position_y_mod = 0.05 * BS;
937 if (m_sneak_node_bb_ymax > 0)
938 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
939 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
940 if (m_sneak_node_exists &&
941 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
942 m_old_node_below_type != "air") {
943 // Old node appears to have been removed; that is,
944 // it wasn't air before but now it is
945 m_need_to_get_new_sneak_node = false;
946 m_sneak_node_exists = false;
947 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
948 // We are on something, so make sure to recalculate the sneak
950 m_need_to_get_new_sneak_node = true;
953 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
954 m_sneak_node_bb_ymax = 0;
955 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
956 v2f player_p2df(position.X, position.Z);
957 f32 min_distance_f = 100000.0 * BS;
958 // If already seeking from some node, compare to it.
959 v3s16 new_sneak_node = m_sneak_node;
960 for (s16 x= -1; x <= 1; x++)
961 for (s16 z= -1; z <= 1; z++) {
962 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
963 v3f pf = intToFloat(p, BS);
964 v2f node_p2df(pf.X, pf.Z);
965 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
966 f32 max_axis_distance_f = MYMAX(
967 std::fabs(player_p2df.X - node_p2df.X),
968 std::fabs(player_p2df.Y - node_p2df.Y));
970 if (distance_f > min_distance_f ||
971 max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)
974 // The node to be sneaked on has to be walkable
975 node = map->getNodeNoEx(p, &is_valid_position);
976 if (!is_valid_position || !nodemgr->get(node).walkable)
978 // And the node above it has to be nonwalkable
979 node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
980 if (!is_valid_position || nodemgr->get(node).walkable)
982 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
983 if (!physics_override_sneak_glitch) {
984 node =map->getNodeNoEx(p + v3s16(0, 2, 0), &is_valid_position);
985 if (!is_valid_position || nodemgr->get(node).walkable)
989 min_distance_f = distance_f;
993 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
995 m_sneak_node = new_sneak_node;
996 m_sneak_node_exists = sneak_node_found;
998 if (sneak_node_found) {
1000 MapNode n = map->getNodeNoEx(m_sneak_node);
1001 std::vector<aabb3f> nodeboxes;
1002 n.getCollisionBoxes(nodemgr, &nodeboxes);
1003 for (const auto &box : nodeboxes) {
1004 if (box.MaxEdge.Y > cb_max)
1005 cb_max = box.MaxEdge.Y;
1007 m_sneak_node_bb_ymax = cb_max;
1011 If sneaking, the player's collision box can be in air, so
1012 this has to be set explicitly
1014 if (sneak_node_found && control.sneak)
1015 touching_ground = true;
1019 Set new position but keep sneak node set
1021 bool sneak_node_exists = m_sneak_node_exists;
1022 setPosition(position);
1023 m_sneak_node_exists = sneak_node_exists;
1028 // Dont report if flying
1029 if (collision_info && !(player_settings.free_move && fly_allowed)) {
1030 for (const auto &info : result.collisions) {
1031 collision_info->push_back(info);
1035 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1036 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
1037 // Set camera impact value to be used for view bobbing
1038 camera_impact = getSpeed().Y * -1;
1042 camera_barely_in_ceiling = false;
1043 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1044 MapNode n = map->getNodeNoEx(camera_np);
1045 if (n.getContent() != CONTENT_IGNORE) {
1046 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1047 camera_barely_in_ceiling = true;
1052 Update the node last under the player
1054 m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);
1055 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
1058 Check properties of the node on which the player is standing
1060 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
1061 // Determine if jumping is possible
1062 m_can_jump = touching_ground && !in_liquid;
1063 if (itemgroup_get(f.groups, "disable_jump"))
1065 // Jump key pressed while jumping off from a bouncy block
1066 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1067 m_speed.Y >= -0.5 * BS) {
1068 float jumpspeed = movement_speed_jump * physics_override_jump;
1069 if (m_speed.Y > 1) {
1070 // Reduce boost when speed already is high
1071 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
1073 m_speed.Y += jumpspeed;
1080 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
1083 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1085 // Slip on slippery nodes
1086 const NodeDefManager *nodemgr = env->getGameDef()->ndef();
1087 Map *map = &env->getMap();
1088 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(
1089 getStandingNodePos()));
1092 slippery = itemgroup_get(f.groups, "slippery");
1094 if (slippery >= 1) {
1095 if (speedH == v3f(0.0f)) {
1096 slippery = slippery * 2;
1098 return core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);
1103 void LocalPlayer::handleAutojump(f32 dtime, Environment *env,
1104 const collisionMoveResult &result, const v3f &initial_position,
1105 const v3f &initial_speed, f32 pos_max_d)
1107 PlayerSettings &player_settings = getPlayerSettings();
1108 if (!player_settings.autojump)
1114 bool control_forward = control.up || player_settings.continuous_forward ||
1115 (!control.up && !control.down &&
1116 control.forw_move_joystick_axis < -0.05);
1117 bool could_autojump =
1118 m_can_jump && !control.jump && !control.sneak && control_forward;
1119 if (!could_autojump)
1122 bool horizontal_collision = false;
1123 for (const auto &colinfo : result.collisions) {
1124 if (colinfo.type == COLLISION_NODE && colinfo.plane != 1) {
1125 horizontal_collision = true;
1126 break; // one is enough
1130 // must be running against something to trigger autojumping
1131 if (!horizontal_collision)
1134 // check for nodes above
1135 v3f headpos_min = m_position + m_collisionbox.MinEdge * 0.99f;
1136 v3f headpos_max = m_position + m_collisionbox.MaxEdge * 0.99f;
1137 headpos_min.Y = headpos_max.Y; // top face of collision box
1138 v3s16 ceilpos_min = floatToInt(headpos_min, BS) + v3s16(0, 1, 0);
1139 v3s16 ceilpos_max = floatToInt(headpos_max, BS) + v3s16(0, 1, 0);
1140 const NodeDefManager *ndef = env->getGameDef()->ndef();
1141 bool is_position_valid;
1142 for (s16 z = ceilpos_min.Z; z <= ceilpos_max.Z; z++) {
1143 for (s16 x = ceilpos_min.X; x <= ceilpos_max.X; x++) {
1144 MapNode n = env->getMap().getNodeNoEx(v3s16(x, ceilpos_max.Y, z), &is_position_valid);
1146 if (!is_position_valid)
1147 break; // won't collide with the void outside
1148 if (n.getContent() == CONTENT_IGNORE)
1149 return; // players collide with ignore blocks -> same as walkable
1150 const ContentFeatures &f = ndef->get(n);
1152 return; // would bump head, don't jump
1156 float jump_height = 1.1f; // TODO: better than a magic number
1157 v3f jump_pos = initial_position + v3f(0.0f, jump_height * BS, 0.0f);
1158 v3f jump_speed = initial_speed;
1160 // try at peak of jump, zero step height
1161 collisionMoveResult jump_result = collisionMoveSimple(env, m_client, pos_max_d,
1162 m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed,
1165 // see if we can get a little bit farther horizontally if we had
1167 v3f run_delta = m_position - initial_position;
1169 v3f jump_delta = jump_pos - initial_position;
1170 jump_delta.Y = 0.0f;
1171 if (jump_delta.getLengthSQ() > run_delta.getLengthSQ() * 1.01f) {
1173 m_autojump_time = 0.1f;