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"
35 LocalPlayer::LocalPlayer(Client *client, const char *name):
36 Player(name, client->idef()),
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 makes_footstep_sound(true),
63 last_animation(NO_ANIM),
65 hotbar_selected_image(""),
66 light_color(255,255,255,255),
67 hurt_tilt_timer(0.0f),
68 hurt_tilt_strength(0.0f),
70 m_sneak_node(32767,32767,32767),
71 m_sneak_node_bb_ymax(0), // To support temporary option for old move code
72 m_sneak_node_bb_top(0,0,0,0,0,0),
73 m_sneak_node_exists(false),
74 m_need_to_get_new_sneak_node(true),
75 m_sneak_ladder_detected(false),
76 m_ledge_detected(false),
77 m_old_node_below(32767,32767,32767),
78 m_old_node_below_type("air"),
80 m_breath(PLAYER_MAX_BREATH),
83 camera_barely_in_ceiling(false),
84 m_collisionbox(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30),
88 // Initialize hp to 0, so that no hearts will be shown if server
89 // doesn't support health points
91 eye_offset_first = v3f(0,0,0);
92 eye_offset_third = v3f(0,0,0);
95 LocalPlayer::~LocalPlayer()
99 static aabb3f getTopBoundingBox(const std::vector<aabb3f> &nodeboxes)
102 b_max.reset(-BS, -BS, -BS);
103 for (std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
104 it != nodeboxes.end(); ++it) {
106 if (box.MaxEdge.Y > b_max.MaxEdge.Y)
108 else if (box.MaxEdge.Y == b_max.MaxEdge.Y)
109 b_max.addInternalBox(box);
111 return aabb3f(v3f(b_max.MinEdge.X, b_max.MaxEdge.Y, b_max.MinEdge.Z), b_max.MaxEdge);
114 #define GETNODE(map, p3, v2, y, valid) \
115 (map)->getNodeNoEx((p3) + v3s16((v2).X, y, (v2).Y), valid)
117 // pos is the node the player is standing inside(!)
118 static bool detectSneakLadder(Map *map, INodeDefManager *nodemgr, v3s16 pos)
120 // Detects a structure known as "sneak ladder" or "sneak elevator"
121 // that relies on bugs to provide a fast means of vertical transportation,
122 // the bugs have since been fixed but this function remains to keep it working.
123 // NOTE: This is just entirely a huge hack and causes way too many problems.
124 bool is_valid_position;
126 // X/Z vectors for 4 neighboring nodes
127 static const v2s16 vecs[] = { v2s16(-1, 0), v2s16(1, 0), v2s16(0, -1), v2s16(0, 1) };
129 for (u16 i = 0; i < ARRLEN(vecs); i++) {
130 const v2s16 vec = vecs[i];
132 // walkability of bottom & top node should differ
133 node = GETNODE(map, pos, vec, 0, &is_valid_position);
134 if (!is_valid_position)
136 bool w = nodemgr->get(node).walkable;
137 node = GETNODE(map, pos, vec, 1, &is_valid_position);
138 if (!is_valid_position || w == nodemgr->get(node).walkable)
141 // check one more node above OR below with corresponding walkability
142 node = GETNODE(map, pos, vec, -1, &is_valid_position);
143 bool ok = is_valid_position && w != nodemgr->get(node).walkable;
145 node = GETNODE(map, pos, vec, 2, &is_valid_position);
146 ok = is_valid_position && w == nodemgr->get(node).walkable;
156 static bool detectLedge(Map *map, INodeDefManager *nodemgr, v3s16 pos)
158 bool is_valid_position;
160 // X/Z vectors for 4 neighboring nodes
161 static const v2s16 vecs[] = {v2s16(-1, 0), v2s16(1, 0), v2s16(0, -1), v2s16(0, 1)};
163 for (u16 i = 0; i < ARRLEN(vecs); i++) {
164 const v2s16 vec = vecs[i];
166 node = GETNODE(map, pos, vec, 1, &is_valid_position);
167 if (is_valid_position && nodemgr->get(node).walkable) {
169 node = GETNODE(map, pos, vec, 2, &is_valid_position);
170 if (is_valid_position && !nodemgr->get(node).walkable)
171 // Space above ledge exists
181 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
182 std::vector<CollisionInfo> *collision_info)
184 // Temporary option for old move code
185 if (!physics_override_new_move) {
186 old_move(dtime, env, pos_max_d, collision_info);
190 Map *map = &env->getMap();
191 INodeDefManager *nodemgr = m_client->ndef();
193 v3f position = getPosition();
195 // Copy parent position if local player is attached
198 setPosition(overridePosition);
199 m_sneak_node_exists = false;
203 // Skip collision detection if noclip mode is used
204 bool fly_allowed = m_client->checkLocalPrivilege("fly");
205 bool noclip = m_client->checkLocalPrivilege("noclip") &&
206 g_settings->getBool("noclip");
207 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
209 position += m_speed * dtime;
210 setPosition(position);
211 m_sneak_node_exists = false;
219 bool is_valid_position;
224 Check if player is in liquid (the oscillating value)
227 // If in liquid, the threshold of coming out is at higher y
230 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
231 node = map->getNodeNoEx(pp, &is_valid_position);
232 if (is_valid_position) {
233 in_liquid = nodemgr->get(node.getContent()).isLiquid();
234 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
239 // If not in liquid, the threshold of going in is at lower y
242 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
243 node = map->getNodeNoEx(pp, &is_valid_position);
244 if (is_valid_position) {
245 in_liquid = nodemgr->get(node.getContent()).isLiquid();
246 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
254 Check if player is in liquid (the stable value)
256 pp = floatToInt(position + v3f(0,0,0), BS);
257 node = map->getNodeNoEx(pp, &is_valid_position);
258 if (is_valid_position) {
259 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
261 in_liquid_stable = false;
265 Check if player is climbing
269 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
270 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
271 node = map->getNodeNoEx(pp, &is_valid_position);
272 bool is_valid_position2;
273 MapNode node2 = map->getNodeNoEx(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;
284 Collision uncertainty radius
285 Make it a bit larger than the maximum distance of movement
287 //f32 d = pos_max_d * 1.1;
288 // A fairly large value in here makes moving smoother
291 // This should always apply, otherwise there are glitches
292 sanity_check(d > pos_max_d);
294 // Max. distance (X, Z) over border for sneaking determined by collision box
295 // * 0.49 to keep the center just barely on the node
296 v3f sneak_max = m_collisionbox.getExtent() * 0.49;
297 if (m_sneak_ladder_detected) {
298 // restore legacy behaviour (this makes the m_speed.Y hack necessary)
299 sneak_max = v3f(0.4 * BS, 0, 0.4 * BS);
303 If sneaking, keep in range from the last walked node and don't
306 if (control.sneak && m_sneak_node_exists &&
307 !(fly_allowed && g_settings->getBool("free_move")) &&
308 !in_liquid && !is_climbing &&
309 physics_override_sneak) {
310 const v3f sn_f = intToFloat(m_sneak_node, BS);
311 const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge;
312 const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge;
313 const v3f old_pos = position;
314 const v3f old_speed = m_speed;
316 position.X = rangelim(position.X,
317 bmin.X - sneak_max.X, bmax.X + sneak_max.X);
318 position.Z = rangelim(position.Z,
319 bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z);
321 if (position.X != old_pos.X)
323 if (position.Z != old_pos.Z)
326 // Because we keep the player collision box on the node, limiting
327 // position.Y is not necessary but useful to prevent players from
328 // being inside a node if sneaking on e.g. the lower part of a stair
329 if (!m_sneak_ladder_detected) {
330 position.Y = MYMAX(position.Y, bmax.Y);
332 // legacy behaviour that sometimes causes some weird slow sinking
333 m_speed.Y = MYMAX(m_speed.Y, 0);
336 if (collision_info != NULL &&
337 m_speed.Y - old_speed.Y > BS) {
338 // Collide with sneak node, report fall damage
339 CollisionInfo sn_info;
340 sn_info.node_p = m_sneak_node;
341 sn_info.old_speed = old_speed;
342 sn_info.new_speed = m_speed;
343 collision_info->push_back(sn_info);
347 float player_stepheight = (m_cao == 0) ? 0.0 :
349 (m_cao->getStepheight() * BS) :
350 (m_cao->getStepheight() -0.4 * BS));
353 player_stepheight += (0.6 * BS);
356 v3f accel_f = v3f(0,0,0);
358 collisionMoveResult result = collisionMoveSimple(env, m_client,
359 pos_max_d, m_collisionbox, player_stepheight, dtime,
360 &position, &m_speed, accel_f);
363 If the player's feet touch the topside of any node, this is
366 Player is allowed to jump when this is true.
368 bool touching_ground_was = touching_ground;
369 touching_ground = result.touching_ground;
371 // We want the top of the sneak node to be below the players feet
373 if (m_sneak_node_exists)
374 position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - 0.05 * BS;
376 position_y_mod = (0.5 - 0.05) * BS;
377 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
379 Check the nodes under the player to see from which node the
380 player is sneaking from, if any. If the node from under
381 the player has been removed, the player falls.
383 if (m_sneak_node_exists &&
384 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
385 m_old_node_below_type != "air") {
386 // Old node appears to have been removed; that is,
387 // it wasn't air before but now it is
388 m_need_to_get_new_sneak_node = false;
389 m_sneak_node_exists = false;
390 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
391 // We are on something, so make sure to recalculate the sneak
393 m_need_to_get_new_sneak_node = true;
396 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
397 v2f player_p2df(position.X, position.Z);
398 f32 min_distance_f = 100000.0 * BS;
399 v3s16 new_sneak_node = m_sneak_node;
400 for(s16 x=-1; x<=1; x++)
401 for(s16 z=-1; z<=1; z++)
403 v3s16 p = current_node + v3s16(x,0,z);
404 v3f pf = intToFloat(p, BS);
405 v2f node_p2df(pf.X, pf.Z);
406 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
408 if (distance_f > min_distance_f ||
409 fabs(player_p2df.X-node_p2df.X) > (.5+.1)*BS + sneak_max.X ||
410 fabs(player_p2df.Y-node_p2df.Y) > (.5+.1)*BS + sneak_max.Z)
414 // The node to be sneaked on has to be walkable
415 node = map->getNodeNoEx(p, &is_valid_position);
416 if (!is_valid_position || !nodemgr->get(node).walkable)
418 // And the node(s) above have to be nonwalkable
420 if (!physics_override_sneak_glitch) {
422 (m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
424 for (u16 y = 1; y <= height; y++) {
425 node = map->getNodeNoEx(p + v3s16(0,y,0), &is_valid_position);
426 if (!is_valid_position || nodemgr->get(node).walkable) {
432 // legacy behaviour: check just one node
433 node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
434 ok = is_valid_position && !nodemgr->get(node).walkable;
439 min_distance_f = distance_f;
443 bool sneak_node_found = (min_distance_f < 100000.0 * BS);
444 m_sneak_node = new_sneak_node;
445 m_sneak_node_exists = sneak_node_found;
447 if (sneak_node_found) {
448 // Update saved top bounding box of sneak node
449 MapNode n = map->getNodeNoEx(m_sneak_node);
450 std::vector<aabb3f> nodeboxes;
451 n.getCollisionBoxes(nodemgr, &nodeboxes);
452 m_sneak_node_bb_top = getTopBoundingBox(nodeboxes);
454 m_sneak_ladder_detected = physics_override_sneak_glitch &&
455 detectSneakLadder(map, nodemgr, floatToInt(position, BS));
457 m_sneak_ladder_detected = false;
462 If 'sneak glitch' enabled detect ledge for old sneak-jump
463 behaviour of climbing onto a ledge 2 nodes up.
465 if (physics_override_sneak_glitch && control.sneak && control.jump)
466 m_ledge_detected = detectLedge(map, nodemgr, floatToInt(position, BS));
469 Set new position but keep sneak node set
471 bool sneak_node_exists = m_sneak_node_exists;
472 setPosition(position);
473 m_sneak_node_exists = sneak_node_exists;
479 // Dont report if flying
480 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
481 for(size_t i=0; i<result.collisions.size(); i++) {
482 const CollisionInfo &info = result.collisions[i];
483 collision_info->push_back(info);
487 if(!result.standing_on_object && !touching_ground_was && touching_ground) {
488 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
489 m_client->event()->put(e);
491 // Set camera impact value to be used for view bobbing
492 camera_impact = getSpeed().Y * -1;
496 camera_barely_in_ceiling = false;
497 v3s16 camera_np = floatToInt(getEyePosition(), BS);
498 MapNode n = map->getNodeNoEx(camera_np);
499 if(n.getContent() != CONTENT_IGNORE){
500 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
501 camera_barely_in_ceiling = true;
507 Update the node last under the player
509 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
510 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
513 Check properties of the node on which the player is standing
515 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
516 // Determine if jumping is possible
517 m_can_jump = touching_ground && !in_liquid;
518 if (itemgroup_get(f.groups, "disable_jump"))
520 else if (control.sneak && m_sneak_ladder_detected && !in_liquid && !is_climbing)
523 // Jump key pressed while jumping off from a bouncy block
524 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
525 m_speed.Y >= -0.5 * BS) {
526 float jumpspeed = movement_speed_jump * physics_override_jump;
528 // Reduce boost when speed already is high
529 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
531 m_speed.Y += jumpspeed;
538 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
540 move(dtime, env, pos_max_d, NULL);
543 void LocalPlayer::applyControl(float dtime)
546 swimming_vertical = false;
548 setPitch(control.pitch);
551 // Nullify speed and don't run positioning code if the player is attached
554 setSpeed(v3f(0,0,0));
558 v3f move_direction = v3f(0,0,1);
559 move_direction.rotateXZBy(getYaw());
561 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
562 v3f speedV = v3f(0,0,0); // Vertical (Y)
564 bool fly_allowed = m_client->checkLocalPrivilege("fly");
565 bool fast_allowed = m_client->checkLocalPrivilege("fast");
567 bool free_move = fly_allowed && g_settings->getBool("free_move");
568 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
569 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
570 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
571 bool continuous_forward = g_settings->getBool("continuous_forward");
572 bool always_fly_fast = g_settings->getBool("always_fly_fast");
574 // Whether superspeed mode is used or not
575 bool superspeed = false;
577 if (always_fly_fast && free_move && fast_move)
580 // Old descend control
581 if(g_settings->getBool("aux1_descends"))
583 // If free movement and fast movement, always move fast
584 if(free_move && fast_move)
587 // Auxiliary button 1 (E)
592 // In free movement mode, aux1 descends
594 speedV.Y = -movement_speed_fast;
596 speedV.Y = -movement_speed_walk;
598 else if(in_liquid || in_liquid_stable)
600 speedV.Y = -movement_speed_walk;
601 swimming_vertical = true;
605 speedV.Y = -movement_speed_climb;
609 // If not free movement but fast is allowed, aux1 is
616 // New minecraft-like descend control
619 // Auxiliary button 1 (E)
624 // aux1 is "Turbo button"
634 // In free movement mode, sneak descends
635 if (fast_move && (control.aux1 || always_fly_fast))
636 speedV.Y = -movement_speed_fast;
638 speedV.Y = -movement_speed_walk;
640 else if(in_liquid || in_liquid_stable)
643 speedV.Y = -movement_speed_fast;
645 speedV.Y = -movement_speed_walk;
646 swimming_vertical = true;
651 speedV.Y = -movement_speed_fast;
653 speedV.Y = -movement_speed_climb;
658 if (continuous_forward)
659 speedH += move_direction;
662 if (continuous_forward) {
666 speedH += move_direction;
670 speedH -= move_direction;
672 if (!control.up && !control.down) {
673 speedH -= move_direction *
674 (control.forw_move_joystick_axis / 32767.f);
677 speedH += move_direction.crossProduct(v3f(0,1,0));
680 speedH += move_direction.crossProduct(v3f(0,-1,0));
682 if (!control.left && !control.right) {
683 speedH -= move_direction.crossProduct(v3f(0,1,0)) *
684 (control.sidew_move_joystick_axis / 32767.f);
689 if (g_settings->getBool("aux1_descends") || always_fly_fast) {
691 speedV.Y = movement_speed_fast;
693 speedV.Y = movement_speed_walk;
695 if(fast_move && control.aux1)
696 speedV.Y = movement_speed_fast;
698 speedV.Y = movement_speed_walk;
704 NOTE: The d value in move() affects jump height by
705 raising the height at which the jump speed is kept
706 at its starting value
708 v3f speedJ = getSpeed();
709 if(speedJ.Y >= -0.5 * BS) {
710 if (m_ledge_detected) {
711 // Limit jump speed to a minimum that allows
712 // jumping up onto a ledge 2 nodes up.
713 speedJ.Y = movement_speed_jump *
714 MYMAX(physics_override_jump, 1.3f);
716 m_ledge_detected = false;
718 speedJ.Y = movement_speed_jump * physics_override_jump;
722 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
723 m_client->event()->put(e);
729 speedV.Y = movement_speed_fast;
731 speedV.Y = movement_speed_walk;
732 swimming_vertical = true;
737 speedV.Y = movement_speed_fast;
739 speedV.Y = movement_speed_climb;
743 // The speed of the player (Y is ignored)
744 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
745 speedH = speedH.normalize() * movement_speed_fast;
746 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
747 speedH = speedH.normalize() * movement_speed_crouch;
749 speedH = speedH.normalize() * movement_speed_walk;
751 // Acceleration increase
752 f32 incH = 0; // Horizontal (X, Z)
753 f32 incV = 0; // Vertical (Y)
754 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
756 // Jumping and falling
757 if(superspeed || (fast_move && control.aux1))
758 incH = movement_acceleration_fast * BS * dtime;
760 incH = movement_acceleration_air * BS * dtime;
761 incV = 0; // No vertical acceleration in air
763 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
764 incH = incV = movement_acceleration_fast * BS * dtime;
766 incH = incV = movement_acceleration_default * BS * dtime;
768 // Accelerate to target speed with maximum increment
769 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
770 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
773 v3s16 LocalPlayer::getStandingNodePos()
775 if(m_sneak_node_exists)
777 return floatToInt(getPosition() - v3f(0, BS, 0), BS);
780 v3s16 LocalPlayer::getFootstepNodePos()
783 // BS * 0.05 below the player's feet ensures a 1/16th height
784 // nodebox is detected instead of the node below it.
785 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
786 // A larger distance below is necessary for a footstep sound
787 // when landing after a jump or fall. BS * 0.5 ensures water
788 // sounds when swimming in 1 node deep water.
789 return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
792 v3s16 LocalPlayer::getLightPosition() const
794 return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
797 v3f LocalPlayer::getEyeOffset() const
799 float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f;
800 return v3f(0, BS * eye_height, 0);
803 // Horizontal acceleration (X and Z), Y direction is ignored
804 void LocalPlayer::accelerateHorizontal(const v3f &target_speed, const f32 max_increase)
806 if (max_increase == 0)
809 v3f d_wanted = target_speed - m_speed;
811 f32 dl = d_wanted.getLength();
812 if (dl > max_increase)
815 v3f d = d_wanted.normalize() * dl;
821 // Vertical acceleration (Y), X and Z directions are ignored
822 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
824 if (max_increase == 0)
827 f32 d_wanted = target_speed.Y - m_speed.Y;
828 if (d_wanted > max_increase)
829 d_wanted = max_increase;
830 else if (d_wanted < -max_increase)
831 d_wanted = -max_increase;
833 m_speed.Y += d_wanted;
836 // Temporary option for old move code
837 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
838 std::vector<CollisionInfo> *collision_info)
840 Map *map = &env->getMap();
841 INodeDefManager *nodemgr = m_client->ndef();
843 v3f position = getPosition();
845 // Copy parent position if local player is attached
847 setPosition(overridePosition);
848 m_sneak_node_exists = false;
852 // Skip collision detection if noclip mode is used
853 bool fly_allowed = m_client->checkLocalPrivilege("fly");
854 bool noclip = m_client->checkLocalPrivilege("noclip") &&
855 g_settings->getBool("noclip");
856 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
858 position += m_speed * dtime;
859 setPosition(position);
860 m_sneak_node_exists = false;
867 bool is_valid_position;
872 Check if player is in liquid (the oscillating value)
875 // If in liquid, the threshold of coming out is at higher y
876 pp = floatToInt(position + v3f(0, BS * 0.1, 0), BS);
877 node = map->getNodeNoEx(pp, &is_valid_position);
878 if (is_valid_position) {
879 in_liquid = nodemgr->get(node.getContent()).isLiquid();
880 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
885 // If not in liquid, the threshold of going in is at lower y
886 pp = floatToInt(position + v3f(0, BS * 0.5, 0), BS);
887 node = map->getNodeNoEx(pp, &is_valid_position);
888 if (is_valid_position) {
889 in_liquid = nodemgr->get(node.getContent()).isLiquid();
890 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
897 Check if player is in liquid (the stable value)
899 pp = floatToInt(position + v3f(0, 0, 0), BS);
900 node = map->getNodeNoEx(pp, &is_valid_position);
901 if (is_valid_position)
902 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
904 in_liquid_stable = false;
907 Check if player is climbing
909 pp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS);
910 v3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS);
911 node = map->getNodeNoEx(pp, &is_valid_position);
912 bool is_valid_position2;
913 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
915 if (!(is_valid_position && is_valid_position2))
918 is_climbing = (nodemgr->get(node.getContent()).climbable ||
919 nodemgr->get(node2.getContent()).climbable) && !free_move;
922 Collision uncertainty radius
923 Make it a bit larger than the maximum distance of movement
925 //f32 d = pos_max_d * 1.1;
926 // A fairly large value in here makes moving smoother
928 // This should always apply, otherwise there are glitches
929 sanity_check(d > pos_max_d);
930 // Maximum distance over border for sneaking
931 f32 sneak_max = BS * 0.4;
934 If sneaking, keep in range from the last walked node and don't
937 if (control.sneak && m_sneak_node_exists &&
938 !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
939 physics_override_sneak) {
940 f32 maxd = 0.5 * BS + sneak_max;
941 v3f lwn_f = intToFloat(m_sneak_node, BS);
942 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
943 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
946 // Move up if necessary
947 f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
948 if (position.Y < new_y)
951 Collision seems broken, since player is sinking when
952 sneaking over the edges of current sneaking_node.
953 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
960 // this shouldn't be hardcoded but transmitted from server
961 float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
964 player_stepheight += (0.6 * BS);
967 v3f accel_f = v3f(0, 0, 0);
969 collisionMoveResult result = collisionMoveSimple(env, m_client,
970 pos_max_d, m_collisionbox, player_stepheight, dtime,
971 &position, &m_speed, accel_f);
974 If the player's feet touch the topside of any node, this is
977 Player is allowed to jump when this is true.
979 bool touching_ground_was = touching_ground;
980 touching_ground = result.touching_ground;
982 //bool standing_on_unloaded = result.standing_on_unloaded;
985 Check the nodes under the player to see from which node the
986 player is sneaking from, if any. If the node from under
987 the player has been removed, the player falls.
989 f32 position_y_mod = 0.05 * BS;
990 if (m_sneak_node_bb_ymax > 0)
991 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
992 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
993 if (m_sneak_node_exists &&
994 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
995 m_old_node_below_type != "air") {
996 // Old node appears to have been removed; that is,
997 // it wasn't air before but now it is
998 m_need_to_get_new_sneak_node = false;
999 m_sneak_node_exists = false;
1000 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
1001 // We are on something, so make sure to recalculate the sneak
1003 m_need_to_get_new_sneak_node = true;
1006 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
1007 m_sneak_node_bb_ymax = 0;
1008 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
1009 v2f player_p2df(position.X, position.Z);
1010 f32 min_distance_f = 100000.0 * BS;
1011 // If already seeking from some node, compare to it.
1012 v3s16 new_sneak_node = m_sneak_node;
1013 for (s16 x= -1; x <= 1; x++)
1014 for (s16 z= -1; z <= 1; z++) {
1015 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
1016 v3f pf = intToFloat(p, BS);
1017 v2f node_p2df(pf.X, pf.Z);
1018 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
1019 f32 max_axis_distance_f = MYMAX(
1020 fabs(player_p2df.X - node_p2df.X),
1021 fabs(player_p2df.Y - node_p2df.Y));
1023 if (distance_f > min_distance_f ||
1024 max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)
1027 // The node to be sneaked on has to be walkable
1028 node = map->getNodeNoEx(p, &is_valid_position);
1029 if (!is_valid_position || nodemgr->get(node).walkable == false)
1031 // And the node above it has to be nonwalkable
1032 node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
1033 if (!is_valid_position || nodemgr->get(node).walkable)
1035 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
1036 if (!physics_override_sneak_glitch) {
1037 node =map->getNodeNoEx(p + v3s16(0, 2, 0), &is_valid_position);
1038 if (!is_valid_position || nodemgr->get(node).walkable)
1042 min_distance_f = distance_f;
1046 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
1048 m_sneak_node = new_sneak_node;
1049 m_sneak_node_exists = sneak_node_found;
1051 if (sneak_node_found) {
1053 MapNode n = map->getNodeNoEx(m_sneak_node);
1054 std::vector<aabb3f> nodeboxes;
1055 n.getCollisionBoxes(nodemgr, &nodeboxes);
1056 for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
1057 it != nodeboxes.end(); ++it) {
1059 if (box.MaxEdge.Y > cb_max)
1060 cb_max = box.MaxEdge.Y;
1062 m_sneak_node_bb_ymax = cb_max;
1066 If sneaking, the player's collision box can be in air, so
1067 this has to be set explicitly
1069 if (sneak_node_found && control.sneak)
1070 touching_ground = true;
1074 Set new position but keep sneak node set
1076 bool sneak_node_exists = m_sneak_node_exists;
1077 setPosition(position);
1078 m_sneak_node_exists = sneak_node_exists;
1083 // Dont report if flying
1084 if (collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
1085 for (size_t i = 0; i < result.collisions.size(); i++) {
1086 const CollisionInfo &info = result.collisions[i];
1087 collision_info->push_back(info);
1091 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1092 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
1093 m_client->event()->put(e);
1094 // Set camera impact value to be used for view bobbing
1095 camera_impact = getSpeed().Y * -1;
1099 camera_barely_in_ceiling = false;
1100 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1101 MapNode n = map->getNodeNoEx(camera_np);
1102 if (n.getContent() != CONTENT_IGNORE) {
1103 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1104 camera_barely_in_ceiling = true;
1109 Update the node last under the player
1111 m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);
1112 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
1115 Check properties of the node on which the player is standing
1117 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
1118 // Determine if jumping is possible
1119 m_can_jump = touching_ground && !in_liquid;
1120 if (itemgroup_get(f.groups, "disable_jump"))
1122 // Jump key pressed while jumping off from a bouncy block
1123 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1124 m_speed.Y >= -0.5 * BS) {
1125 float jumpspeed = movement_speed_jump * physics_override_jump;
1126 if (m_speed.Y > 1) {
1127 // Reduce boost when speed already is high
1128 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
1130 m_speed.Y += jumpspeed;