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