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):
37 m_sneak_node(32767,32767,32767),
38 m_sneak_node_exists(false),
39 m_old_node_below(32767,32767,32767),
40 m_old_node_below_type("air"),
41 m_need_to_get_new_sneak_node(true),
44 // Initialize hp to 0, so that no hearts will be shown if server
45 // doesn't support health points
49 LocalPlayer::~LocalPlayer()
53 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
54 core::list<CollisionInfo> *collision_info)
56 INodeDefManager *nodemgr = m_gamedef->ndef();
58 v3f position = getPosition();
60 v3f old_speed = m_speed;
62 // Skip collision detection if a special movement mode is used
63 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
64 bool free_move = fly_allowed && g_settings->getBool("free_move");
67 position += m_speed * dtime;
68 setPosition(position);
77 Check if player is in water (the oscillating value)
80 // If in water, the threshold of coming out is at higher y
83 v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
84 in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
86 // If not in water, the threshold of going in is at lower y
89 v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
90 in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
93 catch(InvalidPositionException &e)
99 Check if player is in water (the stable value)
102 v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
103 in_water_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
105 catch(InvalidPositionException &e)
107 in_water_stable = false;
111 Check if player is climbing
115 v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
116 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
117 is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable ||
118 nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move);
120 catch(InvalidPositionException &e)
126 Collision uncertainty radius
127 Make it a bit larger than the maximum distance of movement
129 //f32 d = pos_max_d * 1.1;
130 // A fairly large value in here makes moving smoother
133 // This should always apply, otherwise there are glitches
134 assert(d > pos_max_d);
136 float player_radius = BS*0.30;
137 float player_height = BS*1.55;
139 // Maximum distance over border for sneaking
140 f32 sneak_max = BS*0.4;
143 If sneaking, keep in range from the last walked node and don't
146 if(control.sneak && m_sneak_node_exists)
148 f32 maxd = 0.5*BS + sneak_max;
149 v3f lwn_f = intToFloat(m_sneak_node, BS);
150 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
151 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
153 f32 min_y = lwn_f.Y + 0.5*BS;
154 if(position.Y < min_y)
164 Calculate player collision box (new and old)
166 core::aabbox3d<f32> playerbox(
175 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
177 v3f accel_f = v3f(0,0,0);
179 collisionMoveResult result = collisionMoveSimple(&map, m_gamedef,
180 pos_max_d, playerbox, player_stepheight, dtime,
181 position, m_speed, accel_f);
184 If the player's feet touch the topside of any node, this is
187 Player is allowed to jump when this is true.
189 bool touching_ground_was = touching_ground;
190 touching_ground = result.touching_ground;
192 //bool standing_on_unloaded = result.standing_on_unloaded;
195 Check the nodes under the player to see from which node the
196 player is sneaking from, if any. If the node from under
197 the player has been removed, the player falls.
199 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
200 if(m_sneak_node_exists &&
201 nodemgr->get(map.getNodeNoEx(m_old_node_below)).name == "air" &&
202 m_old_node_below_type != "air")
204 // Old node appears to have been removed; that is,
205 // it wasn't air before but now it is
206 m_need_to_get_new_sneak_node = false;
207 m_sneak_node_exists = false;
209 else if(nodemgr->get(map.getNodeNoEx(current_node)).name != "air")
211 // We are on something, so make sure to recalculate the sneak
213 m_need_to_get_new_sneak_node = true;
215 if(m_need_to_get_new_sneak_node)
217 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
218 v2f player_p2df(position.X, position.Z);
219 f32 min_distance_f = 100000.0*BS;
220 // If already seeking from some node, compare to it.
221 /*if(m_sneak_node_exists)
223 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
224 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
225 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
226 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
227 // Ignore if player is not on the same level (likely dropped)
228 if(d_vert_f < 0.15*BS)
229 min_distance_f = d_horiz_f;
231 v3s16 new_sneak_node = m_sneak_node;
232 for(s16 x=-1; x<=1; x++)
233 for(s16 z=-1; z<=1; z++)
235 v3s16 p = pos_i_bottom + v3s16(x,0,z);
236 v3f pf = intToFloat(p, BS);
237 v2f node_p2df(pf.X, pf.Z);
238 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
239 f32 max_axis_distance_f = MYMAX(
240 fabs(player_p2df.X-node_p2df.X),
241 fabs(player_p2df.Y-node_p2df.Y));
243 if(distance_f > min_distance_f ||
244 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
248 // The node to be sneaked on has to be walkable
249 if(nodemgr->get(map.getNode(p)).walkable == false)
251 // And the node above it has to be nonwalkable
252 if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true)
255 catch(InvalidPositionException &e)
260 min_distance_f = distance_f;
264 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
266 m_sneak_node = new_sneak_node;
267 m_sneak_node_exists = sneak_node_found;
270 If sneaking, the player's collision box can be in air, so
271 this has to be set explicitly
273 if(sneak_node_found && control.sneak)
274 touching_ground = true;
280 setPosition(position);
285 bool bouncy_jump = false;
288 for(size_t i=0; i<result.collisions.size(); i++){
289 const CollisionInfo &info = result.collisions[i];
290 collision_info->push_back(info);
291 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
297 if(bouncy_jump && control.jump){
299 touching_ground = false;
300 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
301 m_gamedef->event()->put(e);
304 if(!touching_ground_was && touching_ground){
305 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
306 m_gamedef->event()->put(e);
310 camera_barely_in_ceiling = false;
311 v3s16 camera_np = floatToInt(getEyePosition(), BS);
312 MapNode n = map.getNodeNoEx(camera_np);
313 if(n.getContent() != CONTENT_IGNORE){
314 if(nodemgr->get(n).walkable){
315 camera_barely_in_ceiling = true;
321 Update the node last under the player
323 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
324 m_old_node_below_type = nodemgr->get(map.getNodeNoEx(m_old_node_below)).name;
327 Check properties of the node on which the player is standing
329 const ContentFeatures &f = nodemgr->get(map.getNodeNoEx(getStandingNodePos()));
330 // Determine if jumping is possible
331 m_can_jump = touching_ground;
332 if(itemgroup_get(f.groups, "disable_jump"))
336 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
338 move(dtime, map, pos_max_d, NULL);
341 void LocalPlayer::applyControl(float dtime)
347 f32 walk_acceleration = 4.0 * BS;
348 f32 walkspeed_max = 4.0 * BS;
350 setPitch(control.pitch);
353 v3f move_direction = v3f(0,0,1);
354 move_direction.rotateXZBy(getYaw());
356 v3f speed = v3f(0,0,0);
358 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
359 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
361 bool free_move = fly_allowed && g_settings->getBool("free_move");
362 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
363 bool continuous_forward = g_settings->getBool("continuous_forward");
365 if(free_move || is_climbing)
367 v3f speed = getSpeed();
372 // Whether superspeed mode is used or not
373 bool superspeed = false;
375 // If free movement and fast movement, always move fast
376 if(free_move && fast_move)
379 // Old descend control
380 if(g_settings->getBool("aux1_descends"))
382 // Auxiliary button 1 (E)
387 // In free movement mode, aux1 descends
388 v3f speed = getSpeed();
392 speed.Y = -walkspeed_max;
397 v3f speed = getSpeed();
403 // If not free movement but fast is allowed, aux1 is
410 // New minecraft-like descend control
413 // Auxiliary button 1 (E)
416 if(!free_move && !is_climbing)
418 // If not free movement but fast is allowed, aux1 is
429 // In free movement mode, sneak descends
430 v3f speed = getSpeed();
434 speed.Y = -walkspeed_max;
439 v3f speed = getSpeed();
446 if(continuous_forward)
447 speed += move_direction;
451 if(continuous_forward)
454 speed += move_direction;
458 speed -= move_direction;
462 speed += move_direction.crossProduct(v3f(0,1,0));
466 speed += move_direction.crossProduct(v3f(0,-1,0));
472 v3f speed = getSpeed();
476 speed.Y = walkspeed_max;
482 NOTE: The d value in move() affects jump height by
483 raising the height at which the jump speed is kept
484 at its starting value
486 v3f speed = getSpeed();
487 if(speed.Y >= -0.5*BS)
492 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
493 m_gamedef->event()->put(e);
496 // Use the oscillating value for getting out of water
497 // (so that the player doesn't fly on the surface)
500 v3f speed = getSpeed();
507 v3f speed = getSpeed();
513 // The speed of the player (Y is ignored)
515 speed = speed.normalize() * walkspeed_max * 5.0;
516 else if(control.sneak)
517 speed = speed.normalize() * walkspeed_max / 3.0;
519 speed = speed.normalize() * walkspeed_max;
521 f32 inc = walk_acceleration * BS * dtime;
523 // Faster acceleration if fast and free movement
524 if(free_move && fast_move)
525 inc = walk_acceleration * BS * dtime * 10;
527 // Accelerate to target speed with maximum increment
528 accelerate(speed, inc);
531 v3s16 LocalPlayer::getStandingNodePos()
533 if(m_sneak_node_exists)
535 return floatToInt(getPosition(), BS);