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