]> git.lizzy.rs Git - dragonfireclient.git/blob - src/localplayer.cpp
Cleanup some header inclusions to improve compilation times
[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 "event.h"
23 #include "collision.h"
24 #include "nodedef.h"
25 #include "settings.h"
26 #include "environment.h"
27 #include "map.h"
28 #include "client.h"
29
30 /*
31         LocalPlayer
32 */
33
34 LocalPlayer::LocalPlayer(Client *client, const char *name):
35         Player(name, client->idef()),
36         parent(0),
37         hp(PLAYER_MAX_HP),
38         got_teleported(false),
39         isAttached(false),
40         touching_ground(false),
41         in_liquid(false),
42         in_liquid_stable(false),
43         liquid_viscosity(0),
44         is_climbing(false),
45         swimming_vertical(false),
46         // Movement overrides are multipliers and must be 1 by default
47         physics_override_speed(1.0f),
48         physics_override_jump(1.0f),
49         physics_override_gravity(1.0f),
50         physics_override_sneak(true),
51         physics_override_sneak_glitch(true),
52         overridePosition(v3f(0,0,0)),
53         last_position(v3f(0,0,0)),
54         last_speed(v3f(0,0,0)),
55         last_pitch(0),
56         last_yaw(0),
57         last_keyPressed(0),
58         last_camera_fov(0),
59         last_wanted_range(0),
60         camera_impact(0.f),
61         last_animation(NO_ANIM),
62         hotbar_image(""),
63         hotbar_selected_image(""),
64         light_color(255,255,255,255),
65         hurt_tilt_timer(0.0f),
66         hurt_tilt_strength(0.0f),
67         m_position(0,0,0),
68         m_sneak_node(32767,32767,32767),
69         m_sneak_node_exists(false),
70         m_need_to_get_new_sneak_node(true),
71         m_sneak_node_bb_ymax(0),
72         m_old_node_below(32767,32767,32767),
73         m_old_node_below_type("air"),
74         m_can_jump(false),
75         m_breath(PLAYER_MAX_BREATH),
76         m_yaw(0),
77         m_pitch(0),
78         camera_barely_in_ceiling(false),
79         m_collisionbox(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30),
80         m_cao(NULL),
81         m_client(client)
82 {
83         // Initialize hp to 0, so that no hearts will be shown if server
84         // doesn't support health points
85         hp = 0;
86         eye_offset_first = v3f(0,0,0);
87         eye_offset_third = v3f(0,0,0);
88 }
89
90 LocalPlayer::~LocalPlayer()
91 {
92 }
93
94 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
95                 std::vector<CollisionInfo> *collision_info)
96 {
97         Map *map = &env->getMap();
98         INodeDefManager *nodemgr = m_client->ndef();
99
100         v3f position = getPosition();
101
102         // Copy parent position if local player is attached
103         if(isAttached)
104         {
105                 setPosition(overridePosition);
106                 m_sneak_node_exists = false;
107                 return;
108         }
109
110         // Skip collision detection if noclip mode is used
111         bool fly_allowed = m_client->checkLocalPrivilege("fly");
112         bool noclip = m_client->checkLocalPrivilege("noclip") &&
113                 g_settings->getBool("noclip");
114         bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
115         if (free_move) {
116                 position += m_speed * dtime;
117                 setPosition(position);
118                 m_sneak_node_exists = false;
119                 return;
120         }
121
122         /*
123                 Collision detection
124         */
125
126         bool is_valid_position;
127         MapNode node;
128         v3s16 pp;
129
130         /*
131                 Check if player is in liquid (the oscillating value)
132         */
133
134         // If in liquid, the threshold of coming out is at higher y
135         if (in_liquid)
136         {
137                 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
138                 node = map->getNodeNoEx(pp, &is_valid_position);
139                 if (is_valid_position) {
140                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
141                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
142                 } else {
143                         in_liquid = false;
144                 }
145         }
146         // If not in liquid, the threshold of going in is at lower y
147         else
148         {
149                 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
150                 node = map->getNodeNoEx(pp, &is_valid_position);
151                 if (is_valid_position) {
152                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
153                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
154                 } else {
155                         in_liquid = false;
156                 }
157         }
158
159
160         /*
161                 Check if player is in liquid (the stable value)
162         */
163         pp = floatToInt(position + v3f(0,0,0), BS);
164         node = map->getNodeNoEx(pp, &is_valid_position);
165         if (is_valid_position) {
166                 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
167         } else {
168                 in_liquid_stable = false;
169         }
170
171         /*
172                 Check if player is climbing
173         */
174
175
176         pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
177         v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
178         node = map->getNodeNoEx(pp, &is_valid_position);
179         bool is_valid_position2;
180         MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
181
182         if (!(is_valid_position && is_valid_position2)) {
183                 is_climbing = false;
184         } else {
185                 is_climbing = (nodemgr->get(node.getContent()).climbable
186                                 || nodemgr->get(node2.getContent()).climbable) && !free_move;
187         }
188
189
190         /*
191                 Collision uncertainty radius
192                 Make it a bit larger than the maximum distance of movement
193         */
194         //f32 d = pos_max_d * 1.1;
195         // A fairly large value in here makes moving smoother
196         f32 d = 0.15*BS;
197
198         // This should always apply, otherwise there are glitches
199         sanity_check(d > pos_max_d);
200
201         // Maximum distance over border for sneaking
202         f32 sneak_max = BS*0.4;
203
204         /*
205                 If sneaking, keep in range from the last walked node and don't
206                 fall off from it
207         */
208         if (control.sneak && m_sneak_node_exists &&
209                         !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
210                         physics_override_sneak && !got_teleported) {
211                 f32 maxd = 0.5 * BS + sneak_max;
212                 v3f lwn_f = intToFloat(m_sneak_node, BS);
213                 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
214                 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
215
216                 if (!is_climbing) {
217                         // Move up if necessary
218                         f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
219                         if (position.Y < new_y)
220                                 position.Y = new_y;
221                         /*
222                                 Collision seems broken, since player is sinking when
223                                 sneaking over the edges of current sneaking_node.
224                                 TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
225                         */
226                         if (m_speed.Y < 0)
227                                 m_speed.Y = 0;
228                 }
229         }
230
231         if (got_teleported)
232                 got_teleported = false;
233
234         // this shouldn't be hardcoded but transmitted from server
235         float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
236
237 #ifdef __ANDROID__
238         player_stepheight += (0.6 * BS);
239 #endif
240
241         v3f accel_f = v3f(0,0,0);
242
243         collisionMoveResult result = collisionMoveSimple(env, m_client,
244                 pos_max_d, m_collisionbox, player_stepheight, dtime,
245                 &position, &m_speed, accel_f);
246
247         /*
248                 If the player's feet touch the topside of any node, this is
249                 set to true.
250
251                 Player is allowed to jump when this is true.
252         */
253         bool touching_ground_was = touching_ground;
254         touching_ground = result.touching_ground;
255
256     //bool standing_on_unloaded = result.standing_on_unloaded;
257
258         /*
259                 Check the nodes under the player to see from which node the
260                 player is sneaking from, if any.  If the node from under
261                 the player has been removed, the player falls.
262         */
263         f32 position_y_mod = 0.05 * BS;
264         if (m_sneak_node_bb_ymax > 0)
265                 position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
266         v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
267         if (m_sneak_node_exists &&
268                         nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
269                         m_old_node_below_type != "air") {
270                 // Old node appears to have been removed; that is,
271                 // it wasn't air before but now it is
272                 m_need_to_get_new_sneak_node = false;
273                 m_sneak_node_exists = false;
274         } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
275                 // We are on something, so make sure to recalculate the sneak
276                 // node.
277                 m_need_to_get_new_sneak_node = true;
278         }
279
280         if (m_need_to_get_new_sneak_node && physics_override_sneak) {
281                 m_sneak_node_bb_ymax = 0;
282                 v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
283                 v2f player_p2df(position.X, position.Z);
284                 f32 min_distance_f = 100000.0 * BS;
285                 // If already seeking from some node, compare to it.
286                 /*if(m_sneak_node_exists)
287                 {
288                         v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
289                         v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
290                         f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
291                         f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
292                         // Ignore if player is not on the same level (likely dropped)
293                         if(d_vert_f < 0.15*BS)
294                                 min_distance_f = d_horiz_f;
295                 }*/
296                 v3s16 new_sneak_node = m_sneak_node;
297                 for(s16 x=-1; x<=1; x++)
298                 for(s16 z=-1; z<=1; z++)
299                 {
300                         v3s16 p = pos_i_bottom + v3s16(x,0,z);
301                         v3f pf = intToFloat(p, BS);
302                         v2f node_p2df(pf.X, pf.Z);
303                         f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
304                         f32 max_axis_distance_f = MYMAX(
305                                         fabs(player_p2df.X-node_p2df.X),
306                                         fabs(player_p2df.Y-node_p2df.Y));
307
308                         if(distance_f > min_distance_f ||
309                                         max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
310                                 continue;
311
312
313                         // The node to be sneaked on has to be walkable
314                         node = map->getNodeNoEx(p, &is_valid_position);
315                         if (!is_valid_position || nodemgr->get(node).walkable == false)
316                                 continue;
317                         // And the node above it has to be nonwalkable
318                         node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
319                         if (!is_valid_position || nodemgr->get(node).walkable) {
320                                 continue;
321                         }
322                         if (!physics_override_sneak_glitch) {
323                                 node =map->getNodeNoEx(p + v3s16(0,2,0), &is_valid_position);
324                                 if (!is_valid_position || nodemgr->get(node).walkable)
325                                         continue;
326                         }
327
328                         min_distance_f = distance_f;
329                         new_sneak_node = p;
330                 }
331
332                 bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
333
334                 m_sneak_node = new_sneak_node;
335                 m_sneak_node_exists = sneak_node_found;
336
337                 if (sneak_node_found) {
338                         f32 cb_max = 0;
339                         MapNode n = map->getNodeNoEx(m_sneak_node);
340                         std::vector<aabb3f> nodeboxes;
341                         n.getCollisionBoxes(nodemgr, &nodeboxes);
342                         for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
343                                         it != nodeboxes.end(); ++it) {
344                                 aabb3f box = *it;
345                                 if (box.MaxEdge.Y > cb_max)
346                                         cb_max = box.MaxEdge.Y;
347                         }
348                         m_sneak_node_bb_ymax = cb_max;
349                 }
350
351                 /*
352                         If sneaking, the player's collision box can be in air, so
353                         this has to be set explicitly
354                 */
355                 if(sneak_node_found && control.sneak)
356                         touching_ground = true;
357         }
358
359         /*
360                 Set new position
361         */
362         setPosition(position);
363
364         /*
365                 Report collisions
366         */
367
368         // Dont report if flying
369         if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
370                 for(size_t i=0; i<result.collisions.size(); i++) {
371                         const CollisionInfo &info = result.collisions[i];
372                         collision_info->push_back(info);
373                 }
374         }
375
376         if(!result.standing_on_object && !touching_ground_was && touching_ground) {
377                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
378                 m_client->event()->put(e);
379
380                 // Set camera impact value to be used for view bobbing
381                 camera_impact = getSpeed().Y * -1;
382         }
383
384         {
385                 camera_barely_in_ceiling = false;
386                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
387                 MapNode n = map->getNodeNoEx(camera_np);
388                 if(n.getContent() != CONTENT_IGNORE){
389                         if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
390                                 camera_barely_in_ceiling = true;
391                         }
392                 }
393         }
394
395         /*
396                 Update the node last under the player
397         */
398         m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
399         m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
400
401         /*
402                 Check properties of the node on which the player is standing
403         */
404         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
405         // Determine if jumping is possible
406         m_can_jump = touching_ground && !in_liquid;
407         if(itemgroup_get(f.groups, "disable_jump"))
408                 m_can_jump = false;
409         // Jump key pressed while jumping off from a bouncy block
410         if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
411                 m_speed.Y >= -0.5 * BS) {
412                 float jumpspeed = movement_speed_jump * physics_override_jump;
413                 if (m_speed.Y > 1) {
414                         // Reduce boost when speed already is high
415                         m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
416                 } else {
417                         m_speed.Y += jumpspeed;
418                 }
419                 setSpeed(m_speed);
420                 m_can_jump = false;
421         }
422 }
423
424 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
425 {
426         move(dtime, env, pos_max_d, NULL);
427 }
428
429 void LocalPlayer::applyControl(float dtime)
430 {
431         // Clear stuff
432         swimming_vertical = false;
433
434         setPitch(control.pitch);
435         setYaw(control.yaw);
436
437         // Nullify speed and don't run positioning code if the player is attached
438         if(isAttached)
439         {
440                 setSpeed(v3f(0,0,0));
441                 return;
442         }
443
444         v3f move_direction = v3f(0,0,1);
445         move_direction.rotateXZBy(getYaw());
446
447         v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
448         v3f speedV = v3f(0,0,0); // Vertical (Y)
449
450         bool fly_allowed = m_client->checkLocalPrivilege("fly");
451         bool fast_allowed = m_client->checkLocalPrivilege("fast");
452
453         bool free_move = fly_allowed && g_settings->getBool("free_move");
454         bool fast_move = fast_allowed && g_settings->getBool("fast_move");
455         // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
456         bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
457         bool continuous_forward = g_settings->getBool("continuous_forward");
458         bool always_fly_fast = g_settings->getBool("always_fly_fast");
459
460         // Whether superspeed mode is used or not
461         bool superspeed = false;
462
463         if (always_fly_fast && free_move && fast_move)
464                 superspeed = true;
465
466         // Old descend control
467         if(g_settings->getBool("aux1_descends"))
468         {
469                 // If free movement and fast movement, always move fast
470                 if(free_move && fast_move)
471                         superspeed = true;
472
473                 // Auxiliary button 1 (E)
474                 if(control.aux1)
475                 {
476                         if(free_move)
477                         {
478                                 // In free movement mode, aux1 descends
479                                 if(fast_move)
480                                         speedV.Y = -movement_speed_fast;
481                                 else
482                                         speedV.Y = -movement_speed_walk;
483                         }
484                         else if(in_liquid || in_liquid_stable)
485                         {
486                                 speedV.Y = -movement_speed_walk;
487                                 swimming_vertical = true;
488                         }
489                         else if(is_climbing)
490                         {
491                                 speedV.Y = -movement_speed_climb;
492                         }
493                         else
494                         {
495                                 // If not free movement but fast is allowed, aux1 is
496                                 // "Turbo button"
497                                 if(fast_move)
498                                         superspeed = true;
499                         }
500                 }
501         }
502         // New minecraft-like descend control
503         else
504         {
505                 // Auxiliary button 1 (E)
506                 if(control.aux1)
507                 {
508                         if(!is_climbing)
509                         {
510                                 // aux1 is "Turbo button"
511                                 if(fast_move)
512                                         superspeed = true;
513                         }
514                 }
515
516                 if(control.sneak)
517                 {
518                         if(free_move)
519                         {
520                                 // In free movement mode, sneak descends
521                                 if (fast_move && (control.aux1 || always_fly_fast))
522                                         speedV.Y = -movement_speed_fast;
523                                 else
524                                         speedV.Y = -movement_speed_walk;
525                         }
526                         else if(in_liquid || in_liquid_stable)
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
544         if (continuous_forward)
545                 speedH += move_direction;
546
547         if (control.up) {
548                 if (continuous_forward) {
549                         if (fast_move)
550                                 superspeed = true;
551                 } else {
552                         speedH += move_direction;
553                 }
554         }
555         if (control.down) {
556                 speedH -= move_direction;
557         }
558         if (!control.up && !control.down) {
559                 speedH -= move_direction *
560                         (control.forw_move_joystick_axis / 32767.f);
561         }
562         if (control.left) {
563                 speedH += move_direction.crossProduct(v3f(0,1,0));
564         }
565         if (control.right) {
566                 speedH += move_direction.crossProduct(v3f(0,-1,0));
567         }
568         if (!control.left && !control.right) {
569                 speedH -= move_direction.crossProduct(v3f(0,1,0)) *
570                         (control.sidew_move_joystick_axis / 32767.f);
571         }
572         if(control.jump)
573         {
574                 if (free_move) {
575                         if (g_settings->getBool("aux1_descends") || always_fly_fast) {
576                                 if (fast_move)
577                                         speedV.Y = movement_speed_fast;
578                                 else
579                                         speedV.Y = movement_speed_walk;
580                         } else {
581                                 if(fast_move && control.aux1)
582                                         speedV.Y = movement_speed_fast;
583                                 else
584                                         speedV.Y = movement_speed_walk;
585                         }
586                 }
587                 else if(m_can_jump)
588                 {
589                         /*
590                                 NOTE: The d value in move() affects jump height by
591                                 raising the height at which the jump speed is kept
592                                 at its starting value
593                         */
594                         v3f speedJ = getSpeed();
595                         if(speedJ.Y >= -0.5 * BS)
596                         {
597                                 speedJ.Y = movement_speed_jump * physics_override_jump;
598                                 setSpeed(speedJ);
599
600                                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
601                                 m_client->event()->put(e);
602                         }
603                 }
604                 else if(in_liquid)
605                 {
606                         if(fast_climb)
607                                 speedV.Y = movement_speed_fast;
608                         else
609                                 speedV.Y = movement_speed_walk;
610                         swimming_vertical = true;
611                 }
612                 else if(is_climbing)
613                 {
614                         if(fast_climb)
615                                 speedV.Y = movement_speed_fast;
616                         else
617                                 speedV.Y = movement_speed_climb;
618                 }
619         }
620
621         // The speed of the player (Y is ignored)
622         if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
623                 speedH = speedH.normalize() * movement_speed_fast;
624         else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
625                 speedH = speedH.normalize() * movement_speed_crouch;
626         else
627                 speedH = speedH.normalize() * movement_speed_walk;
628
629         // Acceleration increase
630         f32 incH = 0; // Horizontal (X, Z)
631         f32 incV = 0; // Vertical (Y)
632         if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
633         {
634                 // Jumping and falling
635                 if(superspeed || (fast_move && control.aux1))
636                         incH = movement_acceleration_fast * BS * dtime;
637                 else
638                         incH = movement_acceleration_air * BS * dtime;
639                 incV = 0; // No vertical acceleration in air
640         }
641         else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
642                 incH = incV = movement_acceleration_fast * BS * dtime;
643         else
644                 incH = incV = movement_acceleration_default * BS * dtime;
645
646         // Accelerate to target speed with maximum increment
647         accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
648         accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
649 }
650
651 v3s16 LocalPlayer::getStandingNodePos()
652 {
653         if(m_sneak_node_exists)
654                 return m_sneak_node;
655         return floatToInt(getPosition() - v3f(0, BS, 0), BS);
656 }
657
658 v3s16 LocalPlayer::getLightPosition() const
659 {
660         return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
661 }
662
663 v3f LocalPlayer::getEyeOffset() const
664 {
665         float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f;
666         return v3f(0, BS * eye_height, 0);
667 }
668
669 // Horizontal acceleration (X and Z), Y direction is ignored
670 void LocalPlayer::accelerateHorizontal(const v3f &target_speed, const f32 max_increase)
671 {
672         if (max_increase == 0)
673                 return;
674
675         v3f d_wanted = target_speed - m_speed;
676         d_wanted.Y = 0;
677         f32 dl = d_wanted.getLength();
678         if (dl > max_increase)
679                 dl = max_increase;
680
681         v3f d = d_wanted.normalize() * dl;
682
683         m_speed.X += d.X;
684         m_speed.Z += d.Z;
685 }
686
687 // Vertical acceleration (Y), X and Z directions are ignored
688 void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
689 {
690         if (max_increase == 0)
691                 return;
692
693         f32 d_wanted = target_speed.Y - m_speed.Y;
694         if (d_wanted > max_increase)
695                 d_wanted = max_increase;
696         else if (d_wanted < -max_increase)
697                 d_wanted = -max_increase;
698
699         m_speed.Y += d_wanted;
700 }
701