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 // Initialize hp to 0, so that no hearts will be shown if server
56 // doesn't support health points
60 LocalPlayer::~LocalPlayer()
64 void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d,
65 std::list<CollisionInfo> *collision_info)
67 Map *map = &env->getMap();
68 INodeDefManager *nodemgr = m_gamedef->ndef();
70 v3f position = getPosition();
72 v3f old_speed = m_speed;
74 // Copy parent position if local player is attached
77 setPosition(overridePosition);
78 m_sneak_node_exists = false;
82 // Skip collision detection if noclip mode is used
83 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
84 bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
85 g_settings->getBool("noclip");
86 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
89 position += m_speed * dtime;
90 setPosition(position);
91 m_sneak_node_exists = false;
100 Check if player is in liquid (the oscillating value)
103 // If in liquid, the threshold of coming out is at higher y
106 v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
107 in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
108 liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
110 // If not in liquid, the threshold of going in is at lower y
113 v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
114 in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
115 liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
118 catch(InvalidPositionException &e)
124 Check if player is in liquid (the stable value)
127 v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
128 in_liquid_stable = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
130 catch(InvalidPositionException &e)
132 in_liquid_stable = false;
136 Check if player is climbing
140 v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
141 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
142 is_climbing = ((nodemgr->get(map->getNode(pp).getContent()).climbable ||
143 nodemgr->get(map->getNode(pp2).getContent()).climbable) && !free_move);
145 catch(InvalidPositionException &e)
151 Collision uncertainty radius
152 Make it a bit larger than the maximum distance of movement
154 //f32 d = pos_max_d * 1.1;
155 // A fairly large value in here makes moving smoother
158 // This should always apply, otherwise there are glitches
159 assert(d > pos_max_d);
161 // Maximum distance over border for sneaking
162 f32 sneak_max = BS*0.4;
165 If sneaking, keep in range from the last walked node and don't
168 if(control.sneak && m_sneak_node_exists && !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid)
170 f32 maxd = 0.5*BS + sneak_max;
171 v3f lwn_f = intToFloat(m_sneak_node, BS);
172 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
173 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
177 f32 min_y = lwn_f.Y + 0.5*BS;
178 if(position.Y < min_y)
188 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
190 v3f accel_f = v3f(0,0,0);
192 collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
193 pos_max_d, m_collisionbox, player_stepheight, dtime,
194 position, m_speed, accel_f);
197 If the player's feet touch the topside of any node, this is
200 Player is allowed to jump when this is true.
202 bool touching_ground_was = touching_ground;
203 touching_ground = result.touching_ground;
205 //bool standing_on_unloaded = result.standing_on_unloaded;
208 Check the nodes under the player to see from which node the
209 player is sneaking from, if any. If the node from under
210 the player has been removed, the player falls.
212 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
213 if(m_sneak_node_exists &&
214 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
215 m_old_node_below_type != "air")
217 // Old node appears to have been removed; that is,
218 // it wasn't air before but now it is
219 m_need_to_get_new_sneak_node = false;
220 m_sneak_node_exists = false;
222 else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
224 // We are on something, so make sure to recalculate the sneak
226 m_need_to_get_new_sneak_node = true;
228 if(m_need_to_get_new_sneak_node)
230 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
231 v2f player_p2df(position.X, position.Z);
232 f32 min_distance_f = 100000.0*BS;
233 // If already seeking from some node, compare to it.
234 /*if(m_sneak_node_exists)
236 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
237 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
238 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
239 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
240 // Ignore if player is not on the same level (likely dropped)
241 if(d_vert_f < 0.15*BS)
242 min_distance_f = d_horiz_f;
244 v3s16 new_sneak_node = m_sneak_node;
245 for(s16 x=-1; x<=1; x++)
246 for(s16 z=-1; z<=1; z++)
248 v3s16 p = pos_i_bottom + v3s16(x,0,z);
249 v3f pf = intToFloat(p, BS);
250 v2f node_p2df(pf.X, pf.Z);
251 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
252 f32 max_axis_distance_f = MYMAX(
253 fabs(player_p2df.X-node_p2df.X),
254 fabs(player_p2df.Y-node_p2df.Y));
256 if(distance_f > min_distance_f ||
257 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
261 // The node to be sneaked on has to be walkable
262 if(nodemgr->get(map->getNode(p)).walkable == false)
264 // And the node above it has to be nonwalkable
265 if(nodemgr->get(map->getNode(p+v3s16(0,1,0))).walkable == true)
268 catch(InvalidPositionException &e)
273 min_distance_f = distance_f;
277 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
279 m_sneak_node = new_sneak_node;
280 m_sneak_node_exists = sneak_node_found;
283 If sneaking, the player's collision box can be in air, so
284 this has to be set explicitly
286 if(sneak_node_found && control.sneak)
287 touching_ground = true;
293 setPosition(position);
298 bool bouncy_jump = false;
299 // Dont report if flying
300 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed))
302 for(size_t i=0; i<result.collisions.size(); i++){
303 const CollisionInfo &info = result.collisions[i];
304 collision_info->push_back(info);
305 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
311 if(bouncy_jump && control.jump){
312 m_speed.Y += movement_speed_jump*BS;
313 touching_ground = false;
314 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
315 m_gamedef->event()->put(e);
318 if(!touching_ground_was && touching_ground){
319 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
320 m_gamedef->event()->put(e);
322 // Set camera impact value to be used for view bobbing
323 camera_impact = getSpeed().Y * -1;
327 camera_barely_in_ceiling = false;
328 v3s16 camera_np = floatToInt(getEyePosition(), BS);
329 MapNode n = map->getNodeNoEx(camera_np);
330 if(n.getContent() != CONTENT_IGNORE){
331 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
332 camera_barely_in_ceiling = true;
338 Update the node last under the player
340 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
341 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
344 Check properties of the node on which the player is standing
346 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
347 // Determine if jumping is possible
348 m_can_jump = touching_ground && !in_liquid;
349 if(itemgroup_get(f.groups, "disable_jump"))
353 void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d)
355 move(dtime, env, pos_max_d, NULL);
358 void LocalPlayer::applyControl(float dtime)
361 swimming_vertical = false;
363 setPitch(control.pitch);
366 // Nullify speed and don't run positioning code if the player is attached
369 setSpeed(v3f(0,0,0));
373 v3f move_direction = v3f(0,0,1);
374 move_direction.rotateXZBy(getYaw());
376 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
377 v3f speedV = v3f(0,0,0); // Vertical (Y)
379 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
380 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
382 bool free_move = fly_allowed && g_settings->getBool("free_move");
383 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
384 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
385 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
386 bool continuous_forward = g_settings->getBool("continuous_forward");
388 // Whether superspeed mode is used or not
389 bool superspeed = false;
391 if(g_settings->getBool("always_fly_fast") && free_move && fast_move)
394 // Old descend control
395 if(g_settings->getBool("aux1_descends"))
397 // If free movement and fast movement, always move fast
398 if(free_move && fast_move)
401 // Auxiliary button 1 (E)
406 // In free movement mode, aux1 descends
408 speedV.Y = -movement_speed_fast;
410 speedV.Y = -movement_speed_walk;
412 else if(in_liquid || in_liquid_stable)
414 speedV.Y = -movement_speed_walk;
415 swimming_vertical = true;
419 speedV.Y = -movement_speed_climb;
423 // If not free movement but fast is allowed, aux1 is
430 // New minecraft-like descend control
433 // Auxiliary button 1 (E)
438 // aux1 is "Turbo button"
448 // In free movement mode, sneak descends
449 if(fast_move && (control.aux1 || g_settings->getBool("always_fly_fast")))
450 speedV.Y = -movement_speed_fast;
452 speedV.Y = -movement_speed_walk;
454 else if(in_liquid || in_liquid_stable)
457 speedV.Y = -movement_speed_fast;
459 speedV.Y = -movement_speed_walk;
460 swimming_vertical = true;
465 speedV.Y = -movement_speed_fast;
467 speedV.Y = -movement_speed_climb;
472 if(continuous_forward)
473 speedH += move_direction;
477 if(continuous_forward)
480 speedH += move_direction;
484 speedH -= move_direction;
488 speedH += move_direction.crossProduct(v3f(0,1,0));
492 speedH += move_direction.crossProduct(v3f(0,-1,0));
498 if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
501 speedV.Y = movement_speed_fast;
503 speedV.Y = movement_speed_walk;
505 if(fast_move && control.aux1)
506 speedV.Y = movement_speed_fast;
508 speedV.Y = movement_speed_walk;
514 NOTE: The d value in move() affects jump height by
515 raising the height at which the jump speed is kept
516 at its starting value
518 v3f speedJ = getSpeed();
519 if(speedJ.Y >= -0.5 * BS)
521 speedJ.Y = movement_speed_jump * physics_override_jump;
524 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
525 m_gamedef->event()->put(e);
531 speedV.Y = movement_speed_fast;
533 speedV.Y = movement_speed_walk;
534 swimming_vertical = true;
539 speedV.Y = movement_speed_fast;
541 speedV.Y = movement_speed_climb;
545 // The speed of the player (Y is ignored)
546 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
547 speedH = speedH.normalize() * movement_speed_fast;
548 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
549 speedH = speedH.normalize() * movement_speed_crouch;
551 speedH = speedH.normalize() * movement_speed_walk;
553 // Acceleration increase
554 f32 incH = 0; // Horizontal (X, Z)
555 f32 incV = 0; // Vertical (Y)
556 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
558 // Jumping and falling
559 if(superspeed || (fast_move && control.aux1))
560 incH = movement_acceleration_fast * BS * dtime;
562 incH = movement_acceleration_air * BS * dtime;
563 incV = 0; // No vertical acceleration in air
565 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
566 incH = incV = movement_acceleration_fast * BS * dtime;
568 incH = incV = movement_acceleration_default * BS * dtime;
570 // Accelerate to target speed with maximum increment
571 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
572 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
575 v3s16 LocalPlayer::getStandingNodePos()
577 if(m_sneak_node_exists)
579 return floatToInt(getPosition(), BS);