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 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);
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.axis != COLLISION_AXIS_Y ||
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;
329 If the player's feet touch the topside of any node, this is
332 Player is allowed to jump when this is true.
334 bool touching_ground_was = touching_ground;
335 touching_ground = result.touching_ground;
336 bool sneak_can_jump = false;
338 // Max. distance (X, Z) over border for sneaking determined by collision box
339 // * 0.49 to keep the center just barely on the node
340 v3f sneak_max = m_collisionbox.getExtent() * 0.49;
342 if (m_sneak_ladder_detected) {
343 // restore legacy behaviour (this makes the m_speed.Y hack necessary)
344 sneak_max = v3f(0.4 * BS, 0, 0.4 * BS);
348 If sneaking, keep on top of last walked node and don't fall off
350 if (could_sneak && m_sneak_node_exists) {
351 const v3f sn_f = intToFloat(m_sneak_node, BS);
352 const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge;
353 const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge;
354 const v3f old_pos = position;
355 const v3f old_speed = m_speed;
356 f32 y_diff = bmax.Y - position.Y;
357 m_standing_node = m_sneak_node;
359 // (BS * 0.6f) is the basic stepheight while standing on ground
360 if (y_diff < BS * 0.6f) {
361 // Only center player when they're on the node
362 position.X = rangelim(position.X,
363 bmin.X - sneak_max.X, bmax.X + sneak_max.X);
364 position.Z = rangelim(position.Z,
365 bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z);
367 if (position.X != old_pos.X)
369 if (position.Z != old_pos.Z)
373 if (y_diff > 0 && m_speed.Y <= 0 &&
374 (physics_override_sneak_glitch || y_diff < BS * 0.6f)) {
375 // Move player to the maximal height when falling or when
376 // the ledge is climbed on the next step.
378 // Smoothen the movement (based on 'position.Y = bmax.Y')
379 position.Y += y_diff * dtime * 22.0f + BS * 0.01f;
380 position.Y = std::min(position.Y, bmax.Y);
384 // Allow jumping on node edges while sneaking
385 if (m_speed.Y == 0 || m_sneak_ladder_detected)
386 sneak_can_jump = true;
388 if (collision_info &&
389 m_speed.Y - old_speed.Y > BS) {
390 // Collide with sneak node, report fall damage
391 CollisionInfo sn_info;
392 sn_info.node_p = m_sneak_node;
393 sn_info.old_speed = old_speed;
394 sn_info.new_speed = m_speed;
395 collision_info->push_back(sn_info);
400 Find the next sneak node if necessary
402 bool new_sneak_node_exists = false;
405 new_sneak_node_exists = updateSneakNode(map, position, sneak_max);
408 Set new position but keep sneak node set
410 setPosition(position);
411 m_sneak_node_exists = new_sneak_node_exists;
417 if(!result.standing_on_object && !touching_ground_was && touching_ground) {
418 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
420 // Set camera impact value to be used for view bobbing
421 camera_impact = getSpeed().Y * -1;
425 camera_barely_in_ceiling = false;
426 v3s16 camera_np = floatToInt(getEyePosition(), BS);
427 MapNode n = map->getNodeNoEx(camera_np);
428 if(n.getContent() != CONTENT_IGNORE){
429 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
430 camera_barely_in_ceiling = true;
436 Check properties of the node on which the player is standing
438 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(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)
443 || sneak_can_jump) && !m_disable_jump;
445 // Jump key pressed while jumping off from a bouncy block
446 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
447 m_speed.Y >= -0.5 * BS) {
448 float jumpspeed = movement_speed_jump * physics_override_jump;
450 // Reduce boost when speed already is high
451 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
453 m_speed.Y += jumpspeed;
460 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
463 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
465 move(dtime, env, pos_max_d, NULL);
468 void LocalPlayer::applyControl(float dtime, Environment *env)
471 swimming_vertical = false;
472 swimming_pitch = false;
474 setPitch(control.pitch);
477 // Nullify speed and don't run positioning code if the player is attached
480 setSpeed(v3f(0,0,0));
484 PlayerSettings &player_settings = getPlayerSettings();
486 // All vectors are relative to the player's yaw,
487 // (and pitch if pitch fly mode enabled),
488 // and will be rotated at the end
489 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
490 v3f speedV = v3f(0,0,0); // Vertical (Y)
492 bool fly_allowed = m_client->checkLocalPrivilege("fly");
493 bool fast_allowed = m_client->checkLocalPrivilege("fast");
495 bool free_move = fly_allowed && player_settings.free_move;
496 bool fast_move = fast_allowed && player_settings.fast_move;
497 bool pitch_move = (free_move || in_liquid) && player_settings.pitch_move;
498 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
499 bool fast_climb = fast_move && control.aux1 && !player_settings.aux1_descends;
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;
587 speedH += v3f(0,0,1);
590 speedH -= v3f(0,0,1);
592 if (!control.up && !control.down) {
593 speedH -= v3f(0,0,1) *
594 (control.forw_move_joystick_axis / 32767.f);
597 speedH += v3f(-1,0,0);
600 speedH += v3f(1,0,0);
602 if (!control.left && !control.right) {
603 speedH += v3f(1,0,0) *
604 (control.sidew_move_joystick_axis / 32767.f);
607 // release autojump after a given time
608 m_autojump_time -= dtime;
609 if (m_autojump_time <= 0.0f)
615 if (player_settings.aux1_descends || always_fly_fast) {
617 speedV.Y = movement_speed_fast;
619 speedV.Y = movement_speed_walk;
621 if(fast_move && control.aux1)
622 speedV.Y = movement_speed_fast;
624 speedV.Y = movement_speed_walk;
630 NOTE: The d value in move() affects jump height by
631 raising the height at which the jump speed is kept
632 at its starting value
634 v3f speedJ = getSpeed();
635 if(speedJ.Y >= -0.5 * BS) {
636 speedJ.Y = movement_speed_jump * physics_override_jump;
638 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_JUMP));
640 } else if (in_liquid && !m_disable_jump) {
642 speedV.Y = movement_speed_fast;
644 speedV.Y = movement_speed_walk;
645 swimming_vertical = true;
646 } else if (is_climbing && !m_disable_jump) {
648 speedV.Y = movement_speed_fast;
650 speedV.Y = movement_speed_climb;
654 // The speed of the player (Y is ignored)
655 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
656 speedH = speedH.normalize() * movement_speed_fast;
657 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
658 speedH = speedH.normalize() * movement_speed_crouch;
660 speedH = speedH.normalize() * movement_speed_walk;
662 // Acceleration increase
663 f32 incH = 0; // Horizontal (X, Z)
664 f32 incV = 0; // Vertical (Y)
665 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
667 // Jumping and falling
668 if(superspeed || (fast_move && control.aux1))
669 incH = movement_acceleration_fast * BS * dtime;
671 incH = movement_acceleration_air * BS * dtime;
672 incV = 0; // No vertical acceleration in air
674 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
675 incH = incV = movement_acceleration_fast * BS * dtime;
677 incH = incV = movement_acceleration_default * BS * dtime;
679 float slip_factor = 1.0f;
680 if (!free_move && !in_liquid && !in_liquid_stable)
681 slip_factor = getSlipFactor(env, speedH);
683 // Don't sink when swimming in pitch mode
684 if (pitch_move && in_liquid) {
685 v3f controlSpeed = speedH + speedV;
686 if (controlSpeed.getLength() > 0.01f)
687 swimming_pitch = true;
690 // Accelerate to target speed with maximum increment
691 accelerate((speedH + speedV) * physics_override_speed,
692 incH * physics_override_speed * slip_factor, incV * physics_override_speed,
696 v3s16 LocalPlayer::getStandingNodePos()
698 if(m_sneak_node_exists)
700 return m_standing_node;
703 v3s16 LocalPlayer::getFootstepNodePos()
705 if (in_liquid_stable)
706 // Emit swimming sound if the player is in liquid
707 return floatToInt(getPosition(), BS);
709 // BS * 0.05 below the player's feet ensures a 1/16th height
710 // nodebox is detected instead of the node below it.
711 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
712 // A larger distance below is necessary for a footstep sound
713 // when landing after a jump or fall. BS * 0.5 ensures water
714 // sounds when swimming in 1 node deep water.
715 return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
718 v3s16 LocalPlayer::getLightPosition() const
720 return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
723 v3f LocalPlayer::getEyeOffset() const
725 float eye_height = camera_barely_in_ceiling ?
726 m_eye_height - 0.125f : m_eye_height;
727 return v3f(0, BS * eye_height, 0);
731 void LocalPlayer::accelerate(const v3f &target_speed, const f32 max_increase_H,
732 const f32 max_increase_V, const bool use_pitch)
734 const f32 yaw = getYaw();
735 const f32 pitch = getPitch();
736 v3f flat_speed = m_speed;
737 // Rotate speed vector by -yaw and -pitch to make it relative to the player's yaw and pitch
738 flat_speed.rotateXZBy(-yaw);
740 flat_speed.rotateYZBy(-pitch);
742 v3f d_wanted = target_speed - flat_speed;
745 // Then compare the horizontal and vertical components with the wanted speed
746 if (max_increase_H > 0) {
747 v3f d_wanted_H = d_wanted * v3f(1,0,1);
748 if (d_wanted_H.getLength() > max_increase_H)
749 d += d_wanted_H.normalize() * max_increase_H;
754 if (max_increase_V > 0) {
755 f32 d_wanted_V = d_wanted.Y;
756 if (d_wanted_V > max_increase_V)
757 d.Y += max_increase_V;
758 else if (d_wanted_V < -max_increase_V)
759 d.Y -= max_increase_V;
764 // Finally rotate it again
772 // Temporary option for old move code
773 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
774 std::vector<CollisionInfo> *collision_info)
776 Map *map = &env->getMap();
777 const NodeDefManager *nodemgr = m_client->ndef();
779 v3f position = getPosition();
781 // Copy parent position if local player is attached
783 setPosition(overridePosition);
784 m_sneak_node_exists = false;
788 PlayerSettings &player_settings = getPlayerSettings();
790 // Skip collision detection if noclip mode is used
791 bool fly_allowed = m_client->checkLocalPrivilege("fly");
792 bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip;
793 bool free_move = noclip && fly_allowed && player_settings.free_move;
795 position += m_speed * dtime;
796 setPosition(position);
797 m_sneak_node_exists = false;
804 bool is_valid_position;
809 Check if player is in liquid (the oscillating value)
812 // If in liquid, the threshold of coming out is at higher y
813 pp = floatToInt(position + v3f(0, BS * 0.1, 0), BS);
814 node = map->getNodeNoEx(pp, &is_valid_position);
815 if (is_valid_position) {
816 in_liquid = nodemgr->get(node.getContent()).isLiquid();
817 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
822 // If not in liquid, the threshold of going in is at lower y
823 pp = floatToInt(position + v3f(0, BS * 0.5, 0), BS);
824 node = map->getNodeNoEx(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;
834 Check if player is in liquid (the stable value)
836 pp = floatToInt(position + v3f(0, 0, 0), BS);
837 node = map->getNodeNoEx(pp, &is_valid_position);
838 if (is_valid_position)
839 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
841 in_liquid_stable = false;
844 Check if player is climbing
846 pp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS);
847 v3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS);
848 node = map->getNodeNoEx(pp, &is_valid_position);
849 bool is_valid_position2;
850 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
852 if (!(is_valid_position && is_valid_position2))
855 is_climbing = (nodemgr->get(node.getContent()).climbable ||
856 nodemgr->get(node2.getContent()).climbable) && !free_move;
859 Collision uncertainty radius
860 Make it a bit larger than the maximum distance of movement
862 //f32 d = pos_max_d * 1.1;
863 // A fairly large value in here makes moving smoother
865 // This should always apply, otherwise there are glitches
866 sanity_check(d > pos_max_d);
867 // Maximum distance over border for sneaking
868 f32 sneak_max = BS * 0.4;
871 If sneaking, keep in range from the last walked node and don't
874 if (control.sneak && m_sneak_node_exists &&
875 !(fly_allowed && player_settings.free_move) && !in_liquid &&
876 physics_override_sneak) {
877 f32 maxd = 0.5 * BS + sneak_max;
878 v3f lwn_f = intToFloat(m_sneak_node, BS);
879 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
880 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
883 // Move up if necessary
884 f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
885 if (position.Y < new_y)
888 Collision seems broken, since player is sinking when
889 sneaking over the edges of current sneaking_node.
890 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
897 // this shouldn't be hardcoded but transmitted from server
898 float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
900 v3f accel_f = v3f(0, 0, 0);
901 const v3f initial_position = position;
902 const v3f initial_speed = m_speed;
904 collisionMoveResult result = collisionMoveSimple(env, m_client,
905 pos_max_d, m_collisionbox, player_stepheight, dtime,
906 &position, &m_speed, accel_f);
908 // Positition was slightly changed; update standing node pos
910 m_standing_node = floatToInt(m_position - v3f(0, 0.1f * BS, 0), BS);
912 m_standing_node = floatToInt(m_position, BS);
915 If the player's feet touch the topside of any node, this is
918 Player is allowed to jump when this is true.
920 bool touching_ground_was = touching_ground;
921 touching_ground = result.touching_ground;
923 //bool standing_on_unloaded = result.standing_on_unloaded;
926 Check the nodes under the player to see from which node the
927 player is sneaking from, if any. If the node from under
928 the player has been removed, the player falls.
930 f32 position_y_mod = 0.05 * BS;
931 if (m_sneak_node_bb_ymax > 0)
932 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
933 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
934 if (m_sneak_node_exists &&
935 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
936 m_old_node_below_type != "air") {
937 // Old node appears to have been removed; that is,
938 // it wasn't air before but now it is
939 m_need_to_get_new_sneak_node = false;
940 m_sneak_node_exists = false;
941 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
942 // We are on something, so make sure to recalculate the sneak
944 m_need_to_get_new_sneak_node = true;
947 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
948 m_sneak_node_bb_ymax = 0;
949 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
950 v2f player_p2df(position.X, position.Z);
951 f32 min_distance_f = 100000.0 * BS;
952 // If already seeking from some node, compare to it.
953 v3s16 new_sneak_node = m_sneak_node;
954 for (s16 x= -1; x <= 1; x++)
955 for (s16 z= -1; z <= 1; z++) {
956 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
957 v3f pf = intToFloat(p, BS);
958 v2f node_p2df(pf.X, pf.Z);
959 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
960 f32 max_axis_distance_f = MYMAX(
961 std::fabs(player_p2df.X - node_p2df.X),
962 std::fabs(player_p2df.Y - node_p2df.Y));
964 if (distance_f > min_distance_f ||
965 max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)
968 // The node to be sneaked on has to be walkable
969 node = map->getNodeNoEx(p, &is_valid_position);
970 if (!is_valid_position || !nodemgr->get(node).walkable)
972 // And the node above it has to be nonwalkable
973 node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
974 if (!is_valid_position || nodemgr->get(node).walkable)
976 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
977 if (!physics_override_sneak_glitch) {
978 node =map->getNodeNoEx(p + v3s16(0, 2, 0), &is_valid_position);
979 if (!is_valid_position || nodemgr->get(node).walkable)
983 min_distance_f = distance_f;
987 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
989 m_sneak_node = new_sneak_node;
990 m_sneak_node_exists = sneak_node_found;
992 if (sneak_node_found) {
994 MapNode n = map->getNodeNoEx(m_sneak_node);
995 std::vector<aabb3f> nodeboxes;
996 n.getCollisionBoxes(nodemgr, &nodeboxes);
997 for (const auto &box : nodeboxes) {
998 if (box.MaxEdge.Y > cb_max)
999 cb_max = box.MaxEdge.Y;
1001 m_sneak_node_bb_ymax = cb_max;
1005 If sneaking, the player's collision box can be in air, so
1006 this has to be set explicitly
1008 if (sneak_node_found && control.sneak)
1009 touching_ground = true;
1013 Set new position but keep sneak node set
1015 bool sneak_node_exists = m_sneak_node_exists;
1016 setPosition(position);
1017 m_sneak_node_exists = sneak_node_exists;
1022 // Dont report if flying
1023 if (collision_info && !(player_settings.free_move && fly_allowed)) {
1024 for (const auto &info : result.collisions) {
1025 collision_info->push_back(info);
1029 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1030 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
1031 // Set camera impact value to be used for view bobbing
1032 camera_impact = getSpeed().Y * -1;
1036 camera_barely_in_ceiling = false;
1037 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1038 MapNode n = map->getNodeNoEx(camera_np);
1039 if (n.getContent() != CONTENT_IGNORE) {
1040 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1041 camera_barely_in_ceiling = true;
1046 Update the node last under the player
1048 m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);
1049 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
1052 Check properties of the node on which the player is standing
1054 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(
1055 getStandingNodePos()));
1057 // Determine if jumping is possible
1058 m_disable_jump = itemgroup_get(f.groups, "disable_jump");
1059 m_can_jump = touching_ground && !m_disable_jump;
1061 // Jump key pressed while jumping off from a bouncy block
1062 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1063 m_speed.Y >= -0.5 * BS) {
1064 float jumpspeed = movement_speed_jump * physics_override_jump;
1065 if (m_speed.Y > 1) {
1066 // Reduce boost when speed already is high
1067 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
1069 m_speed.Y += jumpspeed;
1076 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
1079 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1081 // Slip on slippery nodes
1082 const NodeDefManager *nodemgr = env->getGameDef()->ndef();
1083 Map *map = &env->getMap();
1084 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(
1085 getStandingNodePos()));
1088 slippery = itemgroup_get(f.groups, "slippery");
1090 if (slippery >= 1) {
1091 if (speedH == v3f(0.0f)) {
1092 slippery = slippery * 2;
1094 return core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);
1099 void LocalPlayer::handleAutojump(f32 dtime, Environment *env,
1100 const collisionMoveResult &result, const v3f &initial_position,
1101 const v3f &initial_speed, f32 pos_max_d)
1103 PlayerSettings &player_settings = getPlayerSettings();
1104 if (!player_settings.autojump)
1110 bool control_forward = control.up ||
1111 (!control.up && !control.down &&
1112 control.forw_move_joystick_axis < -0.05);
1113 bool could_autojump =
1114 m_can_jump && !control.jump && !control.sneak && control_forward;
1115 if (!could_autojump)
1118 bool horizontal_collision = false;
1119 for (const auto &colinfo : result.collisions) {
1120 if (colinfo.type == COLLISION_NODE && colinfo.plane != 1) {
1121 horizontal_collision = true;
1122 break; // one is enough
1126 // must be running against something to trigger autojumping
1127 if (!horizontal_collision)
1130 // check for nodes above
1131 v3f headpos_min = m_position + m_collisionbox.MinEdge * 0.99f;
1132 v3f headpos_max = m_position + m_collisionbox.MaxEdge * 0.99f;
1133 headpos_min.Y = headpos_max.Y; // top face of collision box
1134 v3s16 ceilpos_min = floatToInt(headpos_min, BS) + v3s16(0, 1, 0);
1135 v3s16 ceilpos_max = floatToInt(headpos_max, BS) + v3s16(0, 1, 0);
1136 const NodeDefManager *ndef = env->getGameDef()->ndef();
1137 bool is_position_valid;
1138 for (s16 z = ceilpos_min.Z; z <= ceilpos_max.Z; z++) {
1139 for (s16 x = ceilpos_min.X; x <= ceilpos_max.X; x++) {
1140 MapNode n = env->getMap().getNodeNoEx(v3s16(x, ceilpos_max.Y, z), &is_position_valid);
1142 if (!is_position_valid)
1143 break; // won't collide with the void outside
1144 if (n.getContent() == CONTENT_IGNORE)
1145 return; // players collide with ignore blocks -> same as walkable
1146 const ContentFeatures &f = ndef->get(n);
1148 return; // would bump head, don't jump
1152 float jump_height = 1.1f; // TODO: better than a magic number
1153 v3f jump_pos = initial_position + v3f(0.0f, jump_height * BS, 0.0f);
1154 v3f jump_speed = initial_speed;
1156 // try at peak of jump, zero step height
1157 collisionMoveResult jump_result = collisionMoveSimple(env, m_client, pos_max_d,
1158 m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed,
1161 // see if we can get a little bit farther horizontally if we had
1163 v3f run_delta = m_position - initial_position;
1165 v3f jump_delta = jump_pos - initial_position;
1166 jump_delta.Y = 0.0f;
1167 if (jump_delta.getLengthSQ() > run_delta.getLengthSQ() * 1.01f) {
1169 m_autojump_time = 0.1f;