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 // Copy parent position if local player is attached
59 setPosition(overridePosition);
63 INodeDefManager *nodemgr = m_gamedef->ndef();
65 v3f position = getPosition();
67 v3f old_speed = m_speed;
69 // Skip collision detection if a special movement mode is used
70 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
71 bool free_move = fly_allowed && g_settings->getBool("free_move");
74 position += m_speed * dtime;
75 setPosition(position);
84 Check if player is in water (the oscillating value)
87 // If in water, the threshold of coming out is at higher y
90 v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
91 in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
93 // If not in water, the threshold of going in is at lower y
96 v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
97 in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
100 catch(InvalidPositionException &e)
106 Check if player is in water (the stable value)
109 v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
110 in_water_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
112 catch(InvalidPositionException &e)
114 in_water_stable = false;
118 Check if player is climbing
122 v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
123 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
124 is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable ||
125 nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move);
127 catch(InvalidPositionException &e)
133 Collision uncertainty radius
134 Make it a bit larger than the maximum distance of movement
136 //f32 d = pos_max_d * 1.1;
137 // A fairly large value in here makes moving smoother
140 // This should always apply, otherwise there are glitches
141 assert(d > pos_max_d);
143 float player_radius = BS*0.30;
144 float player_height = BS*1.55;
146 // Maximum distance over border for sneaking
147 f32 sneak_max = BS*0.4;
150 If sneaking, keep in range from the last walked node and don't
153 if(control.sneak && m_sneak_node_exists)
155 f32 maxd = 0.5*BS + sneak_max;
156 v3f lwn_f = intToFloat(m_sneak_node, BS);
157 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
158 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
162 f32 min_y = lwn_f.Y + 0.5*BS;
163 if(position.Y < min_y)
174 Calculate player collision box (new and old)
176 core::aabbox3d<f32> playerbox(
185 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
187 v3f accel_f = v3f(0,0,0);
189 collisionMoveResult result = collisionMoveSimple(&map, m_gamedef,
190 pos_max_d, playerbox, player_stepheight, dtime,
191 position, m_speed, accel_f);
194 If the player's feet touch the topside of any node, this is
197 Player is allowed to jump when this is true.
199 bool touching_ground_was = touching_ground;
200 touching_ground = result.touching_ground;
202 //bool standing_on_unloaded = result.standing_on_unloaded;
205 Check the nodes under the player to see from which node the
206 player is sneaking from, if any. If the node from under
207 the player has been removed, the player falls.
209 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
210 if(m_sneak_node_exists &&
211 nodemgr->get(map.getNodeNoEx(m_old_node_below)).name == "air" &&
212 m_old_node_below_type != "air")
214 // Old node appears to have been removed; that is,
215 // it wasn't air before but now it is
216 m_need_to_get_new_sneak_node = false;
217 m_sneak_node_exists = false;
219 else if(nodemgr->get(map.getNodeNoEx(current_node)).name != "air")
221 // We are on something, so make sure to recalculate the sneak
223 m_need_to_get_new_sneak_node = true;
225 if(m_need_to_get_new_sneak_node)
227 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
228 v2f player_p2df(position.X, position.Z);
229 f32 min_distance_f = 100000.0*BS;
230 // If already seeking from some node, compare to it.
231 /*if(m_sneak_node_exists)
233 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
234 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
235 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
236 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
237 // Ignore if player is not on the same level (likely dropped)
238 if(d_vert_f < 0.15*BS)
239 min_distance_f = d_horiz_f;
241 v3s16 new_sneak_node = m_sneak_node;
242 for(s16 x=-1; x<=1; x++)
243 for(s16 z=-1; z<=1; z++)
245 v3s16 p = pos_i_bottom + v3s16(x,0,z);
246 v3f pf = intToFloat(p, BS);
247 v2f node_p2df(pf.X, pf.Z);
248 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
249 f32 max_axis_distance_f = MYMAX(
250 fabs(player_p2df.X-node_p2df.X),
251 fabs(player_p2df.Y-node_p2df.Y));
253 if(distance_f > min_distance_f ||
254 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
258 // The node to be sneaked on has to be walkable
259 if(nodemgr->get(map.getNode(p)).walkable == false)
261 // And the node above it has to be nonwalkable
262 if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true)
265 catch(InvalidPositionException &e)
270 min_distance_f = distance_f;
274 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
276 m_sneak_node = new_sneak_node;
277 m_sneak_node_exists = sneak_node_found;
280 If sneaking, the player's collision box can be in air, so
281 this has to be set explicitly
283 if(sneak_node_found && control.sneak)
284 touching_ground = true;
290 setPosition(position);
295 bool bouncy_jump = false;
298 for(size_t i=0; i<result.collisions.size(); i++){
299 const CollisionInfo &info = result.collisions[i];
300 collision_info->push_back(info);
301 if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
307 if(bouncy_jump && control.jump){
309 touching_ground = false;
310 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
311 m_gamedef->event()->put(e);
314 if(!touching_ground_was && touching_ground){
315 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
316 m_gamedef->event()->put(e);
320 camera_barely_in_ceiling = false;
321 v3s16 camera_np = floatToInt(getEyePosition(), BS);
322 MapNode n = map.getNodeNoEx(camera_np);
323 if(n.getContent() != CONTENT_IGNORE){
324 if(nodemgr->get(n).walkable){
325 camera_barely_in_ceiling = true;
331 Update the node last under the player
333 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
334 m_old_node_below_type = nodemgr->get(map.getNodeNoEx(m_old_node_below)).name;
337 Check properties of the node on which the player is standing
339 const ContentFeatures &f = nodemgr->get(map.getNodeNoEx(getStandingNodePos()));
340 // Determine if jumping is possible
341 m_can_jump = touching_ground;
342 if(itemgroup_get(f.groups, "disable_jump"))
346 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
348 move(dtime, map, pos_max_d, NULL);
351 void LocalPlayer::applyControl(float dtime)
357 f32 walk_acceleration = 4.0 * BS;
358 f32 walkspeed_max = 4.0 * BS;
360 setPitch(control.pitch);
363 v3f move_direction = v3f(0,0,1);
364 move_direction.rotateXZBy(getYaw());
366 v3f speed = v3f(0,0,0);
368 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
369 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
371 bool free_move = fly_allowed && g_settings->getBool("free_move");
372 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
373 bool continuous_forward = g_settings->getBool("continuous_forward");
375 if(free_move || is_climbing)
377 v3f speed = getSpeed();
382 // Whether superspeed mode is used or not
383 bool superspeed = false;
385 // If free movement and fast movement, always move fast
386 if(free_move && fast_move)
389 // Old descend control
390 if(g_settings->getBool("aux1_descends"))
392 // Auxiliary button 1 (E)
397 // In free movement mode, aux1 descends
398 v3f speed = getSpeed();
402 speed.Y = -walkspeed_max;
407 v3f speed = getSpeed();
413 // If not free movement but fast is allowed, aux1 is
420 // New minecraft-like descend control
423 // Auxiliary button 1 (E)
426 if(!free_move && !is_climbing)
428 // If not free movement but fast is allowed, aux1 is
439 // In free movement mode, sneak descends
440 v3f speed = getSpeed();
444 speed.Y = -walkspeed_max;
449 v3f speed = getSpeed();
456 if(continuous_forward)
457 speed += move_direction;
461 if(continuous_forward)
464 speed += move_direction;
468 speed -= move_direction;
472 speed += move_direction.crossProduct(v3f(0,1,0));
476 speed += move_direction.crossProduct(v3f(0,-1,0));
482 v3f speed = getSpeed();
486 speed.Y = walkspeed_max;
492 NOTE: The d value in move() affects jump height by
493 raising the height at which the jump speed is kept
494 at its starting value
496 v3f speed = getSpeed();
497 if(speed.Y >= -0.5*BS)
502 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
503 m_gamedef->event()->put(e);
506 // Use the oscillating value for getting out of water
507 // (so that the player doesn't fly on the surface)
510 v3f speed = getSpeed();
517 v3f speed = getSpeed();
523 // The speed of the player (Y is ignored)
525 speed = speed.normalize() * walkspeed_max * 5.0;
526 else if(control.sneak)
527 speed = speed.normalize() * walkspeed_max / 3.0;
529 speed = speed.normalize() * walkspeed_max;
531 f32 inc = walk_acceleration * BS * dtime;
533 // Faster acceleration if fast and free movement
534 if(free_move && fast_move)
535 inc = walk_acceleration * BS * dtime * 10;
537 // Accelerate to target speed with maximum increment
538 accelerate(speed, inc);
541 v3s16 LocalPlayer::getStandingNodePos()
543 if(m_sneak_node_exists)
545 return floatToInt(getPosition(), BS);