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(!result.standing_on_object && !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;
519 if (continuous_forward) {
523 speedH += move_direction;
528 speedH -= move_direction;
532 speedH += move_direction.crossProduct(v3f(0,1,0));
536 speedH += move_direction.crossProduct(v3f(0,-1,0));
541 if (g_settings->getBool("aux1_descends") || always_fly_fast) {
543 speedV.Y = movement_speed_fast;
545 speedV.Y = movement_speed_walk;
547 if(fast_move && control.aux1)
548 speedV.Y = movement_speed_fast;
550 speedV.Y = movement_speed_walk;
556 NOTE: The d value in move() affects jump height by
557 raising the height at which the jump speed is kept
558 at its starting value
560 v3f speedJ = getSpeed();
561 if(speedJ.Y >= -0.5 * BS)
563 speedJ.Y = movement_speed_jump * physics_override_jump;
566 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
567 m_gamedef->event()->put(e);
573 speedV.Y = movement_speed_fast;
575 speedV.Y = movement_speed_walk;
576 swimming_vertical = true;
581 speedV.Y = movement_speed_fast;
583 speedV.Y = movement_speed_climb;
587 // The speed of the player (Y is ignored)
588 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
589 speedH = speedH.normalize() * movement_speed_fast;
590 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
591 speedH = speedH.normalize() * movement_speed_crouch;
593 speedH = speedH.normalize() * movement_speed_walk;
595 // Acceleration increase
596 f32 incH = 0; // Horizontal (X, Z)
597 f32 incV = 0; // Vertical (Y)
598 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
600 // Jumping and falling
601 if(superspeed || (fast_move && control.aux1))
602 incH = movement_acceleration_fast * BS * dtime;
604 incH = movement_acceleration_air * BS * dtime;
605 incV = 0; // No vertical acceleration in air
607 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
608 incH = incV = movement_acceleration_fast * BS * dtime;
610 incH = incV = movement_acceleration_default * BS * dtime;
612 // Accelerate to target speed with maximum increment
613 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
614 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
617 v3s16 LocalPlayer::getStandingNodePos()
619 if(m_sneak_node_exists)
621 return floatToInt(getPosition() - v3f(0, BS, 0), BS);