3 Copyright (C) 2010-2012 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"
29 #include "util/numeric.h"
35 LocalPlayer::LocalPlayer(IGameDef *gamedef):
38 overridePosition(v3f(0,0,0)),
39 last_position(v3f(0,0,0)),
40 last_speed(v3f(0,0,0)),
44 m_sneak_node(32767,32767,32767),
45 m_sneak_node_exists(false),
46 m_old_node_below(32767,32767,32767),
47 m_old_node_below_type("air"),
48 m_need_to_get_new_sneak_node(true),
51 // Initialize hp to 0, so that no hearts will be shown if server
52 // doesn't support health points
56 LocalPlayer::~LocalPlayer()
60 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
61 core::list<CollisionInfo> *collision_info)
63 INodeDefManager *nodemgr = m_gamedef->ndef();
65 v3f position = getPosition();
67 v3f old_speed = m_speed;
69 // Copy parent position if local player is attached
72 setPosition(overridePosition);
76 // Skip collision detection if noclip mode is used
77 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
78 bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
79 g_settings->getBool("noclip");
80 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
83 position += m_speed * dtime;
84 setPosition(position);
93 Check if player is in water (the oscillating value)
96 // If in water, the threshold of coming out is at higher y
99 v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
100 in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
102 // If not in water, the threshold of going in is at lower y
105 v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
106 in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
109 catch(InvalidPositionException &e)
115 Check if player is in water (the stable value)
118 v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
119 in_water_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
121 catch(InvalidPositionException &e)
123 in_water_stable = false;
127 Check if player is climbing
131 v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
132 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
133 is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable ||
134 nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move);
136 catch(InvalidPositionException &e)
142 Collision uncertainty radius
143 Make it a bit larger than the maximum distance of movement
145 //f32 d = pos_max_d * 1.1;
146 // A fairly large value in here makes moving smoother
149 // This should always apply, otherwise there are glitches
150 assert(d > pos_max_d);
152 float player_radius = BS*0.30;
153 float player_height = BS*1.55;
155 // Maximum distance over border for sneaking
156 f32 sneak_max = BS*0.4;
159 If sneaking, keep in range from the last walked node and don't
162 if(control.sneak && m_sneak_node_exists)
164 f32 maxd = 0.5*BS + sneak_max;
165 v3f lwn_f = intToFloat(m_sneak_node, BS);
166 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
167 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
171 f32 min_y = lwn_f.Y + 0.5*BS;
172 if(position.Y < min_y)
183 Calculate player collision box (new and old)
185 core::aabbox3d<f32> playerbox(
194 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
196 v3f accel_f = v3f(0,0,0);
198 collisionMoveResult result = collisionMoveSimple(&map, m_gamedef,
199 pos_max_d, playerbox, player_stepheight, dtime,
200 position, m_speed, accel_f);
203 If the player's feet touch the topside of any node, this is
206 Player is allowed to jump when this is true.
208 bool touching_ground_was = touching_ground;
209 touching_ground = result.touching_ground;
211 //bool standing_on_unloaded = result.standing_on_unloaded;
214 Check the nodes under the player to see from which node the
215 player is sneaking from, if any. If the node from under
216 the player has been removed, the player falls.
218 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
219 if(m_sneak_node_exists &&
220 nodemgr->get(map.getNodeNoEx(m_old_node_below)).name == "air" &&
221 m_old_node_below_type != "air")
223 // Old node appears to have been removed; that is,
224 // it wasn't air before but now it is
225 m_need_to_get_new_sneak_node = false;
226 m_sneak_node_exists = false;
228 else if(nodemgr->get(map.getNodeNoEx(current_node)).name != "air")
230 // We are on something, so make sure to recalculate the sneak
232 m_need_to_get_new_sneak_node = true;
234 if(m_need_to_get_new_sneak_node)
236 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
237 v2f player_p2df(position.X, position.Z);
238 f32 min_distance_f = 100000.0*BS;
239 // If already seeking from some node, compare to it.
240 /*if(m_sneak_node_exists)
242 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
243 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
244 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
245 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
246 // Ignore if player is not on the same level (likely dropped)
247 if(d_vert_f < 0.15*BS)
248 min_distance_f = d_horiz_f;
250 v3s16 new_sneak_node = m_sneak_node;
251 for(s16 x=-1; x<=1; x++)
252 for(s16 z=-1; z<=1; z++)
254 v3s16 p = pos_i_bottom + v3s16(x,0,z);
255 v3f pf = intToFloat(p, BS);
256 v2f node_p2df(pf.X, pf.Z);
257 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
258 f32 max_axis_distance_f = MYMAX(
259 fabs(player_p2df.X-node_p2df.X),
260 fabs(player_p2df.Y-node_p2df.Y));
262 if(distance_f > min_distance_f ||
263 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
267 // The node to be sneaked on has to be walkable
268 if(nodemgr->get(map.getNode(p)).walkable == false)
270 // And the node above it has to be nonwalkable
271 if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true)
274 catch(InvalidPositionException &e)
279 min_distance_f = distance_f;
283 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
285 m_sneak_node = new_sneak_node;
286 m_sneak_node_exists = sneak_node_found;
289 If sneaking, the player's collision box can be in air, so
290 this has to be set explicitly
292 if(sneak_node_found && control.sneak)
293 touching_ground = true;
299 setPosition(position);
304 bool bouncy_jump = false;
305 // Dont report if flying
306 if(collision_info && !g_settings->getBool("free_move"))
308 for(size_t i=0; i<result.collisions.size(); i++){
309 const CollisionInfo &info = result.collisions[i];
310 collision_info->push_back(info);
311 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
317 if(bouncy_jump && control.jump){
319 touching_ground = false;
320 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
321 m_gamedef->event()->put(e);
324 if(!touching_ground_was && touching_ground){
325 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
326 m_gamedef->event()->put(e);
330 camera_barely_in_ceiling = false;
331 v3s16 camera_np = floatToInt(getEyePosition(), BS);
332 MapNode n = map.getNodeNoEx(camera_np);
333 if(n.getContent() != CONTENT_IGNORE){
334 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
335 camera_barely_in_ceiling = true;
341 Update the node last under the player
343 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
344 m_old_node_below_type = nodemgr->get(map.getNodeNoEx(m_old_node_below)).name;
347 Check properties of the node on which the player is standing
349 const ContentFeatures &f = nodemgr->get(map.getNodeNoEx(getStandingNodePos()));
350 // Determine if jumping is possible
351 m_can_jump = touching_ground;
352 if(itemgroup_get(f.groups, "disable_jump"))
356 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
358 move(dtime, map, pos_max_d, NULL);
361 void LocalPlayer::applyControl(float dtime)
367 f32 walk_acceleration = 4.0 * BS;
368 f32 walkspeed_max = 4.0 * BS;
370 setPitch(control.pitch);
373 // Nullify speed and don't run positioning code if the player is attached
376 setSpeed(v3f(0,0,0));
380 v3f move_direction = v3f(0,0,1);
381 move_direction.rotateXZBy(getYaw());
383 v3f speed = v3f(0,0,0);
385 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
386 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
388 bool free_move = fly_allowed && g_settings->getBool("free_move");
389 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
390 bool continuous_forward = g_settings->getBool("continuous_forward");
392 if(free_move || is_climbing)
394 v3f speed = getSpeed();
399 // Whether superspeed mode is used or not
400 bool superspeed = false;
402 // If free movement and fast movement, always move fast
403 if(free_move && fast_move)
406 // Old descend control
407 if(g_settings->getBool("aux1_descends"))
409 // Auxiliary button 1 (E)
414 // In free movement mode, aux1 descends
415 v3f speed = getSpeed();
419 speed.Y = -walkspeed_max;
424 v3f speed = getSpeed();
430 // If not free movement but fast is allowed, aux1 is
437 // New minecraft-like descend control
440 // Auxiliary button 1 (E)
443 if(!free_move && !is_climbing)
445 // If not free movement but fast is allowed, aux1 is
456 // In free movement mode, sneak descends
457 v3f speed = getSpeed();
461 speed.Y = -walkspeed_max;
466 v3f speed = getSpeed();
473 if(continuous_forward)
474 speed += move_direction;
478 if(continuous_forward)
481 speed += move_direction;
485 speed -= move_direction;
489 speed += move_direction.crossProduct(v3f(0,1,0));
493 speed += move_direction.crossProduct(v3f(0,-1,0));
499 v3f speed = getSpeed();
503 speed.Y = walkspeed_max;
509 NOTE: The d value in move() affects jump height by
510 raising the height at which the jump speed is kept
511 at its starting value
513 v3f speed = getSpeed();
514 if(speed.Y >= -0.5*BS)
519 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
520 m_gamedef->event()->put(e);
523 // Use the oscillating value for getting out of water
524 // (so that the player doesn't fly on the surface)
527 v3f speed = getSpeed();
534 v3f speed = getSpeed();
540 // The speed of the player (Y is ignored)
542 speed = speed.normalize() * walkspeed_max * 5.0;
543 else if(control.sneak)
544 speed = speed.normalize() * walkspeed_max / 3.0;
546 speed = speed.normalize() * walkspeed_max;
548 f32 inc = walk_acceleration * BS * dtime;
550 // Faster acceleration if fast and free movement
551 if(free_move && fast_move)
552 inc = walk_acceleration * BS * dtime * 10;
554 // Accelerate to target speed with maximum increment
555 accelerate(speed, inc);
558 v3s16 LocalPlayer::getStandingNodePos()
560 if(m_sneak_node_exists)
562 return floatToInt(getPosition(), BS);