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 a special movement mode is used
77 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
78 bool free_move = fly_allowed && g_settings->getBool("free_move");
81 position += m_speed * dtime;
82 setPosition(position);
91 Check if player is in water (the oscillating value)
94 // If in water, the threshold of coming out is at higher y
97 v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
98 in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
100 // If not in water, the threshold of going in is at lower y
103 v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
104 in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
107 catch(InvalidPositionException &e)
113 Check if player is in water (the stable value)
116 v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
117 in_water_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
119 catch(InvalidPositionException &e)
121 in_water_stable = false;
125 Check if player is climbing
129 v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
130 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
131 is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable ||
132 nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move);
134 catch(InvalidPositionException &e)
140 Collision uncertainty radius
141 Make it a bit larger than the maximum distance of movement
143 //f32 d = pos_max_d * 1.1;
144 // A fairly large value in here makes moving smoother
147 // This should always apply, otherwise there are glitches
148 assert(d > pos_max_d);
150 float player_radius = BS*0.30;
151 float player_height = BS*1.55;
153 // Maximum distance over border for sneaking
154 f32 sneak_max = BS*0.4;
157 If sneaking, keep in range from the last walked node and don't
160 if(control.sneak && m_sneak_node_exists)
162 f32 maxd = 0.5*BS + sneak_max;
163 v3f lwn_f = intToFloat(m_sneak_node, BS);
164 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
165 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
169 f32 min_y = lwn_f.Y + 0.5*BS;
170 if(position.Y < min_y)
181 Calculate player collision box (new and old)
183 core::aabbox3d<f32> playerbox(
192 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
194 v3f accel_f = v3f(0,0,0);
196 collisionMoveResult result = collisionMoveSimple(&map, m_gamedef,
197 pos_max_d, playerbox, player_stepheight, dtime,
198 position, m_speed, accel_f);
201 If the player's feet touch the topside of any node, this is
204 Player is allowed to jump when this is true.
206 bool touching_ground_was = touching_ground;
207 touching_ground = result.touching_ground;
209 //bool standing_on_unloaded = result.standing_on_unloaded;
212 Check the nodes under the player to see from which node the
213 player is sneaking from, if any. If the node from under
214 the player has been removed, the player falls.
216 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
217 if(m_sneak_node_exists &&
218 nodemgr->get(map.getNodeNoEx(m_old_node_below)).name == "air" &&
219 m_old_node_below_type != "air")
221 // Old node appears to have been removed; that is,
222 // it wasn't air before but now it is
223 m_need_to_get_new_sneak_node = false;
224 m_sneak_node_exists = false;
226 else if(nodemgr->get(map.getNodeNoEx(current_node)).name != "air")
228 // We are on something, so make sure to recalculate the sneak
230 m_need_to_get_new_sneak_node = true;
232 if(m_need_to_get_new_sneak_node)
234 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
235 v2f player_p2df(position.X, position.Z);
236 f32 min_distance_f = 100000.0*BS;
237 // If already seeking from some node, compare to it.
238 /*if(m_sneak_node_exists)
240 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
241 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
242 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
243 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
244 // Ignore if player is not on the same level (likely dropped)
245 if(d_vert_f < 0.15*BS)
246 min_distance_f = d_horiz_f;
248 v3s16 new_sneak_node = m_sneak_node;
249 for(s16 x=-1; x<=1; x++)
250 for(s16 z=-1; z<=1; z++)
252 v3s16 p = pos_i_bottom + v3s16(x,0,z);
253 v3f pf = intToFloat(p, BS);
254 v2f node_p2df(pf.X, pf.Z);
255 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
256 f32 max_axis_distance_f = MYMAX(
257 fabs(player_p2df.X-node_p2df.X),
258 fabs(player_p2df.Y-node_p2df.Y));
260 if(distance_f > min_distance_f ||
261 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
265 // The node to be sneaked on has to be walkable
266 if(nodemgr->get(map.getNode(p)).walkable == false)
268 // And the node above it has to be nonwalkable
269 if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true)
272 catch(InvalidPositionException &e)
277 min_distance_f = distance_f;
281 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
283 m_sneak_node = new_sneak_node;
284 m_sneak_node_exists = sneak_node_found;
287 If sneaking, the player's collision box can be in air, so
288 this has to be set explicitly
290 if(sneak_node_found && control.sneak)
291 touching_ground = true;
297 setPosition(position);
302 bool bouncy_jump = false;
305 for(size_t i=0; i<result.collisions.size(); i++){
306 const CollisionInfo &info = result.collisions[i];
307 collision_info->push_back(info);
308 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
314 if(bouncy_jump && control.jump){
316 touching_ground = false;
317 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
318 m_gamedef->event()->put(e);
321 if(!touching_ground_was && touching_ground){
322 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
323 m_gamedef->event()->put(e);
327 camera_barely_in_ceiling = false;
328 v3s16 camera_np = floatToInt(getEyePosition(), BS);
329 MapNode n = map.getNodeNoEx(camera_np);
330 if(n.getContent() != CONTENT_IGNORE){
331 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
332 camera_barely_in_ceiling = true;
338 Update the node last under the player
340 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
341 m_old_node_below_type = nodemgr->get(map.getNodeNoEx(m_old_node_below)).name;
344 Check properties of the node on which the player is standing
346 const ContentFeatures &f = nodemgr->get(map.getNodeNoEx(getStandingNodePos()));
347 // Determine if jumping is possible
348 m_can_jump = touching_ground;
349 if(itemgroup_get(f.groups, "disable_jump"))
353 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
355 move(dtime, map, pos_max_d, NULL);
358 void LocalPlayer::applyControl(float dtime)
364 f32 walk_acceleration = 4.0 * BS;
365 f32 walkspeed_max = 4.0 * BS;
367 setPitch(control.pitch);
370 // Nullify speed and don't run positioning code if the player is attached
373 setSpeed(v3f(0,0,0));
377 v3f move_direction = v3f(0,0,1);
378 move_direction.rotateXZBy(getYaw());
380 v3f speed = v3f(0,0,0);
382 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
383 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
385 bool free_move = fly_allowed && g_settings->getBool("free_move");
386 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
387 bool continuous_forward = g_settings->getBool("continuous_forward");
389 if(free_move || is_climbing)
391 v3f speed = getSpeed();
396 // Whether superspeed mode is used or not
397 bool superspeed = false;
399 // If free movement and fast movement, always move fast
400 if(free_move && fast_move)
403 // Old descend control
404 if(g_settings->getBool("aux1_descends"))
406 // Auxiliary button 1 (E)
411 // In free movement mode, aux1 descends
412 v3f speed = getSpeed();
416 speed.Y = -walkspeed_max;
421 v3f speed = getSpeed();
427 // If not free movement but fast is allowed, aux1 is
434 // New minecraft-like descend control
437 // Auxiliary button 1 (E)
440 if(!free_move && !is_climbing)
442 // If not free movement but fast is allowed, aux1 is
453 // In free movement mode, sneak descends
454 v3f speed = getSpeed();
458 speed.Y = -walkspeed_max;
463 v3f speed = getSpeed();
470 if(continuous_forward)
471 speed += move_direction;
475 if(continuous_forward)
478 speed += move_direction;
482 speed -= move_direction;
486 speed += move_direction.crossProduct(v3f(0,1,0));
490 speed += move_direction.crossProduct(v3f(0,-1,0));
496 v3f speed = getSpeed();
500 speed.Y = walkspeed_max;
506 NOTE: The d value in move() affects jump height by
507 raising the height at which the jump speed is kept
508 at its starting value
510 v3f speed = getSpeed();
511 if(speed.Y >= -0.5*BS)
516 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
517 m_gamedef->event()->put(e);
520 // Use the oscillating value for getting out of water
521 // (so that the player doesn't fly on the surface)
524 v3f speed = getSpeed();
531 v3f speed = getSpeed();
537 // The speed of the player (Y is ignored)
539 speed = speed.normalize() * walkspeed_max * 5.0;
540 else if(control.sneak)
541 speed = speed.normalize() * walkspeed_max / 3.0;
543 speed = speed.normalize() * walkspeed_max;
545 f32 inc = walk_acceleration * BS * dtime;
547 // Faster acceleration if fast and free movement
548 if(free_move && fast_move)
549 inc = walk_acceleration * BS * dtime * 10;
551 // Accelerate to target speed with maximum increment
552 accelerate(speed, inc);
555 v3s16 LocalPlayer::getStandingNodePos()
557 if(m_sneak_node_exists)
559 return floatToInt(getPosition(), BS);