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 // this shouldn't be hardcoded but transmitted from server
209 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
212 player_stepheight += (0.5 * BS);
215 v3f accel_f = v3f(0,0,0);
217 collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
218 pos_max_d, m_collisionbox, player_stepheight, dtime,
219 position, m_speed, accel_f);
222 If the player's feet touch the topside of any node, this is
225 Player is allowed to jump when this is true.
227 bool touching_ground_was = touching_ground;
228 touching_ground = result.touching_ground;
230 //bool standing_on_unloaded = result.standing_on_unloaded;
233 Check the nodes under the player to see from which node the
234 player is sneaking from, if any. If the node from under
235 the player has been removed, the player falls.
237 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
238 if(m_sneak_node_exists &&
239 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
240 m_old_node_below_type != "air")
242 // Old node appears to have been removed; that is,
243 // it wasn't air before but now it is
244 m_need_to_get_new_sneak_node = false;
245 m_sneak_node_exists = false;
247 else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
249 // We are on something, so make sure to recalculate the sneak
251 m_need_to_get_new_sneak_node = true;
253 if(m_need_to_get_new_sneak_node && physics_override_sneak)
255 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
256 v2f player_p2df(position.X, position.Z);
257 f32 min_distance_f = 100000.0*BS;
258 // If already seeking from some node, compare to it.
259 /*if(m_sneak_node_exists)
261 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
262 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
263 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
264 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
265 // Ignore if player is not on the same level (likely dropped)
266 if(d_vert_f < 0.15*BS)
267 min_distance_f = d_horiz_f;
269 v3s16 new_sneak_node = m_sneak_node;
270 for(s16 x=-1; x<=1; x++)
271 for(s16 z=-1; z<=1; z++)
273 v3s16 p = pos_i_bottom + v3s16(x,0,z);
274 v3f pf = intToFloat(p, BS);
275 v2f node_p2df(pf.X, pf.Z);
276 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
277 f32 max_axis_distance_f = MYMAX(
278 fabs(player_p2df.X-node_p2df.X),
279 fabs(player_p2df.Y-node_p2df.Y));
281 if(distance_f > min_distance_f ||
282 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
286 // The node to be sneaked on has to be walkable
287 node = map->getNodeNoEx(p, &is_valid_position);
288 if (!is_valid_position || nodemgr->get(node).walkable == false)
290 // And the node above it has to be nonwalkable
291 node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
292 if (!is_valid_position || nodemgr->get(node).walkable) {
295 if (!physics_override_sneak_glitch) {
296 node =map->getNodeNoEx(p + v3s16(0,2,0), &is_valid_position);
297 if (!is_valid_position || nodemgr->get(node).walkable)
301 min_distance_f = distance_f;
305 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
307 m_sneak_node = new_sneak_node;
308 m_sneak_node_exists = sneak_node_found;
311 If sneaking, the player's collision box can be in air, so
312 this has to be set explicitly
314 if(sneak_node_found && control.sneak)
315 touching_ground = true;
321 setPosition(position);
326 bool bouncy_jump = false;
327 // Dont report if flying
328 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed))
330 for(size_t i=0; i<result.collisions.size(); i++){
331 const CollisionInfo &info = result.collisions[i];
332 collision_info->push_back(info);
333 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
339 if(bouncy_jump && control.jump){
340 m_speed.Y += movement_speed_jump*BS;
341 touching_ground = false;
342 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
343 m_gamedef->event()->put(e);
346 if(!touching_ground_was && touching_ground){
347 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
348 m_gamedef->event()->put(e);
350 // Set camera impact value to be used for view bobbing
351 camera_impact = getSpeed().Y * -1;
355 camera_barely_in_ceiling = false;
356 v3s16 camera_np = floatToInt(getEyePosition(), BS);
357 MapNode n = map->getNodeNoEx(camera_np);
358 if(n.getContent() != CONTENT_IGNORE){
359 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
360 camera_barely_in_ceiling = true;
366 Update the node last under the player
368 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
369 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
372 Check properties of the node on which the player is standing
374 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
375 // Determine if jumping is possible
376 m_can_jump = touching_ground && !in_liquid;
377 if(itemgroup_get(f.groups, "disable_jump"))
381 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
383 move(dtime, env, pos_max_d, NULL);
386 void LocalPlayer::applyControl(float dtime)
389 swimming_vertical = false;
391 setPitch(control.pitch);
394 // Nullify speed and don't run positioning code if the player is attached
397 setSpeed(v3f(0,0,0));
401 v3f move_direction = v3f(0,0,1);
402 move_direction.rotateXZBy(getYaw());
404 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
405 v3f speedV = v3f(0,0,0); // Vertical (Y)
407 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
408 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
410 bool free_move = fly_allowed && g_settings->getBool("free_move");
411 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
412 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
413 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
414 bool continuous_forward = g_settings->getBool("continuous_forward");
416 // Whether superspeed mode is used or not
417 bool superspeed = false;
419 if(g_settings->getBool("always_fly_fast") && free_move && fast_move)
422 // Old descend control
423 if(g_settings->getBool("aux1_descends"))
425 // If free movement and fast movement, always move fast
426 if(free_move && fast_move)
429 // Auxiliary button 1 (E)
434 // In free movement mode, aux1 descends
436 speedV.Y = -movement_speed_fast;
438 speedV.Y = -movement_speed_walk;
440 else if(in_liquid || in_liquid_stable)
442 speedV.Y = -movement_speed_walk;
443 swimming_vertical = true;
447 speedV.Y = -movement_speed_climb;
451 // If not free movement but fast is allowed, aux1 is
458 // New minecraft-like descend control
461 // Auxiliary button 1 (E)
466 // aux1 is "Turbo button"
476 // In free movement mode, sneak descends
477 if(fast_move && (control.aux1 || g_settings->getBool("always_fly_fast")))
478 speedV.Y = -movement_speed_fast;
480 speedV.Y = -movement_speed_walk;
482 else if(in_liquid || in_liquid_stable)
485 speedV.Y = -movement_speed_fast;
487 speedV.Y = -movement_speed_walk;
488 swimming_vertical = true;
493 speedV.Y = -movement_speed_fast;
495 speedV.Y = -movement_speed_climb;
500 if(continuous_forward)
501 speedH += move_direction;
505 if(continuous_forward)
508 speedH += move_direction;
512 speedH -= move_direction;
516 speedH += move_direction.crossProduct(v3f(0,1,0));
520 speedH += move_direction.crossProduct(v3f(0,-1,0));
526 if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
529 speedV.Y = movement_speed_fast;
531 speedV.Y = movement_speed_walk;
533 if(fast_move && control.aux1)
534 speedV.Y = movement_speed_fast;
536 speedV.Y = movement_speed_walk;
542 NOTE: The d value in move() affects jump height by
543 raising the height at which the jump speed is kept
544 at its starting value
546 v3f speedJ = getSpeed();
547 if(speedJ.Y >= -0.5 * BS)
549 speedJ.Y = movement_speed_jump * physics_override_jump;
552 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
553 m_gamedef->event()->put(e);
559 speedV.Y = movement_speed_fast;
561 speedV.Y = movement_speed_walk;
562 swimming_vertical = true;
567 speedV.Y = movement_speed_fast;
569 speedV.Y = movement_speed_climb;
573 // The speed of the player (Y is ignored)
574 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
575 speedH = speedH.normalize() * movement_speed_fast;
576 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
577 speedH = speedH.normalize() * movement_speed_crouch;
579 speedH = speedH.normalize() * movement_speed_walk;
581 // Acceleration increase
582 f32 incH = 0; // Horizontal (X, Z)
583 f32 incV = 0; // Vertical (Y)
584 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
586 // Jumping and falling
587 if(superspeed || (fast_move && control.aux1))
588 incH = movement_acceleration_fast * BS * dtime;
590 incH = movement_acceleration_air * BS * dtime;
591 incV = 0; // No vertical acceleration in air
593 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
594 incH = incV = movement_acceleration_fast * BS * dtime;
596 incH = incV = movement_acceleration_default * BS * dtime;
598 // Accelerate to target speed with maximum increment
599 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
600 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
603 v3s16 LocalPlayer::getStandingNodePos()
605 if(m_sneak_node_exists)
607 return floatToInt(getPosition() - v3f(0, BS, 0), BS);