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;
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;
575 speedH += v3f(0.0f, 0.0f, 1.0f);
578 speedH -= v3f(0.0f, 0.0f, 1.0f);
580 if (!control.up && !control.down)
581 speedH -= v3f(0.0f, 0.0f, 1.0f) * (control.forw_move_joystick_axis / 32767.f);
584 speedH += v3f(-1.0f, 0.0f, 0.0f);
587 speedH += v3f(1.0f, 0.0f, 0.0f);
589 if (!control.left && !control.right)
590 speedH += v3f(1.0f, 0.0f, 0.0f) * (control.sidew_move_joystick_axis / 32767.f);
593 // release autojump after a given time
594 m_autojump_time -= dtime;
595 if (m_autojump_time <= 0.0f)
601 if (player_settings.aux1_descends || always_fly_fast) {
603 speedV.Y = movement_speed_fast;
605 speedV.Y = movement_speed_walk;
607 if (fast_move && control.aux1)
608 speedV.Y = movement_speed_fast;
610 speedV.Y = movement_speed_walk;
612 } else if (m_can_jump || g_settings->getBool("jetpack")) {
614 NOTE: The d value in move() affects jump height by
615 raising the height at which the jump speed is kept
616 at its starting value
618 v3f speedJ = getSpeed();
619 if (speedJ.Y >= -0.5f * BS || g_settings->getBool("jetpack")) {
620 speedJ.Y = movement_speed_jump * physics_override_jump;
622 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_JUMP));
624 } else if (in_liquid && !m_disable_jump) {
626 speedV.Y = movement_speed_fast;
628 speedV.Y = movement_speed_walk;
629 swimming_vertical = true;
630 } else if (is_climbing && !m_disable_jump) {
632 speedV.Y = movement_speed_fast;
634 speedV.Y = movement_speed_climb;
638 // The speed of the player (Y is ignored)
639 if (superspeed || (is_climbing && fast_climb) ||
640 ((in_liquid || in_liquid_stable) && fast_climb))
641 speedH = speedH.normalize() * movement_speed_fast;
642 else if (control.sneak && !free_move && !in_liquid && !in_liquid_stable && !g_settings->getBool("no_slow"))
643 speedH = speedH.normalize() * movement_speed_crouch;
645 speedH = speedH.normalize() * movement_speed_walk;
647 // Acceleration increase
648 f32 incH = 0.0f; // Horizontal (X, Z)
649 f32 incV = 0.0f; // Vertical (Y)
650 if ((!touching_ground && !free_move && !is_climbing && !in_liquid) ||
651 (!free_move && m_can_jump && control.jump)) {
652 // Jumping and falling
653 if (superspeed || (fast_move && control.aux1))
654 incH = movement_acceleration_fast * BS * dtime;
656 incH = movement_acceleration_air * BS * dtime;
657 incV = 0.0f; // No vertical acceleration in air
658 } else if (superspeed || (is_climbing && fast_climb) ||
659 ((in_liquid || in_liquid_stable) && fast_climb)) {
660 incH = incV = movement_acceleration_fast * BS * dtime;
662 incH = incV = movement_acceleration_default * BS * dtime;
665 float slip_factor = 1.0f;
666 if (!free_move && !in_liquid && !in_liquid_stable)
667 slip_factor = getSlipFactor(env, speedH);
669 // Don't sink when swimming in pitch mode
670 if (pitch_move && in_liquid) {
671 v3f controlSpeed = speedH + speedV;
672 if (controlSpeed.getLength() > 0.01f)
673 swimming_pitch = true;
676 // Accelerate to target speed with maximum increment
677 accelerate((speedH + speedV) * physics_override_speed,
678 incH * physics_override_speed * slip_factor, incV * physics_override_speed,
682 v3s16 LocalPlayer::getStandingNodePos()
684 if (m_sneak_node_exists)
687 return m_standing_node;
690 v3s16 LocalPlayer::getFootstepNodePos()
692 // Emit swimming sound if the player is in liquid
693 if (in_liquid_stable)
694 return floatToInt(getPosition(), BS);
696 // BS * 0.05 below the player's feet ensures a 1/16th height
697 // nodebox is detected instead of the node below it.
699 return floatToInt(getPosition() - v3f(0.0f, BS * 0.05f, 0.0f), BS);
701 // A larger distance below is necessary for a footstep sound
702 // when landing after a jump or fall. BS * 0.5 ensures water
703 // sounds when swimming in 1 node deep water.
704 return floatToInt(getPosition() - v3f(0.0f, BS * 0.5f, 0.0f), BS);
707 v3s16 LocalPlayer::getLightPosition() const
709 return floatToInt(m_position + v3f(0.0f, BS * 1.5f, 0.0f), BS);
712 v3f LocalPlayer::getEyeOffset() const
714 float eye_height = camera_barely_in_ceiling ? m_eye_height - 0.125f : m_eye_height;
715 return v3f(0.0f, BS * eye_height, 0.0f);
718 ClientActiveObject *LocalPlayer::getParent() const
720 return (m_cao && ! g_settings->getBool("entity_speed")) ? m_cao->getParent() : nullptr;
723 bool LocalPlayer::isDead() const
725 FATAL_ERROR_IF(!getCAO(), "LocalPlayer's CAO isn't initialized");
726 return !getCAO()->isImmortal() && hp == 0;
729 void LocalPlayer::tryReattach(int id)
731 PointedThing pointed(id, v3f(0, 0, 0), v3s16(0, 0, 0), 0);
732 m_client->interact(INTERACT_PLACE, pointed);
733 m_cao->m_waiting_for_reattach = 10;
736 bool LocalPlayer::isWaitingForReattach() const
738 return g_settings->getBool("entity_speed") && m_cao && ! m_cao->getParent() && m_cao->m_waiting_for_reattach > 0;
742 void LocalPlayer::accelerate(const v3f &target_speed, const f32 max_increase_H,
743 const f32 max_increase_V, const bool use_pitch)
745 const f32 yaw = getYaw();
746 const f32 pitch = getPitch();
747 v3f flat_speed = m_speed;
748 // Rotate speed vector by -yaw and -pitch to make it relative to the player's yaw and pitch
749 flat_speed.rotateXZBy(-yaw);
751 flat_speed.rotateYZBy(-pitch);
753 v3f d_wanted = target_speed - flat_speed;
756 // Then compare the horizontal and vertical components with the wanted speed
757 if (max_increase_H > 0.0f) {
758 v3f d_wanted_H = d_wanted * v3f(1.0f, 0.0f, 1.0f);
759 if (d_wanted_H.getLength() > max_increase_H)
760 d += d_wanted_H.normalize() * max_increase_H;
765 if (max_increase_V > 0.0f) {
766 f32 d_wanted_V = d_wanted.Y;
767 if (d_wanted_V > max_increase_V)
768 d.Y += max_increase_V;
769 else if (d_wanted_V < -max_increase_V)
770 d.Y -= max_increase_V;
775 // Finally rotate it again
783 // Temporary option for old move code
784 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
785 std::vector<CollisionInfo> *collision_info)
787 Map *map = &env->getMap();
788 const NodeDefManager *nodemgr = m_client->ndef();
790 v3f position = getPosition();
792 // Copy parent position if local player is attached
794 setPosition(m_cao->getPosition());
795 m_sneak_node_exists = false;
796 added_velocity = v3f(0.0f);
800 PlayerSettings &player_settings = getPlayerSettings();
802 // Skip collision detection if noclip mode is used
803 bool fly_allowed = m_client->checkLocalPrivilege("fly") || g_settings->getBool("freecam");
804 bool noclip = (m_client->checkLocalPrivilege("noclip") && player_settings.noclip) || g_settings->getBool("freecam");
805 bool free_move = (noclip && fly_allowed && player_settings.free_move) || g_settings->getBool("freecam");
807 position += m_speed * dtime;
808 setPosition(position);
810 touching_ground = false;
811 m_sneak_node_exists = false;
812 added_velocity = v3f(0.0f);
816 m_speed += added_velocity;
817 added_velocity = v3f(0.0f);
822 bool is_valid_position;
827 Check if player is in liquid (the oscillating value)
830 // If in liquid, the threshold of coming out is at higher y
831 pp = floatToInt(position + v3f(0.0f, BS * 0.1f, 0.0f), BS);
832 node = map->getNode(pp, &is_valid_position);
833 if (is_valid_position) {
834 in_liquid = nodemgr->get(node.getContent()).isLiquid();
835 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
840 // If not in liquid, the threshold of going in is at lower y
841 pp = floatToInt(position + v3f(0.0f, BS * 0.5f, 0.0f), BS);
842 node = map->getNode(pp, &is_valid_position);
843 if (is_valid_position) {
844 in_liquid = nodemgr->get(node.getContent()).isLiquid();
845 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
852 Check if player is in liquid (the stable value)
854 pp = floatToInt(position + v3f(0.0f), BS);
855 node = map->getNode(pp, &is_valid_position);
856 if (is_valid_position)
857 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
859 in_liquid_stable = false;
862 Check if player is climbing
864 pp = floatToInt(position + v3f(0.0f, 0.5f * BS, 0.0f), BS);
865 v3s16 pp2 = floatToInt(position + v3f(0.0f, -0.2f * BS, 0.0f), BS);
866 node = map->getNode(pp, &is_valid_position);
867 bool is_valid_position2;
868 MapNode node2 = map->getNode(pp2, &is_valid_position2);
870 if (!(is_valid_position && is_valid_position2))
873 is_climbing = (nodemgr->get(node.getContent()).climbable ||
874 nodemgr->get(node2.getContent()).climbable) && !free_move;
877 Collision uncertainty radius
878 Make it a bit larger than the maximum distance of movement
880 //f32 d = pos_max_d * 1.1;
881 // A fairly large value in here makes moving smoother
883 // This should always apply, otherwise there are glitches
884 sanity_check(d > pos_max_d);
885 // Maximum distance over border for sneaking
886 f32 sneak_max = BS * 0.4f;
889 If sneaking, keep in range from the last walked node and don't
892 if (control.sneak && m_sneak_node_exists &&
893 !(fly_allowed && player_settings.free_move) && !in_liquid &&
894 physics_override_sneak) {
895 f32 maxd = 0.5f * BS + sneak_max;
896 v3f lwn_f = intToFloat(m_sneak_node, BS);
897 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
898 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
901 // Move up if necessary
902 f32 new_y = (lwn_f.Y - 0.5f * BS) + m_sneak_node_bb_ymax;
903 if (position.Y < new_y)
906 Collision seems broken, since player is sinking when
907 sneaking over the edges of current sneaking_node.
908 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
910 if (m_speed.Y < 0.0f)
915 // TODO: This shouldn't be hardcoded but decided by the server
916 float player_stepheight = touching_ground ? (BS * 0.6f) : (BS * 0.2f);
919 const v3f initial_position = position;
920 const v3f initial_speed = m_speed;
922 collisionMoveResult result = collisionMoveSimple(env, m_client,
923 pos_max_d, m_collisionbox, player_stepheight, dtime,
924 &position, &m_speed, accel_f, NULL, true, true);
926 // Positition was slightly changed; update standing node pos
928 m_standing_node = floatToInt(m_position - v3f(0.0f, 0.1f * BS, 0.0f), BS);
930 m_standing_node = floatToInt(m_position, BS);
933 If the player's feet touch the topside of any node, this is
936 Player is allowed to jump when this is true.
938 bool touching_ground_was = touching_ground;
939 touching_ground = result.touching_ground;
941 //bool standing_on_unloaded = result.standing_on_unloaded;
944 Check the nodes under the player to see from which node the
945 player is sneaking from, if any. If the node from under
946 the player has been removed, the player falls.
948 f32 position_y_mod = 0.05f * BS;
949 if (m_sneak_node_bb_ymax > 0.0f)
950 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
951 v3s16 current_node = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS);
952 if (m_sneak_node_exists &&
953 nodemgr->get(map->getNode(m_old_node_below)).name == "air" &&
954 m_old_node_below_type != "air") {
955 // Old node appears to have been removed; that is,
956 // it wasn't air before but now it is
957 m_need_to_get_new_sneak_node = false;
958 m_sneak_node_exists = false;
959 } else if (nodemgr->get(map->getNode(current_node)).name != "air") {
960 // We are on something, so make sure to recalculate the sneak
962 m_need_to_get_new_sneak_node = true;
965 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
966 m_sneak_node_bb_ymax = 0.0f;
967 v3s16 pos_i_bottom = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS);
968 v2f player_p2df(position.X, position.Z);
969 f32 min_distance_f = 100000.0f * BS;
970 // If already seeking from some node, compare to it.
971 v3s16 new_sneak_node = m_sneak_node;
972 for (s16 x= -1; x <= 1; x++)
973 for (s16 z= -1; z <= 1; z++) {
974 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
975 v3f pf = intToFloat(p, BS);
976 v2f node_p2df(pf.X, pf.Z);
977 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
978 f32 max_axis_distance_f = MYMAX(
979 std::fabs(player_p2df.X - node_p2df.X),
980 std::fabs(player_p2df.Y - node_p2df.Y));
982 if (distance_f > min_distance_f ||
983 max_axis_distance_f > 0.5f * BS + sneak_max + 0.1f * BS)
986 // The node to be sneaked on has to be walkable
987 node = map->getNode(p, &is_valid_position);
988 if (!is_valid_position || !nodemgr->get(node).walkable)
990 // And the node above it has to be nonwalkable
991 node = map->getNode(p + v3s16(0, 1, 0), &is_valid_position);
992 if (!is_valid_position || nodemgr->get(node).walkable)
994 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
995 if (!physics_override_sneak_glitch) {
996 node = map->getNode(p + v3s16(0, 2, 0), &is_valid_position);
997 if (!is_valid_position || nodemgr->get(node).walkable)
1001 min_distance_f = distance_f;
1005 bool sneak_node_found = (min_distance_f < 100000.0f * BS * 0.9f);
1007 m_sneak_node = new_sneak_node;
1008 m_sneak_node_exists = sneak_node_found;
1010 if (sneak_node_found) {
1012 MapNode n = map->getNode(m_sneak_node);
1013 std::vector<aabb3f> nodeboxes;
1014 n.getCollisionBoxes(nodemgr, &nodeboxes);
1015 for (const auto &box : nodeboxes) {
1016 if (box.MaxEdge.Y > cb_max)
1017 cb_max = box.MaxEdge.Y;
1019 m_sneak_node_bb_ymax = cb_max;
1023 If sneaking, the player's collision box can be in air, so
1024 this has to be set explicitly
1026 if (sneak_node_found && control.sneak)
1027 touching_ground = true;
1031 Set new position but keep sneak node set
1033 bool sneak_node_exists = m_sneak_node_exists;
1034 setPosition(position);
1035 m_sneak_node_exists = sneak_node_exists;
1040 // Don't report if flying
1041 if (collision_info && !(player_settings.free_move && fly_allowed)) {
1042 for (const auto &info : result.collisions) {
1043 collision_info->push_back(info);
1047 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1048 m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND));
1049 // Set camera impact value to be used for view bobbing
1050 camera_impact = getSpeed().Y * -1.0f;
1054 camera_barely_in_ceiling = false;
1055 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1056 MapNode n = map->getNode(camera_np);
1057 if (n.getContent() != CONTENT_IGNORE) {
1058 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1059 camera_barely_in_ceiling = true;
1064 Update the node last under the player
1066 m_old_node_below = floatToInt(position - v3f(0.0f, BS / 2.0f, 0.0f), BS);
1067 m_old_node_below_type = nodemgr->get(map->getNode(m_old_node_below)).name;
1070 Check properties of the node on which the player is standing
1072 const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos()));
1074 // Determine if jumping is possible
1075 m_disable_jump = itemgroup_get(f.groups, "disable_jump");
1076 m_can_jump = touching_ground && !m_disable_jump;
1078 // Jump key pressed while jumping off from a bouncy block
1079 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1080 m_speed.Y >= -0.5f * BS) {
1081 float jumpspeed = movement_speed_jump * physics_override_jump;
1082 if (m_speed.Y > 1.0f) {
1083 // Reduce boost when speed already is high
1084 m_speed.Y += jumpspeed / (1.0f + (m_speed.Y / 16.0f));
1086 m_speed.Y += jumpspeed;
1093 handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
1096 float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
1098 // Slip on slippery nodes
1099 const NodeDefManager *nodemgr = env->getGameDef()->ndef();
1100 Map *map = &env->getMap();
1101 const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos()));
1103 if (f.walkable && ! g_settings->getBool("antislip"))
1104 slippery = itemgroup_get(f.groups, "slippery");
1106 if (slippery >= 1) {
1107 if (speedH == v3f(0.0f))
1110 return core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f);
1115 void LocalPlayer::handleAutojump(f32 dtime, Environment *env,
1116 const collisionMoveResult &result, const v3f &initial_position,
1117 const v3f &initial_speed, f32 pos_max_d)
1119 PlayerSettings &player_settings = getPlayerSettings();
1120 if (!player_settings.autojump)
1126 bool control_forward = control.up ||
1127 (!control.up && !control.down &&
1128 control.forw_move_joystick_axis < -0.05f);
1130 bool could_autojump =
1131 m_can_jump && !control.jump && !control.sneak && control_forward;
1133 if (!could_autojump)
1136 bool horizontal_collision = false;
1137 for (const auto &colinfo : result.collisions) {
1138 if (colinfo.type == COLLISION_NODE && colinfo.plane != 1) {
1139 horizontal_collision = true;
1140 break; // one is enough
1144 // must be running against something to trigger autojumping
1145 if (!horizontal_collision)
1148 // check for nodes above
1149 v3f headpos_min = m_position + m_collisionbox.MinEdge * 0.99f;
1150 v3f headpos_max = m_position + m_collisionbox.MaxEdge * 0.99f;
1151 headpos_min.Y = headpos_max.Y; // top face of collision box
1152 v3s16 ceilpos_min = floatToInt(headpos_min, BS) + v3s16(0, 1, 0);
1153 v3s16 ceilpos_max = floatToInt(headpos_max, BS) + v3s16(0, 1, 0);
1154 const NodeDefManager *ndef = env->getGameDef()->ndef();
1155 bool is_position_valid;
1156 for (s16 z = ceilpos_min.Z; z <= ceilpos_max.Z; ++z) {
1157 for (s16 x = ceilpos_min.X; x <= ceilpos_max.X; ++x) {
1158 MapNode n = env->getMap().getNode(v3s16(x, ceilpos_max.Y, z), &is_position_valid);
1160 if (!is_position_valid)
1161 break; // won't collide with the void outside
1162 if (n.getContent() == CONTENT_IGNORE)
1163 return; // players collide with ignore blocks -> same as walkable
1164 const ContentFeatures &f = ndef->get(n);
1166 return; // would bump head, don't jump
1170 float jump_height = 1.1f; // TODO: better than a magic number
1171 v3f jump_pos = initial_position + v3f(0.0f, jump_height * BS, 0.0f);
1172 v3f jump_speed = initial_speed;
1174 // try at peak of jump, zero step height
1175 collisionMoveResult jump_result = collisionMoveSimple(env, m_client, pos_max_d,
1176 m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed, v3f(0.0f), NULL, true, true);
1178 // see if we can get a little bit farther horizontally if we had
1180 v3f run_delta = m_position - initial_position;
1182 v3f jump_delta = jump_pos - initial_position;
1183 jump_delta.Y = 0.0f;
1184 if (jump_delta.getLengthSQ() > run_delta.getLengthSQ() * 1.01f) {
1186 m_autojump_time = 0.1f;