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"
22 #include "main.h" // For g_settings
24 #include "collision.h"
28 #include "environment.h"
30 #include "util/numeric.h"
36 LocalPlayer::LocalPlayer(IGameDef *gamedef):
40 overridePosition(v3f(0,0,0)),
41 last_position(v3f(0,0,0)),
42 last_speed(v3f(0,0,0)),
47 hotbar_selected_image(""),
48 m_sneak_node(32767,32767,32767),
49 m_sneak_node_exists(false),
50 m_old_node_below(32767,32767,32767),
51 m_old_node_below_type("air"),
52 m_need_to_get_new_sneak_node(true),
55 last_animation(NO_ANIM),
56 eye_offset_first(v3f(0,0,0)),
57 eye_offset_third(v3f(0,0,0))
59 // Initialize hp to 0, so that no hearts will be shown if server
60 // doesn't support health points
64 LocalPlayer::~LocalPlayer()
68 void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d,
69 std::list<CollisionInfo> *collision_info)
71 Map *map = &env->getMap();
72 INodeDefManager *nodemgr = m_gamedef->ndef();
74 v3f position = getPosition();
76 v3f old_speed = m_speed;
78 // Copy parent position if local player is attached
81 setPosition(overridePosition);
82 m_sneak_node_exists = false;
86 // Skip collision detection if noclip mode is used
87 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
88 bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
89 g_settings->getBool("noclip");
90 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
93 position += m_speed * dtime;
94 setPosition(position);
95 m_sneak_node_exists = false;
104 Check if player is in liquid (the oscillating value)
107 // If in liquid, the threshold of coming out is at higher y
110 v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
111 in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
112 liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
114 // If not in liquid, the threshold of going in is at lower y
117 v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
118 in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
119 liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
122 catch(InvalidPositionException &e)
128 Check if player is in liquid (the stable value)
131 v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
132 in_liquid_stable = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
134 catch(InvalidPositionException &e)
136 in_liquid_stable = false;
140 Check if player is climbing
144 v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
145 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
146 is_climbing = ((nodemgr->get(map->getNode(pp).getContent()).climbable ||
147 nodemgr->get(map->getNode(pp2).getContent()).climbable) && !free_move);
149 catch(InvalidPositionException &e)
155 Collision uncertainty radius
156 Make it a bit larger than the maximum distance of movement
158 //f32 d = pos_max_d * 1.1;
159 // A fairly large value in here makes moving smoother
162 // This should always apply, otherwise there are glitches
163 assert(d > pos_max_d);
165 // Maximum distance over border for sneaking
166 f32 sneak_max = BS*0.4;
169 If sneaking, keep in range from the last walked node and don't
172 if(control.sneak && m_sneak_node_exists &&
173 !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
174 physics_override_sneak)
176 f32 maxd = 0.5*BS + sneak_max;
177 v3f lwn_f = intToFloat(m_sneak_node, BS);
178 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
179 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
183 f32 min_y = lwn_f.Y + 0.5*BS;
184 if(position.Y < min_y)
194 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
196 v3f accel_f = v3f(0,0,0);
198 collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
199 pos_max_d, m_collisionbox, player_stepheight, dtime,
200 position, m_speed, accel_f);
203 If the player's feet touch the topside of any node, this is
206 Player is allowed to jump when this is true.
208 bool touching_ground_was = touching_ground;
209 touching_ground = result.touching_ground;
211 //bool standing_on_unloaded = result.standing_on_unloaded;
214 Check the nodes under the player to see from which node the
215 player is sneaking from, if any. If the node from under
216 the player has been removed, the player falls.
218 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
219 if(m_sneak_node_exists &&
220 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
221 m_old_node_below_type != "air")
223 // Old node appears to have been removed; that is,
224 // it wasn't air before but now it is
225 m_need_to_get_new_sneak_node = false;
226 m_sneak_node_exists = false;
228 else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
230 // We are on something, so make sure to recalculate the sneak
232 m_need_to_get_new_sneak_node = true;
234 if(m_need_to_get_new_sneak_node && physics_override_sneak)
236 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
237 v2f player_p2df(position.X, position.Z);
238 f32 min_distance_f = 100000.0*BS;
239 // If already seeking from some node, compare to it.
240 /*if(m_sneak_node_exists)
242 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
243 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
244 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
245 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
246 // Ignore if player is not on the same level (likely dropped)
247 if(d_vert_f < 0.15*BS)
248 min_distance_f = d_horiz_f;
250 v3s16 new_sneak_node = m_sneak_node;
251 for(s16 x=-1; x<=1; x++)
252 for(s16 z=-1; z<=1; z++)
254 v3s16 p = pos_i_bottom + v3s16(x,0,z);
255 v3f pf = intToFloat(p, BS);
256 v2f node_p2df(pf.X, pf.Z);
257 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
258 f32 max_axis_distance_f = MYMAX(
259 fabs(player_p2df.X-node_p2df.X),
260 fabs(player_p2df.Y-node_p2df.Y));
262 if(distance_f > min_distance_f ||
263 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
267 // The node to be sneaked on has to be walkable
268 if(nodemgr->get(map->getNode(p)).walkable == false)
270 // And the node above it has to be nonwalkable
271 if(nodemgr->get(map->getNode(p+v3s16(0,1,0))).walkable == true)
273 if (!physics_override_sneak_glitch) {
274 if (nodemgr->get(map->getNode(p+v3s16(0,2,0))).walkable)
278 catch(InvalidPositionException &e)
283 min_distance_f = distance_f;
287 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
289 m_sneak_node = new_sneak_node;
290 m_sneak_node_exists = sneak_node_found;
293 If sneaking, the player's collision box can be in air, so
294 this has to be set explicitly
296 if(sneak_node_found && control.sneak)
297 touching_ground = true;
303 setPosition(position);
308 bool bouncy_jump = false;
309 // Dont report if flying
310 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed))
312 for(size_t i=0; i<result.collisions.size(); i++){
313 const CollisionInfo &info = result.collisions[i];
314 collision_info->push_back(info);
315 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
321 if(bouncy_jump && control.jump){
322 m_speed.Y += movement_speed_jump*BS;
323 touching_ground = false;
324 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
325 m_gamedef->event()->put(e);
328 if(!touching_ground_was && touching_ground){
329 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
330 m_gamedef->event()->put(e);
332 // Set camera impact value to be used for view bobbing
333 camera_impact = getSpeed().Y * -1;
337 camera_barely_in_ceiling = false;
338 v3s16 camera_np = floatToInt(getEyePosition(), BS);
339 MapNode n = map->getNodeNoEx(camera_np);
340 if(n.getContent() != CONTENT_IGNORE){
341 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
342 camera_barely_in_ceiling = true;
348 Update the node last under the player
350 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
351 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
354 Check properties of the node on which the player is standing
356 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
357 // Determine if jumping is possible
358 m_can_jump = touching_ground && !in_liquid;
359 if(itemgroup_get(f.groups, "disable_jump"))
363 void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d)
365 move(dtime, env, pos_max_d, NULL);
368 void LocalPlayer::applyControl(float dtime)
371 swimming_vertical = false;
373 setPitch(control.pitch);
376 // Nullify speed and don't run positioning code if the player is attached
379 setSpeed(v3f(0,0,0));
383 v3f move_direction = v3f(0,0,1);
384 move_direction.rotateXZBy(getYaw());
386 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
387 v3f speedV = v3f(0,0,0); // Vertical (Y)
389 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
390 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
392 bool free_move = fly_allowed && g_settings->getBool("free_move");
393 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
394 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
395 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
396 bool continuous_forward = g_settings->getBool("continuous_forward");
398 // Whether superspeed mode is used or not
399 bool superspeed = false;
401 if(g_settings->getBool("always_fly_fast") && free_move && fast_move)
404 // Old descend control
405 if(g_settings->getBool("aux1_descends"))
407 // If free movement and fast movement, always move fast
408 if(free_move && fast_move)
411 // Auxiliary button 1 (E)
416 // In free movement mode, aux1 descends
418 speedV.Y = -movement_speed_fast;
420 speedV.Y = -movement_speed_walk;
422 else if(in_liquid || in_liquid_stable)
424 speedV.Y = -movement_speed_walk;
425 swimming_vertical = true;
429 speedV.Y = -movement_speed_climb;
433 // If not free movement but fast is allowed, aux1 is
440 // New minecraft-like descend control
443 // Auxiliary button 1 (E)
448 // aux1 is "Turbo button"
458 // In free movement mode, sneak descends
459 if(fast_move && (control.aux1 || g_settings->getBool("always_fly_fast")))
460 speedV.Y = -movement_speed_fast;
462 speedV.Y = -movement_speed_walk;
464 else if(in_liquid || in_liquid_stable)
467 speedV.Y = -movement_speed_fast;
469 speedV.Y = -movement_speed_walk;
470 swimming_vertical = true;
475 speedV.Y = -movement_speed_fast;
477 speedV.Y = -movement_speed_climb;
482 if(continuous_forward)
483 speedH += move_direction;
487 if(continuous_forward)
490 speedH += move_direction;
494 speedH -= move_direction;
498 speedH += move_direction.crossProduct(v3f(0,1,0));
502 speedH += move_direction.crossProduct(v3f(0,-1,0));
508 if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
511 speedV.Y = movement_speed_fast;
513 speedV.Y = movement_speed_walk;
515 if(fast_move && control.aux1)
516 speedV.Y = movement_speed_fast;
518 speedV.Y = movement_speed_walk;
524 NOTE: The d value in move() affects jump height by
525 raising the height at which the jump speed is kept
526 at its starting value
528 v3f speedJ = getSpeed();
529 if(speedJ.Y >= -0.5 * BS)
531 speedJ.Y = movement_speed_jump * physics_override_jump;
534 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
535 m_gamedef->event()->put(e);
541 speedV.Y = movement_speed_fast;
543 speedV.Y = movement_speed_walk;
544 swimming_vertical = true;
549 speedV.Y = movement_speed_fast;
551 speedV.Y = movement_speed_climb;
555 // The speed of the player (Y is ignored)
556 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
557 speedH = speedH.normalize() * movement_speed_fast;
558 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
559 speedH = speedH.normalize() * movement_speed_crouch;
561 speedH = speedH.normalize() * movement_speed_walk;
563 // Acceleration increase
564 f32 incH = 0; // Horizontal (X, Z)
565 f32 incV = 0; // Vertical (Y)
566 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
568 // Jumping and falling
569 if(superspeed || (fast_move && control.aux1))
570 incH = movement_acceleration_fast * BS * dtime;
572 incH = movement_acceleration_air * BS * dtime;
573 incV = 0; // No vertical acceleration in air
575 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
576 incH = incV = movement_acceleration_fast * BS * dtime;
578 incH = incV = movement_acceleration_default * BS * dtime;
580 // Accelerate to target speed with maximum increment
581 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
582 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
585 v3s16 LocalPlayer::getStandingNodePos()
587 if(m_sneak_node_exists)
589 return floatToInt(getPosition() - v3f(0, BS, 0), BS);