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 = n.getCollisionBoxes(nodemgr);
314 for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
315 it != nodeboxes.end(); ++it) {
317 if (box.MaxEdge.Y > cb_max)
318 cb_max = box.MaxEdge.Y;
320 m_sneak_node_bb_ymax = cb_max;
324 If sneaking, the player's collision box can be in air, so
325 this has to be set explicitly
327 if(sneak_node_found && control.sneak)
328 touching_ground = true;
334 setPosition(position);
340 // Dont report if flying
341 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
342 for(size_t i=0; i<result.collisions.size(); i++) {
343 const CollisionInfo &info = result.collisions[i];
344 collision_info->push_back(info);
348 if(!result.standing_on_object && !touching_ground_was && touching_ground) {
349 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
350 m_gamedef->event()->put(e);
352 // Set camera impact value to be used for view bobbing
353 camera_impact = getSpeed().Y * -1;
357 camera_barely_in_ceiling = false;
358 v3s16 camera_np = floatToInt(getEyePosition(), BS);
359 MapNode n = map->getNodeNoEx(camera_np);
360 if(n.getContent() != CONTENT_IGNORE){
361 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
362 camera_barely_in_ceiling = true;
368 Update the node last under the player
370 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
371 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
374 Check properties of the node on which the player is standing
376 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
377 // Determine if jumping is possible
378 m_can_jump = touching_ground && !in_liquid;
379 if(itemgroup_get(f.groups, "disable_jump"))
381 // Jump key pressed while jumping off from a bouncy block
382 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
383 m_speed.Y >= -0.5 * BS) {
384 float jumpspeed = movement_speed_jump * physics_override_jump;
386 // Reduce boost when speed already is high
387 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
389 m_speed.Y += jumpspeed;
396 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
398 move(dtime, env, pos_max_d, NULL);
401 void LocalPlayer::applyControl(float dtime)
404 swimming_vertical = false;
406 setPitch(control.pitch);
409 // Nullify speed and don't run positioning code if the player is attached
412 setSpeed(v3f(0,0,0));
416 v3f move_direction = v3f(0,0,1);
417 move_direction.rotateXZBy(getYaw());
419 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
420 v3f speedV = v3f(0,0,0); // Vertical (Y)
422 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
423 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
425 bool free_move = fly_allowed && g_settings->getBool("free_move");
426 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
427 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
428 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
429 bool continuous_forward = g_settings->getBool("continuous_forward");
430 bool always_fly_fast = g_settings->getBool("always_fly_fast");
432 // Whether superspeed mode is used or not
433 bool superspeed = false;
435 if (always_fly_fast && free_move && fast_move)
438 // Old descend control
439 if(g_settings->getBool("aux1_descends"))
441 // If free movement and fast movement, always move fast
442 if(free_move && fast_move)
445 // Auxiliary button 1 (E)
450 // In free movement mode, aux1 descends
452 speedV.Y = -movement_speed_fast;
454 speedV.Y = -movement_speed_walk;
456 else if(in_liquid || in_liquid_stable)
458 speedV.Y = -movement_speed_walk;
459 swimming_vertical = true;
463 speedV.Y = -movement_speed_climb;
467 // If not free movement but fast is allowed, aux1 is
474 // New minecraft-like descend control
477 // Auxiliary button 1 (E)
482 // aux1 is "Turbo button"
492 // In free movement mode, sneak descends
493 if (fast_move && (control.aux1 || always_fly_fast))
494 speedV.Y = -movement_speed_fast;
496 speedV.Y = -movement_speed_walk;
498 else if(in_liquid || in_liquid_stable)
501 speedV.Y = -movement_speed_fast;
503 speedV.Y = -movement_speed_walk;
504 swimming_vertical = true;
509 speedV.Y = -movement_speed_fast;
511 speedV.Y = -movement_speed_climb;
516 if (continuous_forward)
517 speedH += move_direction;
520 if (continuous_forward) {
524 speedH += move_direction;
529 speedH -= move_direction;
533 speedH += move_direction.crossProduct(v3f(0,1,0));
537 speedH += move_direction.crossProduct(v3f(0,-1,0));
542 if (g_settings->getBool("aux1_descends") || always_fly_fast) {
544 speedV.Y = movement_speed_fast;
546 speedV.Y = movement_speed_walk;
548 if(fast_move && control.aux1)
549 speedV.Y = movement_speed_fast;
551 speedV.Y = movement_speed_walk;
557 NOTE: The d value in move() affects jump height by
558 raising the height at which the jump speed is kept
559 at its starting value
561 v3f speedJ = getSpeed();
562 if(speedJ.Y >= -0.5 * BS)
564 speedJ.Y = movement_speed_jump * physics_override_jump;
567 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
568 m_gamedef->event()->put(e);
574 speedV.Y = movement_speed_fast;
576 speedV.Y = movement_speed_walk;
577 swimming_vertical = true;
582 speedV.Y = movement_speed_fast;
584 speedV.Y = movement_speed_climb;
588 // The speed of the player (Y is ignored)
589 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
590 speedH = speedH.normalize() * movement_speed_fast;
591 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
592 speedH = speedH.normalize() * movement_speed_crouch;
594 speedH = speedH.normalize() * movement_speed_walk;
596 // Acceleration increase
597 f32 incH = 0; // Horizontal (X, Z)
598 f32 incV = 0; // Vertical (Y)
599 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
601 // Jumping and falling
602 if(superspeed || (fast_move && control.aux1))
603 incH = movement_acceleration_fast * BS * dtime;
605 incH = movement_acceleration_air * BS * dtime;
606 incV = 0; // No vertical acceleration in air
608 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
609 incH = incV = movement_acceleration_fast * BS * dtime;
611 incH = incV = movement_acceleration_default * BS * dtime;
613 // Accelerate to target speed with maximum increment
614 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
615 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
618 v3s16 LocalPlayer::getStandingNodePos()
620 if(m_sneak_node_exists)
622 return floatToInt(getPosition() - v3f(0, BS, 0), BS);