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)),
46 m_sneak_node(32767,32767,32767),
47 m_sneak_node_exists(false),
48 m_old_node_below(32767,32767,32767),
49 m_old_node_below_type("air"),
50 m_need_to_get_new_sneak_node(true),
53 // Initialize hp to 0, so that no hearts will be shown if server
54 // doesn't support health points
58 LocalPlayer::~LocalPlayer()
62 void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d,
63 std::list<CollisionInfo> *collision_info)
65 Map *map = &env->getMap();
66 INodeDefManager *nodemgr = m_gamedef->ndef();
68 v3f position = getPosition();
70 v3f old_speed = m_speed;
72 // Copy parent position if local player is attached
75 setPosition(overridePosition);
76 m_sneak_node_exists = false;
80 // Skip collision detection if noclip mode is used
81 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
82 bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
83 g_settings->getBool("noclip");
84 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
87 position += m_speed * dtime;
88 setPosition(position);
89 m_sneak_node_exists = false;
98 Check if player is in liquid (the oscillating value)
101 // If in liquid, the threshold of coming out is at higher y
104 v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
105 in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
106 liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
108 // If not in liquid, the threshold of going in is at lower y
111 v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
112 in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
113 liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
116 catch(InvalidPositionException &e)
122 Check if player is in liquid (the stable value)
125 v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
126 in_liquid_stable = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
128 catch(InvalidPositionException &e)
130 in_liquid_stable = false;
134 Check if player is climbing
138 v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
139 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
140 is_climbing = ((nodemgr->get(map->getNode(pp).getContent()).climbable ||
141 nodemgr->get(map->getNode(pp2).getContent()).climbable) && !free_move);
143 catch(InvalidPositionException &e)
149 Collision uncertainty radius
150 Make it a bit larger than the maximum distance of movement
152 //f32 d = pos_max_d * 1.1;
153 // A fairly large value in here makes moving smoother
156 // This should always apply, otherwise there are glitches
157 assert(d > pos_max_d);
159 // Maximum distance over border for sneaking
160 f32 sneak_max = BS*0.4;
163 If sneaking, keep in range from the last walked node and don't
166 if(control.sneak && m_sneak_node_exists && !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid)
168 f32 maxd = 0.5*BS + sneak_max;
169 v3f lwn_f = intToFloat(m_sneak_node, BS);
170 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
171 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
175 f32 min_y = lwn_f.Y + 0.5*BS;
176 if(position.Y < min_y)
186 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
188 v3f accel_f = v3f(0,0,0);
190 collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
191 pos_max_d, m_collisionbox, player_stepheight, dtime,
192 position, m_speed, accel_f);
195 If the player's feet touch the topside of any node, this is
198 Player is allowed to jump when this is true.
200 bool touching_ground_was = touching_ground;
201 touching_ground = result.touching_ground;
203 //bool standing_on_unloaded = result.standing_on_unloaded;
206 Check the nodes under the player to see from which node the
207 player is sneaking from, if any. If the node from under
208 the player has been removed, the player falls.
210 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
211 if(m_sneak_node_exists &&
212 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
213 m_old_node_below_type != "air")
215 // Old node appears to have been removed; that is,
216 // it wasn't air before but now it is
217 m_need_to_get_new_sneak_node = false;
218 m_sneak_node_exists = false;
220 else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
222 // We are on something, so make sure to recalculate the sneak
224 m_need_to_get_new_sneak_node = true;
226 if(m_need_to_get_new_sneak_node)
228 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
229 v2f player_p2df(position.X, position.Z);
230 f32 min_distance_f = 100000.0*BS;
231 // If already seeking from some node, compare to it.
232 /*if(m_sneak_node_exists)
234 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
235 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
236 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
237 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
238 // Ignore if player is not on the same level (likely dropped)
239 if(d_vert_f < 0.15*BS)
240 min_distance_f = d_horiz_f;
242 v3s16 new_sneak_node = m_sneak_node;
243 for(s16 x=-1; x<=1; x++)
244 for(s16 z=-1; z<=1; z++)
246 v3s16 p = pos_i_bottom + v3s16(x,0,z);
247 v3f pf = intToFloat(p, BS);
248 v2f node_p2df(pf.X, pf.Z);
249 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
250 f32 max_axis_distance_f = MYMAX(
251 fabs(player_p2df.X-node_p2df.X),
252 fabs(player_p2df.Y-node_p2df.Y));
254 if(distance_f > min_distance_f ||
255 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
259 // The node to be sneaked on has to be walkable
260 if(nodemgr->get(map->getNode(p)).walkable == false)
262 // And the node above it has to be nonwalkable
263 if(nodemgr->get(map->getNode(p+v3s16(0,1,0))).walkable == true)
266 catch(InvalidPositionException &e)
271 min_distance_f = distance_f;
275 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
277 m_sneak_node = new_sneak_node;
278 m_sneak_node_exists = sneak_node_found;
281 If sneaking, the player's collision box can be in air, so
282 this has to be set explicitly
284 if(sneak_node_found && control.sneak)
285 touching_ground = true;
291 setPosition(position);
296 bool bouncy_jump = false;
297 // Dont report if flying
298 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed))
300 for(size_t i=0; i<result.collisions.size(); i++){
301 const CollisionInfo &info = result.collisions[i];
302 collision_info->push_back(info);
303 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
309 if(bouncy_jump && control.jump){
310 m_speed.Y += movement_speed_jump*BS;
311 touching_ground = false;
312 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
313 m_gamedef->event()->put(e);
316 if(!touching_ground_was && touching_ground){
317 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
318 m_gamedef->event()->put(e);
320 // Set camera impact value to be used for view bobbing
321 camera_impact = getSpeed().Y * -1;
325 camera_barely_in_ceiling = false;
326 v3s16 camera_np = floatToInt(getEyePosition(), BS);
327 MapNode n = map->getNodeNoEx(camera_np);
328 if(n.getContent() != CONTENT_IGNORE){
329 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
330 camera_barely_in_ceiling = true;
336 Update the node last under the player
338 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
339 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
342 Check properties of the node on which the player is standing
344 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
345 // Determine if jumping is possible
346 m_can_jump = touching_ground && !in_liquid;
347 if(itemgroup_get(f.groups, "disable_jump"))
351 void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d)
353 move(dtime, env, pos_max_d, NULL);
356 void LocalPlayer::applyControl(float dtime)
359 swimming_vertical = false;
361 setPitch(control.pitch);
364 // Nullify speed and don't run positioning code if the player is attached
367 setSpeed(v3f(0,0,0));
371 v3f move_direction = v3f(0,0,1);
372 move_direction.rotateXZBy(getYaw());
374 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
375 v3f speedV = v3f(0,0,0); // Vertical (Y)
377 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
378 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
380 bool free_move = fly_allowed && g_settings->getBool("free_move");
381 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
382 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
383 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
384 bool continuous_forward = g_settings->getBool("continuous_forward");
386 // Whether superspeed mode is used or not
387 bool superspeed = false;
389 if(g_settings->getBool("always_fly_fast") && free_move && fast_move)
392 // Old descend control
393 if(g_settings->getBool("aux1_descends"))
395 // If free movement and fast movement, always move fast
396 if(free_move && fast_move)
399 // Auxiliary button 1 (E)
404 // In free movement mode, aux1 descends
406 speedV.Y = -movement_speed_fast;
408 speedV.Y = -movement_speed_walk;
410 else if(in_liquid || in_liquid_stable)
412 speedV.Y = -movement_speed_walk;
413 swimming_vertical = true;
417 speedV.Y = -movement_speed_climb;
421 // If not free movement but fast is allowed, aux1 is
428 // New minecraft-like descend control
431 // Auxiliary button 1 (E)
436 // aux1 is "Turbo button"
446 // In free movement mode, sneak descends
447 if(fast_move && (control.aux1 || g_settings->getBool("always_fly_fast")))
448 speedV.Y = -movement_speed_fast;
450 speedV.Y = -movement_speed_walk;
452 else if(in_liquid || in_liquid_stable)
455 speedV.Y = -movement_speed_fast;
457 speedV.Y = -movement_speed_walk;
458 swimming_vertical = true;
463 speedV.Y = -movement_speed_fast;
465 speedV.Y = -movement_speed_climb;
470 if(continuous_forward)
471 speedH += move_direction;
475 if(continuous_forward)
478 speedH += move_direction;
482 speedH -= move_direction;
486 speedH += move_direction.crossProduct(v3f(0,1,0));
490 speedH += move_direction.crossProduct(v3f(0,-1,0));
496 if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
499 speedV.Y = movement_speed_fast;
501 speedV.Y = movement_speed_walk;
503 if(fast_move && control.aux1)
504 speedV.Y = movement_speed_fast;
506 speedV.Y = movement_speed_walk;
512 NOTE: The d value in move() affects jump height by
513 raising the height at which the jump speed is kept
514 at its starting value
516 v3f speedJ = getSpeed();
517 if(speedJ.Y >= -0.5 * BS)
519 speedJ.Y = movement_speed_jump * physics_override_jump;
522 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
523 m_gamedef->event()->put(e);
529 speedV.Y = movement_speed_fast;
531 speedV.Y = movement_speed_walk;
532 swimming_vertical = true;
537 speedV.Y = movement_speed_fast;
539 speedV.Y = movement_speed_climb;
543 // The speed of the player (Y is ignored)
544 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
545 speedH = speedH.normalize() * movement_speed_fast;
546 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
547 speedH = speedH.normalize() * movement_speed_crouch;
549 speedH = speedH.normalize() * movement_speed_walk;
551 // Acceleration increase
552 f32 incH = 0; // Horizontal (X, Z)
553 f32 incV = 0; // Vertical (Y)
554 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
556 // Jumping and falling
557 if(superspeed || (fast_move && control.aux1))
558 incH = movement_acceleration_fast * BS * dtime;
560 incH = movement_acceleration_air * BS * dtime;
561 incV = 0; // No vertical acceleration in air
563 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
564 incH = incV = movement_acceleration_fast * BS * dtime;
566 incH = incV = movement_acceleration_default * BS * dtime;
568 // Accelerate to target speed with maximum increment
569 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
570 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
573 v3s16 LocalPlayer::getStandingNodePos()
575 if(m_sneak_node_exists)
577 return floatToInt(getPosition(), BS);