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, const char *name):
37 Player(gamedef, name),
40 overridePosition(v3f(0,0,0)),
41 last_position(v3f(0,0,0)),
42 last_speed(v3f(0,0,0)),
46 eye_offset_first(v3f(0,0,0)),
47 eye_offset_third(v3f(0,0,0)),
48 last_animation(NO_ANIM),
50 hotbar_selected_image(""),
51 light_color(255,255,255,255),
52 m_sneak_node(32767,32767,32767),
53 m_sneak_node_exists(false),
54 m_old_node_below(32767,32767,32767),
55 m_old_node_below_type("air"),
56 m_need_to_get_new_sneak_node(true),
60 // Initialize hp to 0, so that no hearts will be shown if server
61 // doesn't support health points
65 LocalPlayer::~LocalPlayer()
69 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
70 std::list<CollisionInfo> *collision_info)
72 Map *map = &env->getMap();
73 INodeDefManager *nodemgr = m_gamedef->ndef();
75 v3f position = getPosition();
77 v3f old_speed = m_speed;
79 // Copy parent position if local player is attached
82 setPosition(overridePosition);
83 m_sneak_node_exists = false;
87 // Skip collision detection if noclip mode is used
88 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
89 bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
90 g_settings->getBool("noclip");
91 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
94 position += m_speed * dtime;
95 setPosition(position);
96 m_sneak_node_exists = false;
105 Check if player is in liquid (the oscillating value)
108 // If in liquid, the threshold of coming out is at higher y
111 v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
112 in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
113 liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
115 // If not in liquid, the threshold of going in is at lower y
118 v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
119 in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
120 liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
123 catch(InvalidPositionException &e)
129 Check if player is in liquid (the stable value)
132 v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
133 in_liquid_stable = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
135 catch(InvalidPositionException &e)
137 in_liquid_stable = false;
141 Check if player is climbing
145 v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
146 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
147 is_climbing = ((nodemgr->get(map->getNode(pp).getContent()).climbable ||
148 nodemgr->get(map->getNode(pp2).getContent()).climbable) && !free_move);
150 catch(InvalidPositionException &e)
156 Collision uncertainty radius
157 Make it a bit larger than the maximum distance of movement
159 //f32 d = pos_max_d * 1.1;
160 // A fairly large value in here makes moving smoother
163 // This should always apply, otherwise there are glitches
164 assert(d > pos_max_d);
166 // Maximum distance over border for sneaking
167 f32 sneak_max = BS*0.4;
170 If sneaking, keep in range from the last walked node and don't
173 if(control.sneak && m_sneak_node_exists &&
174 !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
175 physics_override_sneak)
177 f32 maxd = 0.5*BS + sneak_max;
178 v3f lwn_f = intToFloat(m_sneak_node, BS);
179 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
180 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
184 f32 min_y = lwn_f.Y + 0.5*BS;
185 if(position.Y < min_y)
195 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
197 v3f accel_f = v3f(0,0,0);
199 collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
200 pos_max_d, m_collisionbox, player_stepheight, dtime,
201 position, m_speed, accel_f);
204 If the player's feet touch the topside of any node, this is
207 Player is allowed to jump when this is true.
209 bool touching_ground_was = touching_ground;
210 touching_ground = result.touching_ground;
212 //bool standing_on_unloaded = result.standing_on_unloaded;
215 Check the nodes under the player to see from which node the
216 player is sneaking from, if any. If the node from under
217 the player has been removed, the player falls.
219 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
220 if(m_sneak_node_exists &&
221 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
222 m_old_node_below_type != "air")
224 // Old node appears to have been removed; that is,
225 // it wasn't air before but now it is
226 m_need_to_get_new_sneak_node = false;
227 m_sneak_node_exists = false;
229 else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
231 // We are on something, so make sure to recalculate the sneak
233 m_need_to_get_new_sneak_node = true;
235 if(m_need_to_get_new_sneak_node && physics_override_sneak)
237 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
238 v2f player_p2df(position.X, position.Z);
239 f32 min_distance_f = 100000.0*BS;
240 // If already seeking from some node, compare to it.
241 /*if(m_sneak_node_exists)
243 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
244 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
245 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
246 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
247 // Ignore if player is not on the same level (likely dropped)
248 if(d_vert_f < 0.15*BS)
249 min_distance_f = d_horiz_f;
251 v3s16 new_sneak_node = m_sneak_node;
252 for(s16 x=-1; x<=1; x++)
253 for(s16 z=-1; z<=1; z++)
255 v3s16 p = pos_i_bottom + v3s16(x,0,z);
256 v3f pf = intToFloat(p, BS);
257 v2f node_p2df(pf.X, pf.Z);
258 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
259 f32 max_axis_distance_f = MYMAX(
260 fabs(player_p2df.X-node_p2df.X),
261 fabs(player_p2df.Y-node_p2df.Y));
263 if(distance_f > min_distance_f ||
264 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
268 // The node to be sneaked on has to be walkable
269 if(nodemgr->get(map->getNode(p)).walkable == false)
271 // And the node above it has to be nonwalkable
272 if(nodemgr->get(map->getNode(p+v3s16(0,1,0))).walkable == true) {
275 if (!physics_override_sneak_glitch) {
276 if (nodemgr->get(map->getNode(p+v3s16(0,2,0))).walkable)
280 catch(InvalidPositionException &e)
285 min_distance_f = distance_f;
289 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
291 m_sneak_node = new_sneak_node;
292 m_sneak_node_exists = sneak_node_found;
295 If sneaking, the player's collision box can be in air, so
296 this has to be set explicitly
298 if(sneak_node_found && control.sneak)
299 touching_ground = true;
305 setPosition(position);
310 bool bouncy_jump = false;
311 // Dont report if flying
312 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed))
314 for(size_t i=0; i<result.collisions.size(); i++){
315 const CollisionInfo &info = result.collisions[i];
316 collision_info->push_back(info);
317 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
323 if(bouncy_jump && control.jump){
324 m_speed.Y += movement_speed_jump*BS;
325 touching_ground = false;
326 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
327 m_gamedef->event()->put(e);
330 if(!touching_ground_was && touching_ground){
331 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
332 m_gamedef->event()->put(e);
334 // Set camera impact value to be used for view bobbing
335 camera_impact = getSpeed().Y * -1;
339 camera_barely_in_ceiling = false;
340 v3s16 camera_np = floatToInt(getEyePosition(), BS);
341 MapNode n = map->getNodeNoEx(camera_np);
342 if(n.getContent() != CONTENT_IGNORE){
343 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
344 camera_barely_in_ceiling = true;
350 Update the node last under the player
352 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
353 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
356 Check properties of the node on which the player is standing
358 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
359 // Determine if jumping is possible
360 m_can_jump = touching_ground && !in_liquid;
361 if(itemgroup_get(f.groups, "disable_jump"))
365 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
367 move(dtime, env, pos_max_d, NULL);
370 void LocalPlayer::applyControl(float dtime)
373 swimming_vertical = false;
375 setPitch(control.pitch);
378 // Nullify speed and don't run positioning code if the player is attached
381 setSpeed(v3f(0,0,0));
385 v3f move_direction = v3f(0,0,1);
386 move_direction.rotateXZBy(getYaw());
388 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
389 v3f speedV = v3f(0,0,0); // Vertical (Y)
391 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
392 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
394 bool free_move = fly_allowed && g_settings->getBool("free_move");
395 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
396 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
397 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
398 bool continuous_forward = g_settings->getBool("continuous_forward");
400 // Whether superspeed mode is used or not
401 bool superspeed = false;
403 if(g_settings->getBool("always_fly_fast") && free_move && fast_move)
406 // Old descend control
407 if(g_settings->getBool("aux1_descends"))
409 // If free movement and fast movement, always move fast
410 if(free_move && fast_move)
413 // Auxiliary button 1 (E)
418 // In free movement mode, aux1 descends
420 speedV.Y = -movement_speed_fast;
422 speedV.Y = -movement_speed_walk;
424 else if(in_liquid || in_liquid_stable)
426 speedV.Y = -movement_speed_walk;
427 swimming_vertical = true;
431 speedV.Y = -movement_speed_climb;
435 // If not free movement but fast is allowed, aux1 is
442 // New minecraft-like descend control
445 // Auxiliary button 1 (E)
450 // aux1 is "Turbo button"
460 // In free movement mode, sneak descends
461 if(fast_move && (control.aux1 || g_settings->getBool("always_fly_fast")))
462 speedV.Y = -movement_speed_fast;
464 speedV.Y = -movement_speed_walk;
466 else if(in_liquid || in_liquid_stable)
469 speedV.Y = -movement_speed_fast;
471 speedV.Y = -movement_speed_walk;
472 swimming_vertical = true;
477 speedV.Y = -movement_speed_fast;
479 speedV.Y = -movement_speed_climb;
484 if(continuous_forward)
485 speedH += move_direction;
489 if(continuous_forward)
492 speedH += move_direction;
496 speedH -= move_direction;
500 speedH += move_direction.crossProduct(v3f(0,1,0));
504 speedH += move_direction.crossProduct(v3f(0,-1,0));
510 if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
513 speedV.Y = movement_speed_fast;
515 speedV.Y = movement_speed_walk;
517 if(fast_move && control.aux1)
518 speedV.Y = movement_speed_fast;
520 speedV.Y = movement_speed_walk;
526 NOTE: The d value in move() affects jump height by
527 raising the height at which the jump speed is kept
528 at its starting value
530 v3f speedJ = getSpeed();
531 if(speedJ.Y >= -0.5 * BS)
533 speedJ.Y = movement_speed_jump * physics_override_jump;
536 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
537 m_gamedef->event()->put(e);
543 speedV.Y = movement_speed_fast;
545 speedV.Y = movement_speed_walk;
546 swimming_vertical = true;
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_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
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 || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
578 incH = incV = movement_acceleration_fast * BS * dtime;
580 incH = incV = movement_acceleration_default * BS * dtime;
582 // Accelerate to target speed with maximum increment
583 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
584 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
587 v3s16 LocalPlayer::getStandingNodePos()
589 if(m_sneak_node_exists)
591 return floatToInt(getPosition() - v3f(0, BS, 0), BS);