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):
39 overridePosition(v3f(0,0,0)),
40 last_position(v3f(0,0,0)),
41 last_speed(v3f(0,0,0)),
45 m_sneak_node(32767,32767,32767),
46 m_sneak_node_exists(false),
47 m_old_node_below(32767,32767,32767),
48 m_old_node_below_type("air"),
49 m_need_to_get_new_sneak_node(true),
52 // Initialize hp to 0, so that no hearts will be shown if server
53 // doesn't support health points
57 LocalPlayer::~LocalPlayer()
61 void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d,
62 std::list<CollisionInfo> *collision_info)
64 Map *map = &env->getMap();
65 INodeDefManager *nodemgr = m_gamedef->ndef();
67 v3f position = getPosition();
69 v3f old_speed = m_speed;
71 // Copy parent position if local player is attached
74 setPosition(overridePosition);
78 // Skip collision detection if noclip mode is used
79 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
80 bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
81 g_settings->getBool("noclip");
82 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
85 position += m_speed * dtime;
86 setPosition(position);
95 Check if player is in liquid (the oscillating value)
98 // If in liquid, the threshold of coming out is at higher y
101 v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
102 in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
103 liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
105 // If not in liquid, the threshold of going in is at lower y
108 v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
109 in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
110 liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
113 catch(InvalidPositionException &e)
119 Check if player is in liquid (the stable value)
122 v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
123 in_liquid_stable = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
125 catch(InvalidPositionException &e)
127 in_liquid_stable = false;
131 Check if player is climbing
135 v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
136 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
137 is_climbing = ((nodemgr->get(map->getNode(pp).getContent()).climbable ||
138 nodemgr->get(map->getNode(pp2).getContent()).climbable) && !free_move);
140 catch(InvalidPositionException &e)
146 Collision uncertainty radius
147 Make it a bit larger than the maximum distance of movement
149 //f32 d = pos_max_d * 1.1;
150 // A fairly large value in here makes moving smoother
153 // This should always apply, otherwise there are glitches
154 assert(d > pos_max_d);
156 float player_radius = BS*0.30;
157 float player_height = BS*1.55;
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)
187 Calculate player collision box (new and old)
189 core::aabbox3d<f32> playerbox(
198 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
200 v3f accel_f = v3f(0,0,0);
202 collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
203 pos_max_d, playerbox, player_stepheight, dtime,
204 position, m_speed, accel_f);
207 If the player's feet touch the topside of any node, this is
210 Player is allowed to jump when this is true.
212 bool touching_ground_was = touching_ground;
213 touching_ground = result.touching_ground;
215 //bool standing_on_unloaded = result.standing_on_unloaded;
218 Check the nodes under the player to see from which node the
219 player is sneaking from, if any. If the node from under
220 the player has been removed, the player falls.
222 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
223 if(m_sneak_node_exists &&
224 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
225 m_old_node_below_type != "air")
227 // Old node appears to have been removed; that is,
228 // it wasn't air before but now it is
229 m_need_to_get_new_sneak_node = false;
230 m_sneak_node_exists = false;
232 else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
234 // We are on something, so make sure to recalculate the sneak
236 m_need_to_get_new_sneak_node = true;
238 if(m_need_to_get_new_sneak_node)
240 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
241 v2f player_p2df(position.X, position.Z);
242 f32 min_distance_f = 100000.0*BS;
243 // If already seeking from some node, compare to it.
244 /*if(m_sneak_node_exists)
246 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
247 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
248 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
249 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
250 // Ignore if player is not on the same level (likely dropped)
251 if(d_vert_f < 0.15*BS)
252 min_distance_f = d_horiz_f;
254 v3s16 new_sneak_node = m_sneak_node;
255 for(s16 x=-1; x<=1; x++)
256 for(s16 z=-1; z<=1; z++)
258 v3s16 p = pos_i_bottom + v3s16(x,0,z);
259 v3f pf = intToFloat(p, BS);
260 v2f node_p2df(pf.X, pf.Z);
261 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
262 f32 max_axis_distance_f = MYMAX(
263 fabs(player_p2df.X-node_p2df.X),
264 fabs(player_p2df.Y-node_p2df.Y));
266 if(distance_f > min_distance_f ||
267 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
271 // The node to be sneaked on has to be walkable
272 if(nodemgr->get(map->getNode(p)).walkable == false)
274 // And the node above it has to be nonwalkable
275 if(nodemgr->get(map->getNode(p+v3s16(0,1,0))).walkable == true)
278 catch(InvalidPositionException &e)
283 min_distance_f = distance_f;
287 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
289 m_sneak_node = new_sneak_node;
290 m_sneak_node_exists = sneak_node_found;
293 If sneaking, the player's collision box can be in air, so
294 this has to be set explicitly
296 if(sneak_node_found && control.sneak)
297 touching_ground = true;
303 setPosition(position);
308 bool bouncy_jump = false;
309 // Dont report if flying
310 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed))
312 for(size_t i=0; i<result.collisions.size(); i++){
313 const CollisionInfo &info = result.collisions[i];
314 collision_info->push_back(info);
315 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
321 if(bouncy_jump && control.jump){
322 m_speed.Y += movement_speed_jump*BS;
323 touching_ground = false;
324 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
325 m_gamedef->event()->put(e);
328 if(!touching_ground_was && touching_ground){
329 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
330 m_gamedef->event()->put(e);
334 camera_barely_in_ceiling = false;
335 v3s16 camera_np = floatToInt(getEyePosition(), BS);
336 MapNode n = map->getNodeNoEx(camera_np);
337 if(n.getContent() != CONTENT_IGNORE){
338 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
339 camera_barely_in_ceiling = true;
345 Update the node last under the player
347 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
348 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
351 Check properties of the node on which the player is standing
353 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
354 // Determine if jumping is possible
355 m_can_jump = touching_ground && !in_liquid;
356 if(itemgroup_get(f.groups, "disable_jump"))
360 void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d)
362 move(dtime, env, pos_max_d, NULL);
365 void LocalPlayer::applyControl(float dtime)
368 swimming_vertical = false;
370 setPitch(control.pitch);
373 // Nullify speed and don't run positioning code if the player is attached
376 setSpeed(v3f(0,0,0));
380 v3f move_direction = v3f(0,0,1);
381 move_direction.rotateXZBy(getYaw());
383 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
384 v3f speedV = v3f(0,0,0); // Vertical (Y)
386 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
387 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
389 bool free_move = fly_allowed && g_settings->getBool("free_move");
390 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
391 bool fast_or_aux1_descends = (fast_move && control.aux1) || (fast_move && g_settings->getBool("aux1_descends"));
392 bool continuous_forward = g_settings->getBool("continuous_forward");
394 // Whether superspeed mode is used or not
395 bool superspeed = false;
397 if(g_settings->getBool("always_fly_fast") && free_move && fast_move)
400 // Old descend control
401 if(g_settings->getBool("aux1_descends"))
403 // If free movement and fast movement, always move fast
404 if(free_move && fast_move)
407 // Auxiliary button 1 (E)
412 // In free movement mode, aux1 descends
414 speedV.Y = -movement_speed_fast;
416 speedV.Y = -movement_speed_walk;
418 else if(in_liquid || in_liquid_stable)
420 // 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
421 speedV.Y = -movement_speed_fast;
422 swimming_vertical = true;
426 // 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
427 speedV.Y = -movement_speed_fast;
431 // If not free movement but fast is allowed, aux1 is
438 // New minecraft-like descend control
441 // Auxiliary button 1 (E)
446 // aux1 is "Turbo button"
456 // In free movement mode, sneak descends
457 if(fast_move && (control.aux1 || g_settings->getBool("always_fly_fast")))
458 speedV.Y = -movement_speed_fast;
460 speedV.Y = -movement_speed_walk;
462 else if(in_liquid || in_liquid_stable)
464 if(fast_or_aux1_descends)
465 // 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
466 speedV.Y = -movement_speed_fast;
468 speedV.Y = -movement_speed_walk;
469 swimming_vertical = true;
473 if(fast_or_aux1_descends)
474 // 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
475 speedV.Y = -movement_speed_fast;
477 speedV.Y = -movement_speed_climb;
482 if(continuous_forward)
483 speedH += move_direction;
487 if(continuous_forward)
490 speedH += move_direction;
494 speedH -= move_direction;
498 speedH += move_direction.crossProduct(v3f(0,1,0));
502 speedH += move_direction.crossProduct(v3f(0,-1,0));
508 if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
511 speedV.Y = movement_speed_fast;
513 speedV.Y = movement_speed_walk;
515 if(fast_move && control.aux1)
516 speedV.Y = movement_speed_fast;
518 speedV.Y = movement_speed_walk;
524 NOTE: The d value in move() affects jump height by
525 raising the height at which the jump speed is kept
526 at its starting value
528 v3f speedJ = getSpeed();
529 if(speedJ.Y >= -0.5 * BS)
531 speedJ.Y = movement_speed_jump;
534 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
535 m_gamedef->event()->put(e);
540 if(fast_or_aux1_descends)
541 // 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
542 speedV.Y = movement_speed_fast;
544 speedV.Y = movement_speed_walk;
545 swimming_vertical = true;
549 if(fast_or_aux1_descends)
550 // 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
551 speedV.Y = movement_speed_fast;
553 speedV.Y = movement_speed_climb;
557 // The speed of the player (Y is ignored)
558 if(superspeed || (is_climbing && fast_or_aux1_descends) || ((in_liquid || in_liquid_stable) && fast_or_aux1_descends))
559 speedH = speedH.normalize() * movement_speed_fast;
560 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
561 speedH = speedH.normalize() * movement_speed_crouch;
563 speedH = speedH.normalize() * movement_speed_walk;
565 // Acceleration increase
566 f32 incH = 0; // Horizontal (X, Z)
567 f32 incV = 0; // Vertical (Y)
568 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
570 // Jumping and falling
571 if(superspeed || (fast_move && control.aux1))
572 incH = movement_acceleration_fast * BS * dtime;
574 incH = movement_acceleration_air * BS * dtime;
575 incV = 0; // No vertical acceleration in air
577 else if(superspeed || (fast_move && control.aux1))
578 incH = incV = movement_acceleration_fast * BS * dtime;
579 else if ((in_liquid || in_liquid_stable) && fast_or_aux1_descends)
580 // 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
581 incH = incV = movement_acceleration_fast * BS * dtime;
583 incH = incV = movement_acceleration_default * BS * dtime;
585 // Accelerate to target speed with maximum increment
586 accelerateHorizontal(speedH, incH);
587 accelerateVertical(speedV, incV);
590 v3s16 LocalPlayer::getStandingNodePos()
592 if(m_sneak_node_exists)
594 return floatToInt(getPosition(), BS);