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);
79 // Skip collision detection if noclip mode is used
80 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
81 bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
82 g_settings->getBool("noclip");
83 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
86 position += m_speed * dtime;
87 setPosition(position);
96 Check if player is in liquid (the oscillating value)
99 // If in liquid, the threshold of coming out is at higher y
102 v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
103 in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
104 liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
106 // If not in liquid, the threshold of going in is at lower y
109 v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
110 in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
111 liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
114 catch(InvalidPositionException &e)
120 Check if player is in liquid (the stable value)
123 v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
124 in_liquid_stable = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
126 catch(InvalidPositionException &e)
128 in_liquid_stable = false;
132 Check if player is climbing
136 v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
137 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
138 is_climbing = ((nodemgr->get(map->getNode(pp).getContent()).climbable ||
139 nodemgr->get(map->getNode(pp2).getContent()).climbable) && !free_move);
141 catch(InvalidPositionException &e)
147 Collision uncertainty radius
148 Make it a bit larger than the maximum distance of movement
150 //f32 d = pos_max_d * 1.1;
151 // A fairly large value in here makes moving smoother
154 // This should always apply, otherwise there are glitches
155 assert(d > pos_max_d);
157 // Maximum distance over border for sneaking
158 f32 sneak_max = BS*0.4;
161 If sneaking, keep in range from the last walked node and don't
164 if(control.sneak && m_sneak_node_exists && !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid)
166 f32 maxd = 0.5*BS + sneak_max;
167 v3f lwn_f = intToFloat(m_sneak_node, BS);
168 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
169 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
173 f32 min_y = lwn_f.Y + 0.5*BS;
174 if(position.Y < min_y)
184 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
186 v3f accel_f = v3f(0,0,0);
188 collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
189 pos_max_d, m_collisionbox, player_stepheight, dtime,
190 position, m_speed, accel_f);
193 If the player's feet touch the topside of any node, this is
196 Player is allowed to jump when this is true.
198 bool touching_ground_was = touching_ground;
199 touching_ground = result.touching_ground;
201 //bool standing_on_unloaded = result.standing_on_unloaded;
204 Check the nodes under the player to see from which node the
205 player is sneaking from, if any. If the node from under
206 the player has been removed, the player falls.
208 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
209 if(m_sneak_node_exists &&
210 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
211 m_old_node_below_type != "air")
213 // Old node appears to have been removed; that is,
214 // it wasn't air before but now it is
215 m_need_to_get_new_sneak_node = false;
216 m_sneak_node_exists = false;
218 else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
220 // We are on something, so make sure to recalculate the sneak
222 m_need_to_get_new_sneak_node = true;
224 if(m_need_to_get_new_sneak_node)
226 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
227 v2f player_p2df(position.X, position.Z);
228 f32 min_distance_f = 100000.0*BS;
229 // If already seeking from some node, compare to it.
230 /*if(m_sneak_node_exists)
232 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
233 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
234 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
235 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
236 // Ignore if player is not on the same level (likely dropped)
237 if(d_vert_f < 0.15*BS)
238 min_distance_f = d_horiz_f;
240 v3s16 new_sneak_node = m_sneak_node;
241 for(s16 x=-1; x<=1; x++)
242 for(s16 z=-1; z<=1; z++)
244 v3s16 p = pos_i_bottom + v3s16(x,0,z);
245 v3f pf = intToFloat(p, BS);
246 v2f node_p2df(pf.X, pf.Z);
247 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
248 f32 max_axis_distance_f = MYMAX(
249 fabs(player_p2df.X-node_p2df.X),
250 fabs(player_p2df.Y-node_p2df.Y));
252 if(distance_f > min_distance_f ||
253 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
257 // The node to be sneaked on has to be walkable
258 if(nodemgr->get(map->getNode(p)).walkable == false)
260 // And the node above it has to be nonwalkable
261 if(nodemgr->get(map->getNode(p+v3s16(0,1,0))).walkable == true)
264 catch(InvalidPositionException &e)
269 min_distance_f = distance_f;
273 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
275 m_sneak_node = new_sneak_node;
276 m_sneak_node_exists = sneak_node_found;
279 If sneaking, the player's collision box can be in air, so
280 this has to be set explicitly
282 if(sneak_node_found && control.sneak)
283 touching_ground = true;
289 setPosition(position);
294 bool bouncy_jump = false;
295 // Dont report if flying
296 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed))
298 for(size_t i=0; i<result.collisions.size(); i++){
299 const CollisionInfo &info = result.collisions[i];
300 collision_info->push_back(info);
301 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
307 if(bouncy_jump && control.jump){
308 m_speed.Y += movement_speed_jump*BS;
309 touching_ground = false;
310 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
311 m_gamedef->event()->put(e);
314 if(!touching_ground_was && touching_ground){
315 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
316 m_gamedef->event()->put(e);
318 // Set camera impact value to be used for view bobbing
319 camera_impact = getSpeed().Y * -1;
323 camera_barely_in_ceiling = false;
324 v3s16 camera_np = floatToInt(getEyePosition(), BS);
325 MapNode n = map->getNodeNoEx(camera_np);
326 if(n.getContent() != CONTENT_IGNORE){
327 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
328 camera_barely_in_ceiling = true;
334 Update the node last under the player
336 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
337 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
340 Check properties of the node on which the player is standing
342 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
343 // Determine if jumping is possible
344 m_can_jump = touching_ground && !in_liquid;
345 if(itemgroup_get(f.groups, "disable_jump"))
349 void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d)
351 move(dtime, env, pos_max_d, NULL);
354 void LocalPlayer::applyControl(float dtime)
357 swimming_vertical = false;
359 setPitch(control.pitch);
362 // Nullify speed and don't run positioning code if the player is attached
365 setSpeed(v3f(0,0,0));
369 v3f move_direction = v3f(0,0,1);
370 move_direction.rotateXZBy(getYaw());
372 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
373 v3f speedV = v3f(0,0,0); // Vertical (Y)
375 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
376 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
378 bool free_move = fly_allowed && g_settings->getBool("free_move");
379 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
380 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
381 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
382 bool continuous_forward = g_settings->getBool("continuous_forward");
384 // Whether superspeed mode is used or not
385 bool superspeed = false;
387 if(g_settings->getBool("always_fly_fast") && free_move && fast_move)
390 // Old descend control
391 if(g_settings->getBool("aux1_descends"))
393 // If free movement and fast movement, always move fast
394 if(free_move && fast_move)
397 // Auxiliary button 1 (E)
402 // In free movement mode, aux1 descends
404 speedV.Y = -movement_speed_fast;
406 speedV.Y = -movement_speed_walk;
408 else if(in_liquid || in_liquid_stable)
410 speedV.Y = -movement_speed_walk;
411 swimming_vertical = true;
415 speedV.Y = -movement_speed_climb;
419 // If not free movement but fast is allowed, aux1 is
426 // New minecraft-like descend control
429 // Auxiliary button 1 (E)
434 // aux1 is "Turbo button"
444 // In free movement mode, sneak descends
445 if(fast_move && (control.aux1 || g_settings->getBool("always_fly_fast")))
446 speedV.Y = -movement_speed_fast;
448 speedV.Y = -movement_speed_walk;
450 else if(in_liquid || in_liquid_stable)
453 speedV.Y = -movement_speed_fast;
455 speedV.Y = -movement_speed_walk;
456 swimming_vertical = true;
461 speedV.Y = -movement_speed_fast;
463 speedV.Y = -movement_speed_climb;
468 if(continuous_forward)
469 speedH += move_direction;
473 if(continuous_forward)
476 speedH += move_direction;
480 speedH -= move_direction;
484 speedH += move_direction.crossProduct(v3f(0,1,0));
488 speedH += move_direction.crossProduct(v3f(0,-1,0));
494 if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
497 speedV.Y = movement_speed_fast;
499 speedV.Y = movement_speed_walk;
501 if(fast_move && control.aux1)
502 speedV.Y = movement_speed_fast;
504 speedV.Y = movement_speed_walk;
510 NOTE: The d value in move() affects jump height by
511 raising the height at which the jump speed is kept
512 at its starting value
514 v3f speedJ = getSpeed();
515 if(speedJ.Y >= -0.5 * BS)
517 speedJ.Y = movement_speed_jump * physics_override_jump;
520 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
521 m_gamedef->event()->put(e);
527 speedV.Y = movement_speed_fast;
529 speedV.Y = movement_speed_walk;
530 swimming_vertical = true;
535 speedV.Y = movement_speed_fast;
537 speedV.Y = movement_speed_climb;
541 // The speed of the player (Y is ignored)
542 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
543 speedH = speedH.normalize() * movement_speed_fast;
544 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
545 speedH = speedH.normalize() * movement_speed_crouch;
547 speedH = speedH.normalize() * movement_speed_walk;
549 // Acceleration increase
550 f32 incH = 0; // Horizontal (X, Z)
551 f32 incV = 0; // Vertical (Y)
552 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
554 // Jumping and falling
555 if(superspeed || (fast_move && control.aux1))
556 incH = movement_acceleration_fast * BS * dtime;
558 incH = movement_acceleration_air * BS * dtime;
559 incV = 0; // No vertical acceleration in air
561 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
562 incH = incV = movement_acceleration_fast * BS * dtime;
564 incH = incV = movement_acceleration_default * BS * dtime;
566 // Accelerate to target speed with maximum increment
567 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
568 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
571 v3s16 LocalPlayer::getStandingNodePos()
573 if(m_sneak_node_exists)
575 return floatToInt(getPosition(), BS);