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") || g_settings->getBool("freecam");
202 bool noclip = (m_client->checkLocalPrivilege("noclip") && player_settings.noclip) || g_settings->getBool("freecam");
203 bool free_move = (player_settings.free_move && fly_allowed) || g_settings->getBool("freecam");
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 || g_settings->getBool("airjump");
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;
468 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
471 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
473 move(dtime, env, pos_max_d, NULL);
476 void LocalPlayer::applyControl(float dtime, Environment *env)
479 swimming_vertical = false;
480 swimming_pitch = false;
482 setPitch(control.pitch);
485 // Nullify speed and don't run positioning code if the player is attached
491 PlayerSettings &player_settings = getPlayerSettings();
493 // All vectors are relative to the player's yaw,
494 // (and pitch if pitch move mode enabled),
495 // and will be rotated at the end
496 v3f speedH, speedV; // Horizontal (X, Z) and Vertical (Y)
498 bool fly_allowed = m_client->checkLocalPrivilege("fly");
499 bool fast_allowed = m_client->checkLocalPrivilege("fast");
501 bool free_move = (fly_allowed && player_settings.free_move) || g_settings->getBool("freecam");
502 bool fast_move = (fast_allowed && player_settings.fast_move) || g_settings->getBool("freecam");
503 bool pitch_move = (free_move || in_liquid) && player_settings.pitch_move;
504 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
505 bool fast_climb = fast_move && control.aux1 && !player_settings.aux1_descends;
506 bool always_fly_fast = player_settings.always_fly_fast;
508 // Whether superspeed mode is used or not
509 bool superspeed = false;
511 if (always_fly_fast && free_move && fast_move)
514 // Old descend control
515 if (player_settings.aux1_descends) {
516 // If free movement and fast movement, always move fast
517 if (free_move && fast_move)
520 // Auxiliary button 1 (E)
523 // In free movement mode, aux1 descends
525 speedV.Y = -movement_speed_fast;
527 speedV.Y = -movement_speed_walk;
528 } else if (in_liquid || in_liquid_stable) {
529 speedV.Y = -movement_speed_walk;
530 swimming_vertical = true;
531 } else if (is_climbing) {
532 speedV.Y = -movement_speed_climb;
534 // If not free movement but fast is allowed, aux1 is
541 // New minecraft-like descend control
543 // Auxiliary button 1 (E)
546 // aux1 is "Turbo button"
554 // In free movement mode, sneak descends
555 if (fast_move && (control.aux1 || always_fly_fast))
556 speedV.Y = -movement_speed_fast;
558 speedV.Y = -movement_speed_walk;
559 } else if (in_liquid || in_liquid_stable) {
561 speedV.Y = -movement_speed_fast;
563 speedV.Y = -movement_speed_walk;
564 swimming_vertical = true;
565 } else if (is_climbing) {
567 speedV.Y = -movement_speed_fast;
569 speedV.Y = -movement_speed_climb;
574 speedH = v3f(sin(control.movement_direction), 0.0f, cos(control.movement_direction));
577 // release autojump after a given time
578 m_autojump_time -= dtime;
579 if (m_autojump_time <= 0.0f)
585 if (player_settings.aux1_descends || always_fly_fast) {
587 speedV.Y = movement_speed_fast;
589 speedV.Y = movement_speed_walk;
591 if (fast_move && control.aux1)
592 speedV.Y = movement_speed_fast;
594 speedV.Y = movement_speed_walk;
596 } else if (m_can_jump || g_settings->getBool("jetpack")) {
598 NOTE: The d value in move() affects jump height by
599 raising the height at which the jump speed is kept
600 at its starting value
602 v3f speedJ = getSpeed();
603 if (speedJ.Y >= -0.5f * BS || g_settings->getBool("jetpack")) {
604 speedJ.Y = movement_speed_jump * physics_override_jump;
606 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_JUMP));
608 } else if (in_liquid && !m_disable_jump) {
610 speedV.Y = movement_speed_fast;
612 speedV.Y = movement_speed_walk;
613 swimming_vertical = true;
614 } else if (is_climbing && !m_disable_jump) {
616 speedV.Y = movement_speed_fast;
618 speedV.Y = movement_speed_climb;
622 // The speed of the player (Y is ignored)
623 if (superspeed || (is_climbing && fast_climb) ||
624 ((in_liquid || in_liquid_stable) && fast_climb))
625 speedH = speedH.normalize() * movement_speed_fast;
626 else if (control.sneak && !free_move && !in_liquid && !in_liquid_stable && !g_settings->getBool("no_slow"))
627 speedH = speedH.normalize() * movement_speed_crouch;
629 speedH = speedH.normalize() * movement_speed_walk;
631 speedH *= control.movement_speed; /* Apply analog input */
633 // Acceleration increase
634 f32 incH = 0.0f; // Horizontal (X, Z)
635 f32 incV = 0.0f; // Vertical (Y)
636 if ((!touching_ground && !free_move && !is_climbing && !in_liquid) ||
637 (!free_move && m_can_jump && control.jump)) {
638 // Jumping and falling
639 if (superspeed || (fast_move && control.aux1))
640 incH = movement_acceleration_fast * BS * dtime;
642 incH = movement_acceleration_air * BS * dtime;
643 incV = 0.0f; // No vertical acceleration in air
644 } else if (superspeed || (is_climbing && fast_climb) ||
645 ((in_liquid || in_liquid_stable) && fast_climb)) {
646 incH = incV = movement_acceleration_fast * BS * dtime;
648 incH = incV = movement_acceleration_default * BS * dtime;
651 float slip_factor = 1.0f;
652 if (!free_move && !in_liquid && !in_liquid_stable)
653 slip_factor = getSlipFactor(env, speedH);
655 // Don't sink when swimming in pitch mode
656 if (pitch_move && in_liquid) {
657 v3f controlSpeed = speedH + speedV;
658 if (controlSpeed.getLength() > 0.01f)
659 swimming_pitch = true;
662 // Accelerate to target speed with maximum increment
663 accelerate((speedH + speedV) * physics_override_speed,
664 incH * physics_override_speed * slip_factor, incV * physics_override_speed,
668 v3s16 LocalPlayer::getStandingNodePos()
670 if (m_sneak_node_exists)
673 return m_standing_node;
676 v3s16 LocalPlayer::getFootstepNodePos()
678 // Emit swimming sound if the player is in liquid
679 if (in_liquid_stable)
680 return floatToInt(getPosition(), BS);
682 // BS * 0.05 below the player's feet ensures a 1/16th height
683 // nodebox is detected instead of the node below it.
685 return floatToInt(getPosition() - v3f(0.0f, BS * 0.05f, 0.0f), BS);
687 // A larger distance below is necessary for a footstep sound
688 // when landing after a jump or fall. BS * 0.5 ensures water
689 // sounds when swimming in 1 node deep water.
690 return floatToInt(getPosition() - v3f(0.0f, BS * 0.5f, 0.0f), BS);
693 v3s16 LocalPlayer::getLightPosition() const
695 return floatToInt(m_position + v3f(0.0f, BS * 1.5f, 0.0f), BS);
698 v3f LocalPlayer::getSendSpeed()
700 v3f speed = getLegitSpeed();
702 if (m_client->modsLoaded())
703 speed = m_client->getScript()->get_send_speed(speed);
708 v3f LocalPlayer::getEyeOffset() const
710 float eye_height = camera_barely_in_ceiling ? m_eye_height - 0.125f : m_eye_height;
711 return v3f(0.0f, BS * eye_height, 0.0f);
714 ClientActiveObject *LocalPlayer::getParent() const
716 return (m_cao && ! g_settings->getBool("entity_speed")) ? m_cao->getParent() : nullptr;
719 bool LocalPlayer::isDead() const
721 FATAL_ERROR_IF(!getCAO(), "LocalPlayer's CAO isn't initialized");
722 return !getCAO()->isImmortal() && hp == 0;
725 void LocalPlayer::tryReattach(int id)
727 PointedThing pointed(id, v3f(0, 0, 0), v3s16(0, 0, 0), 0);
728 m_client->interact(INTERACT_PLACE, pointed);
729 m_cao->m_waiting_for_reattach = 10;
732 bool LocalPlayer::isWaitingForReattach() const
734 return g_settings->getBool("entity_speed") && m_cao && ! m_cao->getParent() && m_cao->m_waiting_for_reattach > 0;
738 void LocalPlayer::accelerate(const v3f &target_speed, const f32 max_increase_H,
739 const f32 max_increase_V, const bool use_pitch)
741 const f32 yaw = getYaw();
742 const f32 pitch = getPitch();
743 v3f flat_speed = m_speed;
744 // Rotate speed vector by -yaw and -pitch to make it relative to the player's yaw and pitch
745 flat_speed.rotateXZBy(-yaw);
747 flat_speed.rotateYZBy(-pitch);
749 v3f d_wanted = target_speed - flat_speed;
752 // Then compare the horizontal and vertical components with the wanted speed
753 if (max_increase_H > 0.0f) {
754 v3f d_wanted_H = d_wanted * v3f(1.0f, 0.0f, 1.0f);
755 if (d_wanted_H.getLength() > max_increase_H)
756 d += d_wanted_H.normalize() * max_increase_H;
761 if (max_increase_V > 0.0f) {
762 f32 d_wanted_V = d_wanted.Y;
763 if (d_wanted_V > max_increase_V)
764 d.Y += max_increase_V;
765 else if (d_wanted_V < -max_increase_V)
766 d.Y -= max_increase_V;
771 // Finally rotate it again
779 // Temporary option for old move code
780 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
781 std::vector<CollisionInfo> *collision_info)
783 Map *map = &env->getMap();
784 const NodeDefManager *nodemgr = m_client->ndef();
786 v3f position = getPosition();
788 // Copy parent position if local player is attached
790 setPosition(m_cao->getPosition());
791 m_sneak_node_exists = false;
792 added_velocity = v3f(0.0f);
796 PlayerSettings &player_settings = getPlayerSettings();
798 // Skip collision detection if noclip mode is used
799 bool fly_allowed = m_client->checkLocalPrivilege("fly") || g_settings->getBool("freecam");
800 bool noclip = (m_client->checkLocalPrivilege("noclip") && player_settings.noclip) || g_settings->getBool("freecam");
801 bool free_move = (noclip && fly_allowed && player_settings.free_move) || g_settings->getBool("freecam");
803 position += m_speed * dtime;
804 setPosition(position);
806 touching_ground = false;
807 m_sneak_node_exists = false;
808 added_velocity = v3f(0.0f);
812 m_speed += added_velocity;
813 added_velocity = v3f(0.0f);
818 bool is_valid_position;
823 Check if player is in liquid (the oscillating value)
826 // If in liquid, the threshold of coming out is at higher y
827 pp = floatToInt(position + v3f(0.0f, BS * 0.1f, 0.0f), BS);
828 node = map->getNode(pp, &is_valid_position);
829 if (is_valid_position) {
830 in_liquid = nodemgr->get(node.getContent()).isLiquid();
831 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
836 // If not in liquid, the threshold of going in is at lower y
837 pp = floatToInt(position + v3f(0.0f, BS * 0.5f, 0.0f), BS);
838 node = map->getNode(pp, &is_valid_position);
839 if (is_valid_position) {
840 in_liquid = nodemgr->get(node.getContent()).isLiquid();
841 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
848 Check if player is in liquid (the stable value)
850 pp = floatToInt(position + v3f(0.0f), BS);
851 node = map->getNode(pp, &is_valid_position);
852 if (is_valid_position)
853 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
855 in_liquid_stable = false;
858 Check if player is climbing
860 pp = floatToInt(position + v3f(0.0f, 0.5f * BS, 0.0f), BS);
861 v3s16 pp2 = floatToInt(position + v3f(0.0f, -0.2f * BS, 0.0f), BS);
862 node = map->getNode(pp, &is_valid_position);
863 bool is_valid_position2;
864 MapNode node2 = map->getNode(pp2, &is_valid_position2);
866 if (!(is_valid_position && is_valid_position2))
869 is_climbing = (nodemgr->get(node.getContent()).climbable ||
870 nodemgr->get(node2.getContent()).climbable) && !free_move;
873 Collision uncertainty radius
874 Make it a bit larger than the maximum distance of movement
876 //f32 d = pos_max_d * 1.1;
877 // A fairly large value in here makes moving smoother
879 // This should always apply, otherwise there are glitches
880 sanity_check(d > pos_max_d);
881 // Maximum distance over border for sneaking
882 f32 sneak_max = BS * 0.4f;
885 If sneaking, keep in range from the last walked node and don't
888 if (control.sneak && m_sneak_node_exists &&
889 !(fly_allowed && player_settings.free_move) && !in_liquid &&
890 physics_override_sneak) {
891 f32 maxd = 0.5f * BS + sneak_max;
892 v3f lwn_f = intToFloat(m_sneak_node, BS);
893 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
894 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
897 // Move up if necessary
898 f32 new_y = (lwn_f.Y - 0.5f * BS) + m_sneak_node_bb_ymax;
899 if (position.Y < new_y)
902 Collision seems broken, since player is sinking when
903 sneaking over the edges of current sneaking_node.
904 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
906 if (m_speed.Y < 0.0f)
911 // TODO: This shouldn't be hardcoded but decided by the server
912 float player_stepheight = touching_ground ? (BS * 0.6f) : (BS * 0.2f);
915 const v3f initial_position = position;
916 const v3f initial_speed = m_speed;
918 collisionMoveResult result = collisionMoveSimple(env, m_client,
919 pos_max_d, m_collisionbox, player_stepheight, dtime,
920 &position, &m_speed, accel_f, NULL, true, true);
922 // Positition was slightly changed; update standing node pos
924 m_standing_node = floatToInt(m_position - v3f(0.0f, 0.1f * BS, 0.0f), BS);
926 m_standing_node = floatToInt(m_position, BS);
929 If the player's feet touch the topside of any node, this is
932 Player is allowed to jump when this is true.
934 bool touching_ground_was = touching_ground;
935 touching_ground = result.touching_ground;
937 //bool standing_on_unloaded = result.standing_on_unloaded;
940 Check the nodes under the player to see from which node the
941 player is sneaking from, if any. If the node from under
942 the player has been removed, the player falls.
944 f32 position_y_mod = 0.05f * BS;
945 if (m_sneak_node_bb_ymax > 0.0f)
946 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
947 v3s16 current_node = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS);
948 if (m_sneak_node_exists &&
949 nodemgr->get(map->getNode(m_old_node_below)).name == "air" &&
950 m_old_node_below_type != "air") {
951 // Old node appears to have been removed; that is,
952 // it wasn't air before but now it is
953 m_need_to_get_new_sneak_node = false;
954 m_sneak_node_exists = false;
955 } else if (nodemgr->get(map->getNode(current_node)).name != "air") {
956 // We are on something, so make sure to recalculate the sneak
958 m_need_to_get_new_sneak_node = true;
961 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
962 m_sneak_node_bb_ymax = 0.0f;
963 v3s16 pos_i_bottom = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS);
964 v2f player_p2df(position.X, position.Z);
965 f32 min_distance_f = 100000.0f * BS;
966 // If already seeking from some node, compare to it.
967 v3s16 new_sneak_node = m_sneak_node;
968 for (s16 x= -1; x <= 1; x++)
969 for (s16 z= -1; z <= 1; z++) {
970 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
971 v3f pf = intToFloat(p, BS);
972 v2f node_p2df(pf.X, pf.Z);
973 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
974 f32 max_axis_distance_f = MYMAX(
975 std::fabs(player_p2df.X - node_p2df.X),
976 std::fabs(player_p2df.Y - node_p2df.Y));
978 if (distance_f > min_distance_f ||
979 max_axis_distance_f > 0.5f * BS + sneak_max + 0.1f * BS)
982 // The node to be sneaked on has to be walkable
983 node = map->getNode(p, &is_valid_position);
984 if (!is_valid_position || !nodemgr->get(node).walkable)
986 // And the node above it has to be nonwalkable
987 node = map->getNode(p + v3s16(0, 1, 0), &is_valid_position);
988 if (!is_valid_position || nodemgr->get(node).walkable)
990 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
991 if (!physics_override_sneak_glitch) {
992 node = map->getNode(p + v3s16(0, 2, 0), &is_valid_position);
993 if (!is_valid_position || nodemgr->get(node).walkable)
997 min_distance_f = distance_f;
1001 bool sneak_node_found = (min_distance_f < 100000.0f * BS * 0.9f);
1003 m_sneak_node = new_sneak_node;
1004 m_sneak_node_exists = sneak_node_found;
1006 if (sneak_node_found) {
1008 MapNode n = map->getNode(m_sneak_node);
1009 std::vector<aabb3f> nodeboxes;
1010 n.getCollisionBoxes(nodemgr, &nodeboxes);
1011 for (const auto &box : nodeboxes) {
1012 if (box.MaxEdge.Y > cb_max)
1013 cb_max = box.MaxEdge.Y;
1015 m_sneak_node_bb_ymax = cb_max;
1019 If sneaking, the player's collision box can be in air, so
1020 this has to be set explicitly
1022 if (sneak_node_found && control.sneak)
1023 touching_ground = true;
1027 Set new position but keep sneak node set
1029 bool sneak_node_exists = m_sneak_node_exists;
1030 setPosition(position);
1031 m_sneak_node_exists = sneak_node_exists;
1036 // Don't report if flying
1037 if (collision_info && !(player_settings.free_move && fly_allowed)) {
1038 for (const auto &info : result.collisions) {
1039 collision_info->push_back(info);
1043 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1044 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
1045 // Set camera impact value to be used for view bobbing
1046 camera_impact = getSpeed().Y * -1.0f;
1050 camera_barely_in_ceiling = false;
1051 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1052 MapNode n = map->getNode(camera_np);
1053 if (n.getContent() != CONTENT_IGNORE) {
1054 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1055 camera_barely_in_ceiling = true;
1060 Update the node last under the player
1062 m_old_node_below = floatToInt(position - v3f(0.0f, BS / 2.0f, 0.0f), BS);
1063 m_old_node_below_type = nodemgr->get(map->getNode(m_old_node_below)).name;
1066 Check properties of the node on which the player is standing
1068 const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos()));
1070 // Determine if jumping is possible
1071 m_disable_jump = itemgroup_get(f.groups, "disable_jump");
1072 m_can_jump = touching_ground && !m_disable_jump;
1074 // Jump key pressed while jumping off from a bouncy block
1075 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1076 m_speed.Y >= -0.5f * BS) {
1077 float jumpspeed = movement_speed_jump * physics_override_jump;
1078 if (m_speed.Y > 1.0f) {
1079 // Reduce boost when speed already is high
1080 m_speed.Y += jumpspeed / (1.0f + (m_speed.Y / 16.0f));
1082 m_speed.Y += jumpspeed;
1089 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
1092 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1094 // Slip on slippery nodes
1095 const NodeDefManager *nodemgr = env->getGameDef()->ndef();
1096 Map *map = &env->getMap();
1097 const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos()));
1099 if (f.walkable && ! g_settings->getBool("antislip"))
1100 slippery = itemgroup_get(f.groups, "slippery");
1102 if (slippery >= 1) {
1103 if (speedH == v3f(0.0f))
1106 return core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);
1111 void LocalPlayer::handleAutojump(f32 dtime, Environment *env,
1112 const collisionMoveResult &result, const v3f &initial_position,
1113 const v3f &initial_speed, f32 pos_max_d)
1115 PlayerSettings &player_settings = getPlayerSettings();
1116 if (!player_settings.autojump)
1122 bool control_forward = keyPressed & (1 << 0);
1124 bool could_autojump =
1125 m_can_jump && !control.jump && !control.sneak && control_forward;
1127 if (!could_autojump)
1130 bool horizontal_collision = false;
1131 for (const auto &colinfo : result.collisions) {
1132 if (colinfo.type == COLLISION_NODE && colinfo.plane != 1) {
1133 horizontal_collision = true;
1134 break; // one is enough
1138 // must be running against something to trigger autojumping
1139 if (!horizontal_collision)
1142 // check for nodes above
1143 v3f headpos_min = m_position + m_collisionbox.MinEdge * 0.99f;
1144 v3f headpos_max = m_position + m_collisionbox.MaxEdge * 0.99f;
1145 headpos_min.Y = headpos_max.Y; // top face of collision box
1146 v3s16 ceilpos_min = floatToInt(headpos_min, BS) + v3s16(0, 1, 0);
1147 v3s16 ceilpos_max = floatToInt(headpos_max, BS) + v3s16(0, 1, 0);
1148 const NodeDefManager *ndef = env->getGameDef()->ndef();
1149 bool is_position_valid;
1150 for (s16 z = ceilpos_min.Z; z <= ceilpos_max.Z; ++z) {
1151 for (s16 x = ceilpos_min.X; x <= ceilpos_max.X; ++x) {
1152 MapNode n = env->getMap().getNode(v3s16(x, ceilpos_max.Y, z), &is_position_valid);
1154 if (!is_position_valid)
1155 break; // won't collide with the void outside
1156 if (n.getContent() == CONTENT_IGNORE)
1157 return; // players collide with ignore blocks -> same as walkable
1158 const ContentFeatures &f = ndef->get(n);
1160 return; // would bump head, don't jump
1164 float jump_height = 1.1f; // TODO: better than a magic number
1165 v3f jump_pos = initial_position + v3f(0.0f, jump_height * BS, 0.0f);
1166 v3f jump_speed = initial_speed;
1168 // try at peak of jump, zero step height
1169 collisionMoveResult jump_result = collisionMoveSimple(env, m_client, pos_max_d,
1170 m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed, v3f(0.0f), NULL, true, true);
1172 // see if we can get a little bit farther horizontally if we had
1174 v3f run_delta = m_position - initial_position;
1176 v3f jump_delta = jump_pos - initial_position;
1177 jump_delta.Y = 0.0f;
1178 if (jump_delta.getLengthSQ() > run_delta.getLengthSQ() * 1.01f) {
1180 m_autojump_time = 0.1f;