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 ?
722 m_eye_height - 0.125f : m_eye_height;
723 return v3f(0, BS * eye_height, 0);
726 // Horizontal acceleration (X and Z), Y direction is ignored
727 void LocalPlayer::accelerateHorizontal(const v3f &target_speed,
728 const f32 max_increase)
730 if (max_increase == 0)
733 v3f d_wanted = target_speed - m_speed;
735 f32 dl = d_wanted.getLength();
736 if (dl > max_increase)
739 v3f d = d_wanted.normalize() * dl;
745 // Vertical acceleration (Y), X and Z directions are ignored
746 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
748 if (max_increase == 0)
751 f32 d_wanted = target_speed.Y - m_speed.Y;
752 if (d_wanted > max_increase)
753 d_wanted = max_increase;
754 else if (d_wanted < -max_increase)
755 d_wanted = -max_increase;
757 m_speed.Y += d_wanted;
760 // Temporary option for old move code
761 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
762 std::vector<CollisionInfo> *collision_info)
764 Map *map = &env->getMap();
765 INodeDefManager *nodemgr = m_client->ndef();
767 v3f position = getPosition();
769 // Copy parent position if local player is attached
771 setPosition(overridePosition);
772 m_sneak_node_exists = false;
776 // Skip collision detection if noclip mode is used
777 bool fly_allowed = m_client->checkLocalPrivilege("fly");
778 bool noclip = m_client->checkLocalPrivilege("noclip") &&
779 g_settings->getBool("noclip");
780 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
782 position += m_speed * dtime;
783 setPosition(position);
784 m_sneak_node_exists = false;
791 bool is_valid_position;
796 Check if player is in liquid (the oscillating value)
799 // If in liquid, the threshold of coming out is at higher y
800 pp = floatToInt(position + v3f(0, BS * 0.1, 0), BS);
801 node = map->getNodeNoEx(pp, &is_valid_position);
802 if (is_valid_position) {
803 in_liquid = nodemgr->get(node.getContent()).isLiquid();
804 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
809 // If not in liquid, the threshold of going in is at lower y
810 pp = floatToInt(position + v3f(0, BS * 0.5, 0), BS);
811 node = map->getNodeNoEx(pp, &is_valid_position);
812 if (is_valid_position) {
813 in_liquid = nodemgr->get(node.getContent()).isLiquid();
814 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
821 Check if player is in liquid (the stable value)
823 pp = floatToInt(position + v3f(0, 0, 0), BS);
824 node = map->getNodeNoEx(pp, &is_valid_position);
825 if (is_valid_position)
826 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
828 in_liquid_stable = false;
831 Check if player is climbing
833 pp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS);
834 v3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS);
835 node = map->getNodeNoEx(pp, &is_valid_position);
836 bool is_valid_position2;
837 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
839 if (!(is_valid_position && is_valid_position2))
842 is_climbing = (nodemgr->get(node.getContent()).climbable ||
843 nodemgr->get(node2.getContent()).climbable) && !free_move;
846 Collision uncertainty radius
847 Make it a bit larger than the maximum distance of movement
849 //f32 d = pos_max_d * 1.1;
850 // A fairly large value in here makes moving smoother
852 // This should always apply, otherwise there are glitches
853 sanity_check(d > pos_max_d);
854 // Maximum distance over border for sneaking
855 f32 sneak_max = BS * 0.4;
858 If sneaking, keep in range from the last walked node and don't
861 if (control.sneak && m_sneak_node_exists &&
862 !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
863 physics_override_sneak) {
864 f32 maxd = 0.5 * BS + sneak_max;
865 v3f lwn_f = intToFloat(m_sneak_node, BS);
866 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
867 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
870 // Move up if necessary
871 f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
872 if (position.Y < new_y)
875 Collision seems broken, since player is sinking when
876 sneaking over the edges of current sneaking_node.
877 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
884 // this shouldn't be hardcoded but transmitted from server
885 float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
888 player_stepheight += (0.6 * BS);
891 v3f accel_f = v3f(0, 0, 0);
893 collisionMoveResult result = collisionMoveSimple(env, m_client,
894 pos_max_d, m_collisionbox, player_stepheight, dtime,
895 &position, &m_speed, accel_f);
898 If the player's feet touch the topside of any node, this is
901 Player is allowed to jump when this is true.
903 bool touching_ground_was = touching_ground;
904 touching_ground = result.touching_ground;
906 //bool standing_on_unloaded = result.standing_on_unloaded;
909 Check the nodes under the player to see from which node the
910 player is sneaking from, if any. If the node from under
911 the player has been removed, the player falls.
913 f32 position_y_mod = 0.05 * BS;
914 if (m_sneak_node_bb_ymax > 0)
915 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
916 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
917 if (m_sneak_node_exists &&
918 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
919 m_old_node_below_type != "air") {
920 // Old node appears to have been removed; that is,
921 // it wasn't air before but now it is
922 m_need_to_get_new_sneak_node = false;
923 m_sneak_node_exists = false;
924 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
925 // We are on something, so make sure to recalculate the sneak
927 m_need_to_get_new_sneak_node = true;
930 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
931 m_sneak_node_bb_ymax = 0;
932 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
933 v2f player_p2df(position.X, position.Z);
934 f32 min_distance_f = 100000.0 * BS;
935 // If already seeking from some node, compare to it.
936 v3s16 new_sneak_node = m_sneak_node;
937 for (s16 x= -1; x <= 1; x++)
938 for (s16 z= -1; z <= 1; z++) {
939 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
940 v3f pf = intToFloat(p, BS);
941 v2f node_p2df(pf.X, pf.Z);
942 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
943 f32 max_axis_distance_f = MYMAX(
944 fabs(player_p2df.X - node_p2df.X),
945 fabs(player_p2df.Y - node_p2df.Y));
947 if (distance_f > min_distance_f ||
948 max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)
951 // The node to be sneaked on has to be walkable
952 node = map->getNodeNoEx(p, &is_valid_position);
953 if (!is_valid_position || !nodemgr->get(node).walkable)
955 // And the node above it has to be nonwalkable
956 node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
957 if (!is_valid_position || nodemgr->get(node).walkable)
959 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
960 if (!physics_override_sneak_glitch) {
961 node =map->getNodeNoEx(p + v3s16(0, 2, 0), &is_valid_position);
962 if (!is_valid_position || nodemgr->get(node).walkable)
966 min_distance_f = distance_f;
970 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
972 m_sneak_node = new_sneak_node;
973 m_sneak_node_exists = sneak_node_found;
975 if (sneak_node_found) {
977 MapNode n = map->getNodeNoEx(m_sneak_node);
978 std::vector<aabb3f> nodeboxes;
979 n.getCollisionBoxes(nodemgr, &nodeboxes);
980 for (const auto &box : nodeboxes) {
981 if (box.MaxEdge.Y > cb_max)
982 cb_max = box.MaxEdge.Y;
984 m_sneak_node_bb_ymax = cb_max;
988 If sneaking, the player's collision box can be in air, so
989 this has to be set explicitly
991 if (sneak_node_found && control.sneak)
992 touching_ground = true;
996 Set new position but keep sneak node set
998 bool sneak_node_exists = m_sneak_node_exists;
999 setPosition(position);
1000 m_sneak_node_exists = sneak_node_exists;
1005 // Dont report if flying
1006 if (collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
1007 for (const auto &info : result.collisions) {
1008 collision_info->push_back(info);
1012 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1013 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
1014 m_client->event()->put(e);
1015 // Set camera impact value to be used for view bobbing
1016 camera_impact = getSpeed().Y * -1;
1020 camera_barely_in_ceiling = false;
1021 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1022 MapNode n = map->getNodeNoEx(camera_np);
1023 if (n.getContent() != CONTENT_IGNORE) {
1024 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1025 camera_barely_in_ceiling = true;
1030 Update the node last under the player
1032 m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);
1033 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
1036 Check properties of the node on which the player is standing
1038 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
1039 // Determine if jumping is possible
1040 m_can_jump = touching_ground && !in_liquid;
1041 if (itemgroup_get(f.groups, "disable_jump"))
1043 // Jump key pressed while jumping off from a bouncy block
1044 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1045 m_speed.Y >= -0.5 * BS) {
1046 float jumpspeed = movement_speed_jump * physics_override_jump;
1047 if (m_speed.Y > 1) {
1048 // Reduce boost when speed already is high
1049 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
1051 m_speed.Y += jumpspeed;
1058 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1061 if (!touching_ground)
1064 float slip_factor = 1.0f;
1065 // Slip on slippery nodes
1066 const INodeDefManager *nodemgr = env->getGameDef()->ndef();
1067 Map *map = &env->getMap();
1068 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(
1069 floatToInt(getPosition() - v3f(0, 0.05f * BS, 0), BS)));
1072 slippery = itemgroup_get(f.groups, "slippery");
1073 } else if (is_slipping) {
1074 // slipping over an edge? Check surroundings for slippery nodes
1075 slippery = 2 << 16; // guard value, bigger than all realistic ones
1076 for (int z = 0; z <= 1; z++) {
1077 for (int x = 0; x <= 1; x++) {
1078 // this should cover all nodes surrounding player position
1079 v3f offset((x - 0.5f) * BS, 0.05f * BS, (z - 0.5f) * BS);
1080 const ContentFeatures &f2 = nodemgr->get(map->getNodeNoEx(
1081 floatToInt(getPosition() - offset, BS)));
1083 // find least slippery node we might be standing on
1084 int s = itemgroup_get(f2.groups, "slippery");
1090 // without any hits, ignore slippery
1091 if (slippery >= (2 << 16))
1094 if (slippery >= 1) {
1095 if (speedH == v3f(0.0f)) {
1096 slippery = slippery * 2;
1098 slip_factor = core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);
1101 // remember this to avoid checking the edge case above too often
1102 is_slipping = false;