]> git.lizzy.rs Git - minetest.git/blob - src/localplayer.cpp
0c94582aaa2be85186d8b88e5c7993427460aa39
[minetest.git] / src / localplayer.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
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.
9
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.
14
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.
18 */
19
20 #include "localplayer.h"
21
22 #include "event.h"
23 #include "collision.h"
24 #include "gamedef.h"
25 #include "nodedef.h"
26 #include "settings.h"
27 #include "environment.h"
28 #include "map.h"
29 #include "util/numeric.h"
30
31 /*
32         LocalPlayer
33 */
34
35 LocalPlayer::LocalPlayer(IGameDef *gamedef, const char *name):
36         Player(gamedef, name),
37         parent(0),
38         isAttached(false),
39         overridePosition(v3f(0,0,0)),
40         last_position(v3f(0,0,0)),
41         last_speed(v3f(0,0,0)),
42         last_pitch(0),
43         last_yaw(0),
44         last_keyPressed(0),
45         camera_impact(0.f),
46         last_animation(NO_ANIM),
47         hotbar_image(""),
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"),
56         m_can_jump(false),
57         m_cao(NULL)
58 {
59         // Initialize hp to 0, so that no hearts will be shown if server
60         // doesn't support health points
61         hp = 0;
62         eye_offset_first = v3f(0,0,0);
63         eye_offset_third = v3f(0,0,0);
64 }
65
66 LocalPlayer::~LocalPlayer()
67 {
68 }
69
70 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
71                 std::vector<CollisionInfo> *collision_info)
72 {
73         Map *map = &env->getMap();
74         INodeDefManager *nodemgr = m_gamedef->ndef();
75
76         v3f position = getPosition();
77
78         // Copy parent position if local player is attached
79         if(isAttached)
80         {
81                 setPosition(overridePosition);
82                 m_sneak_node_exists = false;
83                 return;
84         }
85
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");
91         if (free_move) {
92                 position += m_speed * dtime;
93                 setPosition(position);
94                 m_sneak_node_exists = false;
95                 return;
96         }
97
98         /*
99                 Collision detection
100         */
101
102         bool is_valid_position;
103         MapNode node;
104         v3s16 pp;
105
106         /*
107                 Check if player is in liquid (the oscillating value)
108         */
109
110         // If in liquid, the threshold of coming out is at higher y
111         if (in_liquid)
112         {
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;
118                 } else {
119                         in_liquid = false;
120                 }
121         }
122         // If not in liquid, the threshold of going in is at lower y
123         else
124         {
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;
130                 } else {
131                         in_liquid = false;
132                 }
133         }
134
135
136         /*
137                 Check if player is in liquid (the stable value)
138         */
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();
143         } else {
144                 in_liquid_stable = false;
145         }
146
147         /*
148                 Check if player is climbing
149         */
150
151
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);
157
158         if (!(is_valid_position && is_valid_position2)) {
159                 is_climbing = false;
160         } else {
161                 is_climbing = (nodemgr->get(node.getContent()).climbable
162                                 || nodemgr->get(node2.getContent()).climbable) && !free_move;
163         }
164
165
166         /*
167                 Collision uncertainty radius
168                 Make it a bit larger than the maximum distance of movement
169         */
170         //f32 d = pos_max_d * 1.1;
171         // A fairly large value in here makes moving smoother
172         f32 d = 0.15*BS;
173
174         // This should always apply, otherwise there are glitches
175         sanity_check(d > pos_max_d);
176
177         // Maximum distance over border for sneaking
178         f32 sneak_max = BS*0.4;
179
180         /*
181                 If sneaking, keep in range from the last walked node and don't
182                 fall off from it
183         */
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);
191
192                 if (!is_climbing) {
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)
196                                 position.Y = new_y;
197                         /*
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.
201                         */
202                         if (m_speed.Y < 0)
203                                 m_speed.Y = 0;
204                 }
205         }
206
207         // this shouldn't be hardcoded but transmitted from server
208         float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
209
210 #ifdef __ANDROID__
211         player_stepheight += (0.5 * BS);
212 #endif
213
214         v3f accel_f = v3f(0,0,0);
215
216         collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
217                 pos_max_d, m_collisionbox, player_stepheight, dtime,
218                 &position, &m_speed, accel_f);
219
220         /*
221                 If the player's feet touch the topside of any node, this is
222                 set to true.
223
224                 Player is allowed to jump when this is true.
225         */
226         bool touching_ground_was = touching_ground;
227         touching_ground = result.touching_ground;
228
229     //bool standing_on_unloaded = result.standing_on_unloaded;
230
231         /*
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.
235         */
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
249                 // node.
250                 m_need_to_get_new_sneak_node = true;
251         }
252
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)
260                 {
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;
268                 }*/
269                 v3s16 new_sneak_node = m_sneak_node;
270                 for(s16 x=-1; x<=1; x++)
271                 for(s16 z=-1; z<=1; z++)
272                 {
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));
280
281                         if(distance_f > min_distance_f ||
282                                         max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
283                                 continue;
284
285
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)
289                                 continue;
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) {
293                                 continue;
294                         }
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)
298                                         continue;
299                         }
300
301                         min_distance_f = distance_f;
302                         new_sneak_node = p;
303                 }
304
305                 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
306
307                 m_sneak_node = new_sneak_node;
308                 m_sneak_node_exists = sneak_node_found;
309
310                 if (sneak_node_found) {
311                         f32 cb_max = 0;
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) {
317                                 aabb3f box = *it;
318                                 if (box.MaxEdge.Y > cb_max)
319                                         cb_max = box.MaxEdge.Y;
320                         }
321                         m_sneak_node_bb_ymax = cb_max;
322                 }
323
324                 /*
325                         If sneaking, the player's collision box can be in air, so
326                         this has to be set explicitly
327                 */
328                 if(sneak_node_found && control.sneak)
329                         touching_ground = true;
330         }
331
332         /*
333                 Set new position
334         */
335         setPosition(position);
336
337         /*
338                 Report collisions
339         */
340
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);
346                 }
347         }
348
349         if(!result.standing_on_object && !touching_ground_was && touching_ground) {
350                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
351                 m_gamedef->event()->put(e);
352
353                 // Set camera impact value to be used for view bobbing
354                 camera_impact = getSpeed().Y * -1;
355         }
356
357         {
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;
364                         }
365                 }
366         }
367
368         /*
369                 Update the node last under the player
370         */
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;
373
374         /*
375                 Check properties of the node on which the player is standing
376         */
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"))
381                 m_can_jump = false;
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;
386                 if (m_speed.Y > 1) {
387                         // Reduce boost when speed already is high
388                         m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
389                 } else {
390                         m_speed.Y += jumpspeed;
391                 }
392                 setSpeed(m_speed);
393                 m_can_jump = false;
394         }
395 }
396
397 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
398 {
399         move(dtime, env, pos_max_d, NULL);
400 }
401
402 void LocalPlayer::applyControl(float dtime)
403 {
404         // Clear stuff
405         swimming_vertical = false;
406
407         setPitch(control.pitch);
408         setYaw(control.yaw);
409
410         // Nullify speed and don't run positioning code if the player is attached
411         if(isAttached)
412         {
413                 setSpeed(v3f(0,0,0));
414                 return;
415         }
416
417         v3f move_direction = v3f(0,0,1);
418         move_direction.rotateXZBy(getYaw());
419
420         v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
421         v3f speedV = v3f(0,0,0); // Vertical (Y)
422
423         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
424         bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
425
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");
432
433         // Whether superspeed mode is used or not
434         bool superspeed = false;
435
436         if (always_fly_fast && free_move && fast_move)
437                 superspeed = true;
438
439         // Old descend control
440         if(g_settings->getBool("aux1_descends"))
441         {
442                 // If free movement and fast movement, always move fast
443                 if(free_move && fast_move)
444                         superspeed = true;
445
446                 // Auxiliary button 1 (E)
447                 if(control.aux1)
448                 {
449                         if(free_move)
450                         {
451                                 // In free movement mode, aux1 descends
452                                 if(fast_move)
453                                         speedV.Y = -movement_speed_fast;
454                                 else
455                                         speedV.Y = -movement_speed_walk;
456                         }
457                         else if(in_liquid || in_liquid_stable)
458                         {
459                                 speedV.Y = -movement_speed_walk;
460                                 swimming_vertical = true;
461                         }
462                         else if(is_climbing)
463                         {
464                                 speedV.Y = -movement_speed_climb;
465                         }
466                         else
467                         {
468                                 // If not free movement but fast is allowed, aux1 is
469                                 // "Turbo button"
470                                 if(fast_move)
471                                         superspeed = true;
472                         }
473                 }
474         }
475         // New minecraft-like descend control
476         else
477         {
478                 // Auxiliary button 1 (E)
479                 if(control.aux1)
480                 {
481                         if(!is_climbing)
482                         {
483                                 // aux1 is "Turbo button"
484                                 if(fast_move)
485                                         superspeed = true;
486                         }
487                 }
488
489                 if(control.sneak)
490                 {
491                         if(free_move)
492                         {
493                                 // In free movement mode, sneak descends
494                                 if (fast_move && (control.aux1 || always_fly_fast))
495                                         speedV.Y = -movement_speed_fast;
496                                 else
497                                         speedV.Y = -movement_speed_walk;
498                         }
499                         else if(in_liquid || in_liquid_stable)
500                         {
501                                 if(fast_climb)
502                                         speedV.Y = -movement_speed_fast;
503                                 else
504                                         speedV.Y = -movement_speed_walk;
505                                 swimming_vertical = true;
506                         }
507                         else if(is_climbing)
508                         {
509                                 if(fast_climb)
510                                         speedV.Y = -movement_speed_fast;
511                                 else
512                                         speedV.Y = -movement_speed_climb;
513                         }
514                 }
515         }
516
517         if (continuous_forward)
518                 speedH += move_direction;
519
520         if (control.up) {
521                 if (continuous_forward) {
522                         if (fast_move)
523                                 superspeed = true;
524                 } else {
525                         speedH += move_direction;
526                 }
527         }
528         if(control.down)
529         {
530                 speedH -= move_direction;
531         }
532         if(control.left)
533         {
534                 speedH += move_direction.crossProduct(v3f(0,1,0));
535         }
536         if(control.right)
537         {
538                 speedH += move_direction.crossProduct(v3f(0,-1,0));
539         }
540         if(control.jump)
541         {
542                 if (free_move) {
543                         if (g_settings->getBool("aux1_descends") || always_fly_fast) {
544                                 if (fast_move)
545                                         speedV.Y = movement_speed_fast;
546                                 else
547                                         speedV.Y = movement_speed_walk;
548                         } else {
549                                 if(fast_move && control.aux1)
550                                         speedV.Y = movement_speed_fast;
551                                 else
552                                         speedV.Y = movement_speed_walk;
553                         }
554                 }
555                 else if(m_can_jump)
556                 {
557                         /*
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
561                         */
562                         v3f speedJ = getSpeed();
563                         if(speedJ.Y >= -0.5 * BS)
564                         {
565                                 speedJ.Y = movement_speed_jump * physics_override_jump;
566                                 setSpeed(speedJ);
567
568                                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
569                                 m_gamedef->event()->put(e);
570                         }
571                 }
572                 else if(in_liquid)
573                 {
574                         if(fast_climb)
575                                 speedV.Y = movement_speed_fast;
576                         else
577                                 speedV.Y = movement_speed_walk;
578                         swimming_vertical = true;
579                 }
580                 else if(is_climbing)
581                 {
582                         if(fast_climb)
583                                 speedV.Y = movement_speed_fast;
584                         else
585                                 speedV.Y = movement_speed_climb;
586                 }
587         }
588
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;
594         else
595                 speedH = speedH.normalize() * movement_speed_walk;
596
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))
601         {
602                 // Jumping and falling
603                 if(superspeed || (fast_move && control.aux1))
604                         incH = movement_acceleration_fast * BS * dtime;
605                 else
606                         incH = movement_acceleration_air * BS * dtime;
607                 incV = 0; // No vertical acceleration in air
608         }
609         else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
610                 incH = incV = movement_acceleration_fast * BS * dtime;
611         else
612                 incH = incV = movement_acceleration_default * BS * dtime;
613
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);
617 }
618
619 v3s16 LocalPlayer::getStandingNodePos()
620 {
621         if(m_sneak_node_exists)
622                 return m_sneak_node;
623         return floatToInt(getPosition() - v3f(0, BS, 0), BS);
624 }
625
626 // Horizontal acceleration (X and Z), Y direction is ignored
627 void LocalPlayer::accelerateHorizontal(const v3f &target_speed, const f32 max_increase)
628 {
629         if (max_increase == 0)
630                 return;
631
632         v3f d_wanted = target_speed - m_speed;
633         d_wanted.Y = 0;
634         f32 dl = d_wanted.getLength();
635         if (dl > max_increase)
636                 dl = max_increase;
637
638         v3f d = d_wanted.normalize() * dl;
639
640         m_speed.X += d.X;
641         m_speed.Z += d.Z;
642 }
643
644 // Vertical acceleration (Y), X and Z directions are ignored
645 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
646 {
647         if (max_increase == 0)
648                 return;
649
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;
655
656         m_speed.Y += d_wanted;
657 }
658