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 m_sneak_node(32767,32767,32767),
40 m_sneak_node_exists(false),
41 m_old_node_below(32767,32767,32767),
42 m_old_node_below_type("air"),
43 m_need_to_get_new_sneak_node(true),
46 // Initialize hp to 0, so that no hearts will be shown if server
47 // doesn't support health points
51 LocalPlayer::~LocalPlayer()
55 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
56 core::list<CollisionInfo> *collision_info)
58 INodeDefManager *nodemgr = m_gamedef->ndef();
60 v3f position = getPosition();
62 v3f old_speed = m_speed;
64 // Copy parent position if local player is attached
67 setPosition(overridePosition);
71 // Skip collision detection if a special movement mode is used
72 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
73 bool free_move = fly_allowed && g_settings->getBool("free_move");
76 position += m_speed * dtime;
77 setPosition(position);
86 Check if player is in water (the oscillating value)
89 // If in water, the threshold of coming out is at higher y
92 v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
93 in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
95 // If not in water, the threshold of going in is at lower y
98 v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
99 in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
102 catch(InvalidPositionException &e)
108 Check if player is in water (the stable value)
111 v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
112 in_water_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
114 catch(InvalidPositionException &e)
116 in_water_stable = false;
120 Check if player is climbing
124 v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
125 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
126 is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable ||
127 nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move);
129 catch(InvalidPositionException &e)
135 Collision uncertainty radius
136 Make it a bit larger than the maximum distance of movement
138 //f32 d = pos_max_d * 1.1;
139 // A fairly large value in here makes moving smoother
142 // This should always apply, otherwise there are glitches
143 assert(d > pos_max_d);
145 float player_radius = BS*0.30;
146 float player_height = BS*1.55;
148 // Maximum distance over border for sneaking
149 f32 sneak_max = BS*0.4;
152 If sneaking, keep in range from the last walked node and don't
155 if(control.sneak && m_sneak_node_exists)
157 f32 maxd = 0.5*BS + sneak_max;
158 v3f lwn_f = intToFloat(m_sneak_node, BS);
159 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
160 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
164 f32 min_y = lwn_f.Y + 0.5*BS;
165 if(position.Y < min_y)
176 Calculate player collision box (new and old)
178 core::aabbox3d<f32> playerbox(
187 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
189 v3f accel_f = v3f(0,0,0);
191 collisionMoveResult result = collisionMoveSimple(&map, m_gamedef,
192 pos_max_d, playerbox, player_stepheight, dtime,
193 position, m_speed, accel_f);
196 If the player's feet touch the topside of any node, this is
199 Player is allowed to jump when this is true.
201 bool touching_ground_was = touching_ground;
202 touching_ground = result.touching_ground;
204 //bool standing_on_unloaded = result.standing_on_unloaded;
207 Check the nodes under the player to see from which node the
208 player is sneaking from, if any. If the node from under
209 the player has been removed, the player falls.
211 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
212 if(m_sneak_node_exists &&
213 nodemgr->get(map.getNodeNoEx(m_old_node_below)).name == "air" &&
214 m_old_node_below_type != "air")
216 // Old node appears to have been removed; that is,
217 // it wasn't air before but now it is
218 m_need_to_get_new_sneak_node = false;
219 m_sneak_node_exists = false;
221 else if(nodemgr->get(map.getNodeNoEx(current_node)).name != "air")
223 // We are on something, so make sure to recalculate the sneak
225 m_need_to_get_new_sneak_node = true;
227 if(m_need_to_get_new_sneak_node)
229 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
230 v2f player_p2df(position.X, position.Z);
231 f32 min_distance_f = 100000.0*BS;
232 // If already seeking from some node, compare to it.
233 /*if(m_sneak_node_exists)
235 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
236 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
237 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
238 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
239 // Ignore if player is not on the same level (likely dropped)
240 if(d_vert_f < 0.15*BS)
241 min_distance_f = d_horiz_f;
243 v3s16 new_sneak_node = m_sneak_node;
244 for(s16 x=-1; x<=1; x++)
245 for(s16 z=-1; z<=1; z++)
247 v3s16 p = pos_i_bottom + v3s16(x,0,z);
248 v3f pf = intToFloat(p, BS);
249 v2f node_p2df(pf.X, pf.Z);
250 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
251 f32 max_axis_distance_f = MYMAX(
252 fabs(player_p2df.X-node_p2df.X),
253 fabs(player_p2df.Y-node_p2df.Y));
255 if(distance_f > min_distance_f ||
256 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
260 // The node to be sneaked on has to be walkable
261 if(nodemgr->get(map.getNode(p)).walkable == false)
263 // And the node above it has to be nonwalkable
264 if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true)
267 catch(InvalidPositionException &e)
272 min_distance_f = distance_f;
276 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
278 m_sneak_node = new_sneak_node;
279 m_sneak_node_exists = sneak_node_found;
282 If sneaking, the player's collision box can be in air, so
283 this has to be set explicitly
285 if(sneak_node_found && control.sneak)
286 touching_ground = true;
292 setPosition(position);
297 bool bouncy_jump = false;
300 for(size_t i=0; i<result.collisions.size(); i++){
301 const CollisionInfo &info = result.collisions[i];
302 collision_info->push_back(info);
303 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
309 if(bouncy_jump && control.jump){
311 touching_ground = false;
312 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
313 m_gamedef->event()->put(e);
316 if(!touching_ground_was && touching_ground){
317 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
318 m_gamedef->event()->put(e);
322 camera_barely_in_ceiling = false;
323 v3s16 camera_np = floatToInt(getEyePosition(), BS);
324 MapNode n = map.getNodeNoEx(camera_np);
325 if(n.getContent() != CONTENT_IGNORE){
326 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
327 camera_barely_in_ceiling = true;
333 Update the node last under the player
335 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
336 m_old_node_below_type = nodemgr->get(map.getNodeNoEx(m_old_node_below)).name;
339 Check properties of the node on which the player is standing
341 const ContentFeatures &f = nodemgr->get(map.getNodeNoEx(getStandingNodePos()));
342 // Determine if jumping is possible
343 m_can_jump = touching_ground;
344 if(itemgroup_get(f.groups, "disable_jump"))
348 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
350 move(dtime, map, pos_max_d, NULL);
353 void LocalPlayer::applyControl(float dtime)
359 f32 walk_acceleration = 4.0 * BS;
360 f32 walkspeed_max = 4.0 * BS;
362 setPitch(control.pitch);
365 // Nullify speed and don't run positioning code if the player is attached
368 setSpeed(v3f(0,0,0));
372 v3f move_direction = v3f(0,0,1);
373 move_direction.rotateXZBy(getYaw());
375 v3f speed = v3f(0,0,0);
377 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
378 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
380 bool free_move = fly_allowed && g_settings->getBool("free_move");
381 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
382 bool continuous_forward = g_settings->getBool("continuous_forward");
384 if(free_move || is_climbing)
386 v3f speed = getSpeed();
391 // Whether superspeed mode is used or not
392 bool superspeed = false;
394 // If free movement and fast movement, always move fast
395 if(free_move && fast_move)
398 // Old descend control
399 if(g_settings->getBool("aux1_descends"))
401 // Auxiliary button 1 (E)
406 // In free movement mode, aux1 descends
407 v3f speed = getSpeed();
411 speed.Y = -walkspeed_max;
416 v3f speed = getSpeed();
422 // If not free movement but fast is allowed, aux1 is
429 // New minecraft-like descend control
432 // Auxiliary button 1 (E)
435 if(!free_move && !is_climbing)
437 // If not free movement but fast is allowed, aux1 is
448 // In free movement mode, sneak descends
449 v3f speed = getSpeed();
453 speed.Y = -walkspeed_max;
458 v3f speed = getSpeed();
465 if(continuous_forward)
466 speed += move_direction;
470 if(continuous_forward)
473 speed += move_direction;
477 speed -= move_direction;
481 speed += move_direction.crossProduct(v3f(0,1,0));
485 speed += move_direction.crossProduct(v3f(0,-1,0));
491 v3f speed = getSpeed();
495 speed.Y = walkspeed_max;
501 NOTE: The d value in move() affects jump height by
502 raising the height at which the jump speed is kept
503 at its starting value
505 v3f speed = getSpeed();
506 if(speed.Y >= -0.5*BS)
511 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
512 m_gamedef->event()->put(e);
515 // Use the oscillating value for getting out of water
516 // (so that the player doesn't fly on the surface)
519 v3f speed = getSpeed();
526 v3f speed = getSpeed();
532 // The speed of the player (Y is ignored)
534 speed = speed.normalize() * walkspeed_max * 5.0;
535 else if(control.sneak)
536 speed = speed.normalize() * walkspeed_max / 3.0;
538 speed = speed.normalize() * walkspeed_max;
540 f32 inc = walk_acceleration * BS * dtime;
542 // Faster acceleration if fast and free movement
543 if(free_move && fast_move)
544 inc = walk_acceleration * BS * dtime * 10;
546 // Accelerate to target speed with maximum increment
547 accelerate(speed, inc);
550 v3s16 LocalPlayer::getStandingNodePos()
552 if(m_sneak_node_exists)
554 return floatToInt(getPosition(), BS);