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 float player_radius = BS*0.30;
158 float player_height = BS*1.55;
160 // Maximum distance over border for sneaking
161 f32 sneak_max = BS*0.4;
164 If sneaking, keep in range from the last walked node and don't
167 if(control.sneak && m_sneak_node_exists && !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid)
169 f32 maxd = 0.5*BS + sneak_max;
170 v3f lwn_f = intToFloat(m_sneak_node, BS);
171 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
172 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
176 f32 min_y = lwn_f.Y + 0.5*BS;
177 if(position.Y < min_y)
188 Calculate player collision box (new and old)
190 core::aabbox3d<f32> playerbox(
199 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
201 v3f accel_f = v3f(0,0,0);
203 collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
204 pos_max_d, playerbox, player_stepheight, dtime,
205 position, m_speed, accel_f);
208 If the player's feet touch the topside of any node, this is
211 Player is allowed to jump when this is true.
213 bool touching_ground_was = touching_ground;
214 touching_ground = result.touching_ground;
216 //bool standing_on_unloaded = result.standing_on_unloaded;
219 Check the nodes under the player to see from which node the
220 player is sneaking from, if any. If the node from under
221 the player has been removed, the player falls.
223 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
224 if(m_sneak_node_exists &&
225 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
226 m_old_node_below_type != "air")
228 // Old node appears to have been removed; that is,
229 // it wasn't air before but now it is
230 m_need_to_get_new_sneak_node = false;
231 m_sneak_node_exists = false;
233 else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
235 // We are on something, so make sure to recalculate the sneak
237 m_need_to_get_new_sneak_node = true;
239 if(m_need_to_get_new_sneak_node)
241 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
242 v2f player_p2df(position.X, position.Z);
243 f32 min_distance_f = 100000.0*BS;
244 // If already seeking from some node, compare to it.
245 /*if(m_sneak_node_exists)
247 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
248 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
249 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
250 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
251 // Ignore if player is not on the same level (likely dropped)
252 if(d_vert_f < 0.15*BS)
253 min_distance_f = d_horiz_f;
255 v3s16 new_sneak_node = m_sneak_node;
256 for(s16 x=-1; x<=1; x++)
257 for(s16 z=-1; z<=1; z++)
259 v3s16 p = pos_i_bottom + v3s16(x,0,z);
260 v3f pf = intToFloat(p, BS);
261 v2f node_p2df(pf.X, pf.Z);
262 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
263 f32 max_axis_distance_f = MYMAX(
264 fabs(player_p2df.X-node_p2df.X),
265 fabs(player_p2df.Y-node_p2df.Y));
267 if(distance_f > min_distance_f ||
268 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
272 // The node to be sneaked on has to be walkable
273 if(nodemgr->get(map->getNode(p)).walkable == false)
275 // And the node above it has to be nonwalkable
276 if(nodemgr->get(map->getNode(p+v3s16(0,1,0))).walkable == true)
279 catch(InvalidPositionException &e)
284 min_distance_f = distance_f;
288 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
290 m_sneak_node = new_sneak_node;
291 m_sneak_node_exists = sneak_node_found;
294 If sneaking, the player's collision box can be in air, so
295 this has to be set explicitly
297 if(sneak_node_found && control.sneak)
298 touching_ground = true;
304 setPosition(position);
309 bool bouncy_jump = false;
310 // Dont report if flying
311 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed))
313 for(size_t i=0; i<result.collisions.size(); i++){
314 const CollisionInfo &info = result.collisions[i];
315 collision_info->push_back(info);
316 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
322 if(bouncy_jump && control.jump){
323 m_speed.Y += movement_speed_jump*BS;
324 touching_ground = false;
325 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
326 m_gamedef->event()->put(e);
329 if(!touching_ground_was && touching_ground){
330 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
331 m_gamedef->event()->put(e);
335 camera_barely_in_ceiling = false;
336 v3s16 camera_np = floatToInt(getEyePosition(), BS);
337 MapNode n = map->getNodeNoEx(camera_np);
338 if(n.getContent() != CONTENT_IGNORE){
339 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
340 camera_barely_in_ceiling = true;
346 Update the node last under the player
348 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
349 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
352 Check properties of the node on which the player is standing
354 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
355 // Determine if jumping is possible
356 m_can_jump = touching_ground && !in_liquid;
357 if(itemgroup_get(f.groups, "disable_jump"))
361 void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d)
363 move(dtime, env, pos_max_d, NULL);
366 void LocalPlayer::applyControl(float dtime)
369 swimming_vertical = false;
371 setPitch(control.pitch);
374 // Nullify speed and don't run positioning code if the player is attached
377 setSpeed(v3f(0,0,0));
381 v3f move_direction = v3f(0,0,1);
382 move_direction.rotateXZBy(getYaw());
384 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
385 v3f speedV = v3f(0,0,0); // Vertical (Y)
387 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
388 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
390 bool free_move = fly_allowed && g_settings->getBool("free_move");
391 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
392 bool fast_or_aux1_descends = (fast_move && control.aux1) || (fast_move && g_settings->getBool("aux1_descends"));
393 bool continuous_forward = g_settings->getBool("continuous_forward");
395 // Whether superspeed mode is used or not
396 bool superspeed = false;
398 if(g_settings->getBool("always_fly_fast") && free_move && fast_move)
401 // Old descend control
402 if(g_settings->getBool("aux1_descends"))
404 // If free movement and fast movement, always move fast
405 if(free_move && fast_move)
408 // Auxiliary button 1 (E)
413 // In free movement mode, aux1 descends
415 speedV.Y = -movement_speed_fast;
417 speedV.Y = -movement_speed_walk;
419 else if(in_liquid || in_liquid_stable)
421 // Always use fast when aux1_descends & fast_move are enabled in liquid, since the aux1 button would mean both turbo and "swim down" causing a conflict
422 speedV.Y = -movement_speed_fast;
423 swimming_vertical = true;
427 // Always use fast when aux1_descends & fast_move are enabled when climbing, since the aux1 button would mean both turbo and "descend" causing a conflict
428 speedV.Y = -movement_speed_fast;
432 // If not free movement but fast is allowed, aux1 is
439 // New minecraft-like descend control
442 // Auxiliary button 1 (E)
447 // aux1 is "Turbo button"
457 // In free movement mode, sneak descends
458 if(fast_move && (control.aux1 || g_settings->getBool("always_fly_fast")))
459 speedV.Y = -movement_speed_fast;
461 speedV.Y = -movement_speed_walk;
463 else if(in_liquid || in_liquid_stable)
465 if(fast_or_aux1_descends)
466 // Always use fast when aux1_descends & fast_move are enabled in liquid, since the aux1 button would mean both turbo and "swim down" causing a conflict
467 speedV.Y = -movement_speed_fast;
469 speedV.Y = -movement_speed_walk;
470 swimming_vertical = true;
474 if(fast_or_aux1_descends)
475 // Always use fast when aux1_descends & fast_move are enabled when climbing, since the aux1 button would mean both turbo and "descend" causing a conflict
476 speedV.Y = -movement_speed_fast;
478 speedV.Y = -movement_speed_climb;
483 if(continuous_forward)
484 speedH += move_direction;
488 if(continuous_forward)
491 speedH += move_direction;
495 speedH -= move_direction;
499 speedH += move_direction.crossProduct(v3f(0,1,0));
503 speedH += move_direction.crossProduct(v3f(0,-1,0));
509 if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
512 speedV.Y = movement_speed_fast;
514 speedV.Y = movement_speed_walk;
516 if(fast_move && control.aux1)
517 speedV.Y = movement_speed_fast;
519 speedV.Y = movement_speed_walk;
525 NOTE: The d value in move() affects jump height by
526 raising the height at which the jump speed is kept
527 at its starting value
529 v3f speedJ = getSpeed();
530 if(speedJ.Y >= -0.5 * BS)
532 speedJ.Y = movement_speed_jump;
535 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
536 m_gamedef->event()->put(e);
541 if(fast_or_aux1_descends)
542 // Always use fast when aux1_descends & fast_move are enabled in liquid, since the aux1 button would mean both turbo and "swim down" causing a conflict
543 speedV.Y = movement_speed_fast;
545 speedV.Y = movement_speed_walk;
546 swimming_vertical = true;
550 if(fast_or_aux1_descends)
551 // Always use fast when aux1_descends & fast_move are enabled when climbing, since the aux1 button would mean both turbo and "descend" causing a conflict
552 speedV.Y = movement_speed_fast;
554 speedV.Y = movement_speed_climb;
558 // The speed of the player (Y is ignored)
559 if(superspeed || (is_climbing && fast_or_aux1_descends) || ((in_liquid || in_liquid_stable) && fast_or_aux1_descends))
560 speedH = speedH.normalize() * movement_speed_fast;
561 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
562 speedH = speedH.normalize() * movement_speed_crouch;
564 speedH = speedH.normalize() * movement_speed_walk;
566 // Acceleration increase
567 f32 incH = 0; // Horizontal (X, Z)
568 f32 incV = 0; // Vertical (Y)
569 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
571 // Jumping and falling
572 if(superspeed || (fast_move && control.aux1))
573 incH = movement_acceleration_fast * BS * dtime;
575 incH = movement_acceleration_air * BS * dtime;
576 incV = 0; // No vertical acceleration in air
578 else if(superspeed || (fast_move && control.aux1))
579 incH = incV = movement_acceleration_fast * BS * dtime;
580 else if ((in_liquid || in_liquid_stable) && fast_or_aux1_descends)
581 // Always use fast when aux1_descends & fast_move are enabled in liquid, since the aux1 button would mean both turbo and "swim down" causing a conflict
582 incH = incV = movement_acceleration_fast * BS * dtime;
584 incH = incV = movement_acceleration_default * BS * dtime;
586 // Accelerate to target speed with maximum increment
587 accelerateHorizontal(speedH, incH);
588 accelerateVertical(speedV, incV);
591 v3s16 LocalPlayer::getStandingNodePos()
593 if(m_sneak_node_exists)
595 return floatToInt(getPosition(), BS);