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"
34 LocalPlayer::LocalPlayer(Client *client, const char *name):
35 Player(name, client->idef()),
38 got_teleported(false),
40 touching_ground(false),
42 in_liquid_stable(false),
45 swimming_vertical(false),
46 // Movement overrides are multipliers and must be 1 by default
47 physics_override_speed(1.0f),
48 physics_override_jump(1.0f),
49 physics_override_gravity(1.0f),
50 physics_override_sneak(true),
51 physics_override_sneak_glitch(false),
52 physics_override_new_move(true), // Temporary option for old move code
53 overridePosition(v3f(0,0,0)),
54 last_position(v3f(0,0,0)),
55 last_speed(v3f(0,0,0)),
62 last_animation(NO_ANIM),
64 hotbar_selected_image(""),
65 light_color(255,255,255,255),
66 hurt_tilt_timer(0.0f),
67 hurt_tilt_strength(0.0f),
69 m_sneak_node(32767,32767,32767),
70 m_sneak_node_bb_ymax(0), // To support temporary option for old move code
71 m_sneak_node_bb_top(0,0,0,0,0,0),
72 m_sneak_node_exists(false),
73 m_need_to_get_new_sneak_node(true),
74 m_sneak_ladder_detected(false),
75 m_ledge_detected(false),
76 m_old_node_below(32767,32767,32767),
77 m_old_node_below_type("air"),
79 m_breath(PLAYER_MAX_BREATH),
82 camera_barely_in_ceiling(false),
83 m_collisionbox(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30),
87 // Initialize hp to 0, so that no hearts will be shown if server
88 // doesn't support health points
90 eye_offset_first = v3f(0,0,0);
91 eye_offset_third = v3f(0,0,0);
94 LocalPlayer::~LocalPlayer()
98 static aabb3f getTopBoundingBox(const std::vector<aabb3f> &nodeboxes)
101 b_max.reset(-BS, -BS, -BS);
102 for (std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
103 it != nodeboxes.end(); ++it) {
105 if (box.MaxEdge.Y > b_max.MaxEdge.Y)
107 else if (box.MaxEdge.Y == b_max.MaxEdge.Y)
108 b_max.addInternalBox(box);
110 return aabb3f(v3f(b_max.MinEdge.X, b_max.MaxEdge.Y, b_max.MinEdge.Z), b_max.MaxEdge);
113 #define GETNODE(map, p3, v2, y, valid) \
114 (map)->getNodeNoEx((p3) + v3s16((v2).X, y, (v2).Y), valid)
116 // pos is the node the player is standing inside(!)
117 static bool detectSneakLadder(Map *map, INodeDefManager *nodemgr, v3s16 pos)
119 // Detects a structure known as "sneak ladder" or "sneak elevator"
120 // that relies on bugs to provide a fast means of vertical transportation,
121 // the bugs have since been fixed but this function remains to keep it working.
122 // NOTE: This is just entirely a huge hack and causes way too many problems.
123 bool is_valid_position;
125 // X/Z vectors for 4 neighboring nodes
126 static const v2s16 vecs[] = { v2s16(-1, 0), v2s16(1, 0), v2s16(0, -1), v2s16(0, 1) };
128 for (u16 i = 0; i < ARRLEN(vecs); i++) {
129 const v2s16 vec = vecs[i];
131 // walkability of bottom & top node should differ
132 node = GETNODE(map, pos, vec, 0, &is_valid_position);
133 if (!is_valid_position)
135 bool w = nodemgr->get(node).walkable;
136 node = GETNODE(map, pos, vec, 1, &is_valid_position);
137 if (!is_valid_position || w == nodemgr->get(node).walkable)
140 // check one more node above OR below with corresponding walkability
141 node = GETNODE(map, pos, vec, -1, &is_valid_position);
142 bool ok = is_valid_position && w != nodemgr->get(node).walkable;
144 node = GETNODE(map, pos, vec, 2, &is_valid_position);
145 ok = is_valid_position && w == nodemgr->get(node).walkable;
155 static bool detectLedge(Map *map, INodeDefManager *nodemgr, v3s16 pos)
157 bool is_valid_position;
159 // X/Z vectors for 4 neighboring nodes
160 static const v2s16 vecs[] = {v2s16(-1, 0), v2s16(1, 0), v2s16(0, -1), v2s16(0, 1)};
162 for (u16 i = 0; i < ARRLEN(vecs); i++) {
163 const v2s16 vec = vecs[i];
165 node = GETNODE(map, pos, vec, 1, &is_valid_position);
166 if (is_valid_position && nodemgr->get(node).walkable) {
168 node = GETNODE(map, pos, vec, 2, &is_valid_position);
169 if (is_valid_position && !nodemgr->get(node).walkable)
170 // Space above ledge exists
180 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
181 std::vector<CollisionInfo> *collision_info)
183 // Temporary option for old move code
184 if (!physics_override_new_move) {
185 old_move(dtime, env, pos_max_d, collision_info);
189 Map *map = &env->getMap();
190 INodeDefManager *nodemgr = m_client->ndef();
192 v3f position = getPosition();
194 // Copy parent position if local player is attached
197 setPosition(overridePosition);
198 m_sneak_node_exists = false;
202 // Skip collision detection if noclip mode is used
203 bool fly_allowed = m_client->checkLocalPrivilege("fly");
204 bool noclip = m_client->checkLocalPrivilege("noclip") &&
205 g_settings->getBool("noclip");
206 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
208 position += m_speed * dtime;
209 setPosition(position);
210 m_sneak_node_exists = false;
218 bool is_valid_position;
223 Check if player is in liquid (the oscillating value)
226 // If in liquid, the threshold of coming out is at higher y
229 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
230 node = map->getNodeNoEx(pp, &is_valid_position);
231 if (is_valid_position) {
232 in_liquid = nodemgr->get(node.getContent()).isLiquid();
233 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
238 // If not in liquid, the threshold of going in is at lower y
241 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
242 node = map->getNodeNoEx(pp, &is_valid_position);
243 if (is_valid_position) {
244 in_liquid = nodemgr->get(node.getContent()).isLiquid();
245 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
253 Check if player is in liquid (the stable value)
255 pp = floatToInt(position + v3f(0,0,0), BS);
256 node = map->getNodeNoEx(pp, &is_valid_position);
257 if (is_valid_position) {
258 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
260 in_liquid_stable = false;
264 Check if player is climbing
268 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
269 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
270 node = map->getNodeNoEx(pp, &is_valid_position);
271 bool is_valid_position2;
272 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
274 if (!(is_valid_position && is_valid_position2)) {
277 is_climbing = (nodemgr->get(node.getContent()).climbable
278 || 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 // Max. distance (X, Z) over border for sneaking determined by collision box
294 // * 0.49 to keep the center just barely on the node
295 v3f sneak_max = m_collisionbox.getExtent() * 0.49;
296 if (m_sneak_ladder_detected) {
297 // restore legacy behaviour (this makes the m_speed.Y hack necessary)
298 sneak_max = v3f(0.4 * BS, 0, 0.4 * BS);
302 If sneaking, keep in range from the last walked node and don't
305 if (control.sneak && m_sneak_node_exists &&
306 !(fly_allowed && g_settings->getBool("free_move")) &&
307 !in_liquid && !is_climbing &&
308 physics_override_sneak && !got_teleported) {
309 v3f sn_f = intToFloat(m_sneak_node, BS);
310 const v3f bmin = m_sneak_node_bb_top.MinEdge;
311 const v3f bmax = m_sneak_node_bb_top.MaxEdge;
313 position.X = rangelim(position.X,
314 sn_f.X+bmin.X - sneak_max.X, sn_f.X+bmax.X + sneak_max.X);
315 position.Z = rangelim(position.Z,
316 sn_f.Z+bmin.Z - sneak_max.Z, sn_f.Z+bmax.Z + sneak_max.Z);
318 // Because we keep the player collision box on the node, limiting
319 // position.Y is not necessary but useful to prevent players from
320 // being inside a node if sneaking on e.g. the lower part of a stair
321 if (!m_sneak_ladder_detected) {
322 position.Y = MYMAX(position.Y, sn_f.Y+bmax.Y);
324 // legacy behaviour that sometimes causes some weird slow sinking
325 m_speed.Y = MYMAX(m_speed.Y, 0);
330 got_teleported = false;
332 // TODO: this shouldn't be hardcoded but transmitted from server
333 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
336 player_stepheight += (0.6 * BS);
339 v3f accel_f = v3f(0,0,0);
341 collisionMoveResult result = collisionMoveSimple(env, m_client,
342 pos_max_d, m_collisionbox, player_stepheight, dtime,
343 &position, &m_speed, accel_f);
346 If the player's feet touch the topside of any node, this is
349 Player is allowed to jump when this is true.
351 bool touching_ground_was = touching_ground;
352 touching_ground = result.touching_ground;
354 // We want the top of the sneak node to be below the players feet
356 if (m_sneak_node_exists)
357 position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - 0.05 * BS;
359 position_y_mod = (0.5 - 0.05) * BS;
360 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
362 Check the nodes under the player to see from which node the
363 player is sneaking from, if any. If the node from under
364 the player has been removed, the player falls.
366 if (m_sneak_node_exists &&
367 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
368 m_old_node_below_type != "air") {
369 // Old node appears to have been removed; that is,
370 // it wasn't air before but now it is
371 m_need_to_get_new_sneak_node = false;
372 m_sneak_node_exists = false;
373 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
374 // We are on something, so make sure to recalculate the sneak
376 m_need_to_get_new_sneak_node = true;
379 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
380 v2f player_p2df(position.X, position.Z);
381 f32 min_distance_f = 100000.0 * BS;
382 v3s16 new_sneak_node = m_sneak_node;
383 for(s16 x=-1; x<=1; x++)
384 for(s16 z=-1; z<=1; z++)
386 v3s16 p = current_node + v3s16(x,0,z);
387 v3f pf = intToFloat(p, BS);
388 v2f node_p2df(pf.X, pf.Z);
389 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
391 if (distance_f > min_distance_f ||
392 fabs(player_p2df.X-node_p2df.X) > (.5+.1)*BS + sneak_max.X ||
393 fabs(player_p2df.Y-node_p2df.Y) > (.5+.1)*BS + sneak_max.Z)
397 // The node to be sneaked on has to be walkable
398 node = map->getNodeNoEx(p, &is_valid_position);
399 if (!is_valid_position || !nodemgr->get(node).walkable)
401 // And the node(s) above have to be nonwalkable
403 if (!physics_override_sneak_glitch) {
405 (m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
407 for (u16 y = 1; y <= height; y++) {
408 node = map->getNodeNoEx(p + v3s16(0,y,0), &is_valid_position);
409 if (!is_valid_position || nodemgr->get(node).walkable) {
415 // legacy behaviour: check just one node
416 node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
417 ok = is_valid_position && !nodemgr->get(node).walkable;
422 min_distance_f = distance_f;
426 bool sneak_node_found = (min_distance_f < 100000.0 * BS);
427 m_sneak_node = new_sneak_node;
428 m_sneak_node_exists = sneak_node_found;
430 if (sneak_node_found) {
431 // Update saved top bounding box of sneak node
432 MapNode n = map->getNodeNoEx(m_sneak_node);
433 std::vector<aabb3f> nodeboxes;
434 n.getCollisionBoxes(nodemgr, &nodeboxes);
435 m_sneak_node_bb_top = getTopBoundingBox(nodeboxes);
437 m_sneak_ladder_detected = physics_override_sneak_glitch &&
438 detectSneakLadder(map, nodemgr, floatToInt(position, BS));
440 m_sneak_ladder_detected = false;
445 If 'sneak glitch' enabled detect ledge for old sneak-jump
446 behaviour of climbing onto a ledge 2 nodes up.
448 if (physics_override_sneak_glitch && control.sneak && control.jump)
449 m_ledge_detected = detectLedge(map, nodemgr, floatToInt(position, BS));
454 setPosition(position);
460 // Dont report if flying
461 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
462 for(size_t i=0; i<result.collisions.size(); i++) {
463 const CollisionInfo &info = result.collisions[i];
464 collision_info->push_back(info);
468 if(!result.standing_on_object && !touching_ground_was && touching_ground) {
469 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
470 m_client->event()->put(e);
472 // Set camera impact value to be used for view bobbing
473 camera_impact = getSpeed().Y * -1;
477 camera_barely_in_ceiling = false;
478 v3s16 camera_np = floatToInt(getEyePosition(), BS);
479 MapNode n = map->getNodeNoEx(camera_np);
480 if(n.getContent() != CONTENT_IGNORE){
481 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
482 camera_barely_in_ceiling = true;
488 Update the node last under the player
490 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
491 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
494 Check properties of the node on which the player is standing
496 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
497 // Determine if jumping is possible
498 m_can_jump = touching_ground && !in_liquid;
499 if (itemgroup_get(f.groups, "disable_jump"))
501 else if (control.sneak && m_sneak_ladder_detected && !in_liquid && !is_climbing)
504 // Jump key pressed while jumping off from a bouncy block
505 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
506 m_speed.Y >= -0.5 * BS) {
507 float jumpspeed = movement_speed_jump * physics_override_jump;
509 // Reduce boost when speed already is high
510 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
512 m_speed.Y += jumpspeed;
519 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
521 move(dtime, env, pos_max_d, NULL);
524 void LocalPlayer::applyControl(float dtime)
527 swimming_vertical = false;
529 setPitch(control.pitch);
532 // Nullify speed and don't run positioning code if the player is attached
535 setSpeed(v3f(0,0,0));
539 v3f move_direction = v3f(0,0,1);
540 move_direction.rotateXZBy(getYaw());
542 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
543 v3f speedV = v3f(0,0,0); // Vertical (Y)
545 bool fly_allowed = m_client->checkLocalPrivilege("fly");
546 bool fast_allowed = m_client->checkLocalPrivilege("fast");
548 bool free_move = fly_allowed && g_settings->getBool("free_move");
549 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
550 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
551 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
552 bool continuous_forward = g_settings->getBool("continuous_forward");
553 bool always_fly_fast = g_settings->getBool("always_fly_fast");
555 // Whether superspeed mode is used or not
556 bool superspeed = false;
558 if (always_fly_fast && free_move && fast_move)
561 // Old descend control
562 if(g_settings->getBool("aux1_descends"))
564 // If free movement and fast movement, always move fast
565 if(free_move && fast_move)
568 // Auxiliary button 1 (E)
573 // In free movement mode, aux1 descends
575 speedV.Y = -movement_speed_fast;
577 speedV.Y = -movement_speed_walk;
579 else if(in_liquid || in_liquid_stable)
581 speedV.Y = -movement_speed_walk;
582 swimming_vertical = true;
586 speedV.Y = -movement_speed_climb;
590 // If not free movement but fast is allowed, aux1 is
597 // New minecraft-like descend control
600 // Auxiliary button 1 (E)
605 // aux1 is "Turbo button"
615 // In free movement mode, sneak descends
616 if (fast_move && (control.aux1 || always_fly_fast))
617 speedV.Y = -movement_speed_fast;
619 speedV.Y = -movement_speed_walk;
621 else if(in_liquid || in_liquid_stable)
624 speedV.Y = -movement_speed_fast;
626 speedV.Y = -movement_speed_walk;
627 swimming_vertical = true;
632 speedV.Y = -movement_speed_fast;
634 speedV.Y = -movement_speed_climb;
639 if (continuous_forward)
640 speedH += move_direction;
643 if (continuous_forward) {
647 speedH += move_direction;
651 speedH -= move_direction;
653 if (!control.up && !control.down) {
654 speedH -= move_direction *
655 (control.forw_move_joystick_axis / 32767.f);
658 speedH += move_direction.crossProduct(v3f(0,1,0));
661 speedH += move_direction.crossProduct(v3f(0,-1,0));
663 if (!control.left && !control.right) {
664 speedH -= move_direction.crossProduct(v3f(0,1,0)) *
665 (control.sidew_move_joystick_axis / 32767.f);
670 if (g_settings->getBool("aux1_descends") || always_fly_fast) {
672 speedV.Y = movement_speed_fast;
674 speedV.Y = movement_speed_walk;
676 if(fast_move && control.aux1)
677 speedV.Y = movement_speed_fast;
679 speedV.Y = movement_speed_walk;
685 NOTE: The d value in move() affects jump height by
686 raising the height at which the jump speed is kept
687 at its starting value
689 v3f speedJ = getSpeed();
690 if(speedJ.Y >= -0.5 * BS) {
691 if (m_ledge_detected) {
692 // Limit jump speed to a minimum that allows
693 // jumping up onto a ledge 2 nodes up.
694 speedJ.Y = movement_speed_jump *
695 MYMAX(physics_override_jump, 1.3f);
697 m_ledge_detected = false;
699 speedJ.Y = movement_speed_jump * physics_override_jump;
703 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
704 m_client->event()->put(e);
710 speedV.Y = movement_speed_fast;
712 speedV.Y = movement_speed_walk;
713 swimming_vertical = true;
718 speedV.Y = movement_speed_fast;
720 speedV.Y = movement_speed_climb;
724 // The speed of the player (Y is ignored)
725 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
726 speedH = speedH.normalize() * movement_speed_fast;
727 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
728 speedH = speedH.normalize() * movement_speed_crouch;
730 speedH = speedH.normalize() * movement_speed_walk;
732 // Acceleration increase
733 f32 incH = 0; // Horizontal (X, Z)
734 f32 incV = 0; // Vertical (Y)
735 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
737 // Jumping and falling
738 if(superspeed || (fast_move && control.aux1))
739 incH = movement_acceleration_fast * BS * dtime;
741 incH = movement_acceleration_air * BS * dtime;
742 incV = 0; // No vertical acceleration in air
744 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
745 incH = incV = movement_acceleration_fast * BS * dtime;
747 incH = incV = movement_acceleration_default * BS * dtime;
749 // Accelerate to target speed with maximum increment
750 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
751 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
754 v3s16 LocalPlayer::getStandingNodePos()
756 if(m_sneak_node_exists)
758 return floatToInt(getPosition() - v3f(0, BS, 0), BS);
761 v3s16 LocalPlayer::getFootstepNodePos()
764 // BS * 0.05 below the player's feet ensures a 1/16th height
765 // nodebox is detected instead of the node below it.
766 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
767 // A larger distance below is necessary for a footstep sound
768 // when landing after a jump or fall. BS * 0.5 ensures water
769 // sounds when swimming in 1 node deep water.
770 return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
773 v3s16 LocalPlayer::getLightPosition() const
775 return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
778 v3f LocalPlayer::getEyeOffset() const
780 float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f;
781 return v3f(0, BS * eye_height, 0);
784 // Horizontal acceleration (X and Z), Y direction is ignored
785 void LocalPlayer::accelerateHorizontal(const v3f &target_speed, const f32 max_increase)
787 if (max_increase == 0)
790 v3f d_wanted = target_speed - m_speed;
792 f32 dl = d_wanted.getLength();
793 if (dl > max_increase)
796 v3f d = d_wanted.normalize() * dl;
802 // Vertical acceleration (Y), X and Z directions are ignored
803 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
805 if (max_increase == 0)
808 f32 d_wanted = target_speed.Y - m_speed.Y;
809 if (d_wanted > max_increase)
810 d_wanted = max_increase;
811 else if (d_wanted < -max_increase)
812 d_wanted = -max_increase;
814 m_speed.Y += d_wanted;
817 // Temporary option for old move code
818 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
819 std::vector<CollisionInfo> *collision_info)
821 Map *map = &env->getMap();
822 INodeDefManager *nodemgr = m_client->ndef();
824 v3f position = getPosition();
826 // Copy parent position if local player is attached
828 setPosition(overridePosition);
829 m_sneak_node_exists = false;
833 // Skip collision detection if noclip mode is used
834 bool fly_allowed = m_client->checkLocalPrivilege("fly");
835 bool noclip = m_client->checkLocalPrivilege("noclip") &&
836 g_settings->getBool("noclip");
837 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
839 position += m_speed * dtime;
840 setPosition(position);
841 m_sneak_node_exists = false;
848 bool is_valid_position;
853 Check if player is in liquid (the oscillating value)
856 // If in liquid, the threshold of coming out is at higher y
857 pp = floatToInt(position + v3f(0, BS * 0.1, 0), BS);
858 node = map->getNodeNoEx(pp, &is_valid_position);
859 if (is_valid_position) {
860 in_liquid = nodemgr->get(node.getContent()).isLiquid();
861 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
866 // If not in liquid, the threshold of going in is at lower y
867 pp = floatToInt(position + v3f(0, BS * 0.5, 0), BS);
868 node = map->getNodeNoEx(pp, &is_valid_position);
869 if (is_valid_position) {
870 in_liquid = nodemgr->get(node.getContent()).isLiquid();
871 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
878 Check if player is in liquid (the stable value)
880 pp = floatToInt(position + v3f(0, 0, 0), BS);
881 node = map->getNodeNoEx(pp, &is_valid_position);
882 if (is_valid_position)
883 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
885 in_liquid_stable = false;
888 Check if player is climbing
890 pp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS);
891 v3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS);
892 node = map->getNodeNoEx(pp, &is_valid_position);
893 bool is_valid_position2;
894 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
896 if (!(is_valid_position && is_valid_position2))
899 is_climbing = (nodemgr->get(node.getContent()).climbable ||
900 nodemgr->get(node2.getContent()).climbable) && !free_move;
903 Collision uncertainty radius
904 Make it a bit larger than the maximum distance of movement
906 //f32 d = pos_max_d * 1.1;
907 // A fairly large value in here makes moving smoother
909 // This should always apply, otherwise there are glitches
910 sanity_check(d > pos_max_d);
911 // Maximum distance over border for sneaking
912 f32 sneak_max = BS * 0.4;
915 If sneaking, keep in range from the last walked node and don't
918 if (control.sneak && m_sneak_node_exists &&
919 !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
920 physics_override_sneak && !got_teleported) {
921 f32 maxd = 0.5 * BS + sneak_max;
922 v3f lwn_f = intToFloat(m_sneak_node, BS);
923 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
924 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
927 // Move up if necessary
928 f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
929 if (position.Y < new_y)
932 Collision seems broken, since player is sinking when
933 sneaking over the edges of current sneaking_node.
934 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
942 got_teleported = false;
944 // this shouldn't be hardcoded but transmitted from server
945 float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
948 player_stepheight += (0.6 * BS);
951 v3f accel_f = v3f(0, 0, 0);
953 collisionMoveResult result = collisionMoveSimple(env, m_client,
954 pos_max_d, m_collisionbox, player_stepheight, dtime,
955 &position, &m_speed, accel_f);
958 If the player's feet touch the topside of any node, this is
961 Player is allowed to jump when this is true.
963 bool touching_ground_was = touching_ground;
964 touching_ground = result.touching_ground;
966 //bool standing_on_unloaded = result.standing_on_unloaded;
969 Check the nodes under the player to see from which node the
970 player is sneaking from, if any. If the node from under
971 the player has been removed, the player falls.
973 f32 position_y_mod = 0.05 * BS;
974 if (m_sneak_node_bb_ymax > 0)
975 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
976 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
977 if (m_sneak_node_exists &&
978 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
979 m_old_node_below_type != "air") {
980 // Old node appears to have been removed; that is,
981 // it wasn't air before but now it is
982 m_need_to_get_new_sneak_node = false;
983 m_sneak_node_exists = false;
984 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
985 // We are on something, so make sure to recalculate the sneak
987 m_need_to_get_new_sneak_node = true;
990 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
991 m_sneak_node_bb_ymax = 0;
992 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
993 v2f player_p2df(position.X, position.Z);
994 f32 min_distance_f = 100000.0 * BS;
995 // If already seeking from some node, compare to it.
996 v3s16 new_sneak_node = m_sneak_node;
997 for (s16 x= -1; x <= 1; x++)
998 for (s16 z= -1; z <= 1; z++) {
999 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
1000 v3f pf = intToFloat(p, BS);
1001 v2f node_p2df(pf.X, pf.Z);
1002 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
1003 f32 max_axis_distance_f = MYMAX(
1004 fabs(player_p2df.X - node_p2df.X),
1005 fabs(player_p2df.Y - node_p2df.Y));
1007 if (distance_f > min_distance_f ||
1008 max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)
1011 // The node to be sneaked on has to be walkable
1012 node = map->getNodeNoEx(p, &is_valid_position);
1013 if (!is_valid_position || nodemgr->get(node).walkable == false)
1015 // And the node above it has to be nonwalkable
1016 node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
1017 if (!is_valid_position || nodemgr->get(node).walkable)
1019 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
1020 if (!physics_override_sneak_glitch) {
1021 node =map->getNodeNoEx(p + v3s16(0, 2, 0), &is_valid_position);
1022 if (!is_valid_position || nodemgr->get(node).walkable)
1026 min_distance_f = distance_f;
1030 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
1032 m_sneak_node = new_sneak_node;
1033 m_sneak_node_exists = sneak_node_found;
1035 if (sneak_node_found) {
1037 MapNode n = map->getNodeNoEx(m_sneak_node);
1038 std::vector<aabb3f> nodeboxes;
1039 n.getCollisionBoxes(nodemgr, &nodeboxes);
1040 for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
1041 it != nodeboxes.end(); ++it) {
1043 if (box.MaxEdge.Y > cb_max)
1044 cb_max = box.MaxEdge.Y;
1046 m_sneak_node_bb_ymax = cb_max;
1050 If sneaking, the player's collision box can be in air, so
1051 this has to be set explicitly
1053 if (sneak_node_found && control.sneak)
1054 touching_ground = true;
1060 setPosition(position);
1065 // Dont report if flying
1066 if (collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
1067 for (size_t i = 0; i < result.collisions.size(); i++) {
1068 const CollisionInfo &info = result.collisions[i];
1069 collision_info->push_back(info);
1073 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1074 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
1075 m_client->event()->put(e);
1076 // Set camera impact value to be used for view bobbing
1077 camera_impact = getSpeed().Y * -1;
1081 camera_barely_in_ceiling = false;
1082 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1083 MapNode n = map->getNodeNoEx(camera_np);
1084 if (n.getContent() != CONTENT_IGNORE) {
1085 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1086 camera_barely_in_ceiling = true;
1091 Update the node last under the player
1093 m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);
1094 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
1097 Check properties of the node on which the player is standing
1099 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
1100 // Determine if jumping is possible
1101 m_can_jump = touching_ground && !in_liquid;
1102 if (itemgroup_get(f.groups, "disable_jump"))
1104 // Jump key pressed while jumping off from a bouncy block
1105 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1106 m_speed.Y >= -0.5 * BS) {
1107 float jumpspeed = movement_speed_jump * physics_override_jump;
1108 if (m_speed.Y > 1) {
1109 // Reduce boost when speed already is high
1110 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
1112 m_speed.Y += jumpspeed;