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)),
46 last_animation(NO_ANIM),
48 hotbar_selected_image(""),
49 light_color(255,255,255,255),
50 m_sneak_node(32767,32767,32767),
51 m_sneak_node_exists(false),
52 m_need_to_get_new_sneak_node(true),
53 m_sneak_node_bb_ymax(0),
54 m_old_node_below(32767,32767,32767),
55 m_old_node_below_type("air"),
59 // Initialize hp to 0, so that no hearts will be shown if server
60 // doesn't support health points
62 eye_offset_first = v3f(0,0,0);
63 eye_offset_third = v3f(0,0,0);
66 LocalPlayer::~LocalPlayer()
70 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
71 std::vector<CollisionInfo> *collision_info)
73 Map *map = &env->getMap();
74 INodeDefManager *nodemgr = m_gamedef->ndef();
76 v3f position = getPosition();
78 // Copy parent position if local player is attached
81 setPosition(overridePosition);
82 m_sneak_node_exists = false;
86 // Skip collision detection if noclip mode is used
87 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
88 bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
89 g_settings->getBool("noclip");
90 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
92 position += m_speed * dtime;
93 setPosition(position);
94 m_sneak_node_exists = false;
102 bool is_valid_position;
107 Check if player is in liquid (the oscillating value)
110 // If in liquid, the threshold of coming out is at higher y
113 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
114 node = map->getNodeNoEx(pp, &is_valid_position);
115 if (is_valid_position) {
116 in_liquid = nodemgr->get(node.getContent()).isLiquid();
117 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
122 // If not in liquid, the threshold of going in is at lower y
125 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
126 node = map->getNodeNoEx(pp, &is_valid_position);
127 if (is_valid_position) {
128 in_liquid = nodemgr->get(node.getContent()).isLiquid();
129 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
137 Check if player is in liquid (the stable value)
139 pp = floatToInt(position + v3f(0,0,0), BS);
140 node = map->getNodeNoEx(pp, &is_valid_position);
141 if (is_valid_position) {
142 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
144 in_liquid_stable = false;
148 Check if player is climbing
152 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
153 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
154 node = map->getNodeNoEx(pp, &is_valid_position);
155 bool is_valid_position2;
156 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
158 if (!(is_valid_position && is_valid_position2)) {
161 is_climbing = (nodemgr->get(node.getContent()).climbable
162 || nodemgr->get(node2.getContent()).climbable) && !free_move;
167 Collision uncertainty radius
168 Make it a bit larger than the maximum distance of movement
170 //f32 d = pos_max_d * 1.1;
171 // A fairly large value in here makes moving smoother
174 // This should always apply, otherwise there are glitches
175 sanity_check(d > pos_max_d);
177 // Maximum distance over border for sneaking
178 f32 sneak_max = BS*0.4;
181 If sneaking, keep in range from the last walked node and don't
184 if (control.sneak && m_sneak_node_exists &&
185 !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
186 physics_override_sneak) {
187 f32 maxd = 0.5 * BS + sneak_max;
188 v3f lwn_f = intToFloat(m_sneak_node, BS);
189 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
190 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
193 // Move up if necessary
194 f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
195 if (position.Y < new_y)
198 Collision seems broken, since player is sinking when
199 sneaking over the edges of current sneaking_node.
200 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
207 // this shouldn't be hardcoded but transmitted from server
208 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
211 player_stepheight += (0.5 * BS);
214 v3f accel_f = v3f(0,0,0);
216 collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
217 pos_max_d, m_collisionbox, player_stepheight, dtime,
218 &position, &m_speed, accel_f);
221 If the player's feet touch the topside of any node, this is
224 Player is allowed to jump when this is true.
226 bool touching_ground_was = touching_ground;
227 touching_ground = result.touching_ground;
229 //bool standing_on_unloaded = result.standing_on_unloaded;
232 Check the nodes under the player to see from which node the
233 player is sneaking from, if any. If the node from under
234 the player has been removed, the player falls.
236 f32 position_y_mod = 0.05 * BS;
237 if (m_sneak_node_bb_ymax > 0)
238 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
239 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
240 if (m_sneak_node_exists &&
241 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
242 m_old_node_below_type != "air") {
243 // Old node appears to have been removed; that is,
244 // it wasn't air before but now it is
245 m_need_to_get_new_sneak_node = false;
246 m_sneak_node_exists = false;
247 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
248 // We are on something, so make sure to recalculate the sneak
250 m_need_to_get_new_sneak_node = true;
253 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
254 m_sneak_node_bb_ymax = 0;
255 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
256 v2f player_p2df(position.X, position.Z);
257 f32 min_distance_f = 100000.0 * BS;
258 // If already seeking from some node, compare to it.
259 /*if(m_sneak_node_exists)
261 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
262 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
263 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
264 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
265 // Ignore if player is not on the same level (likely dropped)
266 if(d_vert_f < 0.15*BS)
267 min_distance_f = d_horiz_f;
269 v3s16 new_sneak_node = m_sneak_node;
270 for(s16 x=-1; x<=1; x++)
271 for(s16 z=-1; z<=1; z++)
273 v3s16 p = pos_i_bottom + v3s16(x,0,z);
274 v3f pf = intToFloat(p, BS);
275 v2f node_p2df(pf.X, pf.Z);
276 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
277 f32 max_axis_distance_f = MYMAX(
278 fabs(player_p2df.X-node_p2df.X),
279 fabs(player_p2df.Y-node_p2df.Y));
281 if(distance_f > min_distance_f ||
282 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
286 // The node to be sneaked on has to be walkable
287 node = map->getNodeNoEx(p, &is_valid_position);
288 if (!is_valid_position || nodemgr->get(node).walkable == false)
290 // And the node above it has to be nonwalkable
291 node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
292 if (!is_valid_position || nodemgr->get(node).walkable) {
295 if (!physics_override_sneak_glitch) {
296 node =map->getNodeNoEx(p + v3s16(0,2,0), &is_valid_position);
297 if (!is_valid_position || nodemgr->get(node).walkable)
301 min_distance_f = distance_f;
305 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
307 m_sneak_node = new_sneak_node;
308 m_sneak_node_exists = sneak_node_found;
310 if (sneak_node_found) {
312 MapNode n = map->getNodeNoEx(m_sneak_node);
313 std::vector<aabb3f> nodeboxes;
314 n.getCollisionBoxes(nodemgr, &nodeboxes);
315 for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
316 it != nodeboxes.end(); ++it) {
318 if (box.MaxEdge.Y > cb_max)
319 cb_max = box.MaxEdge.Y;
321 m_sneak_node_bb_ymax = cb_max;
325 If sneaking, the player's collision box can be in air, so
326 this has to be set explicitly
328 if(sneak_node_found && control.sneak)
329 touching_ground = true;
335 setPosition(position);
341 // Dont report if flying
342 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
343 for(size_t i=0; i<result.collisions.size(); i++) {
344 const CollisionInfo &info = result.collisions[i];
345 collision_info->push_back(info);
349 if(!result.standing_on_object && !touching_ground_was && touching_ground) {
350 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
351 m_gamedef->event()->put(e);
353 // Set camera impact value to be used for view bobbing
354 camera_impact = getSpeed().Y * -1;
358 camera_barely_in_ceiling = false;
359 v3s16 camera_np = floatToInt(getEyePosition(), BS);
360 MapNode n = map->getNodeNoEx(camera_np);
361 if(n.getContent() != CONTENT_IGNORE){
362 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
363 camera_barely_in_ceiling = true;
369 Update the node last under the player
371 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
372 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
375 Check properties of the node on which the player is standing
377 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
378 // Determine if jumping is possible
379 m_can_jump = touching_ground && !in_liquid;
380 if(itemgroup_get(f.groups, "disable_jump"))
382 // Jump key pressed while jumping off from a bouncy block
383 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
384 m_speed.Y >= -0.5 * BS) {
385 float jumpspeed = movement_speed_jump * physics_override_jump;
387 // Reduce boost when speed already is high
388 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
390 m_speed.Y += jumpspeed;
397 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
399 move(dtime, env, pos_max_d, NULL);
402 void LocalPlayer::applyControl(float dtime)
405 swimming_vertical = false;
407 setPitch(control.pitch);
410 // Nullify speed and don't run positioning code if the player is attached
413 setSpeed(v3f(0,0,0));
417 v3f move_direction = v3f(0,0,1);
418 move_direction.rotateXZBy(getYaw());
420 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
421 v3f speedV = v3f(0,0,0); // Vertical (Y)
423 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
424 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
426 bool free_move = fly_allowed && g_settings->getBool("free_move");
427 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
428 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
429 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
430 bool continuous_forward = g_settings->getBool("continuous_forward");
431 bool always_fly_fast = g_settings->getBool("always_fly_fast");
433 // Whether superspeed mode is used or not
434 bool superspeed = false;
436 if (always_fly_fast && free_move && fast_move)
439 // Old descend control
440 if(g_settings->getBool("aux1_descends"))
442 // If free movement and fast movement, always move fast
443 if(free_move && fast_move)
446 // Auxiliary button 1 (E)
451 // In free movement mode, aux1 descends
453 speedV.Y = -movement_speed_fast;
455 speedV.Y = -movement_speed_walk;
457 else if(in_liquid || in_liquid_stable)
459 speedV.Y = -movement_speed_walk;
460 swimming_vertical = true;
464 speedV.Y = -movement_speed_climb;
468 // If not free movement but fast is allowed, aux1 is
475 // New minecraft-like descend control
478 // Auxiliary button 1 (E)
483 // aux1 is "Turbo button"
493 // In free movement mode, sneak descends
494 if (fast_move && (control.aux1 || always_fly_fast))
495 speedV.Y = -movement_speed_fast;
497 speedV.Y = -movement_speed_walk;
499 else if(in_liquid || in_liquid_stable)
502 speedV.Y = -movement_speed_fast;
504 speedV.Y = -movement_speed_walk;
505 swimming_vertical = true;
510 speedV.Y = -movement_speed_fast;
512 speedV.Y = -movement_speed_climb;
517 if (continuous_forward)
518 speedH += move_direction;
521 if (continuous_forward) {
525 speedH += move_direction;
530 speedH -= move_direction;
534 speedH += move_direction.crossProduct(v3f(0,1,0));
538 speedH += move_direction.crossProduct(v3f(0,-1,0));
543 if (g_settings->getBool("aux1_descends") || always_fly_fast) {
545 speedV.Y = movement_speed_fast;
547 speedV.Y = movement_speed_walk;
549 if(fast_move && control.aux1)
550 speedV.Y = movement_speed_fast;
552 speedV.Y = movement_speed_walk;
558 NOTE: The d value in move() affects jump height by
559 raising the height at which the jump speed is kept
560 at its starting value
562 v3f speedJ = getSpeed();
563 if(speedJ.Y >= -0.5 * BS)
565 speedJ.Y = movement_speed_jump * physics_override_jump;
568 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
569 m_gamedef->event()->put(e);
575 speedV.Y = movement_speed_fast;
577 speedV.Y = movement_speed_walk;
578 swimming_vertical = true;
583 speedV.Y = movement_speed_fast;
585 speedV.Y = movement_speed_climb;
589 // The speed of the player (Y is ignored)
590 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
591 speedH = speedH.normalize() * movement_speed_fast;
592 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
593 speedH = speedH.normalize() * movement_speed_crouch;
595 speedH = speedH.normalize() * movement_speed_walk;
597 // Acceleration increase
598 f32 incH = 0; // Horizontal (X, Z)
599 f32 incV = 0; // Vertical (Y)
600 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
602 // Jumping and falling
603 if(superspeed || (fast_move && control.aux1))
604 incH = movement_acceleration_fast * BS * dtime;
606 incH = movement_acceleration_air * BS * dtime;
607 incV = 0; // No vertical acceleration in air
609 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
610 incH = incV = movement_acceleration_fast * BS * dtime;
612 incH = incV = movement_acceleration_default * BS * dtime;
614 // Accelerate to target speed with maximum increment
615 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
616 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
619 v3s16 LocalPlayer::getStandingNodePos()
621 if(m_sneak_node_exists)
623 return floatToInt(getPosition() - v3f(0, BS, 0), BS);
626 // Horizontal acceleration (X and Z), Y direction is ignored
627 void LocalPlayer::accelerateHorizontal(const v3f &target_speed, const f32 max_increase)
629 if (max_increase == 0)
632 v3f d_wanted = target_speed - m_speed;
634 f32 dl = d_wanted.getLength();
635 if (dl > max_increase)
638 v3f d = d_wanted.normalize() * dl;
644 // Vertical acceleration (Y), X and Z directions are ignored
645 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
647 if (max_increase == 0)
650 f32 d_wanted = target_speed.Y - m_speed.Y;
651 if (d_wanted > max_increase)
652 d_wanted = max_increase;
653 else if (d_wanted < -max_increase)
654 d_wanted = -max_increase;
656 m_speed.Y += d_wanted;