Merge remote branch 'origin/master'
[oweals/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 "main.h" // For g_settings
23 #include "event.h"
24 #include "collision.h"
25 #include "gamedef.h"
26 #include "nodedef.h"
27 #include "settings.h"
28 #include "environment.h"
29 #include "map.h"
30 #include "util/numeric.h"
31
32 /*
33         LocalPlayer
34 */
35
36 LocalPlayer::LocalPlayer(IGameDef *gamedef):
37         Player(gamedef),
38         parent(0),
39         isAttached(false),
40         overridePosition(v3f(0,0,0)),
41         last_position(v3f(0,0,0)),
42         last_speed(v3f(0,0,0)),
43         last_pitch(0),
44         last_yaw(0),
45         last_keyPressed(0),
46         m_sneak_node(32767,32767,32767),
47         m_sneak_node_exists(false),
48         m_old_node_below(32767,32767,32767),
49         m_old_node_below_type("air"),
50         m_need_to_get_new_sneak_node(true),
51         m_can_jump(false)
52 {
53         // Initialize hp to 0, so that no hearts will be shown if server
54         // doesn't support health points
55         hp = 0;
56 }
57
58 LocalPlayer::~LocalPlayer()
59 {
60 }
61
62 void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d,
63                 std::list<CollisionInfo> *collision_info)
64 {
65         Map *map = &env->getMap();
66         INodeDefManager *nodemgr = m_gamedef->ndef();
67
68         v3f position = getPosition();
69
70         v3f old_speed = m_speed;
71
72         // Copy parent position if local player is attached
73         if(isAttached)
74         {
75                 setPosition(overridePosition);
76                 return;
77         }
78
79         // Skip collision detection if noclip mode is used
80         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
81         bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
82                 g_settings->getBool("noclip");
83         bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
84         if(free_move)
85         {
86         position += m_speed * dtime;
87                 setPosition(position);
88                 return;
89         }
90
91         /*
92                 Collision detection
93         */
94         
95         /*
96                 Check if player is in liquid (the oscillating value)
97         */
98         try{
99                 // If in liquid, the threshold of coming out is at higher y
100                 if(in_liquid)
101                 {
102                         v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
103                         in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
104                         liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
105                 }
106                 // If not in liquid, the threshold of going in is at lower y
107                 else
108                 {
109                         v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
110                         in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
111                         liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
112                 }
113         }
114         catch(InvalidPositionException &e)
115         {
116                 in_liquid = false;
117         }
118
119         /*
120                 Check if player is in liquid (the stable value)
121         */
122         try{
123                 v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
124                 in_liquid_stable = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
125         }
126         catch(InvalidPositionException &e)
127         {
128                 in_liquid_stable = false;
129         }
130
131         /*
132                 Check if player is climbing
133         */
134
135         try {
136                 v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
137                 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
138                 is_climbing = ((nodemgr->get(map->getNode(pp).getContent()).climbable ||
139                 nodemgr->get(map->getNode(pp2).getContent()).climbable) && !free_move);
140         }
141         catch(InvalidPositionException &e)
142         {
143                 is_climbing = false;
144         }
145
146         /*
147                 Collision uncertainty radius
148                 Make it a bit larger than the maximum distance of movement
149         */
150         //f32 d = pos_max_d * 1.1;
151         // A fairly large value in here makes moving smoother
152         f32 d = 0.15*BS;
153
154         // This should always apply, otherwise there are glitches
155         assert(d > pos_max_d);
156
157         float player_radius = BS*0.30;
158         float player_height = BS*1.55;
159         
160         // Maximum distance over border for sneaking
161         f32 sneak_max = BS*0.4;
162
163         /*
164                 If sneaking, keep in range from the last walked node and don't
165                 fall off from it
166         */
167         if(control.sneak && m_sneak_node_exists && !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid)
168         {
169                 f32 maxd = 0.5*BS + sneak_max;
170                 v3f lwn_f = intToFloat(m_sneak_node, BS);
171                 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
172                 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
173                 
174                 if(!is_climbing)
175                 {
176                         f32 min_y = lwn_f.Y + 0.5*BS;
177                         if(position.Y < min_y)
178                         {
179                                 position.Y = min_y;
180
181                                 if(m_speed.Y < 0)
182                                         m_speed.Y = 0;
183                         }
184                 }
185         }
186
187         /*
188                 Calculate player collision box (new and old)
189         */
190         core::aabbox3d<f32> playerbox(
191                 -player_radius,
192                 0.0,
193                 -player_radius,
194                 player_radius,
195                 player_height,
196                 player_radius
197         );
198
199         float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
200
201         v3f accel_f = v3f(0,0,0);
202
203         collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
204                         pos_max_d, playerbox, player_stepheight, dtime,
205                         position, m_speed, accel_f);
206
207         /*
208                 If the player's feet touch the topside of any node, this is
209                 set to true.
210
211                 Player is allowed to jump when this is true.
212         */
213         bool touching_ground_was = touching_ground;
214         touching_ground = result.touching_ground;
215     
216     //bool standing_on_unloaded = result.standing_on_unloaded;
217
218         /*
219                 Check the nodes under the player to see from which node the
220                 player is sneaking from, if any.  If the node from under
221                 the player has been removed, the player falls.
222         */
223         v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
224         if(m_sneak_node_exists &&
225            nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
226            m_old_node_below_type != "air")
227         {
228                 // Old node appears to have been removed; that is,
229                 // it wasn't air before but now it is
230                 m_need_to_get_new_sneak_node = false;
231                 m_sneak_node_exists = false;
232         }
233         else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
234         {
235                 // We are on something, so make sure to recalculate the sneak
236                 // node.
237                 m_need_to_get_new_sneak_node = true;
238         }
239         if(m_need_to_get_new_sneak_node)
240         {
241                 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
242                 v2f player_p2df(position.X, position.Z);
243                 f32 min_distance_f = 100000.0*BS;
244                 // If already seeking from some node, compare to it.
245                 /*if(m_sneak_node_exists)
246                 {
247                         v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
248                         v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
249                         f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
250                         f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
251                         // Ignore if player is not on the same level (likely dropped)
252                         if(d_vert_f < 0.15*BS)
253                                 min_distance_f = d_horiz_f;
254                 }*/
255                 v3s16 new_sneak_node = m_sneak_node;
256                 for(s16 x=-1; x<=1; x++)
257                 for(s16 z=-1; z<=1; z++)
258                 {
259                         v3s16 p = pos_i_bottom + v3s16(x,0,z);
260                         v3f pf = intToFloat(p, BS);
261                         v2f node_p2df(pf.X, pf.Z);
262                         f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
263                         f32 max_axis_distance_f = MYMAX(
264                                         fabs(player_p2df.X-node_p2df.X),
265                                         fabs(player_p2df.Y-node_p2df.Y));
266                                         
267                         if(distance_f > min_distance_f ||
268                                         max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
269                                 continue;
270
271                         try{
272                                 // The node to be sneaked on has to be walkable
273                                 if(nodemgr->get(map->getNode(p)).walkable == false)
274                                         continue;
275                                 // And the node above it has to be nonwalkable
276                                 if(nodemgr->get(map->getNode(p+v3s16(0,1,0))).walkable == true)
277                                         continue;
278                         }
279                         catch(InvalidPositionException &e)
280                         {
281                                 continue;
282                         }
283
284                         min_distance_f = distance_f;
285                         new_sneak_node = p;
286                 }
287                 
288                 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
289
290                 m_sneak_node = new_sneak_node;
291                 m_sneak_node_exists = sneak_node_found;
292
293                 /*
294                         If sneaking, the player's collision box can be in air, so
295                         this has to be set explicitly
296                 */
297                 if(sneak_node_found && control.sneak)
298                         touching_ground = true;
299         }
300         
301         /*
302                 Set new position
303         */
304         setPosition(position);
305         
306         /*
307                 Report collisions
308         */
309         bool bouncy_jump = false;
310         // Dont report if flying
311         if(collision_info && !(g_settings->getBool("free_move") && fly_allowed))
312         {
313                 for(size_t i=0; i<result.collisions.size(); i++){
314                         const CollisionInfo &info = result.collisions[i];
315                         collision_info->push_back(info);
316                         if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
317                                         info.bouncy)
318                                 bouncy_jump = true;
319                 }
320         }
321
322         if(bouncy_jump && control.jump){
323                 m_speed.Y += movement_speed_jump*BS;
324                 touching_ground = false;
325                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
326                 m_gamedef->event()->put(e);
327         }
328
329         if(!touching_ground_was && touching_ground){
330                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
331                 m_gamedef->event()->put(e);
332
333                 // Set camera impact value to be used for view bobbing
334                 camera_impact = getSpeed().Y * -1;
335         }
336
337         {
338                 camera_barely_in_ceiling = false;
339                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
340                 MapNode n = map->getNodeNoEx(camera_np);
341                 if(n.getContent() != CONTENT_IGNORE){
342                         if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
343                                 camera_barely_in_ceiling = true;
344                         }
345                 }
346         }
347
348         /*
349                 Update the node last under the player
350         */
351         m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
352         m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
353         
354         /*
355                 Check properties of the node on which the player is standing
356         */
357         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
358         // Determine if jumping is possible
359         m_can_jump = touching_ground && !in_liquid;
360         if(itemgroup_get(f.groups, "disable_jump"))
361                 m_can_jump = false;
362 }
363
364 void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d)
365 {
366         move(dtime, env, pos_max_d, NULL);
367 }
368
369 void LocalPlayer::applyControl(float dtime)
370 {
371         // Clear stuff
372         swimming_vertical = false;
373
374         setPitch(control.pitch);
375         setYaw(control.yaw);
376
377         // Nullify speed and don't run positioning code if the player is attached
378         if(isAttached)
379         {
380                 setSpeed(v3f(0,0,0));
381                 return;
382         }
383
384         v3f move_direction = v3f(0,0,1);
385         move_direction.rotateXZBy(getYaw());
386         
387         v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
388         v3f speedV = v3f(0,0,0); // Vertical (Y)
389         
390         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
391         bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
392
393         bool free_move = fly_allowed && g_settings->getBool("free_move");
394         bool fast_move = fast_allowed && g_settings->getBool("fast_move");
395         // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
396         bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
397         bool continuous_forward = g_settings->getBool("continuous_forward");
398
399         // Whether superspeed mode is used or not
400         bool superspeed = false;
401         
402         if(g_settings->getBool("always_fly_fast") && free_move && fast_move)
403                 superspeed = true;
404
405         // Old descend control
406         if(g_settings->getBool("aux1_descends"))
407         {
408                 // If free movement and fast movement, always move fast
409                 if(free_move && fast_move)
410                         superspeed = true;
411                 
412                 // Auxiliary button 1 (E)
413                 if(control.aux1)
414                 {
415                         if(free_move)
416                         {
417                                 // In free movement mode, aux1 descends
418                                 if(fast_move)
419                                         speedV.Y = -movement_speed_fast;
420                                 else
421                                         speedV.Y = -movement_speed_walk;
422                         }
423                         else if(in_liquid || in_liquid_stable)
424                         {
425                                 speedV.Y = -movement_speed_walk;
426                                 swimming_vertical = true;
427                         }
428                         else if(is_climbing)
429                         {
430                                 speedV.Y = -movement_speed_climb;
431                         }
432                         else
433                         {
434                                 // If not free movement but fast is allowed, aux1 is
435                                 // "Turbo button"
436                                 if(fast_move)
437                                         superspeed = true;
438                         }
439                 }
440         }
441         // New minecraft-like descend control
442         else
443         {
444                 // Auxiliary button 1 (E)
445                 if(control.aux1)
446                 {
447                         if(!is_climbing)
448                         {
449                                 // aux1 is "Turbo button"
450                                 if(fast_move)
451                                         superspeed = true;
452                         }
453                 }
454
455                 if(control.sneak)
456                 {
457                         if(free_move)
458                         {
459                                 // In free movement mode, sneak descends
460                                 if(fast_move && (control.aux1 || g_settings->getBool("always_fly_fast")))
461                                         speedV.Y = -movement_speed_fast;
462                                 else
463                                         speedV.Y = -movement_speed_walk;
464                         }
465                         else if(in_liquid || in_liquid_stable)
466                         {
467                                 if(fast_climb)
468                                         speedV.Y = -movement_speed_fast;
469                                 else
470                                         speedV.Y = -movement_speed_walk;
471                                 swimming_vertical = true;
472                         }
473                         else if(is_climbing)
474                         {
475                                 if(fast_climb)
476                                         speedV.Y = -movement_speed_fast;
477                                 else
478                                         speedV.Y = -movement_speed_climb;
479                         }
480                 }
481         }
482
483         if(continuous_forward)
484                 speedH += move_direction;
485
486         if(control.up)
487         {
488                 if(continuous_forward)
489                         superspeed = true;
490                 else
491                         speedH += move_direction;
492         }
493         if(control.down)
494         {
495                 speedH -= move_direction;
496         }
497         if(control.left)
498         {
499                 speedH += move_direction.crossProduct(v3f(0,1,0));
500         }
501         if(control.right)
502         {
503                 speedH += move_direction.crossProduct(v3f(0,-1,0));
504         }
505         if(control.jump)
506         {
507                 if(free_move)
508                 {                       
509                         if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
510                         {
511                                 if(fast_move)
512                                         speedV.Y = movement_speed_fast;
513                                 else
514                                         speedV.Y = movement_speed_walk;
515                         } else {
516                                 if(fast_move && control.aux1)
517                                         speedV.Y = movement_speed_fast;
518                                 else
519                                         speedV.Y = movement_speed_walk;
520                         }
521                 }
522                 else if(m_can_jump)
523                 {
524                         /*
525                                 NOTE: The d value in move() affects jump height by
526                                 raising the height at which the jump speed is kept
527                                 at its starting value
528                         */
529                         v3f speedJ = getSpeed();
530                         if(speedJ.Y >= -0.5 * BS)
531                         {
532                                 speedJ.Y = movement_speed_jump * physics_override_jump;
533                                 setSpeed(speedJ);
534                                 
535                                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
536                                 m_gamedef->event()->put(e);
537                         }
538                 }
539                 else if(in_liquid)
540                 {
541                         if(fast_climb)
542                                 speedV.Y = movement_speed_fast;
543                         else
544                                 speedV.Y = movement_speed_walk;
545                         swimming_vertical = true;
546                 }
547                 else if(is_climbing)
548                 {
549                         if(fast_climb)
550                                 speedV.Y = movement_speed_fast;
551                         else
552                                 speedV.Y = movement_speed_climb;
553                 }
554         }
555
556         // The speed of the player (Y is ignored)
557         if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
558                 speedH = speedH.normalize() * movement_speed_fast;
559         else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
560                 speedH = speedH.normalize() * movement_speed_crouch;
561         else
562                 speedH = speedH.normalize() * movement_speed_walk;
563
564         // Acceleration increase
565         f32 incH = 0; // Horizontal (X, Z)
566         f32 incV = 0; // Vertical (Y)
567         if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
568         {
569                 // Jumping and falling
570                 if(superspeed || (fast_move && control.aux1))
571                         incH = movement_acceleration_fast * BS * dtime;
572                 else
573                         incH = movement_acceleration_air * BS * dtime;
574                 incV = 0; // No vertical acceleration in air
575         }
576         else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
577                 incH = incV = movement_acceleration_fast * BS * dtime;
578         else
579                 incH = incV = movement_acceleration_default * BS * dtime;
580
581         // Accelerate to target speed with maximum increment
582         accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
583         accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
584 }
585
586 v3s16 LocalPlayer::getStandingNodePos()
587 {
588         if(m_sneak_node_exists)
589                 return m_sneak_node;
590         return floatToInt(getPosition(), BS);
591 }
592