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"
29 #include "util/numeric.h"
35 LocalPlayer::LocalPlayer(IGameDef *gamedef):
38 overridePosition(v3f(0,0,0)),
39 last_position(v3f(0,0,0)),
40 last_speed(v3f(0,0,0)),
44 m_sneak_node(32767,32767,32767),
45 m_sneak_node_exists(false),
46 m_old_node_below(32767,32767,32767),
47 m_old_node_below_type("air"),
48 m_need_to_get_new_sneak_node(true),
51 // Initialize hp to 0, so that no hearts will be shown if server
52 // doesn't support health points
56 LocalPlayer::~LocalPlayer()
60 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
61 std::list<CollisionInfo> *collision_info)
63 INodeDefManager *nodemgr = m_gamedef->ndef();
65 v3f position = getPosition();
67 v3f old_speed = m_speed;
69 // Copy parent position if local player is attached
72 setPosition(overridePosition);
76 // Skip collision detection if noclip mode is used
77 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
78 bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
79 g_settings->getBool("noclip");
80 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
83 position += m_speed * dtime;
84 setPosition(position);
93 Check if player is in liquid (the oscillating value)
96 // If in liquid, the threshold of coming out is at higher y
99 v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
100 in_liquid = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
101 liquid_viscosity = nodemgr->get(map.getNode(pp).getContent()).liquid_viscosity;
103 // If not in liquid, the threshold of going in is at lower y
106 v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
107 in_liquid = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
108 liquid_viscosity = nodemgr->get(map.getNode(pp).getContent()).liquid_viscosity;
111 catch(InvalidPositionException &e)
117 Check if player is in liquid (the stable value)
120 v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
121 in_liquid_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
123 catch(InvalidPositionException &e)
125 in_liquid_stable = false;
129 Check if player is climbing
133 v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
134 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
135 is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable ||
136 nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move);
138 catch(InvalidPositionException &e)
144 Collision uncertainty radius
145 Make it a bit larger than the maximum distance of movement
147 //f32 d = pos_max_d * 1.1;
148 // A fairly large value in here makes moving smoother
151 // This should always apply, otherwise there are glitches
152 assert(d > pos_max_d);
154 float player_radius = BS*0.30;
155 float player_height = BS*1.55;
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)
185 Calculate player collision box (new and old)
187 core::aabbox3d<f32> playerbox(
196 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
198 v3f accel_f = v3f(0,0,0);
200 collisionMoveResult result = collisionMoveSimple(&map, m_gamedef,
201 pos_max_d, playerbox, player_stepheight, dtime,
202 position, m_speed, accel_f);
205 If the player's feet touch the topside of any node, this is
208 Player is allowed to jump when this is true.
210 bool touching_ground_was = touching_ground;
211 touching_ground = result.touching_ground;
213 //bool standing_on_unloaded = result.standing_on_unloaded;
216 Check the nodes under the player to see from which node the
217 player is sneaking from, if any. If the node from under
218 the player has been removed, the player falls.
220 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
221 if(m_sneak_node_exists &&
222 nodemgr->get(map.getNodeNoEx(m_old_node_below)).name == "air" &&
223 m_old_node_below_type != "air")
225 // Old node appears to have been removed; that is,
226 // it wasn't air before but now it is
227 m_need_to_get_new_sneak_node = false;
228 m_sneak_node_exists = false;
230 else if(nodemgr->get(map.getNodeNoEx(current_node)).name != "air")
232 // We are on something, so make sure to recalculate the sneak
234 m_need_to_get_new_sneak_node = true;
236 if(m_need_to_get_new_sneak_node)
238 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
239 v2f player_p2df(position.X, position.Z);
240 f32 min_distance_f = 100000.0*BS;
241 // If already seeking from some node, compare to it.
242 /*if(m_sneak_node_exists)
244 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
245 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
246 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
247 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
248 // Ignore if player is not on the same level (likely dropped)
249 if(d_vert_f < 0.15*BS)
250 min_distance_f = d_horiz_f;
252 v3s16 new_sneak_node = m_sneak_node;
253 for(s16 x=-1; x<=1; x++)
254 for(s16 z=-1; z<=1; z++)
256 v3s16 p = pos_i_bottom + v3s16(x,0,z);
257 v3f pf = intToFloat(p, BS);
258 v2f node_p2df(pf.X, pf.Z);
259 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
260 f32 max_axis_distance_f = MYMAX(
261 fabs(player_p2df.X-node_p2df.X),
262 fabs(player_p2df.Y-node_p2df.Y));
264 if(distance_f > min_distance_f ||
265 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
269 // The node to be sneaked on has to be walkable
270 if(nodemgr->get(map.getNode(p)).walkable == false)
272 // And the node above it has to be nonwalkable
273 if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true)
276 catch(InvalidPositionException &e)
281 min_distance_f = distance_f;
285 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
287 m_sneak_node = new_sneak_node;
288 m_sneak_node_exists = sneak_node_found;
291 If sneaking, the player's collision box can be in air, so
292 this has to be set explicitly
294 if(sneak_node_found && control.sneak)
295 touching_ground = true;
301 setPosition(position);
306 bool bouncy_jump = false;
307 // Dont report if flying
308 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed))
310 for(size_t i=0; i<result.collisions.size(); i++){
311 const CollisionInfo &info = result.collisions[i];
312 collision_info->push_back(info);
313 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
319 if(bouncy_jump && control.jump){
320 m_speed.Y += movement_speed_jump*BS;
321 touching_ground = false;
322 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
323 m_gamedef->event()->put(e);
326 if(!touching_ground_was && touching_ground){
327 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
328 m_gamedef->event()->put(e);
332 camera_barely_in_ceiling = false;
333 v3s16 camera_np = floatToInt(getEyePosition(), BS);
334 MapNode n = map.getNodeNoEx(camera_np);
335 if(n.getContent() != CONTENT_IGNORE){
336 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
337 camera_barely_in_ceiling = true;
343 Update the node last under the player
345 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
346 m_old_node_below_type = nodemgr->get(map.getNodeNoEx(m_old_node_below)).name;
349 Check properties of the node on which the player is standing
351 const ContentFeatures &f = nodemgr->get(map.getNodeNoEx(getStandingNodePos()));
352 // Determine if jumping is possible
353 m_can_jump = touching_ground && !in_liquid;
354 if(itemgroup_get(f.groups, "disable_jump"))
358 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
360 move(dtime, map, pos_max_d, NULL);
363 void LocalPlayer::applyControl(float dtime)
366 swimming_vertical = false;
368 setPitch(control.pitch);
371 // Nullify speed and don't run positioning code if the player is attached
374 setSpeed(v3f(0,0,0));
378 v3f move_direction = v3f(0,0,1);
379 move_direction.rotateXZBy(getYaw());
381 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
382 v3f speedV = v3f(0,0,0); // Vertical (Y)
384 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
385 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
387 bool free_move = fly_allowed && g_settings->getBool("free_move");
388 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
389 bool fast_or_aux1_descends = (fast_move && control.aux1) || (fast_move && g_settings->getBool("aux1_descends"));
390 bool continuous_forward = g_settings->getBool("continuous_forward");
392 // Whether superspeed mode is used or not
393 bool superspeed = false;
395 if(g_settings->getBool("always_fly_fast") && free_move && fast_move)
398 // Old descend control
399 if(g_settings->getBool("aux1_descends"))
401 // If free movement and fast movement, always move fast
402 if(free_move && fast_move)
405 // Auxiliary button 1 (E)
410 // In free movement mode, aux1 descends
412 speedV.Y = -movement_speed_fast;
414 speedV.Y = -movement_speed_walk;
416 else if(in_liquid || in_liquid_stable)
418 // 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
419 speedV.Y = -movement_speed_fast;
420 swimming_vertical = true;
424 // 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
425 speedV.Y = -movement_speed_fast;
429 // If not free movement but fast is allowed, aux1 is
436 // New minecraft-like descend control
439 // Auxiliary button 1 (E)
444 // aux1 is "Turbo button"
454 // In free movement mode, sneak descends
455 if(fast_move && (control.aux1 || g_settings->getBool("always_fly_fast")))
456 speedV.Y = -movement_speed_fast;
458 speedV.Y = -movement_speed_walk;
460 else if(in_liquid || in_liquid_stable)
462 if(fast_or_aux1_descends)
463 // 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
464 speedV.Y = -movement_speed_fast;
466 speedV.Y = -movement_speed_walk;
467 swimming_vertical = true;
471 if(fast_or_aux1_descends)
472 // 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
473 speedV.Y = -movement_speed_fast;
475 speedV.Y = -movement_speed_climb;
480 if(continuous_forward)
481 speedH += move_direction;
485 if(continuous_forward)
488 speedH += move_direction;
492 speedH -= move_direction;
496 speedH += move_direction.crossProduct(v3f(0,1,0));
500 speedH += move_direction.crossProduct(v3f(0,-1,0));
506 if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
509 speedV.Y = movement_speed_fast;
511 speedV.Y = movement_speed_walk;
513 if(fast_move && control.aux1)
514 speedV.Y = movement_speed_fast;
516 speedV.Y = movement_speed_walk;
522 NOTE: The d value in move() affects jump height by
523 raising the height at which the jump speed is kept
524 at its starting value
526 v3f speedJ = getSpeed();
527 if(speedJ.Y >= -0.5 * BS)
529 speedJ.Y = movement_speed_jump;
532 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
533 m_gamedef->event()->put(e);
538 if(fast_or_aux1_descends)
539 // 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
540 speedV.Y = movement_speed_fast;
542 speedV.Y = movement_speed_walk;
543 swimming_vertical = true;
547 if(fast_or_aux1_descends)
548 // 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
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_or_aux1_descends) || ((in_liquid || in_liquid_stable) && fast_or_aux1_descends))
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 || (fast_move && control.aux1))
576 incH = incV = movement_acceleration_fast * BS * dtime;
577 else if ((in_liquid || in_liquid_stable) && fast_or_aux1_descends)
578 // 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
579 incH = incV = movement_acceleration_fast * BS * dtime;
581 incH = incV = movement_acceleration_default * BS * dtime;
583 // Accelerate to target speed with maximum increment
584 accelerateHorizontal(speedH, incH);
585 accelerateVertical(speedV, incV);
588 v3s16 LocalPlayer::getStandingNodePos()
590 if(m_sneak_node_exists)
592 return floatToInt(getPosition(), BS);