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_ledge_detected(false),
74 m_old_node_below(32767,32767,32767),
75 m_old_node_below_type("air"),
77 m_breath(PLAYER_MAX_BREATH),
80 camera_barely_in_ceiling(false),
81 m_collisionbox(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30),
85 // Initialize hp to 0, so that no hearts will be shown if server
86 // doesn't support health points
88 eye_offset_first = v3f(0,0,0);
89 eye_offset_third = v3f(0,0,0);
92 LocalPlayer::~LocalPlayer()
96 static aabb3f getTopBoundingBox(const std::vector<aabb3f> &nodeboxes)
99 b_max.reset(-BS, -BS, -BS);
100 for (std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
101 it != nodeboxes.end(); ++it) {
103 if (box.MaxEdge.Y > b_max.MaxEdge.Y)
105 else if (box.MaxEdge.Y == b_max.MaxEdge.Y)
106 b_max.addInternalBox(box);
108 return aabb3f(v3f(b_max.MinEdge.X, b_max.MaxEdge.Y, b_max.MinEdge.Z), b_max.MaxEdge);
111 #define GETNODE(map, p3, v2, y, valid) \
112 (map)->getNodeNoEx((p3) + v3s16((v2).X, y, (v2).Y), valid)
114 // pos is the node the player is standing inside(!)
115 static bool detectSneakLadder(Map *map, INodeDefManager *nodemgr, v3s16 pos)
117 // Detects a structure known as "sneak ladder" or "sneak elevator"
118 // that relies on bugs to provide a fast means of vertical transportation,
119 // the bugs have since been fixed but this function remains to keep it working.
120 // NOTE: This is just entirely a huge hack and causes way too many problems.
121 bool is_valid_position;
123 // X/Z vectors for 4 neighboring nodes
124 static const v2s16 vecs[] = { v2s16(-1, 0), v2s16(1, 0), v2s16(0, -1), v2s16(0, 1) };
126 for (u16 i = 0; i < ARRLEN(vecs); i++) {
127 const v2s16 vec = vecs[i];
129 // walkability of bottom & top node should differ
130 node = GETNODE(map, pos, vec, 0, &is_valid_position);
131 if (!is_valid_position)
133 bool w = nodemgr->get(node).walkable;
134 node = GETNODE(map, pos, vec, 1, &is_valid_position);
135 if (!is_valid_position || w == nodemgr->get(node).walkable)
138 // check one more node above OR below with corresponding walkability
139 node = GETNODE(map, pos, vec, -1, &is_valid_position);
140 bool ok = is_valid_position && w != nodemgr->get(node).walkable;
142 node = GETNODE(map, pos, vec, 2, &is_valid_position);
143 ok = is_valid_position && w == nodemgr->get(node).walkable;
153 static bool detectLedge(Map *map, INodeDefManager *nodemgr, v3s16 pos)
155 bool is_valid_position;
157 // X/Z vectors for 4 neighboring nodes
158 static const v2s16 vecs[] = {v2s16(-1, 0), v2s16(1, 0), v2s16(0, -1), v2s16(0, 1)};
160 for (u16 i = 0; i < ARRLEN(vecs); i++) {
161 const v2s16 vec = vecs[i];
163 node = GETNODE(map, pos, vec, 1, &is_valid_position);
164 if (is_valid_position && nodemgr->get(node).walkable) {
166 node = GETNODE(map, pos, vec, 2, &is_valid_position);
167 if (is_valid_position && !nodemgr->get(node).walkable)
168 // Space above ledge exists
178 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
179 std::vector<CollisionInfo> *collision_info)
181 Map *map = &env->getMap();
182 INodeDefManager *nodemgr = m_client->ndef();
184 v3f position = getPosition();
186 // Copy parent position if local player is attached
189 setPosition(overridePosition);
190 m_sneak_node_exists = false;
194 // Skip collision detection if noclip mode is used
195 bool fly_allowed = m_client->checkLocalPrivilege("fly");
196 bool noclip = m_client->checkLocalPrivilege("noclip") &&
197 g_settings->getBool("noclip");
198 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
200 position += m_speed * dtime;
201 setPosition(position);
202 m_sneak_node_exists = false;
210 bool is_valid_position;
215 Check if player is in liquid (the oscillating value)
218 // If in liquid, the threshold of coming out is at higher y
221 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
222 node = map->getNodeNoEx(pp, &is_valid_position);
223 if (is_valid_position) {
224 in_liquid = nodemgr->get(node.getContent()).isLiquid();
225 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
230 // If not in liquid, the threshold of going in is at lower y
233 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
234 node = map->getNodeNoEx(pp, &is_valid_position);
235 if (is_valid_position) {
236 in_liquid = nodemgr->get(node.getContent()).isLiquid();
237 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
245 Check if player is in liquid (the stable value)
247 pp = floatToInt(position + v3f(0,0,0), BS);
248 node = map->getNodeNoEx(pp, &is_valid_position);
249 if (is_valid_position) {
250 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
252 in_liquid_stable = false;
256 Check if player is climbing
260 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
261 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
262 node = map->getNodeNoEx(pp, &is_valid_position);
263 bool is_valid_position2;
264 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
266 if (!(is_valid_position && is_valid_position2)) {
269 is_climbing = (nodemgr->get(node.getContent()).climbable
270 || nodemgr->get(node2.getContent()).climbable) && !free_move;
275 Collision uncertainty radius
276 Make it a bit larger than the maximum distance of movement
278 //f32 d = pos_max_d * 1.1;
279 // A fairly large value in here makes moving smoother
282 // This should always apply, otherwise there are glitches
283 sanity_check(d > pos_max_d);
285 // Max. distance (X, Z) over border for sneaking determined by collision box
286 // * 0.49 to keep the center just barely on the node
287 v3f sneak_max = m_collisionbox.getExtent() * 0.49;
288 if (m_sneak_ladder_detected) {
289 // restore legacy behaviour (this makes the m_speed.Y hack necessary)
290 sneak_max = v3f(0.4 * BS, 0, 0.4 * BS);
294 If sneaking, keep in range from the last walked node and don't
297 if (control.sneak && m_sneak_node_exists &&
298 !(fly_allowed && g_settings->getBool("free_move")) &&
299 !in_liquid && !is_climbing &&
300 physics_override_sneak && !got_teleported) {
301 v3f sn_f = intToFloat(m_sneak_node, BS);
302 const v3f bmin = m_sneak_node_bb_top.MinEdge;
303 const v3f bmax = m_sneak_node_bb_top.MaxEdge;
305 position.X = rangelim(position.X,
306 sn_f.X+bmin.X - sneak_max.X, sn_f.X+bmax.X + sneak_max.X);
307 position.Z = rangelim(position.Z,
308 sn_f.Z+bmin.Z - sneak_max.Z, sn_f.Z+bmax.Z + sneak_max.Z);
310 // Because we keep the player collision box on the node, limiting
311 // position.Y is not necessary but useful to prevent players from
312 // being inside a node if sneaking on e.g. the lower part of a stair
313 if (!m_sneak_ladder_detected) {
314 position.Y = MYMAX(position.Y, sn_f.Y+bmax.Y);
316 // legacy behaviour that sometimes causes some weird slow sinking
317 m_speed.Y = MYMAX(m_speed.Y, 0);
322 got_teleported = false;
324 // TODO: this shouldn't be hardcoded but transmitted from server
325 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
328 player_stepheight += (0.6 * BS);
331 v3f accel_f = v3f(0,0,0);
333 collisionMoveResult result = collisionMoveSimple(env, m_client,
334 pos_max_d, m_collisionbox, player_stepheight, dtime,
335 &position, &m_speed, accel_f);
338 If the player's feet touch the topside of any node, this is
341 Player is allowed to jump when this is true.
343 bool touching_ground_was = touching_ground;
344 touching_ground = result.touching_ground;
346 // We want the top of the sneak node to be below the players feet
348 if (m_sneak_node_exists)
349 position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - 0.05 * BS;
351 position_y_mod = (0.5 - 0.05) * BS;
352 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
354 Check the nodes under the player to see from which node the
355 player is sneaking from, if any. If the node from under
356 the player has been removed, the player falls.
358 if (m_sneak_node_exists &&
359 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
360 m_old_node_below_type != "air") {
361 // Old node appears to have been removed; that is,
362 // it wasn't air before but now it is
363 m_need_to_get_new_sneak_node = false;
364 m_sneak_node_exists = false;
365 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
366 // We are on something, so make sure to recalculate the sneak
368 m_need_to_get_new_sneak_node = true;
371 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
372 v2f player_p2df(position.X, position.Z);
373 f32 min_distance_f = 100000.0 * BS;
374 v3s16 new_sneak_node = m_sneak_node;
375 for(s16 x=-1; x<=1; x++)
376 for(s16 z=-1; z<=1; z++)
378 v3s16 p = current_node + v3s16(x,0,z);
379 v3f pf = intToFloat(p, BS);
380 v2f node_p2df(pf.X, pf.Z);
381 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
383 if (distance_f > min_distance_f ||
384 fabs(player_p2df.X-node_p2df.X) > (.5+.1)*BS + sneak_max.X ||
385 fabs(player_p2df.Y-node_p2df.Y) > (.5+.1)*BS + sneak_max.Z)
389 // The node to be sneaked on has to be walkable
390 node = map->getNodeNoEx(p, &is_valid_position);
391 if (!is_valid_position || !nodemgr->get(node).walkable)
393 // And the node(s) above have to be nonwalkable
395 if (!physics_override_sneak_glitch) {
397 (m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
399 for (u16 y = 1; y <= height; y++) {
400 node = map->getNodeNoEx(p + v3s16(0,y,0), &is_valid_position);
401 if (!is_valid_position || nodemgr->get(node).walkable) {
407 // legacy behaviour: check just one node
408 node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
409 ok = is_valid_position && !nodemgr->get(node).walkable;
414 min_distance_f = distance_f;
418 bool sneak_node_found = (min_distance_f < 100000.0 * BS);
419 m_sneak_node = new_sneak_node;
420 m_sneak_node_exists = sneak_node_found;
422 if (sneak_node_found) {
423 // Update saved top bounding box of sneak node
424 MapNode n = map->getNodeNoEx(m_sneak_node);
425 std::vector<aabb3f> nodeboxes;
426 n.getCollisionBoxes(nodemgr, &nodeboxes);
427 m_sneak_node_bb_top = getTopBoundingBox(nodeboxes);
429 m_sneak_ladder_detected = physics_override_sneak_glitch &&
430 detectSneakLadder(map, nodemgr, floatToInt(position, BS));
432 m_sneak_ladder_detected = false;
437 If 'sneak glitch' enabled detect ledge for old sneak-jump
438 behaviour of climbing onto a ledge 2 nodes up.
440 if (physics_override_sneak_glitch && control.sneak && control.jump)
441 m_ledge_detected = detectLedge(map, nodemgr, floatToInt(position, BS));
446 setPosition(position);
452 // Dont report if flying
453 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
454 for(size_t i=0; i<result.collisions.size(); i++) {
455 const CollisionInfo &info = result.collisions[i];
456 collision_info->push_back(info);
460 if(!result.standing_on_object && !touching_ground_was && touching_ground) {
461 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
462 m_client->event()->put(e);
464 // Set camera impact value to be used for view bobbing
465 camera_impact = getSpeed().Y * -1;
469 camera_barely_in_ceiling = false;
470 v3s16 camera_np = floatToInt(getEyePosition(), BS);
471 MapNode n = map->getNodeNoEx(camera_np);
472 if(n.getContent() != CONTENT_IGNORE){
473 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
474 camera_barely_in_ceiling = true;
480 Update the node last under the player
482 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
483 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
486 Check properties of the node on which the player is standing
488 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
489 // Determine if jumping is possible
490 m_can_jump = touching_ground && !in_liquid;
491 if (itemgroup_get(f.groups, "disable_jump"))
493 else if (control.sneak && m_sneak_ladder_detected && !in_liquid && !is_climbing)
496 // Jump key pressed while jumping off from a bouncy block
497 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
498 m_speed.Y >= -0.5 * BS) {
499 float jumpspeed = movement_speed_jump * physics_override_jump;
501 // Reduce boost when speed already is high
502 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
504 m_speed.Y += jumpspeed;
511 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
513 move(dtime, env, pos_max_d, NULL);
516 void LocalPlayer::applyControl(float dtime)
519 swimming_vertical = false;
521 setPitch(control.pitch);
524 // Nullify speed and don't run positioning code if the player is attached
527 setSpeed(v3f(0,0,0));
531 v3f move_direction = v3f(0,0,1);
532 move_direction.rotateXZBy(getYaw());
534 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
535 v3f speedV = v3f(0,0,0); // Vertical (Y)
537 bool fly_allowed = m_client->checkLocalPrivilege("fly");
538 bool fast_allowed = m_client->checkLocalPrivilege("fast");
540 bool free_move = fly_allowed && g_settings->getBool("free_move");
541 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
542 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
543 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
544 bool continuous_forward = g_settings->getBool("continuous_forward");
545 bool always_fly_fast = g_settings->getBool("always_fly_fast");
547 // Whether superspeed mode is used or not
548 bool superspeed = false;
550 if (always_fly_fast && free_move && fast_move)
553 // Old descend control
554 if(g_settings->getBool("aux1_descends"))
556 // If free movement and fast movement, always move fast
557 if(free_move && fast_move)
560 // Auxiliary button 1 (E)
565 // In free movement mode, aux1 descends
567 speedV.Y = -movement_speed_fast;
569 speedV.Y = -movement_speed_walk;
571 else if(in_liquid || in_liquid_stable)
573 speedV.Y = -movement_speed_walk;
574 swimming_vertical = true;
578 speedV.Y = -movement_speed_climb;
582 // If not free movement but fast is allowed, aux1 is
589 // New minecraft-like descend control
592 // Auxiliary button 1 (E)
597 // aux1 is "Turbo button"
607 // In free movement mode, sneak descends
608 if (fast_move && (control.aux1 || always_fly_fast))
609 speedV.Y = -movement_speed_fast;
611 speedV.Y = -movement_speed_walk;
613 else if(in_liquid || in_liquid_stable)
616 speedV.Y = -movement_speed_fast;
618 speedV.Y = -movement_speed_walk;
619 swimming_vertical = true;
624 speedV.Y = -movement_speed_fast;
626 speedV.Y = -movement_speed_climb;
631 if (continuous_forward)
632 speedH += move_direction;
635 if (continuous_forward) {
639 speedH += move_direction;
643 speedH -= move_direction;
645 if (!control.up && !control.down) {
646 speedH -= move_direction *
647 (control.forw_move_joystick_axis / 32767.f);
650 speedH += move_direction.crossProduct(v3f(0,1,0));
653 speedH += move_direction.crossProduct(v3f(0,-1,0));
655 if (!control.left && !control.right) {
656 speedH -= move_direction.crossProduct(v3f(0,1,0)) *
657 (control.sidew_move_joystick_axis / 32767.f);
662 if (g_settings->getBool("aux1_descends") || always_fly_fast) {
664 speedV.Y = movement_speed_fast;
666 speedV.Y = movement_speed_walk;
668 if(fast_move && control.aux1)
669 speedV.Y = movement_speed_fast;
671 speedV.Y = movement_speed_walk;
677 NOTE: The d value in move() affects jump height by
678 raising the height at which the jump speed is kept
679 at its starting value
681 v3f speedJ = getSpeed();
682 if(speedJ.Y >= -0.5 * BS) {
683 if (m_ledge_detected) {
684 // Limit jump speed to a minimum that allows
685 // jumping up onto a ledge 2 nodes up.
686 speedJ.Y = movement_speed_jump *
687 MYMAX(physics_override_jump, 1.3f);
689 m_ledge_detected = false;
691 speedJ.Y = movement_speed_jump * physics_override_jump;
695 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
696 m_client->event()->put(e);
702 speedV.Y = movement_speed_fast;
704 speedV.Y = movement_speed_walk;
705 swimming_vertical = true;
710 speedV.Y = movement_speed_fast;
712 speedV.Y = movement_speed_climb;
716 // The speed of the player (Y is ignored)
717 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
718 speedH = speedH.normalize() * movement_speed_fast;
719 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
720 speedH = speedH.normalize() * movement_speed_crouch;
722 speedH = speedH.normalize() * movement_speed_walk;
724 // Acceleration increase
725 f32 incH = 0; // Horizontal (X, Z)
726 f32 incV = 0; // Vertical (Y)
727 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
729 // Jumping and falling
730 if(superspeed || (fast_move && control.aux1))
731 incH = movement_acceleration_fast * BS * dtime;
733 incH = movement_acceleration_air * BS * dtime;
734 incV = 0; // No vertical acceleration in air
736 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
737 incH = incV = movement_acceleration_fast * BS * dtime;
739 incH = incV = movement_acceleration_default * BS * dtime;
741 // Accelerate to target speed with maximum increment
742 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
743 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
746 v3s16 LocalPlayer::getStandingNodePos()
748 if(m_sneak_node_exists)
750 return floatToInt(getPosition() - v3f(0, BS, 0), BS);
753 v3s16 LocalPlayer::getFootstepNodePos()
756 // BS * 0.05 below the player's feet ensures a 1/16th height
757 // nodebox is detected instead of the node below it.
758 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
759 // A larger distance below is necessary for a footstep sound
760 // when landing after a jump or fall. BS * 0.5 ensures water
761 // sounds when swimming in 1 node deep water.
762 return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
765 v3s16 LocalPlayer::getLightPosition() const
767 return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
770 v3f LocalPlayer::getEyeOffset() const
772 float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f;
773 return v3f(0, BS * eye_height, 0);
776 // Horizontal acceleration (X and Z), Y direction is ignored
777 void LocalPlayer::accelerateHorizontal(const v3f &target_speed, const f32 max_increase)
779 if (max_increase == 0)
782 v3f d_wanted = target_speed - m_speed;
784 f32 dl = d_wanted.getLength();
785 if (dl > max_increase)
788 v3f d = d_wanted.normalize() * dl;
794 // Vertical acceleration (Y), X and Z directions are ignored
795 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
797 if (max_increase == 0)
800 f32 d_wanted = target_speed.Y - m_speed.Y;
801 if (d_wanted > max_increase)
802 d_wanted = max_increase;
803 else if (d_wanted < -max_increase)
804 d_wanted = -max_increase;
806 m_speed.Y += d_wanted;