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"
30 #include "util/pointedthing.h"
31 #include "client/game.h"
37 LocalPlayer::LocalPlayer(Client *client, const char *name):
38 Player(name, client->idef()),
43 static aabb3f getNodeBoundingBox(const std::vector<aabb3f> &nodeboxes)
45 if (nodeboxes.empty())
46 return aabb3f(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
50 std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
51 b_max = aabb3f(it->MinEdge, it->MaxEdge);
54 for (; it != nodeboxes.end(); ++it)
55 b_max.addInternalBox(*it);
60 bool LocalPlayer::updateSneakNode(Map *map, const v3f &position,
63 static const v3s16 dir9_center[9] = {
75 const NodeDefManager *nodemgr = m_client->ndef();
77 bool is_valid_position;
78 bool new_sneak_node_exists = m_sneak_node_exists;
80 // We want the top of the sneak node to be below the players feet
81 f32 position_y_mod = 0.05f * BS;
82 if (m_sneak_node_exists)
83 position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - position_y_mod;
85 // Get position of current standing node
86 const v3s16 current_node = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS);
88 if (current_node != m_sneak_node) {
89 new_sneak_node_exists = false;
91 node = map->getNode(current_node, &is_valid_position);
92 if (!is_valid_position || nodemgr->get(node).walkable)
93 new_sneak_node_exists = false;
96 // Keep old sneak node
97 if (new_sneak_node_exists)
100 // Get new sneak node
101 m_sneak_ladder_detected = false;
102 f32 min_distance_f = 100000.0f * BS;
104 for (const auto &d : dir9_center) {
105 const v3s16 p = current_node + d;
106 const v3f pf = intToFloat(p, BS);
107 const v2f diff(position.X - pf.X, position.Z - pf.Z);
108 f32 distance_f = diff.getLength();
110 if (distance_f > min_distance_f ||
111 fabs(diff.X) > (0.5f + 0.1f) * BS + sneak_max.X ||
112 fabs(diff.Y) > (0.5f + 0.1f) * BS + sneak_max.Z)
116 // The node to be sneaked on has to be walkable
117 node = map->getNode(p, &is_valid_position);
118 if (!is_valid_position || ! nodemgr->get(node).walkable)
120 // And the node(s) above have to be nonwalkable
122 if (!physics_override_sneak_glitch) {
124 ceilf((m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS);
125 for (u16 y = 1; y <= height; y++) {
126 node = map->getNode(p + v3s16(0, y, 0), &is_valid_position);
127 if (!is_valid_position || nodemgr->get(node).walkable) {
133 // legacy behaviour: check just one node
134 node = map->getNode(p + v3s16(0, 1, 0), &is_valid_position);
135 ok = is_valid_position && ! nodemgr->get(node).walkable;
140 min_distance_f = distance_f;
142 new_sneak_node_exists = true;
145 if (!new_sneak_node_exists)
148 // Update saved top bounding box of sneak node
149 node = map->getNode(m_sneak_node);
150 std::vector<aabb3f> nodeboxes;
151 node.getCollisionBoxes(nodemgr, &nodeboxes);
152 m_sneak_node_bb_top = getNodeBoundingBox(nodeboxes);
154 if (physics_override_sneak_glitch) {
155 // Detect sneak ladder:
156 // Node two meters above sneak node must be solid
157 node = map->getNode(m_sneak_node + v3s16(0, 2, 0),
159 if (is_valid_position && nodemgr->get(node).walkable) {
160 // Node three meters above: must be non-solid
161 node = map->getNode(m_sneak_node + v3s16(0, 3, 0),
163 m_sneak_ladder_detected = is_valid_position &&
164 ! nodemgr->get(node).walkable;
170 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
171 std::vector<CollisionInfo> *collision_info)
173 if (m_cao && m_cao->m_waiting_for_reattach > 0)
174 m_cao->m_waiting_for_reattach -= dtime;
176 // Node at feet position, update each ClientEnvironment::step()
177 if (!collision_info || collision_info->empty())
178 m_standing_node = floatToInt(m_position, BS);
180 // Temporary option for old move code
181 if (!physics_override_new_move) {
182 old_move(dtime, env, pos_max_d, collision_info);
186 Map *map = &env->getMap();
187 const NodeDefManager *nodemgr = m_client->ndef();
189 v3f position = getPosition();
191 // Copy parent position if local player is attached
193 setPosition(m_cao->getPosition());
194 added_velocity = v3f(0.0f); // ignored
198 PlayerSettings &player_settings = getPlayerSettings();
200 // Skip collision detection if noclip mode is used
201 bool fly_allowed = m_client->checkLocalPrivilege("fly");
202 bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip;
203 bool free_move = player_settings.free_move && fly_allowed;
205 if (noclip && free_move) {
206 position += m_speed * dtime;
207 setPosition(position);
209 touching_ground = false;
210 added_velocity = v3f(0.0f); // ignored
214 m_speed += added_velocity;
215 added_velocity = v3f(0.0f);
221 bool is_valid_position;
226 Check if player is in liquid (the oscillating value)
229 // If in liquid, the threshold of coming out is at higher y
232 pp = floatToInt(position + v3f(0.0f, BS * 0.1f, 0.0f), BS);
233 node = map->getNode(pp, &is_valid_position);
234 if (is_valid_position) {
235 in_liquid = nodemgr->get(node.getContent()).isLiquid();
236 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
241 // If not in liquid, the threshold of going in is at lower y
243 pp = floatToInt(position + v3f(0.0f, BS * 0.5f, 0.0f), BS);
244 node = map->getNode(pp, &is_valid_position);
245 if (is_valid_position) {
246 in_liquid = nodemgr->get(node.getContent()).isLiquid();
247 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
255 Check if player is in liquid (the stable value)
257 pp = floatToInt(position + v3f(0.0f), BS);
258 node = map->getNode(pp, &is_valid_position);
259 if (is_valid_position) {
260 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
262 in_liquid_stable = false;
266 Check if player is climbing
269 pp = floatToInt(position + v3f(0.0f, 0.5f * BS, 0.0f), BS);
270 v3s16 pp2 = floatToInt(position + v3f(0.0f, -0.2f * BS, 0.0f), BS);
271 node = map->getNode(pp, &is_valid_position);
272 bool is_valid_position2;
273 MapNode node2 = map->getNode(pp2, &is_valid_position2);
275 if (!(is_valid_position && is_valid_position2)) {
278 is_climbing = (nodemgr->get(node.getContent()).climbable ||
279 nodemgr->get(node2.getContent()).climbable) && !free_move;
283 Collision uncertainty radius
284 Make it a bit larger than the maximum distance of movement
286 //f32 d = pos_max_d * 1.1;
287 // A fairly large value in here makes moving smoother
290 // This should always apply, otherwise there are glitches
291 sanity_check(d > pos_max_d);
293 // Player object property step height is multiplied by BS in
294 // /src/script/common/c_content.cpp and /src/content_sao.cpp
295 float player_stepheight = (m_cao == nullptr) ? 0.0f :
296 (touching_ground ? m_cao->getStepHeight() : (0.2f * BS));
299 const v3f initial_position = position;
300 const v3f initial_speed = m_speed;
302 collisionMoveResult result = collisionMoveSimple(env, m_client,
303 pos_max_d, m_collisionbox, player_stepheight, dtime,
304 &position, &m_speed, accel_f, NULL, true, true);
306 bool could_sneak = control.sneak && !free_move && !in_liquid &&
307 !is_climbing && physics_override_sneak;
309 // Add new collisions to the vector
310 if (collision_info && !free_move) {
311 v3f diff = intToFloat(m_standing_node, BS) - position;
312 f32 distance = diff.getLength();
313 // Force update each ClientEnvironment::step()
314 bool is_first = collision_info->empty();
316 for (const auto &colinfo : result.collisions) {
317 collision_info->push_back(colinfo);
319 if (colinfo.type != COLLISION_NODE ||
320 colinfo.axis != COLLISION_AXIS_Y ||
321 (could_sneak && m_sneak_node_exists))
324 diff = intToFloat(colinfo.node_p, BS) - position;
326 // Find nearest colliding node
327 f32 len = diff.getLength();
328 if (is_first || len < distance) {
329 m_standing_node = colinfo.node_p;
337 If the player's feet touch the topside of any node, this is
340 Player is allowed to jump when this is true.
342 bool touching_ground_was = touching_ground;
343 touching_ground = result.touching_ground;
344 bool sneak_can_jump = false;
346 // Max. distance (X, Z) over border for sneaking determined by collision box
347 // * 0.49 to keep the center just barely on the node
348 v3f sneak_max = m_collisionbox.getExtent() * 0.49;
350 if (m_sneak_ladder_detected) {
351 // restore legacy behaviour (this makes the m_speed.Y hack necessary)
352 sneak_max = v3f(0.4f * BS, 0.0f, 0.4f * BS);
356 If sneaking, keep on top of last walked node and don't fall off
358 if (could_sneak && m_sneak_node_exists) {
359 const v3f sn_f = intToFloat(m_sneak_node, BS);
360 const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge;
361 const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge;
362 const v3f old_pos = position;
363 const v3f old_speed = m_speed;
364 f32 y_diff = bmax.Y - position.Y;
365 m_standing_node = m_sneak_node;
367 // (BS * 0.6f) is the basic stepheight while standing on ground
368 if (y_diff < BS * 0.6f) {
369 // Only center player when they're on the node
370 position.X = rangelim(position.X,
371 bmin.X - sneak_max.X, bmax.X + sneak_max.X);
372 position.Z = rangelim(position.Z,
373 bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z);
375 if (position.X != old_pos.X)
377 if (position.Z != old_pos.Z)
381 if (y_diff > 0 && m_speed.Y <= 0.0f &&
382 (physics_override_sneak_glitch || y_diff < BS * 0.6f)) {
383 // Move player to the maximal height when falling or when
384 // the ledge is climbed on the next step.
386 // Smoothen the movement (based on 'position.Y = bmax.Y')
387 position.Y += y_diff * dtime * 22.0f + BS * 0.01f;
388 position.Y = std::min(position.Y, bmax.Y);
392 // Allow jumping on node edges while sneaking
393 if (m_speed.Y == 0.0f || m_sneak_ladder_detected)
394 sneak_can_jump = true;
396 if (collision_info &&
397 m_speed.Y - old_speed.Y > BS) {
398 // Collide with sneak node, report fall damage
399 CollisionInfo sn_info;
400 sn_info.node_p = m_sneak_node;
401 sn_info.old_speed = old_speed;
402 sn_info.new_speed = m_speed;
403 collision_info->push_back(sn_info);
408 Find the next sneak node if necessary
410 bool new_sneak_node_exists = false;
413 new_sneak_node_exists = updateSneakNode(map, position, sneak_max);
416 Set new position but keep sneak node set
418 setPosition(position);
419 m_sneak_node_exists = new_sneak_node_exists;
425 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
426 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
428 // Set camera impact value to be used for view bobbing
429 camera_impact = getSpeed().Y * -1;
433 camera_barely_in_ceiling = false;
434 v3s16 camera_np = floatToInt(getEyePosition(), BS);
435 MapNode n = map->getNode(camera_np);
436 if (n.getContent() != CONTENT_IGNORE) {
437 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
438 camera_barely_in_ceiling = true;
443 Check properties of the node on which the player is standing
445 const ContentFeatures &f = nodemgr->get(map->getNode(m_standing_node));
446 const ContentFeatures &f1 = nodemgr->get(map->getNode(m_standing_node + v3s16(0, 1, 0)));
448 // Determine if jumping is possible
449 m_disable_jump = itemgroup_get(f.groups, "disable_jump") ||
450 itemgroup_get(f1.groups, "disable_jump");
451 m_can_jump = ((touching_ground && !is_climbing) || sneak_can_jump) && !m_disable_jump;
453 // Jump key pressed while jumping off from a bouncy block
454 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
455 m_speed.Y >= -0.5f * BS) {
456 float jumpspeed = movement_speed_jump * physics_override_jump;
457 if (m_speed.Y > 1.0f) {
458 // Reduce boost when speed already is high
459 m_speed.Y += jumpspeed / (1.0f + (m_speed.Y / 16.0f));
461 m_speed.Y += jumpspeed;
465 m_legit_speed = m_speed;
470 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
473 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
475 move(dtime, env, pos_max_d, NULL);
478 void LocalPlayer::applyControl(float dtime, Environment *env)
481 swimming_vertical = false;
482 swimming_pitch = false;
484 setPitch(control.pitch);
487 // Nullify speed and don't run positioning code if the player is attached
493 PlayerSettings &player_settings = getPlayerSettings();
495 // All vectors are relative to the player's yaw,
496 // (and pitch if pitch move mode enabled),
497 // and will be rotated at the end
498 v3f speedH, speedV; // Horizontal (X, Z) and Vertical (Y)
500 bool fly_allowed = m_client->checkLocalPrivilege("fly");
501 bool fast_allowed = m_client->checkLocalPrivilege("fast");
503 bool free_move = fly_allowed && player_settings.free_move;
504 bool fast_move = fast_allowed && player_settings.fast_move;
505 bool pitch_move = (free_move || in_liquid) && player_settings.pitch_move;
506 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
507 bool fast_climb = fast_move && control.aux1 && !player_settings.aux1_descends;
508 bool always_fly_fast = player_settings.always_fly_fast;
510 // Whether superspeed mode is used or not
511 bool superspeed = false;
513 if (always_fly_fast && free_move && fast_move)
516 // Old descend control
517 if (player_settings.aux1_descends) {
518 // If free movement and fast movement, always move fast
519 if (free_move && fast_move)
522 // Auxiliary button 1 (E)
525 // In free movement mode, aux1 descends
527 speedV.Y = -movement_speed_fast;
529 speedV.Y = -movement_speed_walk;
530 } else if (in_liquid || in_liquid_stable) {
531 speedV.Y = -movement_speed_walk;
532 swimming_vertical = true;
533 } else if (is_climbing) {
534 speedV.Y = -movement_speed_climb;
536 // If not free movement but fast is allowed, aux1 is
543 // New minecraft-like descend control
545 // Auxiliary button 1 (E)
548 // aux1 is "Turbo button"
556 // In free movement mode, sneak descends
557 if (fast_move && (control.aux1 || always_fly_fast))
558 speedV.Y = -movement_speed_fast;
560 speedV.Y = -movement_speed_walk;
561 } else if (in_liquid || in_liquid_stable) {
563 speedV.Y = -movement_speed_fast;
565 speedV.Y = -movement_speed_walk;
566 swimming_vertical = true;
567 } else if (is_climbing) {
569 speedV.Y = -movement_speed_fast;
571 speedV.Y = -movement_speed_climb;
577 speedH += v3f(0.0f, 0.0f, 1.0f);
580 speedH -= v3f(0.0f, 0.0f, 1.0f);
582 if (!control.up && !control.down)
583 speedH -= v3f(0.0f, 0.0f, 1.0f) * (control.forw_move_joystick_axis / 32767.f);
586 speedH += v3f(-1.0f, 0.0f, 0.0f);
589 speedH += v3f(1.0f, 0.0f, 0.0f);
591 if (!control.left && !control.right)
592 speedH += v3f(1.0f, 0.0f, 0.0f) * (control.sidew_move_joystick_axis / 32767.f);
595 // release autojump after a given time
596 m_autojump_time -= dtime;
597 if (m_autojump_time <= 0.0f)
603 if (player_settings.aux1_descends || always_fly_fast) {
605 speedV.Y = movement_speed_fast;
607 speedV.Y = movement_speed_walk;
609 if (fast_move && control.aux1)
610 speedV.Y = movement_speed_fast;
612 speedV.Y = movement_speed_walk;
614 } else if (m_can_jump || g_settings->getBool("jetpack")) {
616 NOTE: The d value in move() affects jump height by
617 raising the height at which the jump speed is kept
618 at its starting value
620 v3f speedJ = getSpeed();
621 if (speedJ.Y >= -0.5f * BS || g_settings->getBool("jetpack")) {
622 speedJ.Y = movement_speed_jump * physics_override_jump;
624 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_JUMP));
626 } else if (in_liquid && !m_disable_jump) {
628 speedV.Y = movement_speed_fast;
630 speedV.Y = movement_speed_walk;
631 swimming_vertical = true;
632 } else if (is_climbing && !m_disable_jump) {
634 speedV.Y = movement_speed_fast;
636 speedV.Y = movement_speed_climb;
640 // The speed of the player (Y is ignored)
641 if (superspeed || (is_climbing && fast_climb) ||
642 ((in_liquid || in_liquid_stable) && fast_climb))
643 speedH = speedH.normalize() * movement_speed_fast;
644 else if (control.sneak && !free_move && !in_liquid && !in_liquid_stable && !g_settings->getBool("no_slow"))
645 speedH = speedH.normalize() * movement_speed_crouch;
647 speedH = speedH.normalize() * movement_speed_walk;
649 // Acceleration increase
650 f32 incH = 0.0f; // Horizontal (X, Z)
651 f32 incV = 0.0f; // Vertical (Y)
652 if ((!touching_ground && !free_move && !is_climbing && !in_liquid) ||
653 (!free_move && m_can_jump && control.jump)) {
654 // Jumping and falling
655 if (superspeed || (fast_move && control.aux1))
656 incH = movement_acceleration_fast * BS * dtime;
658 incH = movement_acceleration_air * BS * dtime;
659 incV = 0.0f; // No vertical acceleration in air
660 } else if (superspeed || (is_climbing && fast_climb) ||
661 ((in_liquid || in_liquid_stable) && fast_climb)) {
662 incH = incV = movement_acceleration_fast * BS * dtime;
664 incH = incV = movement_acceleration_default * BS * dtime;
667 float slip_factor = 1.0f;
668 if (!free_move && !in_liquid && !in_liquid_stable)
669 slip_factor = getSlipFactor(env, speedH);
671 // Don't sink when swimming in pitch mode
672 if (pitch_move && in_liquid) {
673 v3f controlSpeed = speedH + speedV;
674 if (controlSpeed.getLength() > 0.01f)
675 swimming_pitch = true;
678 // Accelerate to target speed with maximum increment
679 accelerate((speedH + speedV) * physics_override_speed,
680 incH * physics_override_speed * slip_factor, incV * physics_override_speed,
684 v3s16 LocalPlayer::getStandingNodePos()
686 if (m_sneak_node_exists)
689 return m_standing_node;
692 v3s16 LocalPlayer::getFootstepNodePos()
694 // Emit swimming sound if the player is in liquid
695 if (in_liquid_stable)
696 return floatToInt(getPosition(), BS);
698 // BS * 0.05 below the player's feet ensures a 1/16th height
699 // nodebox is detected instead of the node below it.
701 return floatToInt(getPosition() - v3f(0.0f, BS * 0.05f, 0.0f), BS);
703 // A larger distance below is necessary for a footstep sound
704 // when landing after a jump or fall. BS * 0.5 ensures water
705 // sounds when swimming in 1 node deep water.
706 return floatToInt(getPosition() - v3f(0.0f, BS * 0.5f, 0.0f), BS);
709 v3s16 LocalPlayer::getLightPosition() const
711 return floatToInt(m_position + v3f(0.0f, BS * 1.5f, 0.0f), BS);
714 v3f LocalPlayer::getEyeOffset() const
716 float eye_height = camera_barely_in_ceiling ? m_eye_height - 0.125f : m_eye_height;
717 return v3f(0.0f, BS * eye_height, 0.0f);
720 ClientActiveObject *LocalPlayer::getParent() const
722 return (m_cao && ! g_settings->getBool("entity_speed")) ? m_cao->getParent() : nullptr;
725 bool LocalPlayer::isDead() const
727 FATAL_ERROR_IF(!getCAO(), "LocalPlayer's CAO isn't initialized");
728 return !getCAO()->isImmortal() && hp == 0;
731 void LocalPlayer::tryReattach(int id)
733 PointedThing pointed(id, v3f(0, 0, 0), v3s16(0, 0, 0), 0);
734 m_client->interact(INTERACT_PLACE, pointed);
735 m_cao->m_waiting_for_reattach = 10;
738 bool LocalPlayer::isWaitingForReattach() const
740 return g_settings->getBool("entity_speed") && m_cao && ! m_cao->getParent() && m_cao->m_waiting_for_reattach > 0;
744 void LocalPlayer::accelerate(const v3f &target_speed, const f32 max_increase_H,
745 const f32 max_increase_V, const bool use_pitch)
747 const f32 yaw = getYaw();
748 const f32 pitch = getPitch();
749 v3f flat_speed = m_speed;
750 // Rotate speed vector by -yaw and -pitch to make it relative to the player's yaw and pitch
751 flat_speed.rotateXZBy(-yaw);
753 flat_speed.rotateYZBy(-pitch);
755 v3f d_wanted = target_speed - flat_speed;
758 // Then compare the horizontal and vertical components with the wanted speed
759 if (max_increase_H > 0.0f) {
760 v3f d_wanted_H = d_wanted * v3f(1.0f, 0.0f, 1.0f);
761 if (d_wanted_H.getLength() > max_increase_H)
762 d += d_wanted_H.normalize() * max_increase_H;
767 if (max_increase_V > 0.0f) {
768 f32 d_wanted_V = d_wanted.Y;
769 if (d_wanted_V > max_increase_V)
770 d.Y += max_increase_V;
771 else if (d_wanted_V < -max_increase_V)
772 d.Y -= max_increase_V;
777 // Finally rotate it again
785 // Temporary option for old move code
786 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
787 std::vector<CollisionInfo> *collision_info)
789 Map *map = &env->getMap();
790 const NodeDefManager *nodemgr = m_client->ndef();
792 v3f position = getPosition();
794 // Copy parent position if local player is attached
796 setPosition(m_cao->getPosition());
797 m_sneak_node_exists = false;
798 added_velocity = v3f(0.0f);
802 PlayerSettings &player_settings = getPlayerSettings();
804 // Skip collision detection if noclip mode is used
805 bool fly_allowed = m_client->checkLocalPrivilege("fly");
806 bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip;
807 bool free_move = noclip && fly_allowed && player_settings.free_move;
809 position += m_speed * dtime;
810 setPosition(position);
812 touching_ground = false;
813 m_sneak_node_exists = false;
814 added_velocity = v3f(0.0f);
818 m_speed += added_velocity;
819 added_velocity = v3f(0.0f);
824 bool is_valid_position;
829 Check if player is in liquid (the oscillating value)
832 // If in liquid, the threshold of coming out is at higher y
833 pp = floatToInt(position + v3f(0.0f, BS * 0.1f, 0.0f), BS);
834 node = map->getNode(pp, &is_valid_position);
835 if (is_valid_position) {
836 in_liquid = nodemgr->get(node.getContent()).isLiquid();
837 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
842 // If not in liquid, the threshold of going in is at lower y
843 pp = floatToInt(position + v3f(0.0f, BS * 0.5f, 0.0f), BS);
844 node = map->getNode(pp, &is_valid_position);
845 if (is_valid_position) {
846 in_liquid = nodemgr->get(node.getContent()).isLiquid();
847 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
854 Check if player is in liquid (the stable value)
856 pp = floatToInt(position + v3f(0.0f), BS);
857 node = map->getNode(pp, &is_valid_position);
858 if (is_valid_position)
859 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
861 in_liquid_stable = false;
864 Check if player is climbing
866 pp = floatToInt(position + v3f(0.0f, 0.5f * BS, 0.0f), BS);
867 v3s16 pp2 = floatToInt(position + v3f(0.0f, -0.2f * BS, 0.0f), BS);
868 node = map->getNode(pp, &is_valid_position);
869 bool is_valid_position2;
870 MapNode node2 = map->getNode(pp2, &is_valid_position2);
872 if (!(is_valid_position && is_valid_position2))
875 is_climbing = (nodemgr->get(node.getContent()).climbable ||
876 nodemgr->get(node2.getContent()).climbable) && !free_move;
879 Collision uncertainty radius
880 Make it a bit larger than the maximum distance of movement
882 //f32 d = pos_max_d * 1.1;
883 // A fairly large value in here makes moving smoother
885 // This should always apply, otherwise there are glitches
886 sanity_check(d > pos_max_d);
887 // Maximum distance over border for sneaking
888 f32 sneak_max = BS * 0.4f;
891 If sneaking, keep in range from the last walked node and don't
894 if (control.sneak && m_sneak_node_exists &&
895 !(fly_allowed && player_settings.free_move) && !in_liquid &&
896 physics_override_sneak) {
897 f32 maxd = 0.5f * BS + sneak_max;
898 v3f lwn_f = intToFloat(m_sneak_node, BS);
899 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
900 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
903 // Move up if necessary
904 f32 new_y = (lwn_f.Y - 0.5f * BS) + m_sneak_node_bb_ymax;
905 if (position.Y < new_y)
908 Collision seems broken, since player is sinking when
909 sneaking over the edges of current sneaking_node.
910 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
912 if (m_speed.Y < 0.0f)
917 // TODO: This shouldn't be hardcoded but decided by the server
918 float player_stepheight = touching_ground ? (BS * 0.6f) : (BS * 0.2f);
921 const v3f initial_position = position;
922 const v3f initial_speed = m_speed;
924 collisionMoveResult result = collisionMoveSimple(env, m_client,
925 pos_max_d, m_collisionbox, player_stepheight, dtime,
926 &position, &m_speed, accel_f, NULL, true, true);
928 // Positition was slightly changed; update standing node pos
930 m_standing_node = floatToInt(m_position - v3f(0.0f, 0.1f * BS, 0.0f), BS);
932 m_standing_node = floatToInt(m_position, BS);
935 If the player's feet touch the topside of any node, this is
938 Player is allowed to jump when this is true.
940 bool touching_ground_was = touching_ground;
941 touching_ground = result.touching_ground;
943 //bool standing_on_unloaded = result.standing_on_unloaded;
946 Check the nodes under the player to see from which node the
947 player is sneaking from, if any. If the node from under
948 the player has been removed, the player falls.
950 f32 position_y_mod = 0.05f * BS;
951 if (m_sneak_node_bb_ymax > 0.0f)
952 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
953 v3s16 current_node = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS);
954 if (m_sneak_node_exists &&
955 nodemgr->get(map->getNode(m_old_node_below)).name == "air" &&
956 m_old_node_below_type != "air") {
957 // Old node appears to have been removed; that is,
958 // it wasn't air before but now it is
959 m_need_to_get_new_sneak_node = false;
960 m_sneak_node_exists = false;
961 } else if (nodemgr->get(map->getNode(current_node)).name != "air") {
962 // We are on something, so make sure to recalculate the sneak
964 m_need_to_get_new_sneak_node = true;
967 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
968 m_sneak_node_bb_ymax = 0.0f;
969 v3s16 pos_i_bottom = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS);
970 v2f player_p2df(position.X, position.Z);
971 f32 min_distance_f = 100000.0f * BS;
972 // If already seeking from some node, compare to it.
973 v3s16 new_sneak_node = m_sneak_node;
974 for (s16 x= -1; x <= 1; x++)
975 for (s16 z= -1; z <= 1; z++) {
976 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
977 v3f pf = intToFloat(p, BS);
978 v2f node_p2df(pf.X, pf.Z);
979 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
980 f32 max_axis_distance_f = MYMAX(
981 std::fabs(player_p2df.X - node_p2df.X),
982 std::fabs(player_p2df.Y - node_p2df.Y));
984 if (distance_f > min_distance_f ||
985 max_axis_distance_f > 0.5f * BS + sneak_max + 0.1f * BS)
988 // The node to be sneaked on has to be walkable
989 node = map->getNode(p, &is_valid_position);
990 if (!is_valid_position || !nodemgr->get(node).walkable)
992 // And the node above it has to be nonwalkable
993 node = map->getNode(p + v3s16(0, 1, 0), &is_valid_position);
994 if (!is_valid_position || nodemgr->get(node).walkable)
996 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
997 if (!physics_override_sneak_glitch) {
998 node = map->getNode(p + v3s16(0, 2, 0), &is_valid_position);
999 if (!is_valid_position || nodemgr->get(node).walkable)
1003 min_distance_f = distance_f;
1007 bool sneak_node_found = (min_distance_f < 100000.0f * BS * 0.9f);
1009 m_sneak_node = new_sneak_node;
1010 m_sneak_node_exists = sneak_node_found;
1012 if (sneak_node_found) {
1014 MapNode n = map->getNode(m_sneak_node);
1015 std::vector<aabb3f> nodeboxes;
1016 n.getCollisionBoxes(nodemgr, &nodeboxes);
1017 for (const auto &box : nodeboxes) {
1018 if (box.MaxEdge.Y > cb_max)
1019 cb_max = box.MaxEdge.Y;
1021 m_sneak_node_bb_ymax = cb_max;
1025 If sneaking, the player's collision box can be in air, so
1026 this has to be set explicitly
1028 if (sneak_node_found && control.sneak)
1029 touching_ground = true;
1033 Set new position but keep sneak node set
1035 bool sneak_node_exists = m_sneak_node_exists;
1036 setPosition(position);
1037 m_sneak_node_exists = sneak_node_exists;
1042 // Don't report if flying
1043 if (collision_info && !(player_settings.free_move && fly_allowed)) {
1044 for (const auto &info : result.collisions) {
1045 collision_info->push_back(info);
1049 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1050 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
1051 // Set camera impact value to be used for view bobbing
1052 camera_impact = getSpeed().Y * -1.0f;
1056 camera_barely_in_ceiling = false;
1057 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1058 MapNode n = map->getNode(camera_np);
1059 if (n.getContent() != CONTENT_IGNORE) {
1060 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1061 camera_barely_in_ceiling = true;
1066 Update the node last under the player
1068 m_old_node_below = floatToInt(position - v3f(0.0f, BS / 2.0f, 0.0f), BS);
1069 m_old_node_below_type = nodemgr->get(map->getNode(m_old_node_below)).name;
1072 Check properties of the node on which the player is standing
1074 const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos()));
1076 // Determine if jumping is possible
1077 m_disable_jump = itemgroup_get(f.groups, "disable_jump");
1078 m_can_jump = touching_ground && !m_disable_jump;
1080 // Jump key pressed while jumping off from a bouncy block
1081 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1082 m_speed.Y >= -0.5f * BS) {
1083 float jumpspeed = movement_speed_jump * physics_override_jump;
1084 if (m_speed.Y > 1.0f) {
1085 // Reduce boost when speed already is high
1086 m_speed.Y += jumpspeed / (1.0f + (m_speed.Y / 16.0f));
1088 m_speed.Y += jumpspeed;
1095 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
1098 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1100 // Slip on slippery nodes
1101 const NodeDefManager *nodemgr = env->getGameDef()->ndef();
1102 Map *map = &env->getMap();
1103 const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos()));
1105 if (f.walkable && ! g_settings->getBool("antislip"))
1106 slippery = itemgroup_get(f.groups, "slippery");
1108 if (slippery >= 1) {
1109 if (speedH == v3f(0.0f))
1112 return core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);
1117 void LocalPlayer::handleAutojump(f32 dtime, Environment *env,
1118 const collisionMoveResult &result, const v3f &initial_position,
1119 const v3f &initial_speed, f32 pos_max_d)
1121 PlayerSettings &player_settings = getPlayerSettings();
1122 if (!player_settings.autojump)
1128 bool control_forward = control.up ||
1129 (!control.up && !control.down &&
1130 control.forw_move_joystick_axis < -0.05f);
1132 bool could_autojump =
1133 m_can_jump && !control.jump && !control.sneak && control_forward;
1135 if (!could_autojump)
1138 bool horizontal_collision = false;
1139 for (const auto &colinfo : result.collisions) {
1140 if (colinfo.type == COLLISION_NODE && colinfo.plane != 1) {
1141 horizontal_collision = true;
1142 break; // one is enough
1146 // must be running against something to trigger autojumping
1147 if (!horizontal_collision)
1150 // check for nodes above
1151 v3f headpos_min = m_position + m_collisionbox.MinEdge * 0.99f;
1152 v3f headpos_max = m_position + m_collisionbox.MaxEdge * 0.99f;
1153 headpos_min.Y = headpos_max.Y; // top face of collision box
1154 v3s16 ceilpos_min = floatToInt(headpos_min, BS) + v3s16(0, 1, 0);
1155 v3s16 ceilpos_max = floatToInt(headpos_max, BS) + v3s16(0, 1, 0);
1156 const NodeDefManager *ndef = env->getGameDef()->ndef();
1157 bool is_position_valid;
1158 for (s16 z = ceilpos_min.Z; z <= ceilpos_max.Z; ++z) {
1159 for (s16 x = ceilpos_min.X; x <= ceilpos_max.X; ++x) {
1160 MapNode n = env->getMap().getNode(v3s16(x, ceilpos_max.Y, z), &is_position_valid);
1162 if (!is_position_valid)
1163 break; // won't collide with the void outside
1164 if (n.getContent() == CONTENT_IGNORE)
1165 return; // players collide with ignore blocks -> same as walkable
1166 const ContentFeatures &f = ndef->get(n);
1168 return; // would bump head, don't jump
1172 float jump_height = 1.1f; // TODO: better than a magic number
1173 v3f jump_pos = initial_position + v3f(0.0f, jump_height * BS, 0.0f);
1174 v3f jump_speed = initial_speed;
1176 // try at peak of jump, zero step height
1177 collisionMoveResult jump_result = collisionMoveSimple(env, m_client, pos_max_d,
1178 m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed, v3f(0.0f), NULL, true, true);
1180 // see if we can get a little bit farther horizontally if we had
1182 v3f run_delta = m_position - initial_position;
1184 v3f jump_delta = jump_pos - initial_position;
1185 jump_delta.Y = 0.0f;
1186 if (jump_delta.getLengthSQ() > run_delta.getLengthSQ() * 1.01f) {
1188 m_autojump_time = 0.1f;