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_exists(false),
70 m_need_to_get_new_sneak_node(true),
71 m_sneak_node_bb_top(0,0,0,0,0,0),
72 m_old_node_below(32767,32767,32767),
73 m_old_node_below_type("air"),
75 m_breath(PLAYER_MAX_BREATH),
78 camera_barely_in_ceiling(false),
79 m_collisionbox(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30),
83 // Initialize hp to 0, so that no hearts will be shown if server
84 // doesn't support health points
86 eye_offset_first = v3f(0,0,0);
87 eye_offset_third = v3f(0,0,0);
90 LocalPlayer::~LocalPlayer()
94 static aabb3f getTopBoundingBox(const std::vector<aabb3f> &nodeboxes, float max_d=1/16*BS)
97 b_max.reset(-BS, -BS, -BS);
98 for (std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
99 it != nodeboxes.end(); ++it) {
101 if (box.MaxEdge.Y > b_max.MaxEdge.Y)
103 else if (box.MaxEdge.Y == b_max.MaxEdge.Y)
104 b_max.addInternalBox(box);
106 return aabb3f(v3f(b_max.MinEdge.X, b_max.MaxEdge.Y, b_max.MinEdge.Z), b_max.MaxEdge);
109 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
110 std::vector<CollisionInfo> *collision_info)
112 Map *map = &env->getMap();
113 INodeDefManager *nodemgr = m_client->ndef();
115 v3f position = getPosition();
117 // Copy parent position if local player is attached
120 setPosition(overridePosition);
121 m_sneak_node_exists = false;
125 // Skip collision detection if noclip mode is used
126 bool fly_allowed = m_client->checkLocalPrivilege("fly");
127 bool noclip = m_client->checkLocalPrivilege("noclip") &&
128 g_settings->getBool("noclip");
129 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
131 position += m_speed * dtime;
132 setPosition(position);
133 m_sneak_node_exists = false;
141 bool is_valid_position;
146 Check if player is in liquid (the oscillating value)
149 // If in liquid, the threshold of coming out is at higher y
152 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
153 node = map->getNodeNoEx(pp, &is_valid_position);
154 if (is_valid_position) {
155 in_liquid = nodemgr->get(node.getContent()).isLiquid();
156 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
161 // If not in liquid, the threshold of going in is at lower y
164 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
165 node = map->getNodeNoEx(pp, &is_valid_position);
166 if (is_valid_position) {
167 in_liquid = nodemgr->get(node.getContent()).isLiquid();
168 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
176 Check if player is in liquid (the stable value)
178 pp = floatToInt(position + v3f(0,0,0), BS);
179 node = map->getNodeNoEx(pp, &is_valid_position);
180 if (is_valid_position) {
181 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
183 in_liquid_stable = false;
187 Check if player is climbing
191 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
192 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
193 node = map->getNodeNoEx(pp, &is_valid_position);
194 bool is_valid_position2;
195 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
197 if (!(is_valid_position && is_valid_position2)) {
200 is_climbing = (nodemgr->get(node.getContent()).climbable
201 || nodemgr->get(node2.getContent()).climbable) && !free_move;
206 Collision uncertainty radius
207 Make it a bit larger than the maximum distance of movement
209 //f32 d = pos_max_d * 1.1;
210 // A fairly large value in here makes moving smoother
213 // This should always apply, otherwise there are glitches
214 sanity_check(d > pos_max_d);
216 // Max. distance (X, Z) over border for sneaking determined by collision box
217 // * 0.49 to keep the center just barely on the node
218 v3f sneak_max = m_collisionbox.getExtent() * 0.49;
221 If sneaking, keep in range from the last walked node and don't
224 if (control.sneak && m_sneak_node_exists &&
225 !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
226 physics_override_sneak && !got_teleported) {
227 v3f sn_f = intToFloat(m_sneak_node, BS);
228 const v3f bmin = m_sneak_node_bb_top.MinEdge;
229 const v3f bmax = m_sneak_node_bb_top.MaxEdge;
231 position.X = rangelim(position.X,
232 sn_f.X+bmin.X - sneak_max.X, sn_f.X+bmax.X + sneak_max.X);
233 position.Z = rangelim(position.Z,
234 sn_f.Z+bmin.Z - sneak_max.Z, sn_f.Z+bmax.Z + sneak_max.Z);
235 // Because we keep the player collision box on the node,
236 // limiting position.Y is not necessary
240 got_teleported = false;
242 // TODO: this shouldn't be hardcoded but transmitted from server
243 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
246 player_stepheight += (0.6 * BS);
249 v3f accel_f = v3f(0,0,0);
251 collisionMoveResult result = collisionMoveSimple(env, m_client,
252 pos_max_d, m_collisionbox, player_stepheight, dtime,
253 &position, &m_speed, accel_f);
256 If the player's feet touch the topside of any node, this is
259 Player is allowed to jump when this is true.
261 bool touching_ground_was = touching_ground;
262 touching_ground = result.touching_ground;
264 //bool standing_on_unloaded = result.standing_on_unloaded;
266 // We want the top of the sneak node to be below the players feet
268 if (m_sneak_node_exists)
269 position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - 0.05 * BS;
271 position_y_mod = (1.0 - 0.05) * BS;
272 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
274 Check the nodes under the player to see from which node the
275 player is sneaking from, if any. If the node from under
276 the player has been removed, the player falls.
278 if (m_sneak_node_exists &&
279 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
280 m_old_node_below_type != "air") {
281 // Old node appears to have been removed; that is,
282 // it wasn't air before but now it is
283 m_need_to_get_new_sneak_node = false;
284 m_sneak_node_exists = false;
285 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
286 // We are on something, so make sure to recalculate the sneak
288 m_need_to_get_new_sneak_node = true;
291 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
292 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
293 v2f player_p2df(position.X, position.Z);
294 f32 min_distance_f = 100000.0 * BS;
295 v3s16 new_sneak_node = m_sneak_node;
296 for(s16 x=-1; x<=1; x++)
297 for(s16 z=-1; z<=1; z++)
299 v3s16 p = pos_i_bottom + v3s16(x,0,z);
300 v3f pf = intToFloat(p, BS);
301 v2f node_p2df(pf.X, pf.Z);
302 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
304 if (distance_f > min_distance_f ||
305 fabs(player_p2df.X-node_p2df.X) > (.5+.1)*BS + sneak_max.X ||
306 fabs(player_p2df.Y-node_p2df.Y) > (.5+.1)*BS + sneak_max.Z)
310 // The node to be sneaked on has to be walkable
311 node = map->getNodeNoEx(p, &is_valid_position);
312 if (!is_valid_position || nodemgr->get(node).walkable == false)
314 // And the node above it has to be nonwalkable
315 node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
316 if (!is_valid_position || nodemgr->get(node).walkable)
318 if (!physics_override_sneak_glitch) {
319 node = map->getNodeNoEx(p + v3s16(0,2,0), &is_valid_position);
320 if (!is_valid_position || nodemgr->get(node).walkable)
324 min_distance_f = distance_f;
328 bool sneak_node_found = (min_distance_f < 100000.0 * BS);
329 m_sneak_node = new_sneak_node;
330 m_sneak_node_exists = sneak_node_found;
332 // Update saved top bounding box of sneak node
333 if (sneak_node_found) {
334 MapNode n = map->getNodeNoEx(m_sneak_node);
335 std::vector<aabb3f> nodeboxes;
336 n.getCollisionBoxes(nodemgr, &nodeboxes);
337 m_sneak_node_bb_top = getTopBoundingBox(nodeboxes);
344 setPosition(position);
350 // Dont report if flying
351 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
352 for(size_t i=0; i<result.collisions.size(); i++) {
353 const CollisionInfo &info = result.collisions[i];
354 collision_info->push_back(info);
358 if(!result.standing_on_object && !touching_ground_was && touching_ground) {
359 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
360 m_client->event()->put(e);
362 // Set camera impact value to be used for view bobbing
363 camera_impact = getSpeed().Y * -1;
367 camera_barely_in_ceiling = false;
368 v3s16 camera_np = floatToInt(getEyePosition(), BS);
369 MapNode n = map->getNodeNoEx(camera_np);
370 if(n.getContent() != CONTENT_IGNORE){
371 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
372 camera_barely_in_ceiling = true;
378 Update the node last under the player
380 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
381 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
384 Check properties of the node on which the player is standing
386 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
387 // Determine if jumping is possible
388 m_can_jump = touching_ground && !in_liquid;
389 if(itemgroup_get(f.groups, "disable_jump"))
391 // Jump key pressed while jumping off from a bouncy block
392 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
393 m_speed.Y >= -0.5 * BS) {
394 float jumpspeed = movement_speed_jump * physics_override_jump;
396 // Reduce boost when speed already is high
397 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
399 m_speed.Y += jumpspeed;
406 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
408 move(dtime, env, pos_max_d, NULL);
411 void LocalPlayer::applyControl(float dtime)
414 swimming_vertical = false;
416 setPitch(control.pitch);
419 // Nullify speed and don't run positioning code if the player is attached
422 setSpeed(v3f(0,0,0));
426 v3f move_direction = v3f(0,0,1);
427 move_direction.rotateXZBy(getYaw());
429 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
430 v3f speedV = v3f(0,0,0); // Vertical (Y)
432 bool fly_allowed = m_client->checkLocalPrivilege("fly");
433 bool fast_allowed = m_client->checkLocalPrivilege("fast");
435 bool free_move = fly_allowed && g_settings->getBool("free_move");
436 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
437 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
438 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
439 bool continuous_forward = g_settings->getBool("continuous_forward");
440 bool always_fly_fast = g_settings->getBool("always_fly_fast");
442 // Whether superspeed mode is used or not
443 bool superspeed = false;
445 if (always_fly_fast && free_move && fast_move)
448 // Old descend control
449 if(g_settings->getBool("aux1_descends"))
451 // If free movement and fast movement, always move fast
452 if(free_move && fast_move)
455 // Auxiliary button 1 (E)
460 // In free movement mode, aux1 descends
462 speedV.Y = -movement_speed_fast;
464 speedV.Y = -movement_speed_walk;
466 else if(in_liquid || in_liquid_stable)
468 speedV.Y = -movement_speed_walk;
469 swimming_vertical = true;
473 speedV.Y = -movement_speed_climb;
477 // If not free movement but fast is allowed, aux1 is
484 // New minecraft-like descend control
487 // Auxiliary button 1 (E)
492 // aux1 is "Turbo button"
502 // In free movement mode, sneak descends
503 if (fast_move && (control.aux1 || always_fly_fast))
504 speedV.Y = -movement_speed_fast;
506 speedV.Y = -movement_speed_walk;
508 else if(in_liquid || in_liquid_stable)
511 speedV.Y = -movement_speed_fast;
513 speedV.Y = -movement_speed_walk;
514 swimming_vertical = true;
519 speedV.Y = -movement_speed_fast;
521 speedV.Y = -movement_speed_climb;
526 if (continuous_forward)
527 speedH += move_direction;
530 if (continuous_forward) {
534 speedH += move_direction;
538 speedH -= move_direction;
540 if (!control.up && !control.down) {
541 speedH -= move_direction *
542 (control.forw_move_joystick_axis / 32767.f);
545 speedH += move_direction.crossProduct(v3f(0,1,0));
548 speedH += move_direction.crossProduct(v3f(0,-1,0));
550 if (!control.left && !control.right) {
551 speedH -= move_direction.crossProduct(v3f(0,1,0)) *
552 (control.sidew_move_joystick_axis / 32767.f);
557 if (g_settings->getBool("aux1_descends") || always_fly_fast) {
559 speedV.Y = movement_speed_fast;
561 speedV.Y = movement_speed_walk;
563 if(fast_move && control.aux1)
564 speedV.Y = movement_speed_fast;
566 speedV.Y = movement_speed_walk;
572 NOTE: The d value in move() affects jump height by
573 raising the height at which the jump speed is kept
574 at its starting value
576 v3f speedJ = getSpeed();
577 if(speedJ.Y >= -0.5 * BS)
579 speedJ.Y = movement_speed_jump * physics_override_jump;
582 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
583 m_client->event()->put(e);
589 speedV.Y = movement_speed_fast;
591 speedV.Y = movement_speed_walk;
592 swimming_vertical = true;
597 speedV.Y = movement_speed_fast;
599 speedV.Y = movement_speed_climb;
603 // The speed of the player (Y is ignored)
604 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
605 speedH = speedH.normalize() * movement_speed_fast;
606 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
607 speedH = speedH.normalize() * movement_speed_crouch;
609 speedH = speedH.normalize() * movement_speed_walk;
611 // Acceleration increase
612 f32 incH = 0; // Horizontal (X, Z)
613 f32 incV = 0; // Vertical (Y)
614 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
616 // Jumping and falling
617 if(superspeed || (fast_move && control.aux1))
618 incH = movement_acceleration_fast * BS * dtime;
620 incH = movement_acceleration_air * BS * dtime;
621 incV = 0; // No vertical acceleration in air
623 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
624 incH = incV = movement_acceleration_fast * BS * dtime;
626 incH = incV = movement_acceleration_default * BS * dtime;
628 // Accelerate to target speed with maximum increment
629 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
630 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
633 v3s16 LocalPlayer::getStandingNodePos()
635 if(m_sneak_node_exists)
637 return floatToInt(getPosition() - v3f(0, BS, 0), BS);
640 v3s16 LocalPlayer::getFootstepNodePos()
643 // BS * 0.05 below the player's feet ensures a 1/16th height
644 // nodebox is detected instead of the node below it.
645 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
646 // A larger distance below is necessary for a footstep sound
647 // when landing after a jump or fall. BS * 0.5 ensures water
648 // sounds when swimming in 1 node deep water.
649 return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
652 v3s16 LocalPlayer::getLightPosition() const
654 return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
657 v3f LocalPlayer::getEyeOffset() const
659 float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f;
660 return v3f(0, BS * eye_height, 0);
663 // Horizontal acceleration (X and Z), Y direction is ignored
664 void LocalPlayer::accelerateHorizontal(const v3f &target_speed, const f32 max_increase)
666 if (max_increase == 0)
669 v3f d_wanted = target_speed - m_speed;
671 f32 dl = d_wanted.getLength();
672 if (dl > max_increase)
675 v3f d = d_wanted.normalize() * dl;
681 // Vertical acceleration (Y), X and Z directions are ignored
682 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
684 if (max_increase == 0)
687 f32 d_wanted = target_speed.Y - m_speed.Y;
688 if (d_wanted > max_increase)
689 d_wanted = max_increase;
690 else if (d_wanted < -max_increase)
691 d_wanted = -max_increase;
693 m_speed.Y += d_wanted;