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"
26 #include "environment.h"
34 LocalPlayer::LocalPlayer(Client *client, const char *name):
35 Player(name, client->idef()),
38 got_teleported(false),
40 touching_ground(false),
42 in_liquid_stable(false),
45 swimming_vertical(false),
46 // Movement overrides are multipliers and must be 1 by default
47 physics_override_speed(1.0f),
48 physics_override_jump(1.0f),
49 physics_override_gravity(1.0f),
50 physics_override_sneak(true),
51 physics_override_sneak_glitch(true),
52 overridePosition(v3f(0,0,0)),
53 last_position(v3f(0,0,0)),
54 last_speed(v3f(0,0,0)),
61 last_animation(NO_ANIM),
63 hotbar_selected_image(""),
64 light_color(255,255,255,255),
65 hurt_tilt_timer(0.0f),
66 hurt_tilt_strength(0.0f),
68 m_sneak_node(32767,32767,32767),
69 m_sneak_node_exists(false),
70 m_need_to_get_new_sneak_node(true),
71 m_sneak_node_bb_ymax(0),
72 m_old_node_below(32767,32767,32767),
73 m_old_node_below_type("air"),
75 m_breath(PLAYER_MAX_BREATH),
78 camera_barely_in_ceiling(false),
79 m_collisionbox(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30),
83 // Initialize hp to 0, so that no hearts will be shown if server
84 // doesn't support health points
86 eye_offset_first = v3f(0,0,0);
87 eye_offset_third = v3f(0,0,0);
90 LocalPlayer::~LocalPlayer()
94 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
95 std::vector<CollisionInfo> *collision_info)
97 Map *map = &env->getMap();
98 INodeDefManager *nodemgr = m_client->ndef();
100 v3f position = getPosition();
102 // Copy parent position if local player is attached
105 setPosition(overridePosition);
106 m_sneak_node_exists = false;
110 // Skip collision detection if noclip mode is used
111 bool fly_allowed = m_client->checkLocalPrivilege("fly");
112 bool noclip = m_client->checkLocalPrivilege("noclip") &&
113 g_settings->getBool("noclip");
114 bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
116 position += m_speed * dtime;
117 setPosition(position);
118 m_sneak_node_exists = false;
126 bool is_valid_position;
131 Check if player is in liquid (the oscillating value)
134 // If in liquid, the threshold of coming out is at higher y
137 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
138 node = map->getNodeNoEx(pp, &is_valid_position);
139 if (is_valid_position) {
140 in_liquid = nodemgr->get(node.getContent()).isLiquid();
141 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
146 // If not in liquid, the threshold of going in is at lower y
149 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
150 node = map->getNodeNoEx(pp, &is_valid_position);
151 if (is_valid_position) {
152 in_liquid = nodemgr->get(node.getContent()).isLiquid();
153 liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
161 Check if player is in liquid (the stable value)
163 pp = floatToInt(position + v3f(0,0,0), BS);
164 node = map->getNodeNoEx(pp, &is_valid_position);
165 if (is_valid_position) {
166 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
168 in_liquid_stable = false;
172 Check if player is climbing
176 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
177 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
178 node = map->getNodeNoEx(pp, &is_valid_position);
179 bool is_valid_position2;
180 MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
182 if (!(is_valid_position && is_valid_position2)) {
185 is_climbing = (nodemgr->get(node.getContent()).climbable
186 || nodemgr->get(node2.getContent()).climbable) && !free_move;
191 Collision uncertainty radius
192 Make it a bit larger than the maximum distance of movement
194 //f32 d = pos_max_d * 1.1;
195 // A fairly large value in here makes moving smoother
198 // This should always apply, otherwise there are glitches
199 sanity_check(d > pos_max_d);
201 // Maximum distance over border for sneaking
202 f32 sneak_max = BS*0.4;
205 If sneaking, keep in range from the last walked node and don't
208 if (control.sneak && m_sneak_node_exists &&
209 !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
210 physics_override_sneak && !got_teleported) {
211 f32 maxd = 0.5 * BS + sneak_max;
212 v3f lwn_f = intToFloat(m_sneak_node, BS);
213 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
214 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
217 // Move up if necessary
218 f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
219 if (position.Y < new_y)
222 Collision seems broken, since player is sinking when
223 sneaking over the edges of current sneaking_node.
224 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
232 got_teleported = false;
234 // this shouldn't be hardcoded but transmitted from server
235 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
238 player_stepheight += (0.6 * BS);
241 v3f accel_f = v3f(0,0,0);
243 collisionMoveResult result = collisionMoveSimple(env, m_client,
244 pos_max_d, m_collisionbox, player_stepheight, dtime,
245 &position, &m_speed, accel_f);
248 If the player's feet touch the topside of any node, this is
251 Player is allowed to jump when this is true.
253 bool touching_ground_was = touching_ground;
254 touching_ground = result.touching_ground;
256 //bool standing_on_unloaded = result.standing_on_unloaded;
259 Check the nodes under the player to see from which node the
260 player is sneaking from, if any. If the node from under
261 the player has been removed, the player falls.
263 f32 position_y_mod = 0.05 * BS;
264 if (m_sneak_node_bb_ymax > 0)
265 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
266 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
267 if (m_sneak_node_exists &&
268 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
269 m_old_node_below_type != "air") {
270 // Old node appears to have been removed; that is,
271 // it wasn't air before but now it is
272 m_need_to_get_new_sneak_node = false;
273 m_sneak_node_exists = false;
274 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
275 // We are on something, so make sure to recalculate the sneak
277 m_need_to_get_new_sneak_node = true;
280 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
281 m_sneak_node_bb_ymax = 0;
282 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
283 v2f player_p2df(position.X, position.Z);
284 f32 min_distance_f = 100000.0 * BS;
285 // If already seeking from some node, compare to it.
286 /*if(m_sneak_node_exists)
288 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
289 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
290 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
291 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
292 // Ignore if player is not on the same level (likely dropped)
293 if(d_vert_f < 0.15*BS)
294 min_distance_f = d_horiz_f;
296 v3s16 new_sneak_node = m_sneak_node;
297 for(s16 x=-1; x<=1; x++)
298 for(s16 z=-1; z<=1; z++)
300 v3s16 p = pos_i_bottom + v3s16(x,0,z);
301 v3f pf = intToFloat(p, BS);
302 v2f node_p2df(pf.X, pf.Z);
303 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
304 f32 max_axis_distance_f = MYMAX(
305 fabs(player_p2df.X-node_p2df.X),
306 fabs(player_p2df.Y-node_p2df.Y));
308 if(distance_f > min_distance_f ||
309 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
313 // The node to be sneaked on has to be walkable
314 node = map->getNodeNoEx(p, &is_valid_position);
315 if (!is_valid_position || nodemgr->get(node).walkable == false)
317 // And the node above it has to be nonwalkable
318 node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
319 if (!is_valid_position || nodemgr->get(node).walkable) {
322 if (!physics_override_sneak_glitch) {
323 node =map->getNodeNoEx(p + v3s16(0,2,0), &is_valid_position);
324 if (!is_valid_position || nodemgr->get(node).walkable)
328 min_distance_f = distance_f;
332 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
334 m_sneak_node = new_sneak_node;
335 m_sneak_node_exists = sneak_node_found;
337 if (sneak_node_found) {
339 MapNode n = map->getNodeNoEx(m_sneak_node);
340 std::vector<aabb3f> nodeboxes;
341 n.getCollisionBoxes(nodemgr, &nodeboxes);
342 for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
343 it != nodeboxes.end(); ++it) {
345 if (box.MaxEdge.Y > cb_max)
346 cb_max = box.MaxEdge.Y;
348 m_sneak_node_bb_ymax = cb_max;
352 If sneaking, the player's collision box can be in air, so
353 this has to be set explicitly
355 if(sneak_node_found && control.sneak)
356 touching_ground = true;
362 setPosition(position);
368 // Dont report if flying
369 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
370 for(size_t i=0; i<result.collisions.size(); i++) {
371 const CollisionInfo &info = result.collisions[i];
372 collision_info->push_back(info);
376 if(!result.standing_on_object && !touching_ground_was && touching_ground) {
377 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
378 m_client->event()->put(e);
380 // Set camera impact value to be used for view bobbing
381 camera_impact = getSpeed().Y * -1;
385 camera_barely_in_ceiling = false;
386 v3s16 camera_np = floatToInt(getEyePosition(), BS);
387 MapNode n = map->getNodeNoEx(camera_np);
388 if(n.getContent() != CONTENT_IGNORE){
389 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
390 camera_barely_in_ceiling = true;
396 Update the node last under the player
398 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
399 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
402 Check properties of the node on which the player is standing
404 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
405 // Determine if jumping is possible
406 m_can_jump = touching_ground && !in_liquid;
407 if(itemgroup_get(f.groups, "disable_jump"))
409 // Jump key pressed while jumping off from a bouncy block
410 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
411 m_speed.Y >= -0.5 * BS) {
412 float jumpspeed = movement_speed_jump * physics_override_jump;
414 // Reduce boost when speed already is high
415 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
417 m_speed.Y += jumpspeed;
424 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
426 move(dtime, env, pos_max_d, NULL);
429 void LocalPlayer::applyControl(float dtime)
432 swimming_vertical = false;
434 setPitch(control.pitch);
437 // Nullify speed and don't run positioning code if the player is attached
440 setSpeed(v3f(0,0,0));
444 v3f move_direction = v3f(0,0,1);
445 move_direction.rotateXZBy(getYaw());
447 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
448 v3f speedV = v3f(0,0,0); // Vertical (Y)
450 bool fly_allowed = m_client->checkLocalPrivilege("fly");
451 bool fast_allowed = m_client->checkLocalPrivilege("fast");
453 bool free_move = fly_allowed && g_settings->getBool("free_move");
454 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
455 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
456 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
457 bool continuous_forward = g_settings->getBool("continuous_forward");
458 bool always_fly_fast = g_settings->getBool("always_fly_fast");
460 // Whether superspeed mode is used or not
461 bool superspeed = false;
463 if (always_fly_fast && free_move && fast_move)
466 // Old descend control
467 if(g_settings->getBool("aux1_descends"))
469 // If free movement and fast movement, always move fast
470 if(free_move && fast_move)
473 // Auxiliary button 1 (E)
478 // In free movement mode, aux1 descends
480 speedV.Y = -movement_speed_fast;
482 speedV.Y = -movement_speed_walk;
484 else if(in_liquid || in_liquid_stable)
486 speedV.Y = -movement_speed_walk;
487 swimming_vertical = true;
491 speedV.Y = -movement_speed_climb;
495 // If not free movement but fast is allowed, aux1 is
502 // New minecraft-like descend control
505 // Auxiliary button 1 (E)
510 // aux1 is "Turbo button"
520 // In free movement mode, sneak descends
521 if (fast_move && (control.aux1 || always_fly_fast))
522 speedV.Y = -movement_speed_fast;
524 speedV.Y = -movement_speed_walk;
526 else if(in_liquid || in_liquid_stable)
529 speedV.Y = -movement_speed_fast;
531 speedV.Y = -movement_speed_walk;
532 swimming_vertical = true;
537 speedV.Y = -movement_speed_fast;
539 speedV.Y = -movement_speed_climb;
544 if (continuous_forward)
545 speedH += move_direction;
548 if (continuous_forward) {
552 speedH += move_direction;
556 speedH -= move_direction;
558 if (!control.up && !control.down) {
559 speedH -= move_direction *
560 (control.forw_move_joystick_axis / 32767.f);
563 speedH += move_direction.crossProduct(v3f(0,1,0));
566 speedH += move_direction.crossProduct(v3f(0,-1,0));
568 if (!control.left && !control.right) {
569 speedH -= move_direction.crossProduct(v3f(0,1,0)) *
570 (control.sidew_move_joystick_axis / 32767.f);
575 if (g_settings->getBool("aux1_descends") || always_fly_fast) {
577 speedV.Y = movement_speed_fast;
579 speedV.Y = movement_speed_walk;
581 if(fast_move && control.aux1)
582 speedV.Y = movement_speed_fast;
584 speedV.Y = movement_speed_walk;
590 NOTE: The d value in move() affects jump height by
591 raising the height at which the jump speed is kept
592 at its starting value
594 v3f speedJ = getSpeed();
595 if(speedJ.Y >= -0.5 * BS)
597 speedJ.Y = movement_speed_jump * physics_override_jump;
600 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
601 m_client->event()->put(e);
607 speedV.Y = movement_speed_fast;
609 speedV.Y = movement_speed_walk;
610 swimming_vertical = true;
615 speedV.Y = movement_speed_fast;
617 speedV.Y = movement_speed_climb;
621 // The speed of the player (Y is ignored)
622 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
623 speedH = speedH.normalize() * movement_speed_fast;
624 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
625 speedH = speedH.normalize() * movement_speed_crouch;
627 speedH = speedH.normalize() * movement_speed_walk;
629 // Acceleration increase
630 f32 incH = 0; // Horizontal (X, Z)
631 f32 incV = 0; // Vertical (Y)
632 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
634 // Jumping and falling
635 if(superspeed || (fast_move && control.aux1))
636 incH = movement_acceleration_fast * BS * dtime;
638 incH = movement_acceleration_air * BS * dtime;
639 incV = 0; // No vertical acceleration in air
641 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
642 incH = incV = movement_acceleration_fast * BS * dtime;
644 incH = incV = movement_acceleration_default * BS * dtime;
646 // Accelerate to target speed with maximum increment
647 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
648 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
651 v3s16 LocalPlayer::getStandingNodePos()
653 if(m_sneak_node_exists)
655 return floatToInt(getPosition() - v3f(0, BS, 0), BS);
658 v3s16 LocalPlayer::getFootstepNodePos()
661 // BS * 0.05 below the player's feet ensures a 1/16th height
662 // nodebox is detected instead of the node below it.
663 return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS);
664 // A larger distance below is necessary for a footstep sound
665 // when landing after a jump or fall. BS * 0.5 ensures water
666 // sounds when swimming in 1 node deep water.
667 return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS);
670 v3s16 LocalPlayer::getLightPosition() const
672 return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
675 v3f LocalPlayer::getEyeOffset() const
677 float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f;
678 return v3f(0, BS * eye_height, 0);
681 // Horizontal acceleration (X and Z), Y direction is ignored
682 void LocalPlayer::accelerateHorizontal(const v3f &target_speed, const f32 max_increase)
684 if (max_increase == 0)
687 v3f d_wanted = target_speed - m_speed;
689 f32 dl = d_wanted.getLength();
690 if (dl > max_increase)
693 v3f d = d_wanted.normalize() * dl;
699 // Vertical acceleration (Y), X and Z directions are ignored
700 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
702 if (max_increase == 0)
705 f32 d_wanted = target_speed.Y - m_speed.Y;
706 if (d_wanted > max_increase)
707 d_wanted = max_increase;
708 else if (d_wanted < -max_increase)
709 d_wanted = -max_increase;
711 m_speed.Y += d_wanted;