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)
40 // Initialize hp to 0, so that no hearts will be shown if server
41 // doesn't support health points
45 LocalPlayer::~LocalPlayer()
49 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
50 core::list<CollisionInfo> *collision_info)
52 INodeDefManager *nodemgr = m_gamedef->ndef();
54 v3f position = getPosition();
56 v3f old_speed = m_speed;
58 // Skip collision detection if a special movement mode is used
59 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
60 bool free_move = fly_allowed && g_settings->getBool("free_move");
63 position += m_speed * dtime;
64 setPosition(position);
73 Check if player is in water (the oscillating value)
76 // If in water, the threshold of coming out is at higher y
79 v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
80 in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
82 // If not in water, the threshold of going in is at lower y
85 v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
86 in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
89 catch(InvalidPositionException &e)
95 Check if player is in water (the stable value)
98 v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
99 in_water_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
101 catch(InvalidPositionException &e)
103 in_water_stable = false;
107 Check if player is climbing
111 v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
112 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
113 is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable ||
114 nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move);
116 catch(InvalidPositionException &e)
122 Collision uncertainty radius
123 Make it a bit larger than the maximum distance of movement
125 //f32 d = pos_max_d * 1.1;
126 // A fairly large value in here makes moving smoother
129 // This should always apply, otherwise there are glitches
130 assert(d > pos_max_d);
132 float player_radius = BS*0.30;
133 float player_height = BS*1.55;
135 // Maximum distance over border for sneaking
136 f32 sneak_max = BS*0.4;
139 If sneaking, keep in range from the last walked node and don't
142 if(control.sneak && m_sneak_node_exists)
144 f32 maxd = 0.5*BS + sneak_max;
145 v3f lwn_f = intToFloat(m_sneak_node, BS);
146 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
147 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
149 f32 min_y = lwn_f.Y + 0.5*BS;
150 if(position.Y < min_y)
160 Calculate player collision box (new and old)
162 core::aabbox3d<f32> playerbox(
171 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
173 v3f accel_f = v3f(0,0,0);
175 collisionMoveResult result = collisionMoveSimple(&map, m_gamedef,
176 pos_max_d, playerbox, player_stepheight, dtime,
177 position, m_speed, accel_f);
180 If the player's feet touch the topside of any node, this is
183 Player is allowed to jump when this is true.
185 bool touching_ground_was = touching_ground;
186 touching_ground = result.touching_ground;
188 bool standing_on_unloaded = result.standing_on_unloaded;
191 Check the nodes under the player to see from which node the
192 player is sneaking from, if any.
195 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
196 v2f player_p2df(position.X, position.Z);
197 f32 min_distance_f = 100000.0*BS;
198 // If already seeking from some node, compare to it.
199 /*if(m_sneak_node_exists)
201 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
202 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
203 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
204 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
205 // Ignore if player is not on the same level (likely dropped)
206 if(d_vert_f < 0.15*BS)
207 min_distance_f = d_horiz_f;
209 v3s16 new_sneak_node = m_sneak_node;
210 for(s16 x=-1; x<=1; x++)
211 for(s16 z=-1; z<=1; z++)
213 v3s16 p = pos_i_bottom + v3s16(x,0,z);
214 v3f pf = intToFloat(p, BS);
215 v2f node_p2df(pf.X, pf.Z);
216 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
217 f32 max_axis_distance_f = MYMAX(
218 fabs(player_p2df.X-node_p2df.X),
219 fabs(player_p2df.Y-node_p2df.Y));
221 if(distance_f > min_distance_f ||
222 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
226 // The node to be sneaked on has to be walkable
227 if(nodemgr->get(map.getNode(p)).walkable == false)
229 // And the node above it has to be nonwalkable
230 if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true)
233 catch(InvalidPositionException &e)
238 min_distance_f = distance_f;
242 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
244 if(control.sneak && m_sneak_node_exists)
247 m_sneak_node = new_sneak_node;
251 m_sneak_node = new_sneak_node;
252 m_sneak_node_exists = sneak_node_found;
256 If sneaking, the player's collision box can be in air, so
257 this has to be set explicitly
259 if(sneak_node_found && control.sneak)
260 touching_ground = true;
266 setPosition(position);
273 // Report fall collision
274 if(old_speed.Y < m_speed.Y - 0.1 && !standing_on_unloaded)
277 info.t = COLLISION_FALL;
278 info.speed = m_speed.Y - old_speed.Y;
279 collision_info->push_back(info);
283 if(!touching_ground_was && touching_ground){
284 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
285 m_gamedef->event()->put(e);
289 camera_barely_in_ceiling = false;
290 v3s16 camera_np = floatToInt(getEyePosition(), BS);
291 MapNode n = map.getNodeNoEx(camera_np);
292 if(n.getContent() != CONTENT_IGNORE){
293 if(nodemgr->get(n).walkable){
294 camera_barely_in_ceiling = true;
300 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
302 move(dtime, map, pos_max_d, NULL);
305 void LocalPlayer::applyControl(float dtime)
311 f32 walk_acceleration = 4.0 * BS;
312 f32 walkspeed_max = 4.0 * BS;
314 setPitch(control.pitch);
317 v3f move_direction = v3f(0,0,1);
318 move_direction.rotateXZBy(getYaw());
320 v3f speed = v3f(0,0,0);
322 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
323 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
325 bool free_move = fly_allowed && g_settings->getBool("free_move");
326 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
327 bool continuous_forward = g_settings->getBool("continuous_forward");
329 if(free_move || is_climbing)
331 v3f speed = getSpeed();
336 // Whether superspeed mode is used or not
337 bool superspeed = false;
339 // If free movement and fast movement, always move fast
340 if(free_move && fast_move)
343 // Auxiliary button 1 (E)
348 // In free movement mode, aux1 descends
349 v3f speed = getSpeed();
353 speed.Y = -walkspeed_max;
358 v3f speed = getSpeed();
364 // If not free movement but fast is allowed, aux1 is
371 if(continuous_forward)
372 speed += move_direction;
376 if(continuous_forward)
379 speed += move_direction;
383 speed -= move_direction;
387 speed += move_direction.crossProduct(v3f(0,1,0));
391 speed += move_direction.crossProduct(v3f(0,-1,0));
397 v3f speed = getSpeed();
401 speed.Y = walkspeed_max;
404 else if(touching_ground)
407 NOTE: The d value in move() affects jump height by
408 raising the height at which the jump speed is kept
409 at its starting value
411 v3f speed = getSpeed();
412 if(speed.Y >= -0.5*BS)
417 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
418 m_gamedef->event()->put(e);
421 // Use the oscillating value for getting out of water
422 // (so that the player doesn't fly on the surface)
425 v3f speed = getSpeed();
432 v3f speed = getSpeed();
438 // The speed of the player (Y is ignored)
440 speed = speed.normalize() * walkspeed_max * 5.0;
441 else if(control.sneak)
442 speed = speed.normalize() * walkspeed_max / 3.0;
444 speed = speed.normalize() * walkspeed_max;
446 f32 inc = walk_acceleration * BS * dtime;
448 // Faster acceleration if fast and free movement
449 if(free_move && fast_move)
450 inc = walk_acceleration * BS * dtime * 10;
452 // Accelerate to target speed with maximum increment
453 accelerate(speed, inc);
456 v3s16 LocalPlayer::getStandingNodePos()
458 if(m_sneak_node_exists)
460 return floatToInt(getPosition(), BS);