Add third person view
[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         hotbar_image(""),
47         hotbar_selected_image(""),
48         m_sneak_node(32767,32767,32767),
49         m_sneak_node_exists(false),
50         m_old_node_below(32767,32767,32767),
51         m_old_node_below_type("air"),
52         m_need_to_get_new_sneak_node(true),
53         m_can_jump(false),
54         camera_mode(0),
55         last_animation(NO_ANIM)
56 {
57         // Initialize hp to 0, so that no hearts will be shown if server
58         // doesn't support health points
59         hp = 0;
60 }
61
62 LocalPlayer::~LocalPlayer()
63 {
64 }
65
66 void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d,
67                 std::list<CollisionInfo> *collision_info)
68 {
69         Map *map = &env->getMap();
70         INodeDefManager *nodemgr = m_gamedef->ndef();
71
72         v3f position = getPosition();
73
74         v3f old_speed = m_speed;
75
76         // Copy parent position if local player is attached
77         if(isAttached)
78         {
79                 setPosition(overridePosition);
80                 m_sneak_node_exists = false;
81                 return;
82         }
83
84         // Skip collision detection if noclip mode is used
85         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
86         bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
87                 g_settings->getBool("noclip");
88         bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
89         if(free_move)
90         {
91         position += m_speed * dtime;
92                 setPosition(position);
93                 m_sneak_node_exists = false;
94                 return;
95         }
96
97         /*
98                 Collision detection
99         */
100         
101         /*
102                 Check if player is in liquid (the oscillating value)
103         */
104         try{
105                 // If in liquid, the threshold of coming out is at higher y
106                 if(in_liquid)
107                 {
108                         v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
109                         in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
110                         liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
111                 }
112                 // If not in liquid, the threshold of going in is at lower y
113                 else
114                 {
115                         v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
116                         in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
117                         liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
118                 }
119         }
120         catch(InvalidPositionException &e)
121         {
122                 in_liquid = false;
123         }
124
125         /*
126                 Check if player is in liquid (the stable value)
127         */
128         try{
129                 v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
130                 in_liquid_stable = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
131         }
132         catch(InvalidPositionException &e)
133         {
134                 in_liquid_stable = false;
135         }
136
137         /*
138                 Check if player is climbing
139         */
140
141         try {
142                 v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
143                 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
144                 is_climbing = ((nodemgr->get(map->getNode(pp).getContent()).climbable ||
145                 nodemgr->get(map->getNode(pp2).getContent()).climbable) && !free_move);
146         }
147         catch(InvalidPositionException &e)
148         {
149                 is_climbing = false;
150         }
151
152         /*
153                 Collision uncertainty radius
154                 Make it a bit larger than the maximum distance of movement
155         */
156         //f32 d = pos_max_d * 1.1;
157         // A fairly large value in here makes moving smoother
158         f32 d = 0.15*BS;
159
160         // This should always apply, otherwise there are glitches
161         assert(d > pos_max_d);
162
163         // Maximum distance over border for sneaking
164         f32 sneak_max = BS*0.4;
165
166         /*
167                 If sneaking, keep in range from the last walked node and don't
168                 fall off from it
169         */
170         if(control.sneak && m_sneak_node_exists &&
171                         !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
172                         physics_override_sneak)
173         {
174                 f32 maxd = 0.5*BS + sneak_max;
175                 v3f lwn_f = intToFloat(m_sneak_node, BS);
176                 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
177                 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
178                 
179                 if(!is_climbing)
180                 {
181                         f32 min_y = lwn_f.Y + 0.5*BS;
182                         if(position.Y < min_y)
183                         {
184                                 position.Y = min_y;
185
186                                 if(m_speed.Y < 0)
187                                         m_speed.Y = 0;
188                         }
189                 }
190         }
191
192         float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
193
194         v3f accel_f = v3f(0,0,0);
195
196         collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
197                         pos_max_d, m_collisionbox, player_stepheight, dtime,
198                         position, m_speed, accel_f);
199
200         /*
201                 If the player's feet touch the topside of any node, this is
202                 set to true.
203
204                 Player is allowed to jump when this is true.
205         */
206         bool touching_ground_was = touching_ground;
207         touching_ground = result.touching_ground;
208     
209     //bool standing_on_unloaded = result.standing_on_unloaded;
210
211         /*
212                 Check the nodes under the player to see from which node the
213                 player is sneaking from, if any.  If the node from under
214                 the player has been removed, the player falls.
215         */
216         v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
217         if(m_sneak_node_exists &&
218            nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
219            m_old_node_below_type != "air")
220         {
221                 // Old node appears to have been removed; that is,
222                 // it wasn't air before but now it is
223                 m_need_to_get_new_sneak_node = false;
224                 m_sneak_node_exists = false;
225         }
226         else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
227         {
228                 // We are on something, so make sure to recalculate the sneak
229                 // node.
230                 m_need_to_get_new_sneak_node = true;
231         }
232         if(m_need_to_get_new_sneak_node && physics_override_sneak)
233         {
234                 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
235                 v2f player_p2df(position.X, position.Z);
236                 f32 min_distance_f = 100000.0*BS;
237                 // If already seeking from some node, compare to it.
238                 /*if(m_sneak_node_exists)
239                 {
240                         v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
241                         v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
242                         f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
243                         f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
244                         // Ignore if player is not on the same level (likely dropped)
245                         if(d_vert_f < 0.15*BS)
246                                 min_distance_f = d_horiz_f;
247                 }*/
248                 v3s16 new_sneak_node = m_sneak_node;
249                 for(s16 x=-1; x<=1; x++)
250                 for(s16 z=-1; z<=1; z++)
251                 {
252                         v3s16 p = pos_i_bottom + v3s16(x,0,z);
253                         v3f pf = intToFloat(p, BS);
254                         v2f node_p2df(pf.X, pf.Z);
255                         f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
256                         f32 max_axis_distance_f = MYMAX(
257                                         fabs(player_p2df.X-node_p2df.X),
258                                         fabs(player_p2df.Y-node_p2df.Y));
259                                         
260                         if(distance_f > min_distance_f ||
261                                         max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
262                                 continue;
263
264                         try{
265                                 // The node to be sneaked on has to be walkable
266                                 if(nodemgr->get(map->getNode(p)).walkable == false)
267                                         continue;
268                                 // And the node above it has to be nonwalkable
269                                 if(nodemgr->get(map->getNode(p+v3s16(0,1,0))).walkable == true)
270                                         continue;
271                                 if (!physics_override_sneak_glitch) {
272                                         if (nodemgr->get(map->getNode(p+v3s16(0,2,0))).walkable)
273                                                 continue;
274                                 }
275                         }
276                         catch(InvalidPositionException &e)
277                         {
278                                 continue;
279                         }
280
281                         min_distance_f = distance_f;
282                         new_sneak_node = p;
283                 }
284                 
285                 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
286
287                 m_sneak_node = new_sneak_node;
288                 m_sneak_node_exists = sneak_node_found;
289
290                 /*
291                         If sneaking, the player's collision box can be in air, so
292                         this has to be set explicitly
293                 */
294                 if(sneak_node_found && control.sneak)
295                         touching_ground = true;
296         }
297         
298         /*
299                 Set new position
300         */
301         setPosition(position);
302         
303         /*
304                 Report collisions
305         */
306         bool bouncy_jump = false;
307         // Dont report if flying
308         if(collision_info && !(g_settings->getBool("free_move") && fly_allowed))
309         {
310                 for(size_t i=0; i<result.collisions.size(); i++){
311                         const CollisionInfo &info = result.collisions[i];
312                         collision_info->push_back(info);
313                         if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
314                                         info.bouncy)
315                                 bouncy_jump = true;
316                 }
317         }
318
319         if(bouncy_jump && control.jump){
320                 m_speed.Y += movement_speed_jump*BS;
321                 touching_ground = false;
322                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
323                 m_gamedef->event()->put(e);
324         }
325
326         if(!touching_ground_was && touching_ground){
327                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
328                 m_gamedef->event()->put(e);
329
330                 // Set camera impact value to be used for view bobbing
331                 camera_impact = getSpeed().Y * -1;
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() - v3f(0, BS, 0), BS);
588 }
589