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 &&
169 !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
170 physics_override_sneak)
172 f32 maxd = 0.5*BS + sneak_max;
173 v3f lwn_f = intToFloat(m_sneak_node, BS);
174 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
175 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
179 f32 min_y = lwn_f.Y + 0.5*BS;
180 if(position.Y < min_y)
190 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
192 v3f accel_f = v3f(0,0,0);
194 collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
195 pos_max_d, m_collisionbox, player_stepheight, dtime,
196 position, m_speed, accel_f);
199 If the player's feet touch the topside of any node, this is
202 Player is allowed to jump when this is true.
204 bool touching_ground_was = touching_ground;
205 touching_ground = result.touching_ground;
207 //bool standing_on_unloaded = result.standing_on_unloaded;
210 Check the nodes under the player to see from which node the
211 player is sneaking from, if any. If the node from under
212 the player has been removed, the player falls.
214 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
215 if(m_sneak_node_exists &&
216 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
217 m_old_node_below_type != "air")
219 // Old node appears to have been removed; that is,
220 // it wasn't air before but now it is
221 m_need_to_get_new_sneak_node = false;
222 m_sneak_node_exists = false;
224 else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
226 // We are on something, so make sure to recalculate the sneak
228 m_need_to_get_new_sneak_node = true;
230 if(m_need_to_get_new_sneak_node && physics_override_sneak)
232 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
233 v2f player_p2df(position.X, position.Z);
234 f32 min_distance_f = 100000.0*BS;
235 // If already seeking from some node, compare to it.
236 /*if(m_sneak_node_exists)
238 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
239 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
240 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
241 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
242 // Ignore if player is not on the same level (likely dropped)
243 if(d_vert_f < 0.15*BS)
244 min_distance_f = d_horiz_f;
246 v3s16 new_sneak_node = m_sneak_node;
247 for(s16 x=-1; x<=1; x++)
248 for(s16 z=-1; z<=1; z++)
250 v3s16 p = pos_i_bottom + v3s16(x,0,z);
251 v3f pf = intToFloat(p, BS);
252 v2f node_p2df(pf.X, pf.Z);
253 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
254 f32 max_axis_distance_f = MYMAX(
255 fabs(player_p2df.X-node_p2df.X),
256 fabs(player_p2df.Y-node_p2df.Y));
258 if(distance_f > min_distance_f ||
259 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
263 // The node to be sneaked on has to be walkable
264 if(nodemgr->get(map->getNode(p)).walkable == false)
266 // And the node above it has to be nonwalkable
267 if(nodemgr->get(map->getNode(p+v3s16(0,1,0))).walkable == true)
269 if (!physics_override_sneak_glitch) {
270 if (nodemgr->get(map->getNode(p+v3s16(0,2,0))).walkable)
274 catch(InvalidPositionException &e)
279 min_distance_f = distance_f;
283 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
285 m_sneak_node = new_sneak_node;
286 m_sneak_node_exists = sneak_node_found;
289 If sneaking, the player's collision box can be in air, so
290 this has to be set explicitly
292 if(sneak_node_found && control.sneak)
293 touching_ground = true;
299 setPosition(position);
304 bool bouncy_jump = false;
305 // Dont report if flying
306 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed))
308 for(size_t i=0; i<result.collisions.size(); i++){
309 const CollisionInfo &info = result.collisions[i];
310 collision_info->push_back(info);
311 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
317 if(bouncy_jump && control.jump){
318 m_speed.Y += movement_speed_jump*BS;
319 touching_ground = false;
320 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
321 m_gamedef->event()->put(e);
324 if(!touching_ground_was && touching_ground){
325 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
326 m_gamedef->event()->put(e);
328 // Set camera impact value to be used for view bobbing
329 camera_impact = getSpeed().Y * -1;
333 camera_barely_in_ceiling = false;
334 v3s16 camera_np = floatToInt(getEyePosition(), BS);
335 MapNode n = map->getNodeNoEx(camera_np);
336 if(n.getContent() != CONTENT_IGNORE){
337 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
338 camera_barely_in_ceiling = true;
344 Update the node last under the player
346 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
347 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
350 Check properties of the node on which the player is standing
352 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
353 // Determine if jumping is possible
354 m_can_jump = touching_ground && !in_liquid;
355 if(itemgroup_get(f.groups, "disable_jump"))
359 void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d)
361 move(dtime, env, pos_max_d, NULL);
364 void LocalPlayer::applyControl(float dtime)
367 swimming_vertical = false;
369 setPitch(control.pitch);
372 // Nullify speed and don't run positioning code if the player is attached
375 setSpeed(v3f(0,0,0));
379 v3f move_direction = v3f(0,0,1);
380 move_direction.rotateXZBy(getYaw());
382 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
383 v3f speedV = v3f(0,0,0); // Vertical (Y)
385 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
386 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
388 bool free_move = fly_allowed && g_settings->getBool("free_move");
389 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
390 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
391 bool fast_climb = fast_move && control.aux1 && !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 speedV.Y = -movement_speed_walk;
421 swimming_vertical = true;
425 speedV.Y = -movement_speed_climb;
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)
463 speedV.Y = -movement_speed_fast;
465 speedV.Y = -movement_speed_walk;
466 swimming_vertical = true;
471 speedV.Y = -movement_speed_fast;
473 speedV.Y = -movement_speed_climb;
478 if(continuous_forward)
479 speedH += move_direction;
483 if(continuous_forward)
486 speedH += move_direction;
490 speedH -= move_direction;
494 speedH += move_direction.crossProduct(v3f(0,1,0));
498 speedH += move_direction.crossProduct(v3f(0,-1,0));
504 if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
507 speedV.Y = movement_speed_fast;
509 speedV.Y = movement_speed_walk;
511 if(fast_move && control.aux1)
512 speedV.Y = movement_speed_fast;
514 speedV.Y = movement_speed_walk;
520 NOTE: The d value in move() affects jump height by
521 raising the height at which the jump speed is kept
522 at its starting value
524 v3f speedJ = getSpeed();
525 if(speedJ.Y >= -0.5 * BS)
527 speedJ.Y = movement_speed_jump * physics_override_jump;
530 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
531 m_gamedef->event()->put(e);
537 speedV.Y = movement_speed_fast;
539 speedV.Y = movement_speed_walk;
540 swimming_vertical = true;
545 speedV.Y = movement_speed_fast;
547 speedV.Y = movement_speed_climb;
551 // The speed of the player (Y is ignored)
552 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
553 speedH = speedH.normalize() * movement_speed_fast;
554 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
555 speedH = speedH.normalize() * movement_speed_crouch;
557 speedH = speedH.normalize() * movement_speed_walk;
559 // Acceleration increase
560 f32 incH = 0; // Horizontal (X, Z)
561 f32 incV = 0; // Vertical (Y)
562 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
564 // Jumping and falling
565 if(superspeed || (fast_move && control.aux1))
566 incH = movement_acceleration_fast * BS * dtime;
568 incH = movement_acceleration_air * BS * dtime;
569 incV = 0; // No vertical acceleration in air
571 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
572 incH = incV = movement_acceleration_fast * BS * dtime;
574 incH = incV = movement_acceleration_default * BS * dtime;
576 // Accelerate to target speed with maximum increment
577 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
578 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
581 v3s16 LocalPlayer::getStandingNodePos()
583 if(m_sneak_node_exists)
585 return floatToInt(getPosition() - v3f(0, BS, 0), BS);