3 Copyright (C) 2010-2013 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"
23 #include "collision.h"
27 #include "environment.h"
29 #include "util/numeric.h"
35 LocalPlayer::LocalPlayer(IGameDef *gamedef, const char *name):
36 Player(gamedef, name),
39 overridePosition(v3f(0,0,0)),
40 last_position(v3f(0,0,0)),
41 last_speed(v3f(0,0,0)),
45 last_animation(NO_ANIM),
47 hotbar_selected_image(""),
48 light_color(255,255,255,255),
49 m_sneak_node(32767,32767,32767),
50 m_sneak_node_exists(false),
51 m_need_to_get_new_sneak_node(true),
52 m_sneak_node_bb_ymax(0),
53 m_old_node_below(32767,32767,32767),
54 m_old_node_below_type("air"),
58 // Initialize hp to 0, so that no hearts will be shown if server
59 // doesn't support health points
61 eye_offset_first = v3f(0,0,0);
62 eye_offset_third = v3f(0,0,0);
65 LocalPlayer::~LocalPlayer()
69 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
70 std::vector<CollisionInfo> *collision_info)
72 Map *map = &env->getMap();
73 INodeDefManager *nodemgr = m_gamedef->ndef();
75 v3f position = getPosition();
77 // Copy parent position if local player is attached
80 setPosition(overridePosition);
81 m_sneak_node_exists = false;
85 // Skip collision detection if noclip mode is used
86 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
87 bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
88 g_settings->getBool("noclip");
89 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
91 position += m_speed * dtime;
92 setPosition(position);
93 m_sneak_node_exists = false;
101 bool is_valid_position;
106 Check if player is in liquid (the oscillating value)
109 // If in liquid, the threshold of coming out is at higher y
112 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
113 node = map->getNodeNoEx(pp, &is_valid_position);
114 if (is_valid_position) {
115 in_liquid = nodemgr->get(node.getContent()).isLiquid();
116 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
121 // If not in liquid, the threshold of going in is at lower y
124 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
125 node = map->getNodeNoEx(pp, &is_valid_position);
126 if (is_valid_position) {
127 in_liquid = nodemgr->get(node.getContent()).isLiquid();
128 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
136 Check if player is in liquid (the stable value)
138 pp = floatToInt(position + v3f(0,0,0), BS);
139 node = map->getNodeNoEx(pp, &is_valid_position);
140 if (is_valid_position) {
141 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
143 in_liquid_stable = false;
147 Check if player is climbing
151 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
152 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
153 node = map->getNodeNoEx(pp, &is_valid_position);
154 bool is_valid_position2;
155 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
157 if (!(is_valid_position && is_valid_position2)) {
160 is_climbing = (nodemgr->get(node.getContent()).climbable
161 || nodemgr->get(node2.getContent()).climbable) && !free_move;
166 Collision uncertainty radius
167 Make it a bit larger than the maximum distance of movement
169 //f32 d = pos_max_d * 1.1;
170 // A fairly large value in here makes moving smoother
173 // This should always apply, otherwise there are glitches
174 sanity_check(d > pos_max_d);
176 // Maximum distance over border for sneaking
177 f32 sneak_max = BS*0.4;
180 If sneaking, keep in range from the last walked node and don't
183 if (control.sneak && m_sneak_node_exists &&
184 !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
185 physics_override_sneak) {
186 f32 maxd = 0.5 * BS + sneak_max;
187 v3f lwn_f = intToFloat(m_sneak_node, BS);
188 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
189 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
192 // Move up if necessary
193 f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
194 if (position.Y < new_y)
197 Collision seems broken, since player is sinking when
198 sneaking over the edges of current sneaking_node.
199 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
206 // this shouldn't be hardcoded but transmitted from server
207 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
210 player_stepheight += (0.5 * BS);
213 v3f accel_f = v3f(0,0,0);
215 collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
216 pos_max_d, m_collisionbox, player_stepheight, dtime,
217 position, m_speed, accel_f);
220 If the player's feet touch the topside of any node, this is
223 Player is allowed to jump when this is true.
225 bool touching_ground_was = touching_ground;
226 touching_ground = result.touching_ground;
228 //bool standing_on_unloaded = result.standing_on_unloaded;
231 Check the nodes under the player to see from which node the
232 player is sneaking from, if any. If the node from under
233 the player has been removed, the player falls.
235 f32 position_y_mod = 0.05 * BS;
236 if (m_sneak_node_bb_ymax > 0)
237 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
238 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
239 if (m_sneak_node_exists &&
240 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
241 m_old_node_below_type != "air") {
242 // Old node appears to have been removed; that is,
243 // it wasn't air before but now it is
244 m_need_to_get_new_sneak_node = false;
245 m_sneak_node_exists = false;
246 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
247 // We are on something, so make sure to recalculate the sneak
249 m_need_to_get_new_sneak_node = true;
252 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
253 m_sneak_node_bb_ymax = 0;
254 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
255 v2f player_p2df(position.X, position.Z);
256 f32 min_distance_f = 100000.0 * BS;
257 // If already seeking from some node, compare to it.
258 /*if(m_sneak_node_exists)
260 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
261 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
262 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
263 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
264 // Ignore if player is not on the same level (likely dropped)
265 if(d_vert_f < 0.15*BS)
266 min_distance_f = d_horiz_f;
268 v3s16 new_sneak_node = m_sneak_node;
269 for(s16 x=-1; x<=1; x++)
270 for(s16 z=-1; z<=1; z++)
272 v3s16 p = pos_i_bottom + v3s16(x,0,z);
273 v3f pf = intToFloat(p, BS);
274 v2f node_p2df(pf.X, pf.Z);
275 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
276 f32 max_axis_distance_f = MYMAX(
277 fabs(player_p2df.X-node_p2df.X),
278 fabs(player_p2df.Y-node_p2df.Y));
280 if(distance_f > min_distance_f ||
281 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
285 // The node to be sneaked on has to be walkable
286 node = map->getNodeNoEx(p, &is_valid_position);
287 if (!is_valid_position || nodemgr->get(node).walkable == false)
289 // And the node above it has to be nonwalkable
290 node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
291 if (!is_valid_position || nodemgr->get(node).walkable) {
294 if (!physics_override_sneak_glitch) {
295 node =map->getNodeNoEx(p + v3s16(0,2,0), &is_valid_position);
296 if (!is_valid_position || nodemgr->get(node).walkable)
300 min_distance_f = distance_f;
304 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
306 m_sneak_node = new_sneak_node;
307 m_sneak_node_exists = sneak_node_found;
309 if (sneak_node_found) {
311 MapNode n = map->getNodeNoEx(m_sneak_node);
312 std::vector<aabb3f> nodeboxes = n.getCollisionBoxes(nodemgr);
313 for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
314 it != nodeboxes.end(); ++it) {
316 if (box.MaxEdge.Y > cb_max)
317 cb_max = box.MaxEdge.Y;
319 m_sneak_node_bb_ymax = cb_max;
323 If sneaking, the player's collision box can be in air, so
324 this has to be set explicitly
326 if(sneak_node_found && control.sneak)
327 touching_ground = true;
333 setPosition(position);
339 // Dont report if flying
340 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
341 for(size_t i=0; i<result.collisions.size(); i++) {
342 const CollisionInfo &info = result.collisions[i];
343 collision_info->push_back(info);
347 if(!touching_ground_was && touching_ground){
348 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
349 m_gamedef->event()->put(e);
351 // Set camera impact value to be used for view bobbing
352 camera_impact = getSpeed().Y * -1;
356 camera_barely_in_ceiling = false;
357 v3s16 camera_np = floatToInt(getEyePosition(), BS);
358 MapNode n = map->getNodeNoEx(camera_np);
359 if(n.getContent() != CONTENT_IGNORE){
360 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
361 camera_barely_in_ceiling = true;
367 Update the node last under the player
369 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
370 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
373 Check properties of the node on which the player is standing
375 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
376 // Determine if jumping is possible
377 m_can_jump = touching_ground && !in_liquid;
378 if(itemgroup_get(f.groups, "disable_jump"))
380 // Jump key pressed while jumping off from a bouncy block
381 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
382 m_speed.Y >= -0.5 * BS) {
383 float jumpspeed = movement_speed_jump * physics_override_jump;
385 // Reduce boost when speed already is high
386 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
388 m_speed.Y += jumpspeed;
395 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
397 move(dtime, env, pos_max_d, NULL);
400 void LocalPlayer::applyControl(float dtime)
403 swimming_vertical = false;
405 setPitch(control.pitch);
408 // Nullify speed and don't run positioning code if the player is attached
411 setSpeed(v3f(0,0,0));
415 v3f move_direction = v3f(0,0,1);
416 move_direction.rotateXZBy(getYaw());
418 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
419 v3f speedV = v3f(0,0,0); // Vertical (Y)
421 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
422 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
424 bool free_move = fly_allowed && g_settings->getBool("free_move");
425 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
426 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
427 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
428 bool continuous_forward = g_settings->getBool("continuous_forward");
429 bool always_fly_fast = g_settings->getBool("always_fly_fast");
431 // Whether superspeed mode is used or not
432 bool superspeed = false;
434 if (always_fly_fast && free_move && fast_move)
437 // Old descend control
438 if(g_settings->getBool("aux1_descends"))
440 // If free movement and fast movement, always move fast
441 if(free_move && fast_move)
444 // Auxiliary button 1 (E)
449 // In free movement mode, aux1 descends
451 speedV.Y = -movement_speed_fast;
453 speedV.Y = -movement_speed_walk;
455 else if(in_liquid || in_liquid_stable)
457 speedV.Y = -movement_speed_walk;
458 swimming_vertical = true;
462 speedV.Y = -movement_speed_climb;
466 // If not free movement but fast is allowed, aux1 is
473 // New minecraft-like descend control
476 // Auxiliary button 1 (E)
481 // aux1 is "Turbo button"
491 // In free movement mode, sneak descends
492 if (fast_move && (control.aux1 || always_fly_fast))
493 speedV.Y = -movement_speed_fast;
495 speedV.Y = -movement_speed_walk;
497 else if(in_liquid || in_liquid_stable)
500 speedV.Y = -movement_speed_fast;
502 speedV.Y = -movement_speed_walk;
503 swimming_vertical = true;
508 speedV.Y = -movement_speed_fast;
510 speedV.Y = -movement_speed_climb;
515 if(continuous_forward)
516 speedH += move_direction;
520 if(continuous_forward)
523 speedH += move_direction;
527 speedH -= move_direction;
531 speedH += move_direction.crossProduct(v3f(0,1,0));
535 speedH += move_direction.crossProduct(v3f(0,-1,0));
540 if (g_settings->getBool("aux1_descends") || always_fly_fast) {
542 speedV.Y = movement_speed_fast;
544 speedV.Y = movement_speed_walk;
546 if(fast_move && control.aux1)
547 speedV.Y = movement_speed_fast;
549 speedV.Y = movement_speed_walk;
555 NOTE: The d value in move() affects jump height by
556 raising the height at which the jump speed is kept
557 at its starting value
559 v3f speedJ = getSpeed();
560 if(speedJ.Y >= -0.5 * BS)
562 speedJ.Y = movement_speed_jump * physics_override_jump;
565 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
566 m_gamedef->event()->put(e);
572 speedV.Y = movement_speed_fast;
574 speedV.Y = movement_speed_walk;
575 swimming_vertical = true;
580 speedV.Y = movement_speed_fast;
582 speedV.Y = movement_speed_climb;
586 // The speed of the player (Y is ignored)
587 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
588 speedH = speedH.normalize() * movement_speed_fast;
589 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
590 speedH = speedH.normalize() * movement_speed_crouch;
592 speedH = speedH.normalize() * movement_speed_walk;
594 // Acceleration increase
595 f32 incH = 0; // Horizontal (X, Z)
596 f32 incV = 0; // Vertical (Y)
597 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
599 // Jumping and falling
600 if(superspeed || (fast_move && control.aux1))
601 incH = movement_acceleration_fast * BS * dtime;
603 incH = movement_acceleration_air * BS * dtime;
604 incV = 0; // No vertical acceleration in air
606 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
607 incH = incV = movement_acceleration_fast * BS * dtime;
609 incH = incV = movement_acceleration_default * BS * dtime;
611 // Accelerate to target speed with maximum increment
612 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
613 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
616 v3s16 LocalPlayer::getStandingNodePos()
618 if(m_sneak_node_exists)
620 return floatToInt(getPosition() - v3f(0, BS, 0), BS);