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;
104 bool is_valid_position;
109 Check if player is in liquid (the oscillating value)
112 // If in liquid, the threshold of coming out is at higher y
115 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
116 node = map->getNodeNoEx(pp, &is_valid_position);
117 if (is_valid_position) {
118 in_liquid = nodemgr->get(node.getContent()).isLiquid();
119 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
124 // If not in liquid, the threshold of going in is at lower y
127 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
128 node = map->getNodeNoEx(pp, &is_valid_position);
129 if (is_valid_position) {
130 in_liquid = nodemgr->get(node.getContent()).isLiquid();
131 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
139 Check if player is in liquid (the stable value)
141 pp = floatToInt(position + v3f(0,0,0), BS);
142 node = map->getNodeNoEx(pp, &is_valid_position);
143 if (is_valid_position) {
144 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
146 in_liquid_stable = false;
150 Check if player is climbing
154 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
155 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
156 node = map->getNodeNoEx(pp, &is_valid_position);
157 bool is_valid_position2;
158 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
160 if (!(is_valid_position && is_valid_position2)) {
163 is_climbing = (nodemgr->get(node.getContent()).climbable
164 || nodemgr->get(node2.getContent()).climbable) && !free_move;
169 Collision uncertainty radius
170 Make it a bit larger than the maximum distance of movement
172 //f32 d = pos_max_d * 1.1;
173 // A fairly large value in here makes moving smoother
176 // This should always apply, otherwise there are glitches
177 assert(d > pos_max_d);
179 // Maximum distance over border for sneaking
180 f32 sneak_max = BS*0.4;
183 If sneaking, keep in range from the last walked node and don't
186 if(control.sneak && m_sneak_node_exists &&
187 !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
188 physics_override_sneak)
190 f32 maxd = 0.5*BS + sneak_max;
191 v3f lwn_f = intToFloat(m_sneak_node, BS);
192 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
193 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
197 f32 min_y = lwn_f.Y + 0.5*BS;
198 if(position.Y < min_y)
208 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
210 v3f accel_f = v3f(0,0,0);
212 collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
213 pos_max_d, m_collisionbox, player_stepheight, dtime,
214 position, m_speed, accel_f);
217 If the player's feet touch the topside of any node, this is
220 Player is allowed to jump when this is true.
222 bool touching_ground_was = touching_ground;
223 touching_ground = result.touching_ground;
225 //bool standing_on_unloaded = result.standing_on_unloaded;
228 Check the nodes under the player to see from which node the
229 player is sneaking from, if any. If the node from under
230 the player has been removed, the player falls.
232 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
233 if(m_sneak_node_exists &&
234 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
235 m_old_node_below_type != "air")
237 // Old node appears to have been removed; that is,
238 // it wasn't air before but now it is
239 m_need_to_get_new_sneak_node = false;
240 m_sneak_node_exists = false;
242 else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
244 // We are on something, so make sure to recalculate the sneak
246 m_need_to_get_new_sneak_node = true;
248 if(m_need_to_get_new_sneak_node && physics_override_sneak)
250 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
251 v2f player_p2df(position.X, position.Z);
252 f32 min_distance_f = 100000.0*BS;
253 // If already seeking from some node, compare to it.
254 /*if(m_sneak_node_exists)
256 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
257 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
258 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
259 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
260 // Ignore if player is not on the same level (likely dropped)
261 if(d_vert_f < 0.15*BS)
262 min_distance_f = d_horiz_f;
264 v3s16 new_sneak_node = m_sneak_node;
265 for(s16 x=-1; x<=1; x++)
266 for(s16 z=-1; z<=1; z++)
268 v3s16 p = pos_i_bottom + v3s16(x,0,z);
269 v3f pf = intToFloat(p, BS);
270 v2f node_p2df(pf.X, pf.Z);
271 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
272 f32 max_axis_distance_f = MYMAX(
273 fabs(player_p2df.X-node_p2df.X),
274 fabs(player_p2df.Y-node_p2df.Y));
276 if(distance_f > min_distance_f ||
277 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
281 // The node to be sneaked on has to be walkable
282 node = map->getNodeNoEx(p, &is_valid_position);
283 if (!is_valid_position || nodemgr->get(node).walkable == false)
285 // And the node above it has to be nonwalkable
286 node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
287 if (!is_valid_position || nodemgr->get(node).walkable) {
290 if (!physics_override_sneak_glitch) {
291 node =map->getNodeNoEx(p + v3s16(0,2,0), &is_valid_position);
292 if (!is_valid_position || nodemgr->get(node).walkable)
296 min_distance_f = distance_f;
300 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
302 m_sneak_node = new_sneak_node;
303 m_sneak_node_exists = sneak_node_found;
306 If sneaking, the player's collision box can be in air, so
307 this has to be set explicitly
309 if(sneak_node_found && control.sneak)
310 touching_ground = true;
316 setPosition(position);
321 bool bouncy_jump = false;
322 // Dont report if flying
323 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed))
325 for(size_t i=0; i<result.collisions.size(); i++){
326 const CollisionInfo &info = result.collisions[i];
327 collision_info->push_back(info);
328 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
334 if(bouncy_jump && control.jump){
335 m_speed.Y += movement_speed_jump*BS;
336 touching_ground = false;
337 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
338 m_gamedef->event()->put(e);
341 if(!touching_ground_was && touching_ground){
342 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
343 m_gamedef->event()->put(e);
345 // Set camera impact value to be used for view bobbing
346 camera_impact = getSpeed().Y * -1;
350 camera_barely_in_ceiling = false;
351 v3s16 camera_np = floatToInt(getEyePosition(), BS);
352 MapNode n = map->getNodeNoEx(camera_np);
353 if(n.getContent() != CONTENT_IGNORE){
354 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
355 camera_barely_in_ceiling = true;
361 Update the node last under the player
363 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
364 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
367 Check properties of the node on which the player is standing
369 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
370 // Determine if jumping is possible
371 m_can_jump = touching_ground && !in_liquid;
372 if(itemgroup_get(f.groups, "disable_jump"))
376 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
378 move(dtime, env, pos_max_d, NULL);
381 void LocalPlayer::applyControl(float dtime)
384 swimming_vertical = false;
386 setPitch(control.pitch);
389 // Nullify speed and don't run positioning code if the player is attached
392 setSpeed(v3f(0,0,0));
396 v3f move_direction = v3f(0,0,1);
397 move_direction.rotateXZBy(getYaw());
399 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
400 v3f speedV = v3f(0,0,0); // Vertical (Y)
402 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
403 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
405 bool free_move = fly_allowed && g_settings->getBool("free_move");
406 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
407 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
408 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
409 bool continuous_forward = g_settings->getBool("continuous_forward");
411 // Whether superspeed mode is used or not
412 bool superspeed = false;
414 if(g_settings->getBool("always_fly_fast") && free_move && fast_move)
417 // Old descend control
418 if(g_settings->getBool("aux1_descends"))
420 // If free movement and fast movement, always move fast
421 if(free_move && fast_move)
424 // Auxiliary button 1 (E)
429 // In free movement mode, aux1 descends
431 speedV.Y = -movement_speed_fast;
433 speedV.Y = -movement_speed_walk;
435 else if(in_liquid || in_liquid_stable)
437 speedV.Y = -movement_speed_walk;
438 swimming_vertical = true;
442 speedV.Y = -movement_speed_climb;
446 // If not free movement but fast is allowed, aux1 is
453 // New minecraft-like descend control
456 // Auxiliary button 1 (E)
461 // aux1 is "Turbo button"
471 // In free movement mode, sneak descends
472 if(fast_move && (control.aux1 || g_settings->getBool("always_fly_fast")))
473 speedV.Y = -movement_speed_fast;
475 speedV.Y = -movement_speed_walk;
477 else if(in_liquid || in_liquid_stable)
480 speedV.Y = -movement_speed_fast;
482 speedV.Y = -movement_speed_walk;
483 swimming_vertical = true;
488 speedV.Y = -movement_speed_fast;
490 speedV.Y = -movement_speed_climb;
495 if(continuous_forward)
496 speedH += move_direction;
500 if(continuous_forward)
503 speedH += move_direction;
507 speedH -= move_direction;
511 speedH += move_direction.crossProduct(v3f(0,1,0));
515 speedH += move_direction.crossProduct(v3f(0,-1,0));
521 if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
524 speedV.Y = movement_speed_fast;
526 speedV.Y = movement_speed_walk;
528 if(fast_move && control.aux1)
529 speedV.Y = movement_speed_fast;
531 speedV.Y = movement_speed_walk;
537 NOTE: The d value in move() affects jump height by
538 raising the height at which the jump speed is kept
539 at its starting value
541 v3f speedJ = getSpeed();
542 if(speedJ.Y >= -0.5 * BS)
544 speedJ.Y = movement_speed_jump * physics_override_jump;
547 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
548 m_gamedef->event()->put(e);
554 speedV.Y = movement_speed_fast;
556 speedV.Y = movement_speed_walk;
557 swimming_vertical = true;
562 speedV.Y = movement_speed_fast;
564 speedV.Y = movement_speed_climb;
568 // The speed of the player (Y is ignored)
569 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
570 speedH = speedH.normalize() * movement_speed_fast;
571 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
572 speedH = speedH.normalize() * movement_speed_crouch;
574 speedH = speedH.normalize() * movement_speed_walk;
576 // Acceleration increase
577 f32 incH = 0; // Horizontal (X, Z)
578 f32 incV = 0; // Vertical (Y)
579 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
581 // Jumping and falling
582 if(superspeed || (fast_move && control.aux1))
583 incH = movement_acceleration_fast * BS * dtime;
585 incH = movement_acceleration_air * BS * dtime;
586 incV = 0; // No vertical acceleration in air
588 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
589 incH = incV = movement_acceleration_fast * BS * dtime;
591 incH = incV = movement_acceleration_default * BS * dtime;
593 // Accelerate to target speed with maximum increment
594 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
595 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
598 v3s16 LocalPlayer::getStandingNodePos()
600 if(m_sneak_node_exists)
602 return floatToInt(getPosition() - v3f(0, BS, 0), BS);