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 // Copy parent position if local player is attached
80 setPosition(overridePosition);
81 m_sneak_node_exists = false;
85 // Skip collision detection if noclip mode is used
86 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
87 bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
88 g_settings->getBool("noclip");
89 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
92 position += m_speed * dtime;
93 setPosition(position);
94 m_sneak_node_exists = false;
102 bool is_valid_position;
107 Check if player is in liquid (the oscillating value)
110 // If in liquid, the threshold of coming out is at higher y
113 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
114 node = map->getNodeNoEx(pp, &is_valid_position);
115 if (is_valid_position) {
116 in_liquid = nodemgr->get(node.getContent()).isLiquid();
117 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
122 // If not in liquid, the threshold of going in is at lower y
125 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
126 node = map->getNodeNoEx(pp, &is_valid_position);
127 if (is_valid_position) {
128 in_liquid = nodemgr->get(node.getContent()).isLiquid();
129 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
137 Check if player is in liquid (the stable value)
139 pp = floatToInt(position + v3f(0,0,0), BS);
140 node = map->getNodeNoEx(pp, &is_valid_position);
141 if (is_valid_position) {
142 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
144 in_liquid_stable = false;
148 Check if player is climbing
152 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
153 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
154 node = map->getNodeNoEx(pp, &is_valid_position);
155 bool is_valid_position2;
156 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
158 if (!(is_valid_position && is_valid_position2)) {
161 is_climbing = (nodemgr->get(node.getContent()).climbable
162 || nodemgr->get(node2.getContent()).climbable) && !free_move;
167 Collision uncertainty radius
168 Make it a bit larger than the maximum distance of movement
170 //f32 d = pos_max_d * 1.1;
171 // A fairly large value in here makes moving smoother
174 // This should always apply, otherwise there are glitches
175 assert(d > pos_max_d);
177 // Maximum distance over border for sneaking
178 f32 sneak_max = BS*0.4;
181 If sneaking, keep in range from the last walked node and don't
184 if(control.sneak && m_sneak_node_exists &&
185 !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
186 physics_override_sneak)
188 f32 maxd = 0.5*BS + sneak_max;
189 v3f lwn_f = intToFloat(m_sneak_node, BS);
190 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
191 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
195 f32 min_y = lwn_f.Y + 0.5*BS;
196 if(position.Y < min_y)
206 // this shouldn't be hardcoded but transmitted from server
207 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
210 player_stepheight += (0.5 * BS);
213 v3f accel_f = v3f(0,0,0);
215 collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
216 pos_max_d, m_collisionbox, player_stepheight, dtime,
217 position, m_speed, accel_f);
220 If the player's feet touch the topside of any node, this is
223 Player is allowed to jump when this is true.
225 bool touching_ground_was = touching_ground;
226 touching_ground = result.touching_ground;
228 //bool standing_on_unloaded = result.standing_on_unloaded;
231 Check the nodes under the player to see from which node the
232 player is sneaking from, if any. If the node from under
233 the player has been removed, the player falls.
235 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
236 if(m_sneak_node_exists &&
237 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
238 m_old_node_below_type != "air")
240 // Old node appears to have been removed; that is,
241 // it wasn't air before but now it is
242 m_need_to_get_new_sneak_node = false;
243 m_sneak_node_exists = false;
245 else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
247 // We are on something, so make sure to recalculate the sneak
249 m_need_to_get_new_sneak_node = true;
251 if(m_need_to_get_new_sneak_node && physics_override_sneak)
253 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
254 v2f player_p2df(position.X, position.Z);
255 f32 min_distance_f = 100000.0*BS;
256 // If already seeking from some node, compare to it.
257 /*if(m_sneak_node_exists)
259 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
260 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
261 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
262 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
263 // Ignore if player is not on the same level (likely dropped)
264 if(d_vert_f < 0.15*BS)
265 min_distance_f = d_horiz_f;
267 v3s16 new_sneak_node = m_sneak_node;
268 for(s16 x=-1; x<=1; x++)
269 for(s16 z=-1; z<=1; z++)
271 v3s16 p = pos_i_bottom + v3s16(x,0,z);
272 v3f pf = intToFloat(p, BS);
273 v2f node_p2df(pf.X, pf.Z);
274 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
275 f32 max_axis_distance_f = MYMAX(
276 fabs(player_p2df.X-node_p2df.X),
277 fabs(player_p2df.Y-node_p2df.Y));
279 if(distance_f > min_distance_f ||
280 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
284 // The node to be sneaked on has to be walkable
285 node = map->getNodeNoEx(p, &is_valid_position);
286 if (!is_valid_position || nodemgr->get(node).walkable == false)
288 // And the node above it has to be nonwalkable
289 node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
290 if (!is_valid_position || nodemgr->get(node).walkable) {
293 if (!physics_override_sneak_glitch) {
294 node =map->getNodeNoEx(p + v3s16(0,2,0), &is_valid_position);
295 if (!is_valid_position || nodemgr->get(node).walkable)
299 min_distance_f = distance_f;
303 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
305 m_sneak_node = new_sneak_node;
306 m_sneak_node_exists = sneak_node_found;
309 If sneaking, the player's collision box can be in air, so
310 this has to be set explicitly
312 if(sneak_node_found && control.sneak)
313 touching_ground = true;
319 setPosition(position);
324 bool bouncy_jump = false;
325 // Dont report if flying
326 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed))
328 for(size_t i=0; i<result.collisions.size(); i++){
329 const CollisionInfo &info = result.collisions[i];
330 collision_info->push_back(info);
331 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
337 if(bouncy_jump && control.jump){
338 m_speed.Y += movement_speed_jump*BS;
339 touching_ground = false;
340 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
341 m_gamedef->event()->put(e);
344 if(!touching_ground_was && touching_ground){
345 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
346 m_gamedef->event()->put(e);
348 // Set camera impact value to be used for view bobbing
349 camera_impact = getSpeed().Y * -1;
353 camera_barely_in_ceiling = false;
354 v3s16 camera_np = floatToInt(getEyePosition(), BS);
355 MapNode n = map->getNodeNoEx(camera_np);
356 if(n.getContent() != CONTENT_IGNORE){
357 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
358 camera_barely_in_ceiling = true;
364 Update the node last under the player
366 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
367 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
370 Check properties of the node on which the player is standing
372 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
373 // Determine if jumping is possible
374 m_can_jump = touching_ground && !in_liquid;
375 if(itemgroup_get(f.groups, "disable_jump"))
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");
414 // Whether superspeed mode is used or not
415 bool superspeed = false;
417 if(g_settings->getBool("always_fly_fast") && free_move && fast_move)
420 // Old descend control
421 if(g_settings->getBool("aux1_descends"))
423 // If free movement and fast movement, always move fast
424 if(free_move && fast_move)
427 // Auxiliary button 1 (E)
432 // In free movement mode, aux1 descends
434 speedV.Y = -movement_speed_fast;
436 speedV.Y = -movement_speed_walk;
438 else if(in_liquid || in_liquid_stable)
440 speedV.Y = -movement_speed_walk;
441 swimming_vertical = true;
445 speedV.Y = -movement_speed_climb;
449 // If not free movement but fast is allowed, aux1 is
456 // New minecraft-like descend control
459 // Auxiliary button 1 (E)
464 // aux1 is "Turbo button"
474 // In free movement mode, sneak descends
475 if(fast_move && (control.aux1 || g_settings->getBool("always_fly_fast")))
476 speedV.Y = -movement_speed_fast;
478 speedV.Y = -movement_speed_walk;
480 else if(in_liquid || in_liquid_stable)
483 speedV.Y = -movement_speed_fast;
485 speedV.Y = -movement_speed_walk;
486 swimming_vertical = true;
491 speedV.Y = -movement_speed_fast;
493 speedV.Y = -movement_speed_climb;
498 if(continuous_forward)
499 speedH += move_direction;
503 if(continuous_forward)
506 speedH += move_direction;
510 speedH -= move_direction;
514 speedH += move_direction.crossProduct(v3f(0,1,0));
518 speedH += move_direction.crossProduct(v3f(0,-1,0));
524 if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
527 speedV.Y = movement_speed_fast;
529 speedV.Y = movement_speed_walk;
531 if(fast_move && control.aux1)
532 speedV.Y = movement_speed_fast;
534 speedV.Y = movement_speed_walk;
540 NOTE: The d value in move() affects jump height by
541 raising the height at which the jump speed is kept
542 at its starting value
544 v3f speedJ = getSpeed();
545 if(speedJ.Y >= -0.5 * BS)
547 speedJ.Y = movement_speed_jump * physics_override_jump;
550 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
551 m_gamedef->event()->put(e);
557 speedV.Y = movement_speed_fast;
559 speedV.Y = movement_speed_walk;
560 swimming_vertical = true;
565 speedV.Y = movement_speed_fast;
567 speedV.Y = movement_speed_climb;
571 // The speed of the player (Y is ignored)
572 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
573 speedH = speedH.normalize() * movement_speed_fast;
574 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
575 speedH = speedH.normalize() * movement_speed_crouch;
577 speedH = speedH.normalize() * movement_speed_walk;
579 // Acceleration increase
580 f32 incH = 0; // Horizontal (X, Z)
581 f32 incV = 0; // Vertical (Y)
582 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
584 // Jumping and falling
585 if(superspeed || (fast_move && control.aux1))
586 incH = movement_acceleration_fast * BS * dtime;
588 incH = movement_acceleration_air * BS * dtime;
589 incV = 0; // No vertical acceleration in air
591 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
592 incH = incV = movement_acceleration_fast * BS * dtime;
594 incH = incV = movement_acceleration_default * BS * dtime;
596 // Accelerate to target speed with maximum increment
597 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
598 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
601 v3s16 LocalPlayer::getStandingNodePos()
603 if(m_sneak_node_exists)
605 return floatToInt(getPosition() - v3f(0, BS, 0), BS);