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, 0.05 * BS, 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, 0.05 * BS ,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);
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);
331 if(!touching_ground_was && touching_ground){
332 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
333 m_gamedef->event()->put(e);
335 // Set camera impact value to be used for view bobbing
336 camera_impact = getSpeed().Y * -1;
340 camera_barely_in_ceiling = false;
341 v3s16 camera_np = floatToInt(getEyePosition(), BS);
342 MapNode n = map->getNodeNoEx(camera_np);
343 if(n.getContent() != CONTENT_IGNORE){
344 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
345 camera_barely_in_ceiling = true;
351 Update the node last under the player
353 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
354 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
357 Check properties of the node on which the player is standing
359 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
360 // Determine if jumping is possible
361 m_can_jump = touching_ground && !in_liquid;
362 if(itemgroup_get(f.groups, "disable_jump"))
364 // Jump key pressed while jumping off from a bouncy block
365 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
366 m_speed.Y >= -0.5 * BS) {
367 float jumpspeed = movement_speed_jump * physics_override_jump;
369 // Reduce boost when speed already is high
370 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
372 m_speed.Y += jumpspeed;
379 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
381 move(dtime, env, pos_max_d, NULL);
384 void LocalPlayer::applyControl(float dtime)
387 swimming_vertical = false;
389 setPitch(control.pitch);
392 // Nullify speed and don't run positioning code if the player is attached
395 setSpeed(v3f(0,0,0));
399 v3f move_direction = v3f(0,0,1);
400 move_direction.rotateXZBy(getYaw());
402 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
403 v3f speedV = v3f(0,0,0); // Vertical (Y)
405 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
406 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
408 bool free_move = fly_allowed && g_settings->getBool("free_move");
409 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
410 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
411 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
412 bool continuous_forward = g_settings->getBool("continuous_forward");
413 bool always_fly_fast = g_settings->getBool("always_fly_fast");
415 // Whether superspeed mode is used or not
416 bool superspeed = false;
418 if (always_fly_fast && free_move && fast_move)
421 // Old descend control
422 if(g_settings->getBool("aux1_descends"))
424 // If free movement and fast movement, always move fast
425 if(free_move && fast_move)
428 // Auxiliary button 1 (E)
433 // In free movement mode, aux1 descends
435 speedV.Y = -movement_speed_fast;
437 speedV.Y = -movement_speed_walk;
439 else if(in_liquid || in_liquid_stable)
441 speedV.Y = -movement_speed_walk;
442 swimming_vertical = true;
446 speedV.Y = -movement_speed_climb;
450 // If not free movement but fast is allowed, aux1 is
457 // New minecraft-like descend control
460 // Auxiliary button 1 (E)
465 // aux1 is "Turbo button"
475 // In free movement mode, sneak descends
476 if (fast_move && (control.aux1 || always_fly_fast))
477 speedV.Y = -movement_speed_fast;
479 speedV.Y = -movement_speed_walk;
481 else if(in_liquid || in_liquid_stable)
484 speedV.Y = -movement_speed_fast;
486 speedV.Y = -movement_speed_walk;
487 swimming_vertical = true;
492 speedV.Y = -movement_speed_fast;
494 speedV.Y = -movement_speed_climb;
499 if(continuous_forward)
500 speedH += move_direction;
504 if(continuous_forward)
507 speedH += move_direction;
511 speedH -= move_direction;
515 speedH += move_direction.crossProduct(v3f(0,1,0));
519 speedH += move_direction.crossProduct(v3f(0,-1,0));
524 if (g_settings->getBool("aux1_descends") || always_fly_fast) {
526 speedV.Y = movement_speed_fast;
528 speedV.Y = movement_speed_walk;
530 if(fast_move && control.aux1)
531 speedV.Y = movement_speed_fast;
533 speedV.Y = movement_speed_walk;
539 NOTE: The d value in move() affects jump height by
540 raising the height at which the jump speed is kept
541 at its starting value
543 v3f speedJ = getSpeed();
544 if(speedJ.Y >= -0.5 * BS)
546 speedJ.Y = movement_speed_jump * physics_override_jump;
549 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
550 m_gamedef->event()->put(e);
556 speedV.Y = movement_speed_fast;
558 speedV.Y = movement_speed_walk;
559 swimming_vertical = true;
564 speedV.Y = movement_speed_fast;
566 speedV.Y = movement_speed_climb;
570 // The speed of the player (Y is ignored)
571 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
572 speedH = speedH.normalize() * movement_speed_fast;
573 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
574 speedH = speedH.normalize() * movement_speed_crouch;
576 speedH = speedH.normalize() * movement_speed_walk;
578 // Acceleration increase
579 f32 incH = 0; // Horizontal (X, Z)
580 f32 incV = 0; // Vertical (Y)
581 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
583 // Jumping and falling
584 if(superspeed || (fast_move && control.aux1))
585 incH = movement_acceleration_fast * BS * dtime;
587 incH = movement_acceleration_air * BS * dtime;
588 incV = 0; // No vertical acceleration in air
590 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
591 incH = incV = movement_acceleration_fast * BS * dtime;
593 incH = incV = movement_acceleration_default * BS * dtime;
595 // Accelerate to target speed with maximum increment
596 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
597 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
600 v3s16 LocalPlayer::getStandingNodePos()
602 if(m_sneak_node_exists)
604 return floatToInt(getPosition() - v3f(0, BS, 0), BS);