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)
43 // Initialize hp to 0, so that no hearts will be shown if server
44 // doesn't support health points
48 LocalPlayer::~LocalPlayer()
52 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
53 core::list<CollisionInfo> *collision_info)
55 INodeDefManager *nodemgr = m_gamedef->ndef();
57 v3f position = getPosition();
59 v3f old_speed = m_speed;
61 // Skip collision detection if a special movement mode is used
62 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
63 bool free_move = fly_allowed && g_settings->getBool("free_move");
66 position += m_speed * dtime;
67 setPosition(position);
76 Check if player is in water (the oscillating value)
79 // If in water, the threshold of coming out is at higher y
82 v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
83 in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
85 // If not in water, the threshold of going in is at lower y
88 v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
89 in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
92 catch(InvalidPositionException &e)
98 Check if player is in water (the stable value)
101 v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
102 in_water_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
104 catch(InvalidPositionException &e)
106 in_water_stable = false;
110 Check if player is climbing
114 v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
115 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
116 is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable ||
117 nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move);
119 catch(InvalidPositionException &e)
125 Collision uncertainty radius
126 Make it a bit larger than the maximum distance of movement
128 //f32 d = pos_max_d * 1.1;
129 // A fairly large value in here makes moving smoother
132 // This should always apply, otherwise there are glitches
133 assert(d > pos_max_d);
135 float player_radius = BS*0.30;
136 float player_height = BS*1.55;
138 // Maximum distance over border for sneaking
139 f32 sneak_max = BS*0.4;
142 If sneaking, keep in range from the last walked node and don't
145 if(control.sneak && m_sneak_node_exists)
147 f32 maxd = 0.5*BS + sneak_max;
148 v3f lwn_f = intToFloat(m_sneak_node, BS);
149 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
150 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
152 f32 min_y = lwn_f.Y + 0.5*BS;
153 if(position.Y < min_y)
163 Calculate player collision box (new and old)
165 core::aabbox3d<f32> playerbox(
174 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
176 v3f accel_f = v3f(0,0,0);
178 collisionMoveResult result = collisionMoveSimple(&map, m_gamedef,
179 pos_max_d, playerbox, player_stepheight, dtime,
180 position, m_speed, accel_f);
183 If the player's feet touch the topside of any node, this is
186 Player is allowed to jump when this is true.
188 bool touching_ground_was = touching_ground;
189 touching_ground = result.touching_ground;
191 bool standing_on_unloaded = result.standing_on_unloaded;
194 Check the nodes under the player to see from which node the
195 player is sneaking from, if any. If the node from under
196 the player has been removed, the player falls.
198 v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
199 if(m_sneak_node_exists &&
200 nodemgr->get(map.getNodeNoEx(m_old_node_below)).name == "air" &&
201 m_old_node_below_type != "air")
203 // Old node appears to have been removed; that is,
204 // it wasn't air before but now it is
205 m_need_to_get_new_sneak_node = false;
206 m_sneak_node_exists = false;
208 else if(nodemgr->get(map.getNodeNoEx(current_node)).name != "air")
210 // We are on something, so make sure to recalculate the sneak
212 m_need_to_get_new_sneak_node = true;
214 if(m_need_to_get_new_sneak_node)
216 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
217 v2f player_p2df(position.X, position.Z);
218 f32 min_distance_f = 100000.0*BS;
219 // If already seeking from some node, compare to it.
220 /*if(m_sneak_node_exists)
222 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
223 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
224 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
225 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
226 // Ignore if player is not on the same level (likely dropped)
227 if(d_vert_f < 0.15*BS)
228 min_distance_f = d_horiz_f;
230 v3s16 new_sneak_node = m_sneak_node;
231 for(s16 x=-1; x<=1; x++)
232 for(s16 z=-1; z<=1; z++)
234 v3s16 p = pos_i_bottom + v3s16(x,0,z);
235 v3f pf = intToFloat(p, BS);
236 v2f node_p2df(pf.X, pf.Z);
237 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
238 f32 max_axis_distance_f = MYMAX(
239 fabs(player_p2df.X-node_p2df.X),
240 fabs(player_p2df.Y-node_p2df.Y));
242 if(distance_f > min_distance_f ||
243 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
247 // The node to be sneaked on has to be walkable
248 if(nodemgr->get(map.getNode(p)).walkable == false)
250 // And the node above it has to be nonwalkable
251 if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true)
254 catch(InvalidPositionException &e)
259 min_distance_f = distance_f;
263 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
265 m_sneak_node = new_sneak_node;
266 m_sneak_node_exists = sneak_node_found;
269 If sneaking, the player's collision box can be in air, so
270 this has to be set explicitly
272 if(sneak_node_found && control.sneak)
273 touching_ground = true;
279 setPosition(position);
286 // Report fall collision
287 if(old_speed.Y < m_speed.Y - 0.1 && !standing_on_unloaded)
290 info.t = COLLISION_FALL;
291 info.speed = m_speed.Y - old_speed.Y;
292 collision_info->push_back(info);
296 if(!touching_ground_was && touching_ground){
297 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
298 m_gamedef->event()->put(e);
302 camera_barely_in_ceiling = false;
303 v3s16 camera_np = floatToInt(getEyePosition(), BS);
304 MapNode n = map.getNodeNoEx(camera_np);
305 if(n.getContent() != CONTENT_IGNORE){
306 if(nodemgr->get(n).walkable){
307 camera_barely_in_ceiling = true;
313 Update the node last under the player
315 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
316 m_old_node_below_type = nodemgr->get(map.getNodeNoEx(m_old_node_below)).name;
319 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
321 move(dtime, map, pos_max_d, NULL);
324 void LocalPlayer::applyControl(float dtime)
330 f32 walk_acceleration = 4.0 * BS;
331 f32 walkspeed_max = 4.0 * BS;
333 setPitch(control.pitch);
336 v3f move_direction = v3f(0,0,1);
337 move_direction.rotateXZBy(getYaw());
339 v3f speed = v3f(0,0,0);
341 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
342 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
344 bool free_move = fly_allowed && g_settings->getBool("free_move");
345 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
346 bool continuous_forward = g_settings->getBool("continuous_forward");
348 if(free_move || is_climbing)
350 v3f speed = getSpeed();
355 // Whether superspeed mode is used or not
356 bool superspeed = false;
358 // If free movement and fast movement, always move fast
359 if(free_move && fast_move)
362 // Auxiliary button 1 (E)
367 // In free movement mode, aux1 descends
368 v3f speed = getSpeed();
372 speed.Y = -walkspeed_max;
377 v3f speed = getSpeed();
383 // If not free movement but fast is allowed, aux1 is
390 if(continuous_forward)
391 speed += move_direction;
395 if(continuous_forward)
398 speed += move_direction;
402 speed -= move_direction;
406 speed += move_direction.crossProduct(v3f(0,1,0));
410 speed += move_direction.crossProduct(v3f(0,-1,0));
416 v3f speed = getSpeed();
420 speed.Y = walkspeed_max;
423 else if(touching_ground)
426 NOTE: The d value in move() affects jump height by
427 raising the height at which the jump speed is kept
428 at its starting value
430 v3f speed = getSpeed();
431 if(speed.Y >= -0.5*BS)
436 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
437 m_gamedef->event()->put(e);
440 // Use the oscillating value for getting out of water
441 // (so that the player doesn't fly on the surface)
444 v3f speed = getSpeed();
451 v3f speed = getSpeed();
457 // The speed of the player (Y is ignored)
459 speed = speed.normalize() * walkspeed_max * 5.0;
460 else if(control.sneak)
461 speed = speed.normalize() * walkspeed_max / 3.0;
463 speed = speed.normalize() * walkspeed_max;
465 f32 inc = walk_acceleration * BS * dtime;
467 // Faster acceleration if fast and free movement
468 if(free_move && fast_move)
469 inc = walk_acceleration * BS * dtime * 10;
471 // Accelerate to target speed with maximum increment
472 accelerate(speed, inc);
475 v3s16 LocalPlayer::getStandingNodePos()
477 if(m_sneak_node_exists)
479 return floatToInt(getPosition(), BS);