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"
23 #include "collision.h"
27 #include "environment.h"
29 #include "util/numeric.h"
35 LocalPlayer::LocalPlayer(IGameDef *gamedef, const char *name):
36 Player(gamedef, name),
39 overridePosition(v3f(0,0,0)),
40 last_position(v3f(0,0,0)),
41 last_speed(v3f(0,0,0)),
45 last_animation(NO_ANIM),
47 hotbar_selected_image(""),
48 light_color(255,255,255,255),
49 m_sneak_node(32767,32767,32767),
50 m_sneak_node_exists(false),
51 m_old_node_below(32767,32767,32767),
52 m_old_node_below_type("air"),
53 m_need_to_get_new_sneak_node(true),
57 // Initialize hp to 0, so that no hearts will be shown if server
58 // doesn't support health points
60 eye_offset_first = v3f(0,0,0);
61 eye_offset_third = v3f(0,0,0);
64 LocalPlayer::~LocalPlayer()
68 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
69 std::vector<CollisionInfo> *collision_info)
71 Map *map = &env->getMap();
72 INodeDefManager *nodemgr = m_gamedef->ndef();
74 v3f position = getPosition();
76 // Copy parent position if local player is attached
79 setPosition(overridePosition);
80 m_sneak_node_exists = false;
84 // Skip collision detection if noclip mode is used
85 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
86 bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
87 g_settings->getBool("noclip");
88 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
90 position += m_speed * dtime;
91 setPosition(position);
92 m_sneak_node_exists = false;
100 bool is_valid_position;
105 Check if player is in liquid (the oscillating value)
108 // If in liquid, the threshold of coming out is at higher y
111 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
112 node = map->getNodeNoEx(pp, &is_valid_position);
113 if (is_valid_position) {
114 in_liquid = nodemgr->get(node.getContent()).isLiquid();
115 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
120 // If not in liquid, the threshold of going in is at lower y
123 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
124 node = map->getNodeNoEx(pp, &is_valid_position);
125 if (is_valid_position) {
126 in_liquid = nodemgr->get(node.getContent()).isLiquid();
127 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
135 Check if player is in liquid (the stable value)
137 pp = floatToInt(position + v3f(0,0,0), BS);
138 node = map->getNodeNoEx(pp, &is_valid_position);
139 if (is_valid_position) {
140 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
142 in_liquid_stable = false;
146 Check if player is climbing
150 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
151 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
152 node = map->getNodeNoEx(pp, &is_valid_position);
153 bool is_valid_position2;
154 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
156 if (!(is_valid_position && is_valid_position2)) {
159 is_climbing = (nodemgr->get(node.getContent()).climbable
160 || nodemgr->get(node2.getContent()).climbable) && !free_move;
165 Collision uncertainty radius
166 Make it a bit larger than the maximum distance of movement
168 //f32 d = pos_max_d * 1.1;
169 // A fairly large value in here makes moving smoother
172 // This should always apply, otherwise there are glitches
173 sanity_check(d > pos_max_d);
175 // Maximum distance over border for sneaking
176 f32 sneak_max = BS*0.4;
179 If sneaking, keep in range from the last walked node and don't
182 if(control.sneak && m_sneak_node_exists &&
183 !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
184 physics_override_sneak)
186 f32 maxd = 0.5*BS + sneak_max;
187 v3f lwn_f = intToFloat(m_sneak_node, BS);
188 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
189 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
193 f32 min_y = lwn_f.Y + 0.5*BS;
194 if(position.Y < min_y)
204 // this shouldn't be hardcoded but transmitted from server
205 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
208 player_stepheight += (0.5 * BS);
211 v3f accel_f = v3f(0,0,0);
213 collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
214 pos_max_d, m_collisionbox, player_stepheight, dtime,
215 position, m_speed, accel_f);
218 If the player's feet touch the topside of any node, this is
221 Player is allowed to jump when this is true.
223 bool touching_ground_was = touching_ground;
224 touching_ground = result.touching_ground;
226 //bool standing_on_unloaded = result.standing_on_unloaded;
229 Check the nodes under the player to see from which node the
230 player is sneaking from, if any. If the node from under
231 the player has been removed, the player falls.
233 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
234 if(m_sneak_node_exists &&
235 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
236 m_old_node_below_type != "air")
238 // Old node appears to have been removed; that is,
239 // it wasn't air before but now it is
240 m_need_to_get_new_sneak_node = false;
241 m_sneak_node_exists = false;
243 else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
245 // We are on something, so make sure to recalculate the sneak
247 m_need_to_get_new_sneak_node = true;
249 if(m_need_to_get_new_sneak_node && physics_override_sneak)
251 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
252 v2f player_p2df(position.X, position.Z);
253 f32 min_distance_f = 100000.0*BS;
254 // If already seeking from some node, compare to it.
255 /*if(m_sneak_node_exists)
257 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
258 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
259 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
260 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
261 // Ignore if player is not on the same level (likely dropped)
262 if(d_vert_f < 0.15*BS)
263 min_distance_f = d_horiz_f;
265 v3s16 new_sneak_node = m_sneak_node;
266 for(s16 x=-1; x<=1; x++)
267 for(s16 z=-1; z<=1; z++)
269 v3s16 p = pos_i_bottom + v3s16(x,0,z);
270 v3f pf = intToFloat(p, BS);
271 v2f node_p2df(pf.X, pf.Z);
272 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
273 f32 max_axis_distance_f = MYMAX(
274 fabs(player_p2df.X-node_p2df.X),
275 fabs(player_p2df.Y-node_p2df.Y));
277 if(distance_f > min_distance_f ||
278 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
282 // The node to be sneaked on has to be walkable
283 node = map->getNodeNoEx(p, &is_valid_position);
284 if (!is_valid_position || nodemgr->get(node).walkable == false)
286 // And the node above it has to be nonwalkable
287 node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
288 if (!is_valid_position || nodemgr->get(node).walkable) {
291 if (!physics_override_sneak_glitch) {
292 node =map->getNodeNoEx(p + v3s16(0,2,0), &is_valid_position);
293 if (!is_valid_position || nodemgr->get(node).walkable)
297 min_distance_f = distance_f;
301 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
303 m_sneak_node = new_sneak_node;
304 m_sneak_node_exists = sneak_node_found;
307 If sneaking, the player's collision box can be in air, so
308 this has to be set explicitly
310 if(sneak_node_found && control.sneak)
311 touching_ground = true;
317 setPosition(position);
322 bool bouncy_jump = false;
323 // Dont report if flying
324 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");
410 bool always_fly_fast = g_settings->getBool("always_fly_fast");
412 // Whether superspeed mode is used or not
413 bool superspeed = false;
415 if (always_fly_fast && free_move && fast_move)
418 // Old descend control
419 if(g_settings->getBool("aux1_descends"))
421 // If free movement and fast movement, always move fast
422 if(free_move && fast_move)
425 // Auxiliary button 1 (E)
430 // In free movement mode, aux1 descends
432 speedV.Y = -movement_speed_fast;
434 speedV.Y = -movement_speed_walk;
436 else if(in_liquid || in_liquid_stable)
438 speedV.Y = -movement_speed_walk;
439 swimming_vertical = true;
443 speedV.Y = -movement_speed_climb;
447 // If not free movement but fast is allowed, aux1 is
454 // New minecraft-like descend control
457 // Auxiliary button 1 (E)
462 // aux1 is "Turbo button"
472 // In free movement mode, sneak descends
473 if (fast_move && (control.aux1 || always_fly_fast))
474 speedV.Y = -movement_speed_fast;
476 speedV.Y = -movement_speed_walk;
478 else if(in_liquid || in_liquid_stable)
481 speedV.Y = -movement_speed_fast;
483 speedV.Y = -movement_speed_walk;
484 swimming_vertical = true;
489 speedV.Y = -movement_speed_fast;
491 speedV.Y = -movement_speed_climb;
496 if(continuous_forward)
497 speedH += move_direction;
501 if(continuous_forward)
504 speedH += move_direction;
508 speedH -= move_direction;
512 speedH += move_direction.crossProduct(v3f(0,1,0));
516 speedH += move_direction.crossProduct(v3f(0,-1,0));
521 if (g_settings->getBool("aux1_descends") || always_fly_fast) {
523 speedV.Y = movement_speed_fast;
525 speedV.Y = movement_speed_walk;
527 if(fast_move && control.aux1)
528 speedV.Y = movement_speed_fast;
530 speedV.Y = movement_speed_walk;
536 NOTE: The d value in move() affects jump height by
537 raising the height at which the jump speed is kept
538 at its starting value
540 v3f speedJ = getSpeed();
541 if(speedJ.Y >= -0.5 * BS)
543 speedJ.Y = movement_speed_jump * physics_override_jump;
546 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
547 m_gamedef->event()->put(e);
553 speedV.Y = movement_speed_fast;
555 speedV.Y = movement_speed_walk;
556 swimming_vertical = true;
561 speedV.Y = movement_speed_fast;
563 speedV.Y = movement_speed_climb;
567 // The speed of the player (Y is ignored)
568 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
569 speedH = speedH.normalize() * movement_speed_fast;
570 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
571 speedH = speedH.normalize() * movement_speed_crouch;
573 speedH = speedH.normalize() * movement_speed_walk;
575 // Acceleration increase
576 f32 incH = 0; // Horizontal (X, Z)
577 f32 incV = 0; // Vertical (Y)
578 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
580 // Jumping and falling
581 if(superspeed || (fast_move && control.aux1))
582 incH = movement_acceleration_fast * BS * dtime;
584 incH = movement_acceleration_air * BS * dtime;
585 incV = 0; // No vertical acceleration in air
587 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
588 incH = incV = movement_acceleration_fast * BS * dtime;
590 incH = incV = movement_acceleration_default * BS * dtime;
592 // Accelerate to target speed with maximum increment
593 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
594 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
597 v3s16 LocalPlayer::getStandingNodePos()
599 if(m_sneak_node_exists)
601 return floatToInt(getPosition() - v3f(0, BS, 0), BS);