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(true),
52 overridePosition(v3f(0,0,0)),
53 last_position(v3f(0,0,0)),
54 last_speed(v3f(0,0,0)),
61 last_animation(NO_ANIM),
63 hotbar_selected_image(""),
64 light_color(255,255,255,255),
65 hurt_tilt_timer(0.0f),
66 hurt_tilt_strength(0.0f),
68 m_sneak_node(32767,32767,32767),
69 m_sneak_node_bb_top(0,0,0,0,0,0),
70 m_sneak_node_exists(false),
71 m_need_to_get_new_sneak_node(true),
72 m_sneak_ladder_detected(false),
73 m_old_node_below(32767,32767,32767),
74 m_old_node_below_type("air"),
76 m_breath(PLAYER_MAX_BREATH),
79 camera_barely_in_ceiling(false),
80 m_collisionbox(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30),
84 // Initialize hp to 0, so that no hearts will be shown if server
85 // doesn't support health points
87 eye_offset_first = v3f(0,0,0);
88 eye_offset_third = v3f(0,0,0);
91 LocalPlayer::~LocalPlayer()
95 static aabb3f getTopBoundingBox(const std::vector<aabb3f> &nodeboxes)
98 b_max.reset(-BS, -BS, -BS);
99 for (std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
100 it != nodeboxes.end(); ++it) {
102 if (box.MaxEdge.Y > b_max.MaxEdge.Y)
104 else if (box.MaxEdge.Y == b_max.MaxEdge.Y)
105 b_max.addInternalBox(box);
107 return aabb3f(v3f(b_max.MinEdge.X, b_max.MaxEdge.Y, b_max.MinEdge.Z), b_max.MaxEdge);
110 #define GETNODE(map, p3, v2, y, valid) \
111 (map)->getNodeNoEx((p3) + v3s16((v2).X, y, (v2).Y), valid)
113 // pos is the node the player is standing inside(!)
114 static bool detectSneakLadder(Map *map, INodeDefManager *nodemgr, v3s16 pos)
116 // Detects a structure known as "sneak ladder" or "sneak elevator"
117 // that relies on bugs to provide a fast means of vertical transportation,
118 // the bugs have since been fixed but this function remains to keep it working.
119 // NOTE: This is just entirely a huge hack and causes way too many problems.
120 bool is_valid_position;
122 // X/Z vectors for 4 neighboring nodes
123 static const v2s16 vecs[] = { v2s16(-1, 0), v2s16(1, 0), v2s16(0, -1), v2s16(0, 1) };
125 for (u16 i = 0; i < ARRLEN(vecs); i++) {
126 const v2s16 vec = vecs[i];
128 // walkability of bottom & top node should differ
129 node = GETNODE(map, pos, vec, 0, &is_valid_position);
130 if (!is_valid_position)
132 bool w = nodemgr->get(node).walkable;
133 node = GETNODE(map, pos, vec, 1, &is_valid_position);
134 if (!is_valid_position || w == nodemgr->get(node).walkable)
137 // check one more node above OR below with corresponding walkability
138 node = GETNODE(map, pos, vec, -1, &is_valid_position);
139 bool ok = is_valid_position && w != nodemgr->get(node).walkable;
141 node = GETNODE(map, pos, vec, 2, &is_valid_position);
142 ok = is_valid_position && w == nodemgr->get(node).walkable;
154 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
155 std::vector<CollisionInfo> *collision_info)
157 Map *map = &env->getMap();
158 INodeDefManager *nodemgr = m_client->ndef();
160 v3f position = getPosition();
162 // Copy parent position if local player is attached
165 setPosition(overridePosition);
166 m_sneak_node_exists = false;
170 // Skip collision detection if noclip mode is used
171 bool fly_allowed = m_client->checkLocalPrivilege("fly");
172 bool noclip = m_client->checkLocalPrivilege("noclip") &&
173 g_settings->getBool("noclip");
174 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
176 position += m_speed * dtime;
177 setPosition(position);
178 m_sneak_node_exists = false;
186 bool is_valid_position;
191 Check if player is in liquid (the oscillating value)
194 // If in liquid, the threshold of coming out is at higher y
197 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
198 node = map->getNodeNoEx(pp, &is_valid_position);
199 if (is_valid_position) {
200 in_liquid = nodemgr->get(node.getContent()).isLiquid();
201 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
206 // If not in liquid, the threshold of going in is at lower y
209 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
210 node = map->getNodeNoEx(pp, &is_valid_position);
211 if (is_valid_position) {
212 in_liquid = nodemgr->get(node.getContent()).isLiquid();
213 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
221 Check if player is in liquid (the stable value)
223 pp = floatToInt(position + v3f(0,0,0), BS);
224 node = map->getNodeNoEx(pp, &is_valid_position);
225 if (is_valid_position) {
226 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
228 in_liquid_stable = false;
232 Check if player is climbing
236 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
237 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
238 node = map->getNodeNoEx(pp, &is_valid_position);
239 bool is_valid_position2;
240 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
242 if (!(is_valid_position && is_valid_position2)) {
245 is_climbing = (nodemgr->get(node.getContent()).climbable
246 || nodemgr->get(node2.getContent()).climbable) && !free_move;
251 Collision uncertainty radius
252 Make it a bit larger than the maximum distance of movement
254 //f32 d = pos_max_d * 1.1;
255 // A fairly large value in here makes moving smoother
258 // This should always apply, otherwise there are glitches
259 sanity_check(d > pos_max_d);
261 // Max. distance (X, Z) over border for sneaking determined by collision box
262 // * 0.49 to keep the center just barely on the node
263 v3f sneak_max = m_collisionbox.getExtent() * 0.49;
264 if (m_sneak_ladder_detected)
265 sneak_max = v3f(0.4 * BS, 0, 0.4 * BS); // restore legacy behaviour
268 If sneaking, keep in range from the last walked node and don't
271 if (control.sneak && m_sneak_node_exists &&
272 !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
273 physics_override_sneak && !got_teleported) {
274 v3f sn_f = intToFloat(m_sneak_node, BS);
275 const v3f bmin = m_sneak_node_bb_top.MinEdge;
276 const v3f bmax = m_sneak_node_bb_top.MaxEdge;
278 position.X = rangelim(position.X,
279 sn_f.X+bmin.X - sneak_max.X, sn_f.X+bmax.X + sneak_max.X);
280 position.Z = rangelim(position.Z,
281 sn_f.Z+bmin.Z - sneak_max.Z, sn_f.Z+bmax.Z + sneak_max.Z);
282 // Because we keep the player collision box on the node,
283 // limiting position.Y is not necessary
285 if (m_sneak_ladder_detected) {
286 // this sometimes causes some weird slow sinking but *shrug*
287 m_speed.Y = MYMAX(m_speed.Y, 0);
292 got_teleported = false;
294 // TODO: this shouldn't be hardcoded but transmitted from server
295 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
298 player_stepheight += (0.6 * BS);
301 v3f accel_f = v3f(0,0,0);
303 collisionMoveResult result = collisionMoveSimple(env, m_client,
304 pos_max_d, m_collisionbox, player_stepheight, dtime,
305 &position, &m_speed, accel_f);
308 If the player's feet touch the topside of any node, this is
311 Player is allowed to jump when this is true.
313 bool touching_ground_was = touching_ground;
314 touching_ground = result.touching_ground;
316 // We want the top of the sneak node to be below the players feet
318 if (m_sneak_node_exists)
319 position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - 0.05 * BS;
321 position_y_mod = (1.0 - 0.05) * BS;
322 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
324 Check the nodes under the player to see from which node the
325 player is sneaking from, if any. If the node from under
326 the player has been removed, the player falls.
328 if (m_sneak_node_exists &&
329 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
330 m_old_node_below_type != "air") {
331 // Old node appears to have been removed; that is,
332 // it wasn't air before but now it is
333 m_need_to_get_new_sneak_node = false;
334 m_sneak_node_exists = false;
335 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
336 // We are on something, so make sure to recalculate the sneak
338 m_need_to_get_new_sneak_node = true;
341 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
342 v2f player_p2df(position.X, position.Z);
343 f32 min_distance_f = 100000.0 * BS;
344 v3s16 new_sneak_node = m_sneak_node;
345 for(s16 x=-1; x<=1; x++)
346 for(s16 z=-1; z<=1; z++)
348 v3s16 p = current_node + v3s16(x,0,z);
349 v3f pf = intToFloat(p, BS);
350 v2f node_p2df(pf.X, pf.Z);
351 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
353 if (distance_f > min_distance_f ||
354 fabs(player_p2df.X-node_p2df.X) > (.5+.1)*BS + sneak_max.X ||
355 fabs(player_p2df.Y-node_p2df.Y) > (.5+.1)*BS + sneak_max.Z)
359 // The node to be sneaked on has to be walkable
360 node = map->getNodeNoEx(p, &is_valid_position);
361 if (!is_valid_position || !nodemgr->get(node).walkable)
363 // And the node(s) above have to be nonwalkable
365 if (!physics_override_sneak_glitch) {
367 (m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
369 for (u16 y = 1; y <= height; y++) {
370 node = map->getNodeNoEx(p + v3s16(0,y,0), &is_valid_position);
371 if (!is_valid_position || nodemgr->get(node).walkable) {
377 // legacy behaviour: check just one node
378 node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
379 ok = is_valid_position && !nodemgr->get(node).walkable;
384 min_distance_f = distance_f;
388 bool sneak_node_found = (min_distance_f < 100000.0 * BS);
389 m_sneak_node = new_sneak_node;
390 m_sneak_node_exists = sneak_node_found;
392 if (sneak_node_found) {
393 // Update saved top bounding box of sneak node
394 MapNode n = map->getNodeNoEx(m_sneak_node);
395 std::vector<aabb3f> nodeboxes;
396 n.getCollisionBoxes(nodemgr, &nodeboxes);
397 m_sneak_node_bb_top = getTopBoundingBox(nodeboxes);
399 m_sneak_ladder_detected = physics_override_sneak_glitch &&
400 detectSneakLadder(map, nodemgr, floatToInt(position, BS));
402 m_sneak_ladder_detected = false;
409 setPosition(position);
415 // Dont report if flying
416 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
417 for(size_t i=0; i<result.collisions.size(); i++) {
418 const CollisionInfo &info = result.collisions[i];
419 collision_info->push_back(info);
423 if(!result.standing_on_object && !touching_ground_was && touching_ground) {
424 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
425 m_client->event()->put(e);
427 // Set camera impact value to be used for view bobbing
428 camera_impact = getSpeed().Y * -1;
432 camera_barely_in_ceiling = false;
433 v3s16 camera_np = floatToInt(getEyePosition(), BS);
434 MapNode n = map->getNodeNoEx(camera_np);
435 if(n.getContent() != CONTENT_IGNORE){
436 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
437 camera_barely_in_ceiling = true;
443 Update the node last under the player
445 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
446 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
449 Check properties of the node on which the player is standing
451 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
452 // Determine if jumping is possible
453 m_can_jump = touching_ground && !in_liquid;
454 if(itemgroup_get(f.groups, "disable_jump"))
456 // Jump key pressed while jumping off from a bouncy block
457 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
458 m_speed.Y >= -0.5 * BS) {
459 float jumpspeed = movement_speed_jump * physics_override_jump;
461 // Reduce boost when speed already is high
462 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
464 m_speed.Y += jumpspeed;
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)
479 swimming_vertical = false;
481 setPitch(control.pitch);
484 // Nullify speed and don't run positioning code if the player is attached
487 setSpeed(v3f(0,0,0));
491 v3f move_direction = v3f(0,0,1);
492 move_direction.rotateXZBy(getYaw());
494 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
495 v3f speedV = v3f(0,0,0); // Vertical (Y)
497 bool fly_allowed = m_client->checkLocalPrivilege("fly");
498 bool fast_allowed = m_client->checkLocalPrivilege("fast");
500 bool free_move = fly_allowed && g_settings->getBool("free_move");
501 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
502 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
503 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
504 bool continuous_forward = g_settings->getBool("continuous_forward");
505 bool always_fly_fast = g_settings->getBool("always_fly_fast");
507 // Whether superspeed mode is used or not
508 bool superspeed = false;
510 if (always_fly_fast && free_move && fast_move)
513 // Old descend control
514 if(g_settings->getBool("aux1_descends"))
516 // If free movement and fast movement, always move fast
517 if(free_move && fast_move)
520 // Auxiliary button 1 (E)
525 // In free movement mode, aux1 descends
527 speedV.Y = -movement_speed_fast;
529 speedV.Y = -movement_speed_walk;
531 else if(in_liquid || in_liquid_stable)
533 speedV.Y = -movement_speed_walk;
534 swimming_vertical = true;
538 speedV.Y = -movement_speed_climb;
542 // If not free movement but fast is allowed, aux1 is
549 // New minecraft-like descend control
552 // Auxiliary button 1 (E)
557 // aux1 is "Turbo button"
567 // In free movement mode, sneak descends
568 if (fast_move && (control.aux1 || always_fly_fast))
569 speedV.Y = -movement_speed_fast;
571 speedV.Y = -movement_speed_walk;
573 else if(in_liquid || in_liquid_stable)
576 speedV.Y = -movement_speed_fast;
578 speedV.Y = -movement_speed_walk;
579 swimming_vertical = true;
584 speedV.Y = -movement_speed_fast;
586 speedV.Y = -movement_speed_climb;
591 if (continuous_forward)
592 speedH += move_direction;
595 if (continuous_forward) {
599 speedH += move_direction;
603 speedH -= move_direction;
605 if (!control.up && !control.down) {
606 speedH -= move_direction *
607 (control.forw_move_joystick_axis / 32767.f);
610 speedH += move_direction.crossProduct(v3f(0,1,0));
613 speedH += move_direction.crossProduct(v3f(0,-1,0));
615 if (!control.left && !control.right) {
616 speedH -= move_direction.crossProduct(v3f(0,1,0)) *
617 (control.sidew_move_joystick_axis / 32767.f);
622 if (g_settings->getBool("aux1_descends") || always_fly_fast) {
624 speedV.Y = movement_speed_fast;
626 speedV.Y = movement_speed_walk;
628 if(fast_move && control.aux1)
629 speedV.Y = movement_speed_fast;
631 speedV.Y = movement_speed_walk;
634 else if(m_can_jump || (control.sneak && m_sneak_ladder_detected))
637 NOTE: The d value in move() affects jump height by
638 raising the height at which the jump speed is kept
639 at its starting value
641 v3f speedJ = getSpeed();
642 if(speedJ.Y >= -0.5 * BS)
644 speedJ.Y = movement_speed_jump * physics_override_jump;
647 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
648 m_client->event()->put(e);
654 speedV.Y = movement_speed_fast;
656 speedV.Y = movement_speed_walk;
657 swimming_vertical = true;
662 speedV.Y = movement_speed_fast;
664 speedV.Y = movement_speed_climb;
668 // The speed of the player (Y is ignored)
669 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
670 speedH = speedH.normalize() * movement_speed_fast;
671 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
672 speedH = speedH.normalize() * movement_speed_crouch;
674 speedH = speedH.normalize() * movement_speed_walk;
676 // Acceleration increase
677 f32 incH = 0; // Horizontal (X, Z)
678 f32 incV = 0; // Vertical (Y)
679 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
681 // Jumping and falling
682 if(superspeed || (fast_move && control.aux1))
683 incH = movement_acceleration_fast * BS * dtime;
685 incH = movement_acceleration_air * BS * dtime;
686 incV = 0; // No vertical acceleration in air
688 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
689 incH = incV = movement_acceleration_fast * BS * dtime;
691 incH = incV = movement_acceleration_default * BS * dtime;
693 // Accelerate to target speed with maximum increment
694 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
695 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
698 v3s16 LocalPlayer::getStandingNodePos()
700 if(m_sneak_node_exists)
702 return floatToInt(getPosition() - v3f(0, BS, 0), BS);
705 v3s16 LocalPlayer::getFootstepNodePos()
708 // BS * 0.05 below the player's feet ensures a 1/16th height
709 // nodebox is detected instead of the node below it.
710 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
711 // A larger distance below is necessary for a footstep sound
712 // when landing after a jump or fall. BS * 0.5 ensures water
713 // sounds when swimming in 1 node deep water.
714 return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
717 v3s16 LocalPlayer::getLightPosition() const
719 return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
722 v3f LocalPlayer::getEyeOffset() const
724 float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f;
725 return v3f(0, BS * eye_height, 0);
728 // Horizontal acceleration (X and Z), Y direction is ignored
729 void LocalPlayer::accelerateHorizontal(const v3f &target_speed, const f32 max_increase)
731 if (max_increase == 0)
734 v3f d_wanted = target_speed - m_speed;
736 f32 dl = d_wanted.getLength();
737 if (dl > max_increase)
740 v3f d = d_wanted.normalize() * dl;
746 // Vertical acceleration (Y), X and Z directions are ignored
747 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
749 if (max_increase == 0)
752 f32 d_wanted = target_speed.Y - m_speed.Y;
753 if (d_wanted > max_increase)
754 d_wanted = max_increase;
755 else if (d_wanted < -max_increase)
756 d_wanted = -max_increase;
758 m_speed.Y += d_wanted;