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 eye_offset_first(v3f(0,0,0)),
46 eye_offset_third(v3f(0,0,0)),
47 last_animation(NO_ANIM),
49 hotbar_selected_image(""),
50 light_color(255,255,255,255),
51 m_sneak_node(32767,32767,32767),
52 m_sneak_node_exists(false),
53 m_old_node_below(32767,32767,32767),
54 m_old_node_below_type("air"),
55 m_need_to_get_new_sneak_node(true),
59 // Initialize hp to 0, so that no hearts will be shown if server
60 // doesn't support health points
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");
91 position += m_speed * dtime;
92 setPosition(position);
93 m_sneak_node_exists = false;
101 bool is_valid_position;
106 Check if player is in liquid (the oscillating value)
109 // If in liquid, the threshold of coming out is at higher y
112 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
113 node = map->getNodeNoEx(pp, &is_valid_position);
114 if (is_valid_position) {
115 in_liquid = nodemgr->get(node.getContent()).isLiquid();
116 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
121 // If not in liquid, the threshold of going in is at lower y
124 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
125 node = map->getNodeNoEx(pp, &is_valid_position);
126 if (is_valid_position) {
127 in_liquid = nodemgr->get(node.getContent()).isLiquid();
128 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
136 Check if player is in liquid (the stable value)
138 pp = floatToInt(position + v3f(0,0,0), BS);
139 node = map->getNodeNoEx(pp, &is_valid_position);
140 if (is_valid_position) {
141 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
143 in_liquid_stable = false;
147 Check if player is climbing
151 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
152 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
153 node = map->getNodeNoEx(pp, &is_valid_position);
154 bool is_valid_position2;
155 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
157 if (!(is_valid_position && is_valid_position2)) {
160 is_climbing = (nodemgr->get(node.getContent()).climbable
161 || nodemgr->get(node2.getContent()).climbable) && !free_move;
166 Collision uncertainty radius
167 Make it a bit larger than the maximum distance of movement
169 //f32 d = pos_max_d * 1.1;
170 // A fairly large value in here makes moving smoother
173 // This should always apply, otherwise there are glitches
174 sanity_check(d > pos_max_d);
176 // Maximum distance over border for sneaking
177 f32 sneak_max = BS*0.4;
180 If sneaking, keep in range from the last walked node and don't
183 if(control.sneak && m_sneak_node_exists &&
184 !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
185 physics_override_sneak)
187 f32 maxd = 0.5*BS + sneak_max;
188 v3f lwn_f = intToFloat(m_sneak_node, BS);
189 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
190 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
194 f32 min_y = lwn_f.Y + 0.5*BS;
195 if(position.Y < min_y)
205 // this shouldn't be hardcoded but transmitted from server
206 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
209 player_stepheight += (0.5 * BS);
212 v3f accel_f = v3f(0,0,0);
214 collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
215 pos_max_d, m_collisionbox, player_stepheight, dtime,
216 position, m_speed, accel_f);
219 If the player's feet touch the topside of any node, this is
222 Player is allowed to jump when this is true.
224 bool touching_ground_was = touching_ground;
225 touching_ground = result.touching_ground;
227 //bool standing_on_unloaded = result.standing_on_unloaded;
230 Check the nodes under the player to see from which node the
231 player is sneaking from, if any. If the node from under
232 the player has been removed, the player falls.
234 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
235 if(m_sneak_node_exists &&
236 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
237 m_old_node_below_type != "air")
239 // Old node appears to have been removed; that is,
240 // it wasn't air before but now it is
241 m_need_to_get_new_sneak_node = false;
242 m_sneak_node_exists = false;
244 else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
246 // We are on something, so make sure to recalculate the sneak
248 m_need_to_get_new_sneak_node = true;
250 if(m_need_to_get_new_sneak_node && physics_override_sneak)
252 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
253 v2f player_p2df(position.X, position.Z);
254 f32 min_distance_f = 100000.0*BS;
255 // If already seeking from some node, compare to it.
256 /*if(m_sneak_node_exists)
258 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
259 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
260 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
261 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
262 // Ignore if player is not on the same level (likely dropped)
263 if(d_vert_f < 0.15*BS)
264 min_distance_f = d_horiz_f;
266 v3s16 new_sneak_node = m_sneak_node;
267 for(s16 x=-1; x<=1; x++)
268 for(s16 z=-1; z<=1; z++)
270 v3s16 p = pos_i_bottom + v3s16(x,0,z);
271 v3f pf = intToFloat(p, BS);
272 v2f node_p2df(pf.X, pf.Z);
273 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
274 f32 max_axis_distance_f = MYMAX(
275 fabs(player_p2df.X-node_p2df.X),
276 fabs(player_p2df.Y-node_p2df.Y));
278 if(distance_f > min_distance_f ||
279 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
283 // The node to be sneaked on has to be walkable
284 node = map->getNodeNoEx(p, &is_valid_position);
285 if (!is_valid_position || nodemgr->get(node).walkable == false)
287 // And the node above it has to be nonwalkable
288 node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
289 if (!is_valid_position || nodemgr->get(node).walkable) {
292 if (!physics_override_sneak_glitch) {
293 node =map->getNodeNoEx(p + v3s16(0,2,0), &is_valid_position);
294 if (!is_valid_position || nodemgr->get(node).walkable)
298 min_distance_f = distance_f;
302 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
304 m_sneak_node = new_sneak_node;
305 m_sneak_node_exists = sneak_node_found;
308 If sneaking, the player's collision box can be in air, so
309 this has to be set explicitly
311 if(sneak_node_found && control.sneak)
312 touching_ground = true;
318 setPosition(position);
323 bool bouncy_jump = false;
324 // Dont report if flying
325 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
326 for(size_t i=0; i<result.collisions.size(); i++) {
327 const CollisionInfo &info = result.collisions[i];
328 collision_info->push_back(info);
329 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
335 if(bouncy_jump && control.jump){
336 m_speed.Y += movement_speed_jump*BS;
337 touching_ground = false;
338 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
339 m_gamedef->event()->put(e);
342 if(!touching_ground_was && touching_ground){
343 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
344 m_gamedef->event()->put(e);
346 // Set camera impact value to be used for view bobbing
347 camera_impact = getSpeed().Y * -1;
351 camera_barely_in_ceiling = false;
352 v3s16 camera_np = floatToInt(getEyePosition(), BS);
353 MapNode n = map->getNodeNoEx(camera_np);
354 if(n.getContent() != CONTENT_IGNORE){
355 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
356 camera_barely_in_ceiling = true;
362 Update the node last under the player
364 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
365 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
368 Check properties of the node on which the player is standing
370 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
371 // Determine if jumping is possible
372 m_can_jump = touching_ground && !in_liquid;
373 if(itemgroup_get(f.groups, "disable_jump"))
377 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
379 move(dtime, env, pos_max_d, NULL);
382 void LocalPlayer::applyControl(float dtime)
385 swimming_vertical = false;
387 setPitch(control.pitch);
390 // Nullify speed and don't run positioning code if the player is attached
393 setSpeed(v3f(0,0,0));
397 v3f move_direction = v3f(0,0,1);
398 move_direction.rotateXZBy(getYaw());
400 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
401 v3f speedV = v3f(0,0,0); // Vertical (Y)
403 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
404 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
406 bool free_move = fly_allowed && g_settings->getBool("free_move");
407 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
408 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
409 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
410 bool continuous_forward = g_settings->getBool("continuous_forward");
412 // Whether superspeed mode is used or not
413 bool superspeed = false;
415 if(g_settings->getBool("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 || g_settings->getBool("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));
522 if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
525 speedV.Y = movement_speed_fast;
527 speedV.Y = movement_speed_walk;
529 if(fast_move && control.aux1)
530 speedV.Y = movement_speed_fast;
532 speedV.Y = movement_speed_walk;
538 NOTE: The d value in move() affects jump height by
539 raising the height at which the jump speed is kept
540 at its starting value
542 v3f speedJ = getSpeed();
543 if(speedJ.Y >= -0.5 * BS)
545 speedJ.Y = movement_speed_jump * physics_override_jump;
548 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
549 m_gamedef->event()->put(e);
555 speedV.Y = movement_speed_fast;
557 speedV.Y = movement_speed_walk;
558 swimming_vertical = true;
563 speedV.Y = movement_speed_fast;
565 speedV.Y = movement_speed_climb;
569 // The speed of the player (Y is ignored)
570 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
571 speedH = speedH.normalize() * movement_speed_fast;
572 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
573 speedH = speedH.normalize() * movement_speed_crouch;
575 speedH = speedH.normalize() * movement_speed_walk;
577 // Acceleration increase
578 f32 incH = 0; // Horizontal (X, Z)
579 f32 incV = 0; // Vertical (Y)
580 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
582 // Jumping and falling
583 if(superspeed || (fast_move && control.aux1))
584 incH = movement_acceleration_fast * BS * dtime;
586 incH = movement_acceleration_air * BS * dtime;
587 incV = 0; // No vertical acceleration in air
589 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
590 incH = incV = movement_acceleration_fast * BS * dtime;
592 incH = incV = movement_acceleration_default * BS * dtime;
594 // Accelerate to target speed with maximum increment
595 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
596 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
599 v3s16 LocalPlayer::getStandingNodePos()
601 if(m_sneak_node_exists)
603 return floatToInt(getPosition() - v3f(0, BS, 0), BS);