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);
155 f32 min_y = lwn_f.Y + 0.5*BS;
156 if(position.Y < min_y)
167 Calculate player collision box (new and old)
169 core::aabbox3d<f32> playerbox(
178 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
180 v3f accel_f = v3f(0,0,0);
182 collisionMoveResult result = collisionMoveSimple(&map, m_gamedef,
183 pos_max_d, playerbox, player_stepheight, dtime,
184 position, m_speed, accel_f);
187 If the player's feet touch the topside of any node, this is
190 Player is allowed to jump when this is true.
192 bool touching_ground_was = touching_ground;
193 touching_ground = result.touching_ground;
195 //bool standing_on_unloaded = result.standing_on_unloaded;
198 Check the nodes under the player to see from which node the
199 player is sneaking from, if any. If the node from under
200 the player has been removed, the player falls.
202 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
203 if(m_sneak_node_exists &&
204 nodemgr->get(map.getNodeNoEx(m_old_node_below)).name == "air" &&
205 m_old_node_below_type != "air")
207 // Old node appears to have been removed; that is,
208 // it wasn't air before but now it is
209 m_need_to_get_new_sneak_node = false;
210 m_sneak_node_exists = false;
212 else if(nodemgr->get(map.getNodeNoEx(current_node)).name != "air")
214 // We are on something, so make sure to recalculate the sneak
216 m_need_to_get_new_sneak_node = true;
218 if(m_need_to_get_new_sneak_node)
220 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
221 v2f player_p2df(position.X, position.Z);
222 f32 min_distance_f = 100000.0*BS;
223 // If already seeking from some node, compare to it.
224 /*if(m_sneak_node_exists)
226 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
227 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
228 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
229 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
230 // Ignore if player is not on the same level (likely dropped)
231 if(d_vert_f < 0.15*BS)
232 min_distance_f = d_horiz_f;
234 v3s16 new_sneak_node = m_sneak_node;
235 for(s16 x=-1; x<=1; x++)
236 for(s16 z=-1; z<=1; z++)
238 v3s16 p = pos_i_bottom + v3s16(x,0,z);
239 v3f pf = intToFloat(p, BS);
240 v2f node_p2df(pf.X, pf.Z);
241 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
242 f32 max_axis_distance_f = MYMAX(
243 fabs(player_p2df.X-node_p2df.X),
244 fabs(player_p2df.Y-node_p2df.Y));
246 if(distance_f > min_distance_f ||
247 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
251 // The node to be sneaked on has to be walkable
252 if(nodemgr->get(map.getNode(p)).walkable == false)
254 // And the node above it has to be nonwalkable
255 if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true)
258 catch(InvalidPositionException &e)
263 min_distance_f = distance_f;
267 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
269 m_sneak_node = new_sneak_node;
270 m_sneak_node_exists = sneak_node_found;
273 If sneaking, the player's collision box can be in air, so
274 this has to be set explicitly
276 if(sneak_node_found && control.sneak)
277 touching_ground = true;
283 setPosition(position);
288 bool bouncy_jump = false;
291 for(size_t i=0; i<result.collisions.size(); i++){
292 const CollisionInfo &info = result.collisions[i];
293 collision_info->push_back(info);
294 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
300 if(bouncy_jump && control.jump){
302 touching_ground = false;
303 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
304 m_gamedef->event()->put(e);
307 if(!touching_ground_was && touching_ground){
308 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
309 m_gamedef->event()->put(e);
313 camera_barely_in_ceiling = false;
314 v3s16 camera_np = floatToInt(getEyePosition(), BS);
315 MapNode n = map.getNodeNoEx(camera_np);
316 if(n.getContent() != CONTENT_IGNORE){
317 if(nodemgr->get(n).walkable){
318 camera_barely_in_ceiling = true;
324 Update the node last under the player
326 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
327 m_old_node_below_type = nodemgr->get(map.getNodeNoEx(m_old_node_below)).name;
330 Check properties of the node on which the player is standing
332 const ContentFeatures &f = nodemgr->get(map.getNodeNoEx(getStandingNodePos()));
333 // Determine if jumping is possible
334 m_can_jump = touching_ground;
335 if(itemgroup_get(f.groups, "disable_jump"))
339 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
341 move(dtime, map, pos_max_d, NULL);
344 void LocalPlayer::applyControl(float dtime)
350 f32 walk_acceleration = 4.0 * BS;
351 f32 walkspeed_max = 4.0 * BS;
353 setPitch(control.pitch);
356 v3f move_direction = v3f(0,0,1);
357 move_direction.rotateXZBy(getYaw());
359 v3f speed = v3f(0,0,0);
361 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
362 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
364 bool free_move = fly_allowed && g_settings->getBool("free_move");
365 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
366 bool continuous_forward = g_settings->getBool("continuous_forward");
368 if(free_move || is_climbing)
370 v3f speed = getSpeed();
375 // Whether superspeed mode is used or not
376 bool superspeed = false;
378 // If free movement and fast movement, always move fast
379 if(free_move && fast_move)
382 // Old descend control
383 if(g_settings->getBool("aux1_descends"))
385 // Auxiliary button 1 (E)
390 // In free movement mode, aux1 descends
391 v3f speed = getSpeed();
395 speed.Y = -walkspeed_max;
400 v3f speed = getSpeed();
406 // If not free movement but fast is allowed, aux1 is
413 // New minecraft-like descend control
416 // Auxiliary button 1 (E)
419 if(!free_move && !is_climbing)
421 // If not free movement but fast is allowed, aux1 is
432 // In free movement mode, sneak descends
433 v3f speed = getSpeed();
437 speed.Y = -walkspeed_max;
442 v3f speed = getSpeed();
449 if(continuous_forward)
450 speed += move_direction;
454 if(continuous_forward)
457 speed += move_direction;
461 speed -= move_direction;
465 speed += move_direction.crossProduct(v3f(0,1,0));
469 speed += move_direction.crossProduct(v3f(0,-1,0));
475 v3f speed = getSpeed();
479 speed.Y = walkspeed_max;
485 NOTE: The d value in move() affects jump height by
486 raising the height at which the jump speed is kept
487 at its starting value
489 v3f speed = getSpeed();
490 if(speed.Y >= -0.5*BS)
495 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
496 m_gamedef->event()->put(e);
499 // Use the oscillating value for getting out of water
500 // (so that the player doesn't fly on the surface)
503 v3f speed = getSpeed();
510 v3f speed = getSpeed();
516 // The speed of the player (Y is ignored)
518 speed = speed.normalize() * walkspeed_max * 5.0;
519 else if(control.sneak)
520 speed = speed.normalize() * walkspeed_max / 3.0;
522 speed = speed.normalize() * walkspeed_max;
524 f32 inc = walk_acceleration * BS * dtime;
526 // Faster acceleration if fast and free movement
527 if(free_move && fast_move)
528 inc = walk_acceleration * BS * dtime * 10;
530 // Accelerate to target speed with maximum increment
531 accelerate(speed, inc);
534 v3s16 LocalPlayer::getStandingNodePos()
536 if(m_sneak_node_exists)
538 return floatToInt(getPosition(), BS);