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
334         {
335                 camera_barely_in_ceiling = false;
336                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
337                 MapNode n = map->getNodeNoEx(camera_np);
338                 if(n.getContent() != CONTENT_IGNORE){
339                         if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
340                                 camera_barely_in_ceiling = true;
341                         }
342                 }
343         }
344
345         /*
346                 Update the node last under the player
347         */
348         m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
349         m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
350         
351         /*
352                 Check properties of the node on which the player is standing
353         */
354         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
355         // Determine if jumping is possible
356         m_can_jump = touching_ground && !in_liquid;
357         if(itemgroup_get(f.groups, "disable_jump"))
358                 m_can_jump = false;
359 }
360
361 void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d)
362 {
363         move(dtime, env, pos_max_d, NULL);
364 }
365
366 void LocalPlayer::applyControl(float dtime)
367 {
368         // Clear stuff
369         swimming_vertical = false;
370
371         setPitch(control.pitch);
372         setYaw(control.yaw);
373
374         // Nullify speed and don't run positioning code if the player is attached
375         if(isAttached)
376         {
377                 setSpeed(v3f(0,0,0));
378                 return;
379         }
380
381         v3f move_direction = v3f(0,0,1);
382         move_direction.rotateXZBy(getYaw());
383         
384         v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
385         v3f speedV = v3f(0,0,0); // Vertical (Y)
386         
387         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
388         bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
389
390         bool free_move = fly_allowed && g_settings->getBool("free_move");
391         bool fast_move = fast_allowed && g_settings->getBool("fast_move");
392         // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
393         bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
394         bool continuous_forward = g_settings->getBool("continuous_forward");
395
396         // Whether superspeed mode is used or not
397         bool superspeed = false;
398         
399         if(g_settings->getBool("always_fly_fast") && free_move && fast_move)
400                 superspeed = true;
401
402         // Old descend control
403         if(g_settings->getBool("aux1_descends"))
404         {
405                 // If free movement and fast movement, always move fast
406                 if(free_move && fast_move)
407                         superspeed = true;
408                 
409                 // Auxiliary button 1 (E)
410                 if(control.aux1)
411                 {
412                         if(free_move)
413                         {
414                                 // In free movement mode, aux1 descends
415                                 if(fast_move)
416                                         speedV.Y = -movement_speed_fast;
417                                 else
418                                         speedV.Y = -movement_speed_walk;
419                         }
420                         else if(in_liquid || in_liquid_stable)
421                         {
422                                 speedV.Y = -movement_speed_walk;
423                                 swimming_vertical = true;
424                         }
425                         else if(is_climbing)
426                         {
427                                 speedV.Y = -movement_speed_climb;
428                         }
429                         else
430                         {
431                                 // If not free movement but fast is allowed, aux1 is
432                                 // "Turbo button"
433                                 if(fast_move)
434                                         superspeed = true;
435                         }
436                 }
437         }
438         // New minecraft-like descend control
439         else
440         {
441                 // Auxiliary button 1 (E)
442                 if(control.aux1)
443                 {
444                         if(!is_climbing)
445                         {
446                                 // aux1 is "Turbo button"
447                                 if(fast_move)
448                                         superspeed = true;
449                         }
450                 }
451
452                 if(control.sneak)
453                 {
454                         if(free_move)
455                         {
456                                 // In free movement mode, sneak descends
457                                 if(fast_move && (control.aux1 || g_settings->getBool("always_fly_fast")))
458                                         speedV.Y = -movement_speed_fast;
459                                 else
460                                         speedV.Y = -movement_speed_walk;
461                         }
462                         else if(in_liquid || in_liquid_stable)
463                         {
464                                 if(fast_climb)
465                                         speedV.Y = -movement_speed_fast;
466                                 else
467                                         speedV.Y = -movement_speed_walk;
468                                 swimming_vertical = true;
469                         }
470                         else if(is_climbing)
471                         {
472                                 if(fast_climb)
473                                         speedV.Y = -movement_speed_fast;
474                                 else
475                                         speedV.Y = -movement_speed_climb;
476                         }
477                 }
478         }
479
480         if(continuous_forward)
481                 speedH += move_direction;
482
483         if(control.up)
484         {
485                 if(continuous_forward)
486                         superspeed = true;
487                 else
488                         speedH += move_direction;
489         }
490         if(control.down)
491         {
492                 speedH -= move_direction;
493         }
494         if(control.left)
495         {
496                 speedH += move_direction.crossProduct(v3f(0,1,0));
497         }
498         if(control.right)
499         {
500                 speedH += move_direction.crossProduct(v3f(0,-1,0));
501         }
502         if(control.jump)
503         {
504                 if(free_move)
505                 {                       
506                         if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
507                         {
508                                 if(fast_move)
509                                         speedV.Y = movement_speed_fast;
510                                 else
511                                         speedV.Y = movement_speed_walk;
512                         } else {
513                                 if(fast_move && control.aux1)
514                                         speedV.Y = movement_speed_fast;
515                                 else
516                                         speedV.Y = movement_speed_walk;
517                         }
518                 }
519                 else if(m_can_jump)
520                 {
521                         /*
522                                 NOTE: The d value in move() affects jump height by
523                                 raising the height at which the jump speed is kept
524                                 at its starting value
525                         */
526                         v3f speedJ = getSpeed();
527                         if(speedJ.Y >= -0.5 * BS)
528                         {
529                                 speedJ.Y = movement_speed_jump * physics_override_jump;
530                                 setSpeed(speedJ);
531                                 
532                                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
533                                 m_gamedef->event()->put(e);
534                         }
535                 }
536                 else if(in_liquid)
537                 {
538                         if(fast_climb)
539                                 speedV.Y = movement_speed_fast;
540                         else
541                                 speedV.Y = movement_speed_walk;
542                         swimming_vertical = true;
543                 }
544                 else if(is_climbing)
545                 {
546                         if(fast_climb)
547                                 speedV.Y = movement_speed_fast;
548                         else
549                                 speedV.Y = movement_speed_climb;
550                 }
551         }
552
553         // The speed of the player (Y is ignored)
554         if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
555                 speedH = speedH.normalize() * movement_speed_fast;
556         else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
557                 speedH = speedH.normalize() * movement_speed_crouch;
558         else
559                 speedH = speedH.normalize() * movement_speed_walk;
560
561         // Acceleration increase
562         f32 incH = 0; // Horizontal (X, Z)
563         f32 incV = 0; // Vertical (Y)
564         if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
565         {
566                 // Jumping and falling
567                 if(superspeed || (fast_move && control.aux1))
568                         incH = movement_acceleration_fast * BS * dtime;
569                 else
570                         incH = movement_acceleration_air * BS * dtime;
571                 incV = 0; // No vertical acceleration in air
572         }
573         else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
574                 incH = incV = movement_acceleration_fast * BS * dtime;
575         else
576                 incH = incV = movement_acceleration_default * BS * dtime;
577
578         // Accelerate to target speed with maximum increment
579         accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
580         accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
581 }
582
583 v3s16 LocalPlayer::getStandingNodePos()
584 {
585         if(m_sneak_node_exists)
586                 return m_sneak_node;
587         return floatToInt(getPosition(), BS);
588 }
589