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