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 // restore legacy behaviour (this makes the m_speed.Y hack necessary)
266 sneak_max = v3f(0.4 * BS, 0, 0.4 * BS);
270 If sneaking, keep in range from the last walked node and don't
273 if (control.sneak && m_sneak_node_exists &&
274 !(fly_allowed && g_settings->getBool("free_move")) &&
275 !in_liquid && !is_climbing &&
276 physics_override_sneak && !got_teleported) {
277 v3f sn_f = intToFloat(m_sneak_node, BS);
278 const v3f bmin = m_sneak_node_bb_top.MinEdge;
279 const v3f bmax = m_sneak_node_bb_top.MaxEdge;
281 position.X = rangelim(position.X,
282 sn_f.X+bmin.X - sneak_max.X, sn_f.X+bmax.X + sneak_max.X);
283 position.Z = rangelim(position.Z,
284 sn_f.Z+bmin.Z - sneak_max.Z, sn_f.Z+bmax.Z + sneak_max.Z);
286 // Because we keep the player collision box on the node, limiting
287 // position.Y is not necessary but useful to prevent players from
288 // being inside a node if sneaking on e.g. the lower part of a stair
289 if (!m_sneak_ladder_detected) {
290 position.Y = MYMAX(position.Y, sn_f.Y+bmax.Y);
292 // legacy behaviour that sometimes causes some weird slow sinking
293 m_speed.Y = MYMAX(m_speed.Y, 0);
298 got_teleported = false;
300 // TODO: this shouldn't be hardcoded but transmitted from server
301 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
304 player_stepheight += (0.6 * BS);
307 v3f accel_f = v3f(0,0,0);
309 collisionMoveResult result = collisionMoveSimple(env, m_client,
310 pos_max_d, m_collisionbox, player_stepheight, dtime,
311 &position, &m_speed, accel_f);
314 If the player's feet touch the topside of any node, this is
317 Player is allowed to jump when this is true.
319 bool touching_ground_was = touching_ground;
320 touching_ground = result.touching_ground;
322 // We want the top of the sneak node to be below the players feet
324 if (m_sneak_node_exists)
325 position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - 0.05 * BS;
327 position_y_mod = (1.0 - 0.05) * BS;
328 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
330 Check the nodes under the player to see from which node the
331 player is sneaking from, if any. If the node from under
332 the player has been removed, the player falls.
334 if (m_sneak_node_exists &&
335 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
336 m_old_node_below_type != "air") {
337 // Old node appears to have been removed; that is,
338 // it wasn't air before but now it is
339 m_need_to_get_new_sneak_node = false;
340 m_sneak_node_exists = false;
341 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
342 // We are on something, so make sure to recalculate the sneak
344 m_need_to_get_new_sneak_node = true;
347 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
348 v2f player_p2df(position.X, position.Z);
349 f32 min_distance_f = 100000.0 * BS;
350 v3s16 new_sneak_node = m_sneak_node;
351 for(s16 x=-1; x<=1; x++)
352 for(s16 z=-1; z<=1; z++)
354 v3s16 p = current_node + v3s16(x,0,z);
355 v3f pf = intToFloat(p, BS);
356 v2f node_p2df(pf.X, pf.Z);
357 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
359 if (distance_f > min_distance_f ||
360 fabs(player_p2df.X-node_p2df.X) > (.5+.1)*BS + sneak_max.X ||
361 fabs(player_p2df.Y-node_p2df.Y) > (.5+.1)*BS + sneak_max.Z)
365 // The node to be sneaked on has to be walkable
366 node = map->getNodeNoEx(p, &is_valid_position);
367 if (!is_valid_position || !nodemgr->get(node).walkable)
369 // And the node(s) above have to be nonwalkable
371 if (!physics_override_sneak_glitch) {
373 (m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
375 for (u16 y = 1; y <= height; y++) {
376 node = map->getNodeNoEx(p + v3s16(0,y,0), &is_valid_position);
377 if (!is_valid_position || nodemgr->get(node).walkable) {
383 // legacy behaviour: check just one node
384 node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
385 ok = is_valid_position && !nodemgr->get(node).walkable;
390 min_distance_f = distance_f;
394 bool sneak_node_found = (min_distance_f < 100000.0 * BS);
395 m_sneak_node = new_sneak_node;
396 m_sneak_node_exists = sneak_node_found;
398 if (sneak_node_found) {
399 // Update saved top bounding box of sneak node
400 MapNode n = map->getNodeNoEx(m_sneak_node);
401 std::vector<aabb3f> nodeboxes;
402 n.getCollisionBoxes(nodemgr, &nodeboxes);
403 m_sneak_node_bb_top = getTopBoundingBox(nodeboxes);
405 m_sneak_ladder_detected = physics_override_sneak_glitch &&
406 detectSneakLadder(map, nodemgr, floatToInt(position, BS));
408 m_sneak_ladder_detected = false;
415 setPosition(position);
421 // Dont report if flying
422 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
423 for(size_t i=0; i<result.collisions.size(); i++) {
424 const CollisionInfo &info = result.collisions[i];
425 collision_info->push_back(info);
429 if(!result.standing_on_object && !touching_ground_was && touching_ground) {
430 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
431 m_client->event()->put(e);
433 // Set camera impact value to be used for view bobbing
434 camera_impact = getSpeed().Y * -1;
438 camera_barely_in_ceiling = false;
439 v3s16 camera_np = floatToInt(getEyePosition(), BS);
440 MapNode n = map->getNodeNoEx(camera_np);
441 if(n.getContent() != CONTENT_IGNORE){
442 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
443 camera_barely_in_ceiling = true;
449 Update the node last under the player
451 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
452 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
455 Check properties of the node on which the player is standing
457 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
458 // Determine if jumping is possible
459 m_can_jump = touching_ground && !in_liquid;
460 if (itemgroup_get(f.groups, "disable_jump"))
462 else if (control.sneak && m_sneak_ladder_detected && !in_liquid && !is_climbing)
465 // Jump key pressed while jumping off from a bouncy block
466 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
467 m_speed.Y >= -0.5 * BS) {
468 float jumpspeed = movement_speed_jump * physics_override_jump;
470 // Reduce boost when speed already is high
471 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
473 m_speed.Y += jumpspeed;
480 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
482 move(dtime, env, pos_max_d, NULL);
485 void LocalPlayer::applyControl(float dtime)
488 swimming_vertical = false;
490 setPitch(control.pitch);
493 // Nullify speed and don't run positioning code if the player is attached
496 setSpeed(v3f(0,0,0));
500 v3f move_direction = v3f(0,0,1);
501 move_direction.rotateXZBy(getYaw());
503 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
504 v3f speedV = v3f(0,0,0); // Vertical (Y)
506 bool fly_allowed = m_client->checkLocalPrivilege("fly");
507 bool fast_allowed = m_client->checkLocalPrivilege("fast");
509 bool free_move = fly_allowed && g_settings->getBool("free_move");
510 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
511 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
512 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
513 bool continuous_forward = g_settings->getBool("continuous_forward");
514 bool always_fly_fast = g_settings->getBool("always_fly_fast");
516 // Whether superspeed mode is used or not
517 bool superspeed = false;
519 if (always_fly_fast && free_move && fast_move)
522 // Old descend control
523 if(g_settings->getBool("aux1_descends"))
525 // If free movement and fast movement, always move fast
526 if(free_move && fast_move)
529 // Auxiliary button 1 (E)
534 // In free movement mode, aux1 descends
536 speedV.Y = -movement_speed_fast;
538 speedV.Y = -movement_speed_walk;
540 else if(in_liquid || in_liquid_stable)
542 speedV.Y = -movement_speed_walk;
543 swimming_vertical = true;
547 speedV.Y = -movement_speed_climb;
551 // If not free movement but fast is allowed, aux1 is
558 // New minecraft-like descend control
561 // Auxiliary button 1 (E)
566 // aux1 is "Turbo button"
576 // In free movement mode, sneak descends
577 if (fast_move && (control.aux1 || always_fly_fast))
578 speedV.Y = -movement_speed_fast;
580 speedV.Y = -movement_speed_walk;
582 else if(in_liquid || in_liquid_stable)
585 speedV.Y = -movement_speed_fast;
587 speedV.Y = -movement_speed_walk;
588 swimming_vertical = true;
593 speedV.Y = -movement_speed_fast;
595 speedV.Y = -movement_speed_climb;
600 if (continuous_forward)
601 speedH += move_direction;
604 if (continuous_forward) {
608 speedH += move_direction;
612 speedH -= move_direction;
614 if (!control.up && !control.down) {
615 speedH -= move_direction *
616 (control.forw_move_joystick_axis / 32767.f);
619 speedH += move_direction.crossProduct(v3f(0,1,0));
622 speedH += move_direction.crossProduct(v3f(0,-1,0));
624 if (!control.left && !control.right) {
625 speedH -= move_direction.crossProduct(v3f(0,1,0)) *
626 (control.sidew_move_joystick_axis / 32767.f);
631 if (g_settings->getBool("aux1_descends") || always_fly_fast) {
633 speedV.Y = movement_speed_fast;
635 speedV.Y = movement_speed_walk;
637 if(fast_move && control.aux1)
638 speedV.Y = movement_speed_fast;
640 speedV.Y = movement_speed_walk;
646 NOTE: The d value in move() affects jump height by
647 raising the height at which the jump speed is kept
648 at its starting value
650 v3f speedJ = getSpeed();
651 if(speedJ.Y >= -0.5 * BS)
653 speedJ.Y = movement_speed_jump * physics_override_jump;
656 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
657 m_client->event()->put(e);
663 speedV.Y = movement_speed_fast;
665 speedV.Y = movement_speed_walk;
666 swimming_vertical = true;
671 speedV.Y = movement_speed_fast;
673 speedV.Y = movement_speed_climb;
677 // The speed of the player (Y is ignored)
678 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
679 speedH = speedH.normalize() * movement_speed_fast;
680 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
681 speedH = speedH.normalize() * movement_speed_crouch;
683 speedH = speedH.normalize() * movement_speed_walk;
685 // Acceleration increase
686 f32 incH = 0; // Horizontal (X, Z)
687 f32 incV = 0; // Vertical (Y)
688 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
690 // Jumping and falling
691 if(superspeed || (fast_move && control.aux1))
692 incH = movement_acceleration_fast * BS * dtime;
694 incH = movement_acceleration_air * BS * dtime;
695 incV = 0; // No vertical acceleration in air
697 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
698 incH = incV = movement_acceleration_fast * BS * dtime;
700 incH = incV = movement_acceleration_default * BS * dtime;
702 // Accelerate to target speed with maximum increment
703 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
704 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
707 v3s16 LocalPlayer::getStandingNodePos()
709 if(m_sneak_node_exists)
711 return floatToInt(getPosition() - v3f(0, BS, 0), BS);
714 v3s16 LocalPlayer::getFootstepNodePos()
717 // BS * 0.05 below the player's feet ensures a 1/16th height
718 // nodebox is detected instead of the node below it.
719 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
720 // A larger distance below is necessary for a footstep sound
721 // when landing after a jump or fall. BS * 0.5 ensures water
722 // sounds when swimming in 1 node deep water.
723 return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
726 v3s16 LocalPlayer::getLightPosition() const
728 return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
731 v3f LocalPlayer::getEyeOffset() const
733 float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f;
734 return v3f(0, BS * eye_height, 0);
737 // Horizontal acceleration (X and Z), Y direction is ignored
738 void LocalPlayer::accelerateHorizontal(const v3f &target_speed, const f32 max_increase)
740 if (max_increase == 0)
743 v3f d_wanted = target_speed - m_speed;
745 f32 dl = d_wanted.getLength();
746 if (dl > max_increase)
749 v3f d = d_wanted.normalize() * dl;
755 // Vertical acceleration (Y), X and Z directions are ignored
756 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
758 if (max_increase == 0)
761 f32 d_wanted = target_speed.Y - m_speed.Y;
762 if (d_wanted > max_increase)
763 d_wanted = max_increase;
764 else if (d_wanted < -max_increase)
765 d_wanted = -max_increase;
767 m_speed.Y += d_wanted;