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