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 && !got_teleported) {
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.
208 got_teleported = false;
210 // this shouldn't be hardcoded but transmitted from server
211 float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
214 player_stepheight += (0.6 * BS);
217 v3f accel_f = v3f(0,0,0);
219 collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
220 pos_max_d, m_collisionbox, player_stepheight, dtime,
221 &position, &m_speed, accel_f);
224 If the player's feet touch the topside of any node, this is
227 Player is allowed to jump when this is true.
229 bool touching_ground_was = touching_ground;
230 touching_ground = result.touching_ground;
232 //bool standing_on_unloaded = result.standing_on_unloaded;
235 Check the nodes under the player to see from which node the
236 player is sneaking from, if any. If the node from under
237 the player has been removed, the player falls.
239 f32 position_y_mod = 0.05 * BS;
240 if (m_sneak_node_bb_ymax > 0)
241 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
242 v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
243 if (m_sneak_node_exists &&
244 nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
245 m_old_node_below_type != "air") {
246 // Old node appears to have been removed; that is,
247 // it wasn't air before but now it is
248 m_need_to_get_new_sneak_node = false;
249 m_sneak_node_exists = false;
250 } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
251 // We are on something, so make sure to recalculate the sneak
253 m_need_to_get_new_sneak_node = true;
256 if (m_need_to_get_new_sneak_node && physics_override_sneak) {
257 m_sneak_node_bb_ymax = 0;
258 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
259 v2f player_p2df(position.X, position.Z);
260 f32 min_distance_f = 100000.0 * BS;
261 // If already seeking from some node, compare to it.
262 /*if(m_sneak_node_exists)
264 v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
265 v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
266 f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
267 f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
268 // Ignore if player is not on the same level (likely dropped)
269 if(d_vert_f < 0.15*BS)
270 min_distance_f = d_horiz_f;
272 v3s16 new_sneak_node = m_sneak_node;
273 for(s16 x=-1; x<=1; x++)
274 for(s16 z=-1; z<=1; z++)
276 v3s16 p = pos_i_bottom + v3s16(x,0,z);
277 v3f pf = intToFloat(p, BS);
278 v2f node_p2df(pf.X, pf.Z);
279 f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
280 f32 max_axis_distance_f = MYMAX(
281 fabs(player_p2df.X-node_p2df.X),
282 fabs(player_p2df.Y-node_p2df.Y));
284 if(distance_f > min_distance_f ||
285 max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
289 // The node to be sneaked on has to be walkable
290 node = map->getNodeNoEx(p, &is_valid_position);
291 if (!is_valid_position || nodemgr->get(node).walkable == false)
293 // And the node above it has to be nonwalkable
294 node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
295 if (!is_valid_position || nodemgr->get(node).walkable) {
298 if (!physics_override_sneak_glitch) {
299 node =map->getNodeNoEx(p + v3s16(0,2,0), &is_valid_position);
300 if (!is_valid_position || nodemgr->get(node).walkable)
304 min_distance_f = distance_f;
308 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
310 m_sneak_node = new_sneak_node;
311 m_sneak_node_exists = sneak_node_found;
313 if (sneak_node_found) {
315 MapNode n = map->getNodeNoEx(m_sneak_node);
316 std::vector<aabb3f> nodeboxes;
317 n.getCollisionBoxes(nodemgr, &nodeboxes);
318 for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
319 it != nodeboxes.end(); ++it) {
321 if (box.MaxEdge.Y > cb_max)
322 cb_max = box.MaxEdge.Y;
324 m_sneak_node_bb_ymax = cb_max;
328 If sneaking, the player's collision box can be in air, so
329 this has to be set explicitly
331 if(sneak_node_found && control.sneak)
332 touching_ground = true;
338 setPosition(position);
344 // Dont report if flying
345 if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
346 for(size_t i=0; i<result.collisions.size(); i++) {
347 const CollisionInfo &info = result.collisions[i];
348 collision_info->push_back(info);
352 if(!result.standing_on_object && !touching_ground_was && touching_ground) {
353 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
354 m_gamedef->event()->put(e);
356 // Set camera impact value to be used for view bobbing
357 camera_impact = getSpeed().Y * -1;
361 camera_barely_in_ceiling = false;
362 v3s16 camera_np = floatToInt(getEyePosition(), BS);
363 MapNode n = map->getNodeNoEx(camera_np);
364 if(n.getContent() != CONTENT_IGNORE){
365 if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
366 camera_barely_in_ceiling = true;
372 Update the node last under the player
374 m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
375 m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
378 Check properties of the node on which the player is standing
380 const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
381 // Determine if jumping is possible
382 m_can_jump = touching_ground && !in_liquid;
383 if(itemgroup_get(f.groups, "disable_jump"))
385 // Jump key pressed while jumping off from a bouncy block
386 if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
387 m_speed.Y >= -0.5 * BS) {
388 float jumpspeed = movement_speed_jump * physics_override_jump;
390 // Reduce boost when speed already is high
391 m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
393 m_speed.Y += jumpspeed;
400 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
402 move(dtime, env, pos_max_d, NULL);
405 void LocalPlayer::applyControl(float dtime)
408 swimming_vertical = false;
410 setPitch(control.pitch);
413 // Nullify speed and don't run positioning code if the player is attached
416 setSpeed(v3f(0,0,0));
420 v3f move_direction = v3f(0,0,1);
421 move_direction.rotateXZBy(getYaw());
423 v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
424 v3f speedV = v3f(0,0,0); // Vertical (Y)
426 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
427 bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
429 bool free_move = fly_allowed && g_settings->getBool("free_move");
430 bool fast_move = fast_allowed && g_settings->getBool("fast_move");
431 // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
432 bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
433 bool continuous_forward = g_settings->getBool("continuous_forward");
434 bool always_fly_fast = g_settings->getBool("always_fly_fast");
436 // Whether superspeed mode is used or not
437 bool superspeed = false;
439 if (always_fly_fast && free_move && fast_move)
442 // Old descend control
443 if(g_settings->getBool("aux1_descends"))
445 // If free movement and fast movement, always move fast
446 if(free_move && fast_move)
449 // Auxiliary button 1 (E)
454 // In free movement mode, aux1 descends
456 speedV.Y = -movement_speed_fast;
458 speedV.Y = -movement_speed_walk;
460 else if(in_liquid || in_liquid_stable)
462 speedV.Y = -movement_speed_walk;
463 swimming_vertical = true;
467 speedV.Y = -movement_speed_climb;
471 // If not free movement but fast is allowed, aux1 is
478 // New minecraft-like descend control
481 // Auxiliary button 1 (E)
486 // aux1 is "Turbo button"
496 // In free movement mode, sneak descends
497 if (fast_move && (control.aux1 || always_fly_fast))
498 speedV.Y = -movement_speed_fast;
500 speedV.Y = -movement_speed_walk;
502 else if(in_liquid || in_liquid_stable)
505 speedV.Y = -movement_speed_fast;
507 speedV.Y = -movement_speed_walk;
508 swimming_vertical = true;
513 speedV.Y = -movement_speed_fast;
515 speedV.Y = -movement_speed_climb;
520 if (continuous_forward)
521 speedH += move_direction;
524 if (continuous_forward) {
528 speedH += move_direction;
533 speedH -= move_direction;
537 speedH += move_direction.crossProduct(v3f(0,1,0));
541 speedH += move_direction.crossProduct(v3f(0,-1,0));
546 if (g_settings->getBool("aux1_descends") || always_fly_fast) {
548 speedV.Y = movement_speed_fast;
550 speedV.Y = movement_speed_walk;
552 if(fast_move && control.aux1)
553 speedV.Y = movement_speed_fast;
555 speedV.Y = movement_speed_walk;
561 NOTE: The d value in move() affects jump height by
562 raising the height at which the jump speed is kept
563 at its starting value
565 v3f speedJ = getSpeed();
566 if(speedJ.Y >= -0.5 * BS)
568 speedJ.Y = movement_speed_jump * physics_override_jump;
571 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
572 m_gamedef->event()->put(e);
578 speedV.Y = movement_speed_fast;
580 speedV.Y = movement_speed_walk;
581 swimming_vertical = true;
586 speedV.Y = movement_speed_fast;
588 speedV.Y = movement_speed_climb;
592 // The speed of the player (Y is ignored)
593 if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
594 speedH = speedH.normalize() * movement_speed_fast;
595 else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
596 speedH = speedH.normalize() * movement_speed_crouch;
598 speedH = speedH.normalize() * movement_speed_walk;
600 // Acceleration increase
601 f32 incH = 0; // Horizontal (X, Z)
602 f32 incV = 0; // Vertical (Y)
603 if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
605 // Jumping and falling
606 if(superspeed || (fast_move && control.aux1))
607 incH = movement_acceleration_fast * BS * dtime;
609 incH = movement_acceleration_air * BS * dtime;
610 incV = 0; // No vertical acceleration in air
612 else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
613 incH = incV = movement_acceleration_fast * BS * dtime;
615 incH = incV = movement_acceleration_default * BS * dtime;
617 // Accelerate to target speed with maximum increment
618 accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
619 accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
622 v3s16 LocalPlayer::getStandingNodePos()
624 if(m_sneak_node_exists)
626 return floatToInt(getPosition() - v3f(0, BS, 0), BS);
629 // Horizontal acceleration (X and Z), Y direction is ignored
630 void LocalPlayer::accelerateHorizontal(const v3f &target_speed, const f32 max_increase)
632 if (max_increase == 0)
635 v3f d_wanted = target_speed - m_speed;
637 f32 dl = d_wanted.getLength();
638 if (dl > max_increase)
641 v3f d = d_wanted.normalize() * dl;
647 // Vertical acceleration (Y), X and Z directions are ignored
648 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
650 if (max_increase == 0)
653 f32 d_wanted = target_speed.Y - m_speed.Y;
654 if (d_wanted > max_increase)
655 d_wanted = max_increase;
656 else if (d_wanted < -max_increase)
657 d_wanted = -max_increase;
659 m_speed.Y += d_wanted;