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 INodeDefManager *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 INodeDefManager *nodemgr = m_client->ndef();
186 v3f position = getPosition();
188 // Copy parent position if local player is attached
190 setPosition(overridePosition);
194 // Skip collision detection if noclip mode is used
195 bool fly_allowed = m_client->checkLocalPrivilege("fly");
196 bool noclip = m_client->checkLocalPrivilege("noclip") &&
197 g_settings->getBool("noclip");
198 bool free_move = g_settings->getBool("free_move") && fly_allowed;
200 if (noclip && free_move) {
201 position += m_speed * dtime;
202 setPosition(position);
210 bool is_valid_position;
215 Check if player is in liquid (the oscillating value)
218 // If in liquid, the threshold of coming out is at higher y
221 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
222 node = map->getNodeNoEx(pp, &is_valid_position);
223 if (is_valid_position) {
224 in_liquid = nodemgr->get(node.getContent()).isLiquid();
225 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
230 // If not in liquid, the threshold of going in is at lower y
233 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
234 node = map->getNodeNoEx(pp, &is_valid_position);
235 if (is_valid_position) {
236 in_liquid = nodemgr->get(node.getContent()).isLiquid();
237 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
245 Check if player is in liquid (the stable value)
247 pp = floatToInt(position + v3f(0,0,0), BS);
248 node = map->getNodeNoEx(pp, &is_valid_position);
249 if (is_valid_position) {
250 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
252 in_liquid_stable = false;
256 Check if player is climbing
260 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
261 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
262 node = map->getNodeNoEx(pp, &is_valid_position);
263 bool is_valid_position2;
264 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
266 if (!(is_valid_position && is_valid_position2)) {
269 is_climbing = (nodemgr->get(node.getContent()).climbable
270 || nodemgr->get(node2.getContent()).climbable) && !free_move;
274 Collision uncertainty radius
275 Make it a bit larger than the maximum distance of movement
277 //f32 d = pos_max_d * 1.1;
278 // A fairly large value in here makes moving smoother
281 // This should always apply, otherwise there are glitches
282 sanity_check(d > pos_max_d);
284 // Player object property step height is multiplied by BS in
285 // /src/script/common/c_content.cpp and /src/content_sao.cpp
286 float player_stepheight = (m_cao == nullptr) ? 0.0f :
287 (touching_ground ? m_cao->getStepHeight() : (0.2f * BS));
289 // TODO this is a problematic hack.
290 // Use a better implementation for autojump, or apply a custom stepheight
291 // to all players, as this currently creates unintended special movement
292 // abilities and advantages for Android players on a server.
295 player_stepheight += (0.6f * BS);
298 v3f accel_f = v3f(0,0,0);
300 collisionMoveResult result = collisionMoveSimple(env, m_client,
301 pos_max_d, m_collisionbox, player_stepheight, dtime,
302 &position, &m_speed, accel_f);
304 bool could_sneak = control.sneak && !free_move && !in_liquid &&
305 !is_climbing && physics_override_sneak;
307 // Add new collisions to the vector
308 if (collision_info && !free_move) {
309 v3f diff = intToFloat(m_standing_node, BS) - position;
310 f32 distance = diff.getLength();
311 // Force update each ClientEnvironment::step()
312 bool is_first = collision_info->empty();
314 for (const auto &colinfo : result.collisions) {
315 collision_info->push_back(colinfo);
317 if (colinfo.type != COLLISION_NODE ||
318 colinfo.new_speed.Y != 0 ||
319 (could_sneak && m_sneak_node_exists))
322 diff = intToFloat(colinfo.node_p, BS) - position;
324 // Find nearest colliding node
325 f32 len = diff.getLength();
326 if (is_first || len < distance) {
327 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.
386 // Allow jumping on node edges while sneaking
387 if (m_speed.Y == 0 || m_sneak_ladder_detected)
388 sneak_can_jump = true;
390 if (collision_info &&
391 m_speed.Y - old_speed.Y > BS) {
392 // Collide with sneak node, report fall damage
393 CollisionInfo sn_info;
394 sn_info.node_p = m_sneak_node;
395 sn_info.old_speed = old_speed;
396 sn_info.new_speed = m_speed;
397 collision_info->push_back(sn_info);
402 Find the next sneak node if necessary
404 bool new_sneak_node_exists = false;
407 new_sneak_node_exists = updateSneakNode(map, position, sneak_max);
410 Set new position but keep sneak node set
412 setPosition(position);
413 m_sneak_node_exists = new_sneak_node_exists;
419 if(!result.standing_on_object && !touching_ground_was && touching_ground) {
420 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
421 m_client->event()->put(e);
423 // Set camera impact value to be used for view bobbing
424 camera_impact = getSpeed().Y * -1;
428 camera_barely_in_ceiling = false;
429 v3s16 camera_np = floatToInt(getEyePosition(), BS);
430 MapNode n = map->getNodeNoEx(camera_np);
431 if(n.getContent() != CONTENT_IGNORE){
432 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
433 camera_barely_in_ceiling = true;
439 Check properties of the node on which the player is standing
441 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(m_standing_node));
442 // Determine if jumping is possible
443 m_can_jump = (touching_ground && !in_liquid && !is_climbing)
445 if (itemgroup_get(f.groups, "disable_jump"))
448 // Jump key pressed while jumping off from a bouncy block
449 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
450 m_speed.Y >= -0.5 * BS) {
451 float jumpspeed = movement_speed_jump * physics_override_jump;
453 // Reduce boost when speed already is high
454 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
456 m_speed.Y += jumpspeed;
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;
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 v3f move_direction = v3f(0,0,1);
484 move_direction.rotateXZBy(getYaw());
486 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
487 v3f speedV = v3f(0,0,0); // Vertical (Y)
489 bool fly_allowed = m_client->checkLocalPrivilege("fly");
490 bool fast_allowed = m_client->checkLocalPrivilege("fast");
492 bool free_move = fly_allowed && g_settings->getBool("free_move");
493 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
494 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
495 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
496 bool continuous_forward = g_settings->getBool("continuous_forward");
497 bool always_fly_fast = g_settings->getBool("always_fly_fast");
499 // Whether superspeed mode is used or not
500 bool superspeed = false;
502 if (always_fly_fast && free_move && fast_move)
505 // Old descend control
506 if(g_settings->getBool("aux1_descends"))
508 // If free movement and fast movement, always move fast
509 if(free_move && fast_move)
512 // Auxiliary button 1 (E)
517 // In free movement mode, aux1 descends
519 speedV.Y = -movement_speed_fast;
521 speedV.Y = -movement_speed_walk;
523 else if(in_liquid || in_liquid_stable)
525 speedV.Y = -movement_speed_walk;
526 swimming_vertical = true;
530 speedV.Y = -movement_speed_climb;
534 // If not free movement but fast is allowed, aux1 is
541 // New minecraft-like descend control
544 // Auxiliary button 1 (E)
549 // aux1 is "Turbo button"
559 // In free movement mode, sneak descends
560 if (fast_move && (control.aux1 || always_fly_fast))
561 speedV.Y = -movement_speed_fast;
563 speedV.Y = -movement_speed_walk;
565 else if(in_liquid || in_liquid_stable)
568 speedV.Y = -movement_speed_fast;
570 speedV.Y = -movement_speed_walk;
571 swimming_vertical = true;
576 speedV.Y = -movement_speed_fast;
578 speedV.Y = -movement_speed_climb;
583 if (continuous_forward)
584 speedH += move_direction;
587 if (continuous_forward) {
591 speedH += move_direction;
595 speedH -= move_direction;
597 if (!control.up && !control.down) {
598 speedH -= move_direction *
599 (control.forw_move_joystick_axis / 32767.f);
602 speedH += move_direction.crossProduct(v3f(0,1,0));
605 speedH += move_direction.crossProduct(v3f(0,-1,0));
607 if (!control.left && !control.right) {
608 speedH -= move_direction.crossProduct(v3f(0,1,0)) *
609 (control.sidew_move_joystick_axis / 32767.f);
614 if (g_settings->getBool("aux1_descends") || always_fly_fast) {
616 speedV.Y = movement_speed_fast;
618 speedV.Y = movement_speed_walk;
620 if(fast_move && control.aux1)
621 speedV.Y = movement_speed_fast;
623 speedV.Y = movement_speed_walk;
629 NOTE: The d value in move() affects jump height by
630 raising the height at which the jump speed is kept
631 at its starting value
633 v3f speedJ = getSpeed();
634 if(speedJ.Y >= -0.5 * BS) {
635 speedJ.Y = movement_speed_jump * physics_override_jump;
638 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
639 m_client->event()->put(e);
645 speedV.Y = movement_speed_fast;
647 speedV.Y = movement_speed_walk;
648 swimming_vertical = true;
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 = getSlipFactor(env, speedH);
685 // Accelerate to target speed with maximum increment
686 accelerateHorizontal(speedH * physics_override_speed,
687 incH * physics_override_speed * slip_factor);
688 accelerateVertical(speedV * physics_override_speed,
689 incV * physics_override_speed);
692 v3s16 LocalPlayer::getStandingNodePos()
694 if(m_sneak_node_exists)
696 return m_standing_node;
699 v3s16 LocalPlayer::getFootstepNodePos()
701 if (in_liquid_stable)
702 // Emit swimming sound if the player is in liquid
703 return floatToInt(getPosition(), BS);
705 // BS * 0.05 below the player's feet ensures a 1/16th height
706 // nodebox is detected instead of the node below it.
707 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
708 // A larger distance below is necessary for a footstep sound
709 // when landing after a jump or fall. BS * 0.5 ensures water
710 // sounds when swimming in 1 node deep water.
711 return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
714 v3s16 LocalPlayer::getLightPosition() const
716 return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
719 v3f LocalPlayer::getEyeOffset() const
721 float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f;
722 return v3f(0, BS * eye_height, 0);
725 // Horizontal acceleration (X and Z), Y direction is ignored
726 void LocalPlayer::accelerateHorizontal(const v3f &target_speed,
727 const f32 max_increase)
729 if (max_increase == 0)
732 v3f d_wanted = target_speed - m_speed;
734 f32 dl = d_wanted.getLength();
735 if (dl > max_increase)
738 v3f d = d_wanted.normalize() * dl;
744 // Vertical acceleration (Y), X and Z directions are ignored
745 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
747 if (max_increase == 0)
750 f32 d_wanted = target_speed.Y - m_speed.Y;
751 if (d_wanted > max_increase)
752 d_wanted = max_increase;
753 else if (d_wanted < -max_increase)
754 d_wanted = -max_increase;
756 m_speed.Y += d_wanted;
759 // Temporary option for old move code
760 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
761 std::vector<CollisionInfo> *collision_info)
763 Map *map = &env->getMap();
764 INodeDefManager *nodemgr = m_client->ndef();
766 v3f position = getPosition();
768 // Copy parent position if local player is attached
770 setPosition(overridePosition);
771 m_sneak_node_exists = false;
775 // Skip collision detection if noclip mode is used
776 bool fly_allowed = m_client->checkLocalPrivilege("fly");
777 bool noclip = m_client->checkLocalPrivilege("noclip") &&
778 g_settings->getBool("noclip");
779 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
781 position += m_speed * dtime;
782 setPosition(position);
783 m_sneak_node_exists = false;
790 bool is_valid_position;
795 Check if player is in liquid (the oscillating value)
798 // If in liquid, the threshold of coming out is at higher y
799 pp = floatToInt(position + v3f(0, BS * 0.1, 0), BS);
800 node = map->getNodeNoEx(pp, &is_valid_position);
801 if (is_valid_position) {
802 in_liquid = nodemgr->get(node.getContent()).isLiquid();
803 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
808 // If not in liquid, the threshold of going in is at lower y
809 pp = floatToInt(position + v3f(0, BS * 0.5, 0), BS);
810 node = map->getNodeNoEx(pp, &is_valid_position);
811 if (is_valid_position) {
812 in_liquid = nodemgr->get(node.getContent()).isLiquid();
813 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
820 Check if player is in liquid (the stable value)
822 pp = floatToInt(position + v3f(0, 0, 0), BS);
823 node = map->getNodeNoEx(pp, &is_valid_position);
824 if (is_valid_position)
825 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
827 in_liquid_stable = false;
830 Check if player is climbing
832 pp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS);
833 v3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS);
834 node = map->getNodeNoEx(pp, &is_valid_position);
835 bool is_valid_position2;
836 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
838 if (!(is_valid_position && is_valid_position2))
841 is_climbing = (nodemgr->get(node.getContent()).climbable ||
842 nodemgr->get(node2.getContent()).climbable) && !free_move;
845 Collision uncertainty radius
846 Make it a bit larger than the maximum distance of movement
848 //f32 d = pos_max_d * 1.1;
849 // A fairly large value in here makes moving smoother
851 // This should always apply, otherwise there are glitches
852 sanity_check(d > pos_max_d);
853 // Maximum distance over border for sneaking
854 f32 sneak_max = BS * 0.4;
857 If sneaking, keep in range from the last walked node and don't
860 if (control.sneak && m_sneak_node_exists &&
861 !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
862 physics_override_sneak) {
863 f32 maxd = 0.5 * BS + sneak_max;
864 v3f lwn_f = intToFloat(m_sneak_node, BS);
865 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
866 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
869 // Move up if necessary
870 f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
871 if (position.Y < new_y)
874 Collision seems broken, since player is sinking when
875 sneaking over the edges of current sneaking_node.
876 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
883 // this shouldn't be hardcoded but transmitted from server
884 float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
887 player_stepheight += (0.6 * BS);
890 v3f accel_f = v3f(0, 0, 0);
892 collisionMoveResult result = collisionMoveSimple(env, m_client,
893 pos_max_d, m_collisionbox, player_stepheight, dtime,
894 &position, &m_speed, accel_f);
897 If the player's feet touch the topside of any node, this is
900 Player is allowed to jump when this is true.
902 bool touching_ground_was = touching_ground;
903 touching_ground = result.touching_ground;
905 //bool standing_on_unloaded = result.standing_on_unloaded;
908 Check the nodes under the player to see from which node the
909 player is sneaking from, if any. If the node from under
910 the player has been removed, the player falls.
912 f32 position_y_mod = 0.05 * BS;
913 if (m_sneak_node_bb_ymax > 0)
914 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
915 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
916 if (m_sneak_node_exists &&
917 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
918 m_old_node_below_type != "air") {
919 // Old node appears to have been removed; that is,
920 // it wasn't air before but now it is
921 m_need_to_get_new_sneak_node = false;
922 m_sneak_node_exists = false;
923 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
924 // We are on something, so make sure to recalculate the sneak
926 m_need_to_get_new_sneak_node = true;
929 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
930 m_sneak_node_bb_ymax = 0;
931 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
932 v2f player_p2df(position.X, position.Z);
933 f32 min_distance_f = 100000.0 * BS;
934 // If already seeking from some node, compare to it.
935 v3s16 new_sneak_node = m_sneak_node;
936 for (s16 x= -1; x <= 1; x++)
937 for (s16 z= -1; z <= 1; z++) {
938 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
939 v3f pf = intToFloat(p, BS);
940 v2f node_p2df(pf.X, pf.Z);
941 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
942 f32 max_axis_distance_f = MYMAX(
943 fabs(player_p2df.X - node_p2df.X),
944 fabs(player_p2df.Y - node_p2df.Y));
946 if (distance_f > min_distance_f ||
947 max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)
950 // The node to be sneaked on has to be walkable
951 node = map->getNodeNoEx(p, &is_valid_position);
952 if (!is_valid_position || !nodemgr->get(node).walkable)
954 // And the node above it has to be nonwalkable
955 node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
956 if (!is_valid_position || nodemgr->get(node).walkable)
958 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
959 if (!physics_override_sneak_glitch) {
960 node =map->getNodeNoEx(p + v3s16(0, 2, 0), &is_valid_position);
961 if (!is_valid_position || nodemgr->get(node).walkable)
965 min_distance_f = distance_f;
969 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
971 m_sneak_node = new_sneak_node;
972 m_sneak_node_exists = sneak_node_found;
974 if (sneak_node_found) {
976 MapNode n = map->getNodeNoEx(m_sneak_node);
977 std::vector<aabb3f> nodeboxes;
978 n.getCollisionBoxes(nodemgr, &nodeboxes);
979 for (const auto &box : nodeboxes) {
980 if (box.MaxEdge.Y > cb_max)
981 cb_max = box.MaxEdge.Y;
983 m_sneak_node_bb_ymax = cb_max;
987 If sneaking, the player's collision box can be in air, so
988 this has to be set explicitly
990 if (sneak_node_found && control.sneak)
991 touching_ground = true;
995 Set new position but keep sneak node set
997 bool sneak_node_exists = m_sneak_node_exists;
998 setPosition(position);
999 m_sneak_node_exists = sneak_node_exists;
1004 // Dont report if flying
1005 if (collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
1006 for (const auto &info : result.collisions) {
1007 collision_info->push_back(info);
1011 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1012 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
1013 m_client->event()->put(e);
1014 // Set camera impact value to be used for view bobbing
1015 camera_impact = getSpeed().Y * -1;
1019 camera_barely_in_ceiling = false;
1020 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1021 MapNode n = map->getNodeNoEx(camera_np);
1022 if (n.getContent() != CONTENT_IGNORE) {
1023 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1024 camera_barely_in_ceiling = true;
1029 Update the node last under the player
1031 m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);
1032 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
1035 Check properties of the node on which the player is standing
1037 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
1038 // Determine if jumping is possible
1039 m_can_jump = touching_ground && !in_liquid;
1040 if (itemgroup_get(f.groups, "disable_jump"))
1042 // Jump key pressed while jumping off from a bouncy block
1043 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1044 m_speed.Y >= -0.5 * BS) {
1045 float jumpspeed = movement_speed_jump * physics_override_jump;
1046 if (m_speed.Y > 1) {
1047 // Reduce boost when speed already is high
1048 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
1050 m_speed.Y += jumpspeed;
1057 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1060 if (!touching_ground)
1063 float slip_factor = 1.0f;
1064 // Slip on slippery nodes
1065 const INodeDefManager *nodemgr = env->getGameDef()->ndef();
1066 Map *map = &env->getMap();
1067 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(
1068 floatToInt(getPosition() - v3f(0, 0.05f * BS, 0), BS)));
1071 slippery = itemgroup_get(f.groups, "slippery");
1072 } else if (is_slipping) {
1073 // slipping over an edge? Check surroundings for slippery nodes
1074 slippery = 2 << 16; // guard value, bigger than all realistic ones
1075 for (int z = 0; z <= 1; z++) {
1076 for (int x = 0; x <= 1; x++) {
1077 // this should cover all nodes surrounding player position
1078 v3f offset((x - 0.5f) * BS, 0.05f * BS, (z - 0.5f) * BS);
1079 const ContentFeatures &f2 = nodemgr->get(map->getNodeNoEx(
1080 floatToInt(getPosition() - offset, BS)));
1082 // find least slippery node we might be standing on
1083 int s = itemgroup_get(f2.groups, "slippery");
1089 // without any hits, ignore slippery
1090 if (slippery >= (2 << 16))
1093 if (slippery >= 1) {
1094 if (speedH == v3f(0.0f)) {
1095 slippery = slippery * 2;
1097 slip_factor = core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);
1100 // remember this to avoid checking the edge case above too often
1101 is_slipping = false;