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::vector<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)) {
327 for(size_t i=0; i<result.collisions.size(); i++) {
328 const CollisionInfo &info = result.collisions[i];
329 collision_info->push_back(info);
330 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
336 if(bouncy_jump && control.jump){
337 m_speed.Y += movement_speed_jump*BS;
338 touching_ground = false;
339 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
340 m_gamedef->event()->put(e);
343 if(!touching_ground_was && touching_ground){
344 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
345 m_gamedef->event()->put(e);
347 // Set camera impact value to be used for view bobbing
348 camera_impact = getSpeed().Y * -1;
352 camera_barely_in_ceiling = false;
353 v3s16 camera_np = floatToInt(getEyePosition(), BS);
354 MapNode n = map->getNodeNoEx(camera_np);
355 if(n.getContent() != CONTENT_IGNORE){
356 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
357 camera_barely_in_ceiling = true;
363 Update the node last under the player
365 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
366 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
369 Check properties of the node on which the player is standing
371 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
372 // Determine if jumping is possible
373 m_can_jump = touching_ground && !in_liquid;
374 if(itemgroup_get(f.groups, "disable_jump"))
378 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
380 move(dtime, env, pos_max_d, NULL);
383 void LocalPlayer::applyControl(float dtime)
386 swimming_vertical = false;
388 setPitch(control.pitch);
391 // Nullify speed and don't run positioning code if the player is attached
394 setSpeed(v3f(0,0,0));
398 v3f move_direction = v3f(0,0,1);
399 move_direction.rotateXZBy(getYaw());
401 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
402 v3f speedV = v3f(0,0,0); // Vertical (Y)
404 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
405 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
407 bool free_move = fly_allowed && g_settings->getBool("free_move");
408 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
409 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
410 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
411 bool continuous_forward = g_settings->getBool("continuous_forward");
413 // Whether superspeed mode is used or not
414 bool superspeed = false;
416 if(g_settings->getBool("always_fly_fast") && free_move && fast_move)
419 // Old descend control
420 if(g_settings->getBool("aux1_descends"))
422 // If free movement and fast movement, always move fast
423 if(free_move && fast_move)
426 // Auxiliary button 1 (E)
431 // In free movement mode, aux1 descends
433 speedV.Y = -movement_speed_fast;
435 speedV.Y = -movement_speed_walk;
437 else if(in_liquid || in_liquid_stable)
439 speedV.Y = -movement_speed_walk;
440 swimming_vertical = true;
444 speedV.Y = -movement_speed_climb;
448 // If not free movement but fast is allowed, aux1 is
455 // New minecraft-like descend control
458 // Auxiliary button 1 (E)
463 // aux1 is "Turbo button"
473 // In free movement mode, sneak descends
474 if(fast_move && (control.aux1 || g_settings->getBool("always_fly_fast")))
475 speedV.Y = -movement_speed_fast;
477 speedV.Y = -movement_speed_walk;
479 else if(in_liquid || in_liquid_stable)
482 speedV.Y = -movement_speed_fast;
484 speedV.Y = -movement_speed_walk;
485 swimming_vertical = true;
490 speedV.Y = -movement_speed_fast;
492 speedV.Y = -movement_speed_climb;
497 if(continuous_forward)
498 speedH += move_direction;
502 if(continuous_forward)
505 speedH += move_direction;
509 speedH -= move_direction;
513 speedH += move_direction.crossProduct(v3f(0,1,0));
517 speedH += move_direction.crossProduct(v3f(0,-1,0));
523 if(g_settings->getBool("aux1_descends") || g_settings->getBool("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);