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;
282 if (!is_climbing && !free_move && g_settings->getBool("spider")) {
283 v3s16 spider_positions[4] = {
284 floatToInt(position + v3f(+1.0f, +0.0f, 0.0f) * BS, BS),
285 floatToInt(position + v3f(-1.0f, +0.0f, 0.0f) * BS, BS),
286 floatToInt(position + v3f( 0.0f, +0.0f, +1.0f) * BS, BS),
287 floatToInt(position + v3f( 0.0f, +0.0f, -1.0f) * BS, BS),
290 for (v3s16 sp : spider_positions) {
292 MapNode node = map->getNode(sp, &is_valid);
294 if (is_valid && nodemgr->get(node.getContent()).walkable) {
302 Collision uncertainty radius
303 Make it a bit larger than the maximum distance of movement
305 //f32 d = pos_max_d * 1.1;
306 // A fairly large value in here makes moving smoother
309 // This should always apply, otherwise there are glitches
310 sanity_check(d > pos_max_d);
312 // Player object property step height is multiplied by BS in
313 // /src/script/common/c_content.cpp and /src/content_sao.cpp
314 float player_stepheight = (m_cao == nullptr) ? 0.0f :
315 (touching_ground ? m_cao->getStepHeight() : (0.2f * BS));
318 const v3f initial_position = position;
319 const v3f initial_speed = m_speed;
321 collisionMoveResult result = collisionMoveSimple(env, m_client,
322 pos_max_d, m_collisionbox, player_stepheight, dtime,
323 &position, &m_speed, accel_f, NULL, true, true);
325 bool could_sneak = control.sneak && !free_move && !in_liquid &&
326 !is_climbing && physics_override_sneak;
328 // Add new collisions to the vector
329 if (collision_info && !free_move) {
330 v3f diff = intToFloat(m_standing_node, BS) - position;
331 f32 distance = diff.getLength();
332 // Force update each ClientEnvironment::step()
333 bool is_first = collision_info->empty();
335 for (const auto &colinfo : result.collisions) {
336 collision_info->push_back(colinfo);
338 if (colinfo.type != COLLISION_NODE ||
339 colinfo.axis != COLLISION_AXIS_Y ||
340 (could_sneak && m_sneak_node_exists))
343 diff = intToFloat(colinfo.node_p, BS) - position;
345 // Find nearest colliding node
346 f32 len = diff.getLength();
347 if (is_first || len < distance) {
348 m_standing_node = colinfo.node_p;
356 If the player's feet touch the topside of any node, this is
359 Player is allowed to jump when this is true.
361 bool touching_ground_was = touching_ground;
362 touching_ground = result.touching_ground || g_settings->getBool("airjump");
363 bool sneak_can_jump = false;
365 // Max. distance (X, Z) over border for sneaking determined by collision box
366 // * 0.49 to keep the center just barely on the node
367 v3f sneak_max = m_collisionbox.getExtent() * 0.49;
369 if (m_sneak_ladder_detected) {
370 // restore legacy behaviour (this makes the m_speed.Y hack necessary)
371 sneak_max = v3f(0.4f * BS, 0.0f, 0.4f * BS);
375 If sneaking, keep on top of last walked node and don't fall off
377 if (could_sneak && m_sneak_node_exists) {
378 const v3f sn_f = intToFloat(m_sneak_node, BS);
379 const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge;
380 const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge;
381 const v3f old_pos = position;
382 const v3f old_speed = m_speed;
383 f32 y_diff = bmax.Y - position.Y;
384 m_standing_node = m_sneak_node;
386 // (BS * 0.6f) is the basic stepheight while standing on ground
387 if (y_diff < BS * 0.6f) {
388 // Only center player when they're on the node
389 position.X = rangelim(position.X,
390 bmin.X - sneak_max.X, bmax.X + sneak_max.X);
391 position.Z = rangelim(position.Z,
392 bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z);
394 if (position.X != old_pos.X)
396 if (position.Z != old_pos.Z)
400 if (y_diff > 0 && m_speed.Y <= 0.0f &&
401 (physics_override_sneak_glitch || y_diff < BS * 0.6f)) {
402 // Move player to the maximal height when falling or when
403 // the ledge is climbed on the next step.
405 // Smoothen the movement (based on 'position.Y = bmax.Y')
406 position.Y += y_diff * dtime * 22.0f + BS * 0.01f;
407 position.Y = std::min(position.Y, bmax.Y);
411 // Allow jumping on node edges while sneaking
412 if (m_speed.Y == 0.0f || m_sneak_ladder_detected)
413 sneak_can_jump = true;
415 if (collision_info &&
416 m_speed.Y - old_speed.Y > BS) {
417 // Collide with sneak node, report fall damage
418 CollisionInfo sn_info;
419 sn_info.node_p = m_sneak_node;
420 sn_info.old_speed = old_speed;
421 sn_info.new_speed = m_speed;
422 collision_info->push_back(sn_info);
427 Find the next sneak node if necessary
429 bool new_sneak_node_exists = false;
432 new_sneak_node_exists = updateSneakNode(map, position, sneak_max);
435 Set new position but keep sneak node set
437 setPosition(position);
438 m_sneak_node_exists = new_sneak_node_exists;
444 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
445 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
447 // Set camera impact value to be used for view bobbing
448 camera_impact = getSpeed().Y * -1;
452 camera_barely_in_ceiling = false;
453 v3s16 camera_np = floatToInt(getEyePosition(), BS);
454 MapNode n = map->getNode(camera_np);
455 if (n.getContent() != CONTENT_IGNORE) {
456 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
457 camera_barely_in_ceiling = true;
462 Check properties of the node on which the player is standing
464 const ContentFeatures &f = nodemgr->get(map->getNode(m_standing_node));
465 const ContentFeatures &f1 = nodemgr->get(map->getNode(m_standing_node + v3s16(0, 1, 0)));
467 // Determine if jumping is possible
468 m_disable_jump = itemgroup_get(f.groups, "disable_jump") ||
469 itemgroup_get(f1.groups, "disable_jump");
470 m_can_jump = ((touching_ground && !is_climbing) || sneak_can_jump) && !m_disable_jump;
472 // Jump key pressed while jumping off from a bouncy block
473 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
474 m_speed.Y >= -0.5f * BS) {
475 float jumpspeed = movement_speed_jump * physics_override_jump;
476 if (m_speed.Y > 1.0f) {
477 // Reduce boost when speed already is high
478 m_speed.Y += jumpspeed / (1.0f + (m_speed.Y / 16.0f));
480 m_speed.Y += jumpspeed;
487 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
490 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
492 move(dtime, env, pos_max_d, NULL);
495 void LocalPlayer::applyControl(float dtime, Environment *env)
498 swimming_vertical = false;
499 swimming_pitch = false;
501 setPitch(control.pitch);
504 // Nullify speed and don't run positioning code if the player is attached
510 PlayerSettings &player_settings = getPlayerSettings();
512 // All vectors are relative to the player's yaw,
513 // (and pitch if pitch move mode enabled),
514 // and will be rotated at the end
515 v3f speedH, speedV; // Horizontal (X, Z) and Vertical (Y)
517 bool fly_allowed = m_client->checkLocalPrivilege("fly");
518 bool fast_allowed = m_client->checkLocalPrivilege("fast");
520 bool free_move = (fly_allowed && player_settings.free_move) || g_settings->getBool("freecam");
521 bool fast_move = (fast_allowed && player_settings.fast_move) || g_settings->getBool("freecam");
522 bool pitch_move = (free_move || in_liquid) && player_settings.pitch_move;
523 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
524 bool fast_climb = fast_move && control.aux1 && !player_settings.aux1_descends;
525 bool always_fly_fast = player_settings.always_fly_fast;
527 // Whether superspeed mode is used or not
528 bool superspeed = false;
530 if (always_fly_fast && free_move && fast_move)
533 // Old descend control
534 if (player_settings.aux1_descends) {
535 // If free movement and fast movement, always move fast
536 if (free_move && fast_move)
539 // Auxiliary button 1 (E)
542 // In free movement mode, aux1 descends
544 speedV.Y = -movement_speed_fast;
546 speedV.Y = -movement_speed_walk;
547 } else if (in_liquid || in_liquid_stable) {
548 speedV.Y = -movement_speed_walk;
549 swimming_vertical = true;
550 } else if (is_climbing) {
551 speedV.Y = -movement_speed_climb;
553 // If not free movement but fast is allowed, aux1 is
560 // New minecraft-like descend control
562 // Auxiliary button 1 (E)
565 // aux1 is "Turbo button"
573 // In free movement mode, sneak descends
574 if (fast_move && (control.aux1 || always_fly_fast))
575 speedV.Y = -movement_speed_fast;
577 speedV.Y = -movement_speed_walk;
578 } else if (in_liquid || in_liquid_stable) {
580 speedV.Y = -movement_speed_fast;
582 speedV.Y = -movement_speed_walk;
583 swimming_vertical = true;
584 } else if (is_climbing) {
586 speedV.Y = -movement_speed_fast;
588 speedV.Y = -movement_speed_climb;
593 speedH = v3f(sin(control.movement_direction), 0.0f, cos(control.movement_direction));
596 // release autojump after a given time
597 m_autojump_time -= dtime;
598 if (m_autojump_time <= 0.0f)
604 if (player_settings.aux1_descends || always_fly_fast) {
606 speedV.Y = movement_speed_fast;
608 speedV.Y = movement_speed_walk;
610 if (fast_move && control.aux1)
611 speedV.Y = movement_speed_fast;
613 speedV.Y = movement_speed_walk;
615 } else if (m_can_jump || g_settings->getBool("jetpack")) {
617 NOTE: The d value in move() affects jump height by
618 raising the height at which the jump speed is kept
619 at its starting value
621 v3f speedJ = getSpeed();
622 if (speedJ.Y >= -0.5f * BS || g_settings->getBool("jetpack")) {
623 speedJ.Y = movement_speed_jump * physics_override_jump;
625 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_JUMP));
627 } else if (in_liquid && !m_disable_jump) {
629 speedV.Y = movement_speed_fast;
631 speedV.Y = movement_speed_walk;
632 swimming_vertical = true;
633 } else if (is_climbing && !m_disable_jump) {
635 speedV.Y = movement_speed_fast;
637 speedV.Y = movement_speed_climb;
641 // The speed of the player (Y is ignored)
642 if (superspeed || (is_climbing && fast_climb) ||
643 ((in_liquid || in_liquid_stable) && fast_climb))
644 speedH = speedH.normalize() * movement_speed_fast;
645 else if (control.sneak && !free_move && !in_liquid && !in_liquid_stable && !g_settings->getBool("no_slow"))
646 speedH = speedH.normalize() * movement_speed_crouch;
648 speedH = speedH.normalize() * movement_speed_walk;
650 speedH *= control.movement_speed; /* Apply analog input */
652 // Acceleration increase
653 f32 incH = 0.0f; // Horizontal (X, Z)
654 f32 incV = 0.0f; // Vertical (Y)
655 if ((!touching_ground && !free_move && !is_climbing && !in_liquid) ||
656 (!free_move && m_can_jump && control.jump)) {
657 // Jumping and falling
658 if (superspeed || (fast_move && control.aux1))
659 incH = movement_acceleration_fast * BS * dtime;
661 incH = movement_acceleration_air * BS * dtime;
662 incV = 0.0f; // No vertical acceleration in air
663 } else if (superspeed || (is_climbing && fast_climb) ||
664 ((in_liquid || in_liquid_stable) && fast_climb)) {
665 incH = incV = movement_acceleration_fast * BS * dtime;
667 incH = incV = movement_acceleration_default * BS * dtime;
670 float slip_factor = 1.0f;
671 if (!free_move && !in_liquid && !in_liquid_stable)
672 slip_factor = getSlipFactor(env, speedH);
674 // Don't sink when swimming in pitch mode
675 if (pitch_move && in_liquid) {
676 v3f controlSpeed = speedH + speedV;
677 if (controlSpeed.getLength() > 0.01f)
678 swimming_pitch = true;
681 // Accelerate to target speed with maximum increment
682 accelerate((speedH + speedV) * physics_override_speed,
683 incH * physics_override_speed * slip_factor, incV * physics_override_speed,
687 v3s16 LocalPlayer::getStandingNodePos()
689 if (m_sneak_node_exists)
692 return m_standing_node;
695 v3s16 LocalPlayer::getFootstepNodePos()
697 // Emit swimming sound if the player is in liquid
698 if (in_liquid_stable)
699 return floatToInt(getPosition(), BS);
701 // BS * 0.05 below the player's feet ensures a 1/16th height
702 // nodebox is detected instead of the node below it.
704 return floatToInt(getPosition() - v3f(0.0f, BS * 0.05f, 0.0f), BS);
706 // A larger distance below is necessary for a footstep sound
707 // when landing after a jump or fall. BS * 0.5 ensures water
708 // sounds when swimming in 1 node deep water.
709 return floatToInt(getPosition() - v3f(0.0f, BS * 0.5f, 0.0f), BS);
712 v3s16 LocalPlayer::getLightPosition() const
714 return floatToInt(m_position + v3f(0.0f, BS * 1.5f, 0.0f), BS);
717 v3f LocalPlayer::getSendSpeed()
719 v3f speed = getLegitSpeed();
721 if (m_client->modsLoaded())
722 speed = m_client->getScript()->get_send_speed(speed);
727 v3f LocalPlayer::getEyeOffset() const
729 float eye_height = camera_barely_in_ceiling ? m_eye_height - 0.125f : m_eye_height;
730 return v3f(0.0f, BS * eye_height, 0.0f);
733 ClientActiveObject *LocalPlayer::getParent() const
735 return (m_cao && ! g_settings->getBool("entity_speed")) ? m_cao->getParent() : nullptr;
738 bool LocalPlayer::isDead() const
740 FATAL_ERROR_IF(!getCAO(), "LocalPlayer's CAO isn't initialized");
741 return !getCAO()->isImmortal() && hp == 0;
744 void LocalPlayer::tryReattach(int id)
746 PointedThing pointed(id, v3f(0, 0, 0), v3s16(0, 0, 0), 0);
747 m_client->interact(INTERACT_PLACE, pointed);
748 m_cao->m_waiting_for_reattach = 10;
751 bool LocalPlayer::isWaitingForReattach() const
753 return g_settings->getBool("entity_speed") && m_cao && ! m_cao->getParent() && m_cao->m_waiting_for_reattach > 0;
757 void LocalPlayer::accelerate(const v3f &target_speed, const f32 max_increase_H,
758 const f32 max_increase_V, const bool use_pitch)
760 const f32 yaw = getYaw();
761 const f32 pitch = getPitch();
762 v3f flat_speed = m_speed;
763 // Rotate speed vector by -yaw and -pitch to make it relative to the player's yaw and pitch
764 flat_speed.rotateXZBy(-yaw);
766 flat_speed.rotateYZBy(-pitch);
768 v3f d_wanted = target_speed - flat_speed;
771 // Then compare the horizontal and vertical components with the wanted speed
772 if (max_increase_H > 0.0f) {
773 v3f d_wanted_H = d_wanted * v3f(1.0f, 0.0f, 1.0f);
774 if (d_wanted_H.getLength() > max_increase_H)
775 d += d_wanted_H.normalize() * max_increase_H;
780 if (max_increase_V > 0.0f) {
781 f32 d_wanted_V = d_wanted.Y;
782 if (d_wanted_V > max_increase_V)
783 d.Y += max_increase_V;
784 else if (d_wanted_V < -max_increase_V)
785 d.Y -= max_increase_V;
790 // Finally rotate it again
798 // Temporary option for old move code
799 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
800 std::vector<CollisionInfo> *collision_info)
802 Map *map = &env->getMap();
803 const NodeDefManager *nodemgr = m_client->ndef();
805 v3f position = getPosition();
807 // Copy parent position if local player is attached
809 setPosition(m_cao->getPosition());
810 m_sneak_node_exists = false;
811 added_velocity = v3f(0.0f);
815 PlayerSettings &player_settings = getPlayerSettings();
817 // Skip collision detection if noclip mode is used
818 bool fly_allowed = m_client->checkLocalPrivilege("fly") || g_settings->getBool("freecam");
819 bool noclip = (m_client->checkLocalPrivilege("noclip") && player_settings.noclip) || g_settings->getBool("freecam");
820 bool free_move = (noclip && fly_allowed && player_settings.free_move) || g_settings->getBool("freecam");
822 position += m_speed * dtime;
823 setPosition(position);
825 touching_ground = false;
826 m_sneak_node_exists = false;
827 added_velocity = v3f(0.0f);
831 m_speed += added_velocity;
832 added_velocity = v3f(0.0f);
837 bool is_valid_position;
842 Check if player is in liquid (the oscillating value)
845 // If in liquid, the threshold of coming out is at higher y
846 pp = floatToInt(position + v3f(0.0f, BS * 0.1f, 0.0f), BS);
847 node = map->getNode(pp, &is_valid_position);
848 if (is_valid_position) {
849 in_liquid = nodemgr->get(node.getContent()).isLiquid();
850 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
855 // If not in liquid, the threshold of going in is at lower y
856 pp = floatToInt(position + v3f(0.0f, BS * 0.5f, 0.0f), BS);
857 node = map->getNode(pp, &is_valid_position);
858 if (is_valid_position) {
859 in_liquid = nodemgr->get(node.getContent()).isLiquid();
860 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
867 Check if player is in liquid (the stable value)
869 pp = floatToInt(position + v3f(0.0f), BS);
870 node = map->getNode(pp, &is_valid_position);
871 if (is_valid_position)
872 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
874 in_liquid_stable = false;
877 Check if player is climbing
879 pp = floatToInt(position + v3f(0.0f, 0.5f * BS, 0.0f), BS);
880 v3s16 pp2 = floatToInt(position + v3f(0.0f, -0.2f * BS, 0.0f), BS);
881 node = map->getNode(pp, &is_valid_position);
882 bool is_valid_position2;
883 MapNode node2 = map->getNode(pp2, &is_valid_position2);
885 if (!(is_valid_position && is_valid_position2))
888 is_climbing = (nodemgr->get(node.getContent()).climbable ||
889 nodemgr->get(node2.getContent()).climbable) && !free_move;
892 Collision uncertainty radius
893 Make it a bit larger than the maximum distance of movement
895 //f32 d = pos_max_d * 1.1;
896 // A fairly large value in here makes moving smoother
898 // This should always apply, otherwise there are glitches
899 sanity_check(d > pos_max_d);
900 // Maximum distance over border for sneaking
901 f32 sneak_max = BS * 0.4f;
904 If sneaking, keep in range from the last walked node and don't
907 if (control.sneak && m_sneak_node_exists &&
908 !(fly_allowed && player_settings.free_move) && !in_liquid &&
909 physics_override_sneak) {
910 f32 maxd = 0.5f * BS + sneak_max;
911 v3f lwn_f = intToFloat(m_sneak_node, BS);
912 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
913 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
916 // Move up if necessary
917 f32 new_y = (lwn_f.Y - 0.5f * BS) + m_sneak_node_bb_ymax;
918 if (position.Y < new_y)
921 Collision seems broken, since player is sinking when
922 sneaking over the edges of current sneaking_node.
923 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
925 if (m_speed.Y < 0.0f)
930 // TODO: This shouldn't be hardcoded but decided by the server
931 float player_stepheight = touching_ground ? (BS * 0.6f) : (BS * 0.2f);
934 const v3f initial_position = position;
935 const v3f initial_speed = m_speed;
937 collisionMoveResult result = collisionMoveSimple(env, m_client,
938 pos_max_d, m_collisionbox, player_stepheight, dtime,
939 &position, &m_speed, accel_f, NULL, true, true);
941 // Positition was slightly changed; update standing node pos
943 m_standing_node = floatToInt(m_position - v3f(0.0f, 0.1f * BS, 0.0f), BS);
945 m_standing_node = floatToInt(m_position, BS);
948 If the player's feet touch the topside of any node, this is
951 Player is allowed to jump when this is true.
953 bool touching_ground_was = touching_ground;
954 touching_ground = result.touching_ground;
956 //bool standing_on_unloaded = result.standing_on_unloaded;
959 Check the nodes under the player to see from which node the
960 player is sneaking from, if any. If the node from under
961 the player has been removed, the player falls.
963 f32 position_y_mod = 0.05f * BS;
964 if (m_sneak_node_bb_ymax > 0.0f)
965 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
966 v3s16 current_node = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS);
967 if (m_sneak_node_exists &&
968 nodemgr->get(map->getNode(m_old_node_below)).name == "air" &&
969 m_old_node_below_type != "air") {
970 // Old node appears to have been removed; that is,
971 // it wasn't air before but now it is
972 m_need_to_get_new_sneak_node = false;
973 m_sneak_node_exists = false;
974 } else if (nodemgr->get(map->getNode(current_node)).name != "air") {
975 // We are on something, so make sure to recalculate the sneak
977 m_need_to_get_new_sneak_node = true;
980 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
981 m_sneak_node_bb_ymax = 0.0f;
982 v3s16 pos_i_bottom = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS);
983 v2f player_p2df(position.X, position.Z);
984 f32 min_distance_f = 100000.0f * BS;
985 // If already seeking from some node, compare to it.
986 v3s16 new_sneak_node = m_sneak_node;
987 for (s16 x= -1; x <= 1; x++)
988 for (s16 z= -1; z <= 1; z++) {
989 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
990 v3f pf = intToFloat(p, BS);
991 v2f node_p2df(pf.X, pf.Z);
992 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
993 f32 max_axis_distance_f = MYMAX(
994 std::fabs(player_p2df.X - node_p2df.X),
995 std::fabs(player_p2df.Y - node_p2df.Y));
997 if (distance_f > min_distance_f ||
998 max_axis_distance_f > 0.5f * BS + sneak_max + 0.1f * BS)
1001 // The node to be sneaked on has to be walkable
1002 node = map->getNode(p, &is_valid_position);
1003 if (!is_valid_position || !nodemgr->get(node).walkable)
1005 // And the node above it has to be nonwalkable
1006 node = map->getNode(p + v3s16(0, 1, 0), &is_valid_position);
1007 if (!is_valid_position || nodemgr->get(node).walkable)
1009 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
1010 if (!physics_override_sneak_glitch) {
1011 node = map->getNode(p + v3s16(0, 2, 0), &is_valid_position);
1012 if (!is_valid_position || nodemgr->get(node).walkable)
1016 min_distance_f = distance_f;
1020 bool sneak_node_found = (min_distance_f < 100000.0f * BS * 0.9f);
1022 m_sneak_node = new_sneak_node;
1023 m_sneak_node_exists = sneak_node_found;
1025 if (sneak_node_found) {
1027 MapNode n = map->getNode(m_sneak_node);
1028 std::vector<aabb3f> nodeboxes;
1029 n.getCollisionBoxes(nodemgr, &nodeboxes);
1030 for (const auto &box : nodeboxes) {
1031 if (box.MaxEdge.Y > cb_max)
1032 cb_max = box.MaxEdge.Y;
1034 m_sneak_node_bb_ymax = cb_max;
1038 If sneaking, the player's collision box can be in air, so
1039 this has to be set explicitly
1041 if (sneak_node_found && control.sneak)
1042 touching_ground = true;
1046 Set new position but keep sneak node set
1048 bool sneak_node_exists = m_sneak_node_exists;
1049 setPosition(position);
1050 m_sneak_node_exists = sneak_node_exists;
1055 // Don't report if flying
1056 if (collision_info && !(player_settings.free_move && fly_allowed)) {
1057 for (const auto &info : result.collisions) {
1058 collision_info->push_back(info);
1062 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1063 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
1064 // Set camera impact value to be used for view bobbing
1065 camera_impact = getSpeed().Y * -1.0f;
1069 camera_barely_in_ceiling = false;
1070 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1071 MapNode n = map->getNode(camera_np);
1072 if (n.getContent() != CONTENT_IGNORE) {
1073 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1074 camera_barely_in_ceiling = true;
1079 Update the node last under the player
1081 m_old_node_below = floatToInt(position - v3f(0.0f, BS / 2.0f, 0.0f), BS);
1082 m_old_node_below_type = nodemgr->get(map->getNode(m_old_node_below)).name;
1085 Check properties of the node on which the player is standing
1087 const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos()));
1089 // Determine if jumping is possible
1090 m_disable_jump = itemgroup_get(f.groups, "disable_jump");
1091 m_can_jump = touching_ground && !m_disable_jump;
1093 // Jump key pressed while jumping off from a bouncy block
1094 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1095 m_speed.Y >= -0.5f * BS) {
1096 float jumpspeed = movement_speed_jump * physics_override_jump;
1097 if (m_speed.Y > 1.0f) {
1098 // Reduce boost when speed already is high
1099 m_speed.Y += jumpspeed / (1.0f + (m_speed.Y / 16.0f));
1101 m_speed.Y += jumpspeed;
1108 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
1111 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1113 // Slip on slippery nodes
1114 const NodeDefManager *nodemgr = env->getGameDef()->ndef();
1115 Map *map = &env->getMap();
1116 const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos()));
1118 if (f.walkable && ! g_settings->getBool("antislip"))
1119 slippery = itemgroup_get(f.groups, "slippery");
1121 if (slippery >= 1) {
1122 if (speedH == v3f(0.0f))
1125 return core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);
1130 void LocalPlayer::handleAutojump(f32 dtime, Environment *env,
1131 const collisionMoveResult &result, const v3f &initial_position,
1132 const v3f &initial_speed, f32 pos_max_d)
1134 PlayerSettings &player_settings = getPlayerSettings();
1135 if (!player_settings.autojump)
1141 bool control_forward = keyPressed & (1 << 0);
1143 bool could_autojump =
1144 m_can_jump && !control.jump && !control.sneak && control_forward;
1146 if (!could_autojump)
1149 bool horizontal_collision = false;
1150 for (const auto &colinfo : result.collisions) {
1151 if (colinfo.type == COLLISION_NODE && colinfo.plane != 1) {
1152 horizontal_collision = true;
1153 break; // one is enough
1157 // must be running against something to trigger autojumping
1158 if (!horizontal_collision)
1161 // check for nodes above
1162 v3f headpos_min = m_position + m_collisionbox.MinEdge * 0.99f;
1163 v3f headpos_max = m_position + m_collisionbox.MaxEdge * 0.99f;
1164 headpos_min.Y = headpos_max.Y; // top face of collision box
1165 v3s16 ceilpos_min = floatToInt(headpos_min, BS) + v3s16(0, 1, 0);
1166 v3s16 ceilpos_max = floatToInt(headpos_max, BS) + v3s16(0, 1, 0);
1167 const NodeDefManager *ndef = env->getGameDef()->ndef();
1168 bool is_position_valid;
1169 for (s16 z = ceilpos_min.Z; z <= ceilpos_max.Z; ++z) {
1170 for (s16 x = ceilpos_min.X; x <= ceilpos_max.X; ++x) {
1171 MapNode n = env->getMap().getNode(v3s16(x, ceilpos_max.Y, z), &is_position_valid);
1173 if (!is_position_valid)
1174 break; // won't collide with the void outside
1175 if (n.getContent() == CONTENT_IGNORE)
1176 return; // players collide with ignore blocks -> same as walkable
1177 const ContentFeatures &f = ndef->get(n);
1179 return; // would bump head, don't jump
1183 float jump_height = 1.1f; // TODO: better than a magic number
1184 v3f jump_pos = initial_position + v3f(0.0f, jump_height * BS, 0.0f);
1185 v3f jump_speed = initial_speed;
1187 // try at peak of jump, zero step height
1188 collisionMoveResult jump_result = collisionMoveSimple(env, m_client, pos_max_d,
1189 m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed, v3f(0.0f), NULL, true, true);
1191 // see if we can get a little bit farther horizontally if we had
1193 v3f run_delta = m_position - initial_position;
1195 v3f jump_delta = jump_pos - initial_position;
1196 jump_delta.Y = 0.0f;
1197 if (jump_delta.getLengthSQ() > run_delta.getLengthSQ() * 1.01f) {
1199 m_autojump_time = 0.1f;