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 // TODO: this shouldn't be hardcoded but transmitted from server
348 float player_stepheight = (touching_ground) ? (BS * 0.6f) : (BS * 0.2f);
351 player_stepheight += (0.6f * BS);
354 v3f accel_f = v3f(0,0,0);
356 collisionMoveResult result = collisionMoveSimple(env, m_client,
357 pos_max_d, m_collisionbox, player_stepheight, dtime,
358 &position, &m_speed, accel_f);
361 If the player's feet touch the topside of any node, this is
364 Player is allowed to jump when this is true.
366 bool touching_ground_was = touching_ground;
367 touching_ground = result.touching_ground;
369 // We want the top of the sneak node to be below the players feet
371 if (m_sneak_node_exists)
372 position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - 0.05 * BS;
374 position_y_mod = (0.5 - 0.05) * BS;
375 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
377 Check the nodes under the player to see from which node the
378 player is sneaking from, if any. If the node from under
379 the player has been removed, the player falls.
381 if (m_sneak_node_exists &&
382 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
383 m_old_node_below_type != "air") {
384 // Old node appears to have been removed; that is,
385 // it wasn't air before but now it is
386 m_need_to_get_new_sneak_node = false;
387 m_sneak_node_exists = false;
388 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
389 // We are on something, so make sure to recalculate the sneak
391 m_need_to_get_new_sneak_node = true;
394 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
395 v2f player_p2df(position.X, position.Z);
396 f32 min_distance_f = 100000.0 * BS;
397 v3s16 new_sneak_node = m_sneak_node;
398 for(s16 x=-1; x<=1; x++)
399 for(s16 z=-1; z<=1; z++)
401 v3s16 p = current_node + v3s16(x,0,z);
402 v3f pf = intToFloat(p, BS);
403 v2f node_p2df(pf.X, pf.Z);
404 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
406 if (distance_f > min_distance_f ||
407 fabs(player_p2df.X-node_p2df.X) > (.5+.1)*BS + sneak_max.X ||
408 fabs(player_p2df.Y-node_p2df.Y) > (.5+.1)*BS + sneak_max.Z)
412 // The node to be sneaked on has to be walkable
413 node = map->getNodeNoEx(p, &is_valid_position);
414 if (!is_valid_position || !nodemgr->get(node).walkable)
416 // And the node(s) above have to be nonwalkable
418 if (!physics_override_sneak_glitch) {
420 (m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
422 for (u16 y = 1; y <= height; y++) {
423 node = map->getNodeNoEx(p + v3s16(0,y,0), &is_valid_position);
424 if (!is_valid_position || nodemgr->get(node).walkable) {
430 // legacy behaviour: check just one node
431 node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
432 ok = is_valid_position && !nodemgr->get(node).walkable;
437 min_distance_f = distance_f;
441 bool sneak_node_found = (min_distance_f < 100000.0 * BS);
442 m_sneak_node = new_sneak_node;
443 m_sneak_node_exists = sneak_node_found;
445 if (sneak_node_found) {
446 // Update saved top bounding box of sneak node
447 MapNode n = map->getNodeNoEx(m_sneak_node);
448 std::vector<aabb3f> nodeboxes;
449 n.getCollisionBoxes(nodemgr, &nodeboxes);
450 m_sneak_node_bb_top = getTopBoundingBox(nodeboxes);
452 m_sneak_ladder_detected = physics_override_sneak_glitch &&
453 detectSneakLadder(map, nodemgr, floatToInt(position, BS));
455 m_sneak_ladder_detected = false;
460 If 'sneak glitch' enabled detect ledge for old sneak-jump
461 behaviour of climbing onto a ledge 2 nodes up.
463 if (physics_override_sneak_glitch && control.sneak && control.jump)
464 m_ledge_detected = detectLedge(map, nodemgr, floatToInt(position, BS));
467 Set new position but keep sneak node set
469 bool sneak_node_exists = m_sneak_node_exists;
470 setPosition(position);
471 m_sneak_node_exists = sneak_node_exists;
477 // Dont report if flying
478 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
479 for(size_t i=0; i<result.collisions.size(); i++) {
480 const CollisionInfo &info = result.collisions[i];
481 collision_info->push_back(info);
485 if(!result.standing_on_object && !touching_ground_was && touching_ground) {
486 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
487 m_client->event()->put(e);
489 // Set camera impact value to be used for view bobbing
490 camera_impact = getSpeed().Y * -1;
494 camera_barely_in_ceiling = false;
495 v3s16 camera_np = floatToInt(getEyePosition(), BS);
496 MapNode n = map->getNodeNoEx(camera_np);
497 if(n.getContent() != CONTENT_IGNORE){
498 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
499 camera_barely_in_ceiling = true;
505 Update the node last under the player
507 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
508 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
511 Check properties of the node on which the player is standing
513 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
514 // Determine if jumping is possible
515 m_can_jump = touching_ground && !in_liquid;
516 if (itemgroup_get(f.groups, "disable_jump"))
518 else if (control.sneak && m_sneak_ladder_detected && !in_liquid && !is_climbing)
521 // Jump key pressed while jumping off from a bouncy block
522 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
523 m_speed.Y >= -0.5 * BS) {
524 float jumpspeed = movement_speed_jump * physics_override_jump;
526 // Reduce boost when speed already is high
527 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
529 m_speed.Y += jumpspeed;
536 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
538 move(dtime, env, pos_max_d, NULL);
541 void LocalPlayer::applyControl(float dtime)
544 swimming_vertical = false;
546 setPitch(control.pitch);
549 // Nullify speed and don't run positioning code if the player is attached
552 setSpeed(v3f(0,0,0));
556 v3f move_direction = v3f(0,0,1);
557 move_direction.rotateXZBy(getYaw());
559 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
560 v3f speedV = v3f(0,0,0); // Vertical (Y)
562 bool fly_allowed = m_client->checkLocalPrivilege("fly");
563 bool fast_allowed = m_client->checkLocalPrivilege("fast");
565 bool free_move = fly_allowed && g_settings->getBool("free_move");
566 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
567 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
568 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
569 bool continuous_forward = g_settings->getBool("continuous_forward");
570 bool always_fly_fast = g_settings->getBool("always_fly_fast");
572 // Whether superspeed mode is used or not
573 bool superspeed = false;
575 if (always_fly_fast && free_move && fast_move)
578 // Old descend control
579 if(g_settings->getBool("aux1_descends"))
581 // If free movement and fast movement, always move fast
582 if(free_move && fast_move)
585 // Auxiliary button 1 (E)
590 // In free movement mode, aux1 descends
592 speedV.Y = -movement_speed_fast;
594 speedV.Y = -movement_speed_walk;
596 else if(in_liquid || in_liquid_stable)
598 speedV.Y = -movement_speed_walk;
599 swimming_vertical = true;
603 speedV.Y = -movement_speed_climb;
607 // If not free movement but fast is allowed, aux1 is
614 // New minecraft-like descend control
617 // Auxiliary button 1 (E)
622 // aux1 is "Turbo button"
632 // In free movement mode, sneak descends
633 if (fast_move && (control.aux1 || always_fly_fast))
634 speedV.Y = -movement_speed_fast;
636 speedV.Y = -movement_speed_walk;
638 else if(in_liquid || in_liquid_stable)
641 speedV.Y = -movement_speed_fast;
643 speedV.Y = -movement_speed_walk;
644 swimming_vertical = true;
649 speedV.Y = -movement_speed_fast;
651 speedV.Y = -movement_speed_climb;
656 if (continuous_forward)
657 speedH += move_direction;
660 if (continuous_forward) {
664 speedH += move_direction;
668 speedH -= move_direction;
670 if (!control.up && !control.down) {
671 speedH -= move_direction *
672 (control.forw_move_joystick_axis / 32767.f);
675 speedH += move_direction.crossProduct(v3f(0,1,0));
678 speedH += move_direction.crossProduct(v3f(0,-1,0));
680 if (!control.left && !control.right) {
681 speedH -= move_direction.crossProduct(v3f(0,1,0)) *
682 (control.sidew_move_joystick_axis / 32767.f);
687 if (g_settings->getBool("aux1_descends") || always_fly_fast) {
689 speedV.Y = movement_speed_fast;
691 speedV.Y = movement_speed_walk;
693 if(fast_move && control.aux1)
694 speedV.Y = movement_speed_fast;
696 speedV.Y = movement_speed_walk;
702 NOTE: The d value in move() affects jump height by
703 raising the height at which the jump speed is kept
704 at its starting value
706 v3f speedJ = getSpeed();
707 if(speedJ.Y >= -0.5 * BS) {
708 if (m_ledge_detected) {
709 // Limit jump speed to a minimum that allows
710 // jumping up onto a ledge 2 nodes up.
711 speedJ.Y = movement_speed_jump *
712 MYMAX(physics_override_jump, 1.3f);
714 m_ledge_detected = false;
716 speedJ.Y = movement_speed_jump * physics_override_jump;
720 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
721 m_client->event()->put(e);
727 speedV.Y = movement_speed_fast;
729 speedV.Y = movement_speed_walk;
730 swimming_vertical = true;
735 speedV.Y = movement_speed_fast;
737 speedV.Y = movement_speed_climb;
741 // The speed of the player (Y is ignored)
742 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
743 speedH = speedH.normalize() * movement_speed_fast;
744 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
745 speedH = speedH.normalize() * movement_speed_crouch;
747 speedH = speedH.normalize() * movement_speed_walk;
749 // Acceleration increase
750 f32 incH = 0; // Horizontal (X, Z)
751 f32 incV = 0; // Vertical (Y)
752 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
754 // Jumping and falling
755 if(superspeed || (fast_move && control.aux1))
756 incH = movement_acceleration_fast * BS * dtime;
758 incH = movement_acceleration_air * BS * dtime;
759 incV = 0; // No vertical acceleration in air
761 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
762 incH = incV = movement_acceleration_fast * BS * dtime;
764 incH = incV = movement_acceleration_default * BS * dtime;
766 // Accelerate to target speed with maximum increment
767 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
768 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
771 v3s16 LocalPlayer::getStandingNodePos()
773 if(m_sneak_node_exists)
775 return floatToInt(getPosition() - v3f(0, BS, 0), BS);
778 v3s16 LocalPlayer::getFootstepNodePos()
781 // BS * 0.05 below the player's feet ensures a 1/16th height
782 // nodebox is detected instead of the node below it.
783 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
784 // A larger distance below is necessary for a footstep sound
785 // when landing after a jump or fall. BS * 0.5 ensures water
786 // sounds when swimming in 1 node deep water.
787 return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
790 v3s16 LocalPlayer::getLightPosition() const
792 return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
795 v3f LocalPlayer::getEyeOffset() const
797 float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f;
798 return v3f(0, BS * eye_height, 0);
801 // Horizontal acceleration (X and Z), Y direction is ignored
802 void LocalPlayer::accelerateHorizontal(const v3f &target_speed, const f32 max_increase)
804 if (max_increase == 0)
807 v3f d_wanted = target_speed - m_speed;
809 f32 dl = d_wanted.getLength();
810 if (dl > max_increase)
813 v3f d = d_wanted.normalize() * dl;
819 // Vertical acceleration (Y), X and Z directions are ignored
820 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
822 if (max_increase == 0)
825 f32 d_wanted = target_speed.Y - m_speed.Y;
826 if (d_wanted > max_increase)
827 d_wanted = max_increase;
828 else if (d_wanted < -max_increase)
829 d_wanted = -max_increase;
831 m_speed.Y += d_wanted;
834 // Temporary option for old move code
835 void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
836 std::vector<CollisionInfo> *collision_info)
838 Map *map = &env->getMap();
839 INodeDefManager *nodemgr = m_client->ndef();
841 v3f position = getPosition();
843 // Copy parent position if local player is attached
845 setPosition(overridePosition);
846 m_sneak_node_exists = false;
850 // Skip collision detection if noclip mode is used
851 bool fly_allowed = m_client->checkLocalPrivilege("fly");
852 bool noclip = m_client->checkLocalPrivilege("noclip") &&
853 g_settings->getBool("noclip");
854 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
856 position += m_speed * dtime;
857 setPosition(position);
858 m_sneak_node_exists = false;
865 bool is_valid_position;
870 Check if player is in liquid (the oscillating value)
873 // If in liquid, the threshold of coming out is at higher y
874 pp = floatToInt(position + v3f(0, BS * 0.1, 0), BS);
875 node = map->getNodeNoEx(pp, &is_valid_position);
876 if (is_valid_position) {
877 in_liquid = nodemgr->get(node.getContent()).isLiquid();
878 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
883 // If not in liquid, the threshold of going in is at lower y
884 pp = floatToInt(position + v3f(0, BS * 0.5, 0), BS);
885 node = map->getNodeNoEx(pp, &is_valid_position);
886 if (is_valid_position) {
887 in_liquid = nodemgr->get(node.getContent()).isLiquid();
888 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
895 Check if player is in liquid (the stable value)
897 pp = floatToInt(position + v3f(0, 0, 0), BS);
898 node = map->getNodeNoEx(pp, &is_valid_position);
899 if (is_valid_position)
900 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
902 in_liquid_stable = false;
905 Check if player is climbing
907 pp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS);
908 v3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS);
909 node = map->getNodeNoEx(pp, &is_valid_position);
910 bool is_valid_position2;
911 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
913 if (!(is_valid_position && is_valid_position2))
916 is_climbing = (nodemgr->get(node.getContent()).climbable ||
917 nodemgr->get(node2.getContent()).climbable) && !free_move;
920 Collision uncertainty radius
921 Make it a bit larger than the maximum distance of movement
923 //f32 d = pos_max_d * 1.1;
924 // A fairly large value in here makes moving smoother
926 // This should always apply, otherwise there are glitches
927 sanity_check(d > pos_max_d);
928 // Maximum distance over border for sneaking
929 f32 sneak_max = BS * 0.4;
932 If sneaking, keep in range from the last walked node and don't
935 if (control.sneak && m_sneak_node_exists &&
936 !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
937 physics_override_sneak) {
938 f32 maxd = 0.5 * BS + sneak_max;
939 v3f lwn_f = intToFloat(m_sneak_node, BS);
940 position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
941 position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd);
944 // Move up if necessary
945 f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
946 if (position.Y < new_y)
949 Collision seems broken, since player is sinking when
950 sneaking over the edges of current sneaking_node.
951 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
958 // this shouldn't be hardcoded but transmitted from server
959 float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
962 player_stepheight += (0.6 * BS);
965 v3f accel_f = v3f(0, 0, 0);
967 collisionMoveResult result = collisionMoveSimple(env, m_client,
968 pos_max_d, m_collisionbox, player_stepheight, dtime,
969 &position, &m_speed, accel_f);
972 If the player's feet touch the topside of any node, this is
975 Player is allowed to jump when this is true.
977 bool touching_ground_was = touching_ground;
978 touching_ground = result.touching_ground;
980 //bool standing_on_unloaded = result.standing_on_unloaded;
983 Check the nodes under the player to see from which node the
984 player is sneaking from, if any. If the node from under
985 the player has been removed, the player falls.
987 f32 position_y_mod = 0.05 * BS;
988 if (m_sneak_node_bb_ymax > 0)
989 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
990 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
991 if (m_sneak_node_exists &&
992 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
993 m_old_node_below_type != "air") {
994 // Old node appears to have been removed; that is,
995 // it wasn't air before but now it is
996 m_need_to_get_new_sneak_node = false;
997 m_sneak_node_exists = false;
998 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
999 // We are on something, so make sure to recalculate the sneak
1001 m_need_to_get_new_sneak_node = true;
1004 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
1005 m_sneak_node_bb_ymax = 0;
1006 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
1007 v2f player_p2df(position.X, position.Z);
1008 f32 min_distance_f = 100000.0 * BS;
1009 // If already seeking from some node, compare to it.
1010 v3s16 new_sneak_node = m_sneak_node;
1011 for (s16 x= -1; x <= 1; x++)
1012 for (s16 z= -1; z <= 1; z++) {
1013 v3s16 p = pos_i_bottom + v3s16(x, 0, z);
1014 v3f pf = intToFloat(p, BS);
1015 v2f node_p2df(pf.X, pf.Z);
1016 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
1017 f32 max_axis_distance_f = MYMAX(
1018 fabs(player_p2df.X - node_p2df.X),
1019 fabs(player_p2df.Y - node_p2df.Y));
1021 if (distance_f > min_distance_f ||
1022 max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS)
1025 // The node to be sneaked on has to be walkable
1026 node = map->getNodeNoEx(p, &is_valid_position);
1027 if (!is_valid_position || nodemgr->get(node).walkable == false)
1029 // And the node above it has to be nonwalkable
1030 node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
1031 if (!is_valid_position || nodemgr->get(node).walkable)
1033 // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable
1034 if (!physics_override_sneak_glitch) {
1035 node =map->getNodeNoEx(p + v3s16(0, 2, 0), &is_valid_position);
1036 if (!is_valid_position || nodemgr->get(node).walkable)
1040 min_distance_f = distance_f;
1044 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
1046 m_sneak_node = new_sneak_node;
1047 m_sneak_node_exists = sneak_node_found;
1049 if (sneak_node_found) {
1051 MapNode n = map->getNodeNoEx(m_sneak_node);
1052 std::vector<aabb3f> nodeboxes;
1053 n.getCollisionBoxes(nodemgr, &nodeboxes);
1054 for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
1055 it != nodeboxes.end(); ++it) {
1057 if (box.MaxEdge.Y > cb_max)
1058 cb_max = box.MaxEdge.Y;
1060 m_sneak_node_bb_ymax = cb_max;
1064 If sneaking, the player's collision box can be in air, so
1065 this has to be set explicitly
1067 if (sneak_node_found && control.sneak)
1068 touching_ground = true;
1072 Set new position but keep sneak node set
1074 bool sneak_node_exists = m_sneak_node_exists;
1075 setPosition(position);
1076 m_sneak_node_exists = sneak_node_exists;
1081 // Dont report if flying
1082 if (collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
1083 for (size_t i = 0; i < result.collisions.size(); i++) {
1084 const CollisionInfo &info = result.collisions[i];
1085 collision_info->push_back(info);
1089 if (!result.standing_on_object && !touching_ground_was && touching_ground) {
1090 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
1091 m_client->event()->put(e);
1092 // Set camera impact value to be used for view bobbing
1093 camera_impact = getSpeed().Y * -1;
1097 camera_barely_in_ceiling = false;
1098 v3s16 camera_np = floatToInt(getEyePosition(), BS);
1099 MapNode n = map->getNodeNoEx(camera_np);
1100 if (n.getContent() != CONTENT_IGNORE) {
1101 if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
1102 camera_barely_in_ceiling = true;
1107 Update the node last under the player
1109 m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS);
1110 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
1113 Check properties of the node on which the player is standing
1115 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
1116 // Determine if jumping is possible
1117 m_can_jump = touching_ground && !in_liquid;
1118 if (itemgroup_get(f.groups, "disable_jump"))
1120 // Jump key pressed while jumping off from a bouncy block
1121 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
1122 m_speed.Y >= -0.5 * BS) {
1123 float jumpspeed = movement_speed_jump * physics_override_jump;
1124 if (m_speed.Y > 1) {
1125 // Reduce boost when speed already is high
1126 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
1128 m_speed.Y += jumpspeed;