64780de75d7c2402d185b06b1e9362a0d57a4dbf
[oweals/minetest.git] / src / player.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
14
15 You should have received a copy of the GNU 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 "player.h"
21 #include "map.h"
22 #include "connection.h"
23 #include "constants.h"
24 #include "utility.h"
25
26 Player::Player():
27         touching_ground(false),
28         in_water(false),
29         in_water_stable(false),
30         swimming_up(false),
31         craftresult_is_preview(true),
32         hp(20),
33         peer_id(PEER_ID_INEXISTENT),
34         m_pitch(0),
35         m_yaw(0),
36         m_speed(0,0,0),
37         m_position(0,0,0)
38 {
39         updateName("<not set>");
40         resetInventory();
41 }
42
43 Player::~Player()
44 {
45 }
46
47 void Player::resetInventory()
48 {
49         inventory.clear();
50         inventory.addList("main", PLAYER_INVENTORY_SIZE);
51         inventory.addList("craft", 9);
52         inventory.addList("craftresult", 1);
53 }
54
55 // Y direction is ignored
56 void Player::accelerate(v3f target_speed, f32 max_increase)
57 {
58         v3f d_wanted = target_speed - m_speed;
59         d_wanted.Y = 0;
60         f32 dl_wanted = d_wanted.getLength();
61         f32 dl = dl_wanted;
62         if(dl > max_increase)
63                 dl = max_increase;
64         
65         v3f d = d_wanted.normalize() * dl;
66
67         m_speed.X += d.X;
68         m_speed.Z += d.Z;
69         //m_speed += d;
70
71 #if 0 // old code
72         if(m_speed.X < target_speed.X - max_increase)
73                 m_speed.X += max_increase;
74         else if(m_speed.X > target_speed.X + max_increase)
75                 m_speed.X -= max_increase;
76         else if(m_speed.X < target_speed.X)
77                 m_speed.X = target_speed.X;
78         else if(m_speed.X > target_speed.X)
79                 m_speed.X = target_speed.X;
80
81         if(m_speed.Z < target_speed.Z - max_increase)
82                 m_speed.Z += max_increase;
83         else if(m_speed.Z > target_speed.Z + max_increase)
84                 m_speed.Z -= max_increase;
85         else if(m_speed.Z < target_speed.Z)
86                 m_speed.Z = target_speed.Z;
87         else if(m_speed.Z > target_speed.Z)
88                 m_speed.Z = target_speed.Z;
89 #endif
90 }
91
92 void Player::serialize(std::ostream &os)
93 {
94         // Utilize a Settings object for storing values
95         Settings args;
96         args.setS32("version", 1);
97         args.set("name", m_name);
98         args.setFloat("pitch", m_pitch);
99         args.setFloat("yaw", m_yaw);
100         args.setV3F("position", m_position);
101         args.setBool("craftresult_is_preview", craftresult_is_preview);
102         args.setS32("hp", hp);
103
104         args.writeLines(os);
105
106         os<<"PlayerArgsEnd\n";
107
108         inventory.serialize(os);
109 }
110
111 void Player::deSerialize(std::istream &is)
112 {
113         Settings args;
114         
115         for(;;)
116         {
117                 if(is.eof())
118                         throw SerializationError
119                                         ("Player::deSerialize(): PlayerArgsEnd not found");
120                 std::string line;
121                 std::getline(is, line);
122                 std::string trimmedline = trim(line);
123                 if(trimmedline == "PlayerArgsEnd")
124                         break;
125                 args.parseConfigLine(line);
126         }
127
128         //args.getS32("version");
129         std::string name = args.get("name");
130         updateName(name.c_str());
131         m_pitch = args.getFloat("pitch");
132         m_yaw = args.getFloat("yaw");
133         m_position = args.getV3F("position");
134         try{
135                 craftresult_is_preview = args.getBool("craftresult_is_preview");
136         }catch(SettingNotFoundException &e){
137                 craftresult_is_preview = true;
138         }
139         try{
140                 hp = args.getS32("hp");
141         }catch(SettingNotFoundException &e){
142                 hp = 20;
143         }
144
145         inventory.deSerialize(is);
146 }
147
148 /*
149         RemotePlayer
150 */
151
152 #ifndef SERVER
153
154 RemotePlayer::RemotePlayer(
155                 scene::ISceneNode* parent,
156                 IrrlichtDevice *device,
157                 s32 id):
158         scene::ISceneNode(parent, (device==NULL)?NULL:device->getSceneManager(), id),
159         m_text(NULL)
160 {
161         m_box = core::aabbox3d<f32>(-BS/2,0,-BS/2,BS/2,BS*2,BS/2);
162
163         if(parent != NULL && device != NULL)
164         {
165                 // ISceneNode stores a member called SceneManager
166                 scene::ISceneManager* mgr = SceneManager;
167                 video::IVideoDriver* driver = mgr->getVideoDriver();
168                 gui::IGUIEnvironment* gui = device->getGUIEnvironment();
169
170                 // Add a text node for showing the name
171                 wchar_t wname[1] = {0};
172                 m_text = mgr->addTextSceneNode(gui->getBuiltInFont(),
173                                 wname, video::SColor(255,255,255,255), this);
174                 m_text->setPosition(v3f(0, (f32)BS*2.1, 0));
175
176                 // Attach a simple mesh to the player for showing an image
177                 scene::SMesh *mesh = new scene::SMesh();
178                 { // Front
179                 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
180                 video::SColor c(255,255,255,255);
181                 video::S3DVertex vertices[4] =
182                 {
183                         video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
184                         video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
185                         video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
186                         video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
187                 };
188                 u16 indices[] = {0,1,2,2,3,0};
189                 buf->append(vertices, 4, indices, 6);
190                 // Set material
191                 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
192                 //buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
193                 buf->getMaterial().setTexture(0, driver->getTexture(porting::getDataPath("player.png").c_str()));
194                 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
195                 buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
196                 //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
197                 buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
198                 // Add to mesh
199                 mesh->addMeshBuffer(buf);
200                 buf->drop();
201                 }
202                 { // Back
203                 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
204                 video::SColor c(255,255,255,255);
205                 video::S3DVertex vertices[4] =
206                 {
207                         video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
208                         video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
209                         video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
210                         video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
211                 };
212                 u16 indices[] = {0,1,2,2,3,0};
213                 buf->append(vertices, 4, indices, 6);
214                 // Set material
215                 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
216                 //buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
217                 buf->getMaterial().setTexture(0, driver->getTexture(porting::getDataPath("player_back.png").c_str()));
218                 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
219                 buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
220                 buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
221                 // Add to mesh
222                 mesh->addMeshBuffer(buf);
223                 buf->drop();
224                 }
225                 m_node = mgr->addMeshSceneNode(mesh, this);
226                 mesh->drop();
227                 m_node->setPosition(v3f(0,0,0));
228         }
229 }
230
231 RemotePlayer::~RemotePlayer()
232 {
233         if(SceneManager != NULL)
234                 ISceneNode::remove();
235 }
236
237 void RemotePlayer::updateName(const char *name)
238 {
239         Player::updateName(name);
240         if(m_text != NULL)
241         {
242                 wchar_t wname[PLAYERNAME_SIZE];
243                 mbstowcs(wname, m_name, strlen(m_name)+1);
244                 m_text->setText(wname);
245         }
246 }
247
248 void RemotePlayer::move(f32 dtime, Map &map, f32 pos_max_d)
249 {
250         m_pos_animation_time_counter += dtime;
251         m_pos_animation_counter += dtime;
252         v3f movevector = m_position - m_oldpos;
253         f32 moveratio;
254         if(m_pos_animation_time < 0.001)
255                 moveratio = 1.0;
256         else
257                 moveratio = m_pos_animation_counter / m_pos_animation_time;
258         if(moveratio > 1.5)
259                 moveratio = 1.5;
260         m_showpos = m_oldpos + movevector * moveratio;
261         
262         ISceneNode::setPosition(m_showpos);
263 }
264
265 #endif
266
267 #ifndef SERVER
268 /*
269         LocalPlayer
270 */
271
272 LocalPlayer::LocalPlayer():
273         m_sneak_node(32767,32767,32767),
274         m_sneak_node_exists(false)
275 {
276         // Initialize hp to 0, so that no hearts will be shown if server
277         // doesn't support health points
278         hp = 0;
279 }
280
281 LocalPlayer::~LocalPlayer()
282 {
283 }
284
285 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
286                 core::list<CollisionInfo> *collision_info)
287 {
288         v3f position = getPosition();
289         v3f oldpos = position;
290         v3s16 oldpos_i = floatToInt(oldpos, BS);
291
292         /*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<","
293                         <<oldpos_i.Z<<")"<<std::endl;*/
294
295         /*
296                 Calculate new position
297         */
298         position += m_speed * dtime;
299
300         // Skip collision detection if a special movement mode is used
301         bool free_move = g_settings.getBool("free_move");
302         if(free_move)
303         {
304                 setPosition(position);
305                 return;
306         }
307
308         /*
309                 Collision detection
310         */
311         
312         // Player position in nodes
313         v3s16 pos_i = floatToInt(position, BS);
314         
315         /*
316                 Check if player is in water (the oscillating value)
317         */
318         try{
319                 // If in water, the threshold of coming out is at higher y
320                 if(in_water)
321                 {
322                         v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
323                         in_water = content_liquid(map.getNode(pp).d);
324                 }
325                 // If not in water, the threshold of going in is at lower y
326                 else
327                 {
328                         v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
329                         in_water = content_liquid(map.getNode(pp).d);
330                 }
331         }
332         catch(InvalidPositionException &e)
333         {
334                 in_water = false;
335         }
336
337         /*
338                 Check if player is in water (the stable value)
339         */
340         try{
341                 v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
342                 in_water_stable = content_liquid(map.getNode(pp).d);
343         }
344         catch(InvalidPositionException &e)
345         {
346                 in_water_stable = false;
347         }
348
349         /*
350                 Collision uncertainty radius
351                 Make it a bit larger than the maximum distance of movement
352         */
353         //f32 d = pos_max_d * 1.1;
354         // A fairly large value in here makes moving smoother
355         f32 d = 0.15*BS;
356
357         // This should always apply, otherwise there are glitches
358         assert(d > pos_max_d);
359
360         float player_radius = BS*0.35;
361         float player_height = BS*1.7;
362         
363         // Maximum distance over border for sneaking
364         f32 sneak_max = BS*0.4;
365
366         /*
367                 If sneaking, player has larger collision radius to keep from
368                 falling
369         */
370         /*if(control.sneak)
371                 player_radius = sneak_max + d*1.1;*/
372         
373         /*
374                 If sneaking, keep in range from the last walked node and don't
375                 fall off from it
376         */
377         if(control.sneak && m_sneak_node_exists)
378         {
379                 f32 maxd = 0.5*BS + sneak_max;
380                 v3f lwn_f = intToFloat(m_sneak_node, BS);
381                 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
382                 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
383                 
384                 f32 min_y = lwn_f.Y + 0.5*BS;
385                 if(position.Y < min_y)
386                 {
387                         position.Y = min_y;
388                         if(m_speed.Y < 0)
389                                 m_speed.Y = 0;
390                 }
391         }
392
393         /*
394                 Calculate player collision box (new and old)
395         */
396         core::aabbox3d<f32> playerbox(
397                 position.X - player_radius,
398                 position.Y - 0.0,
399                 position.Z - player_radius,
400                 position.X + player_radius,
401                 position.Y + player_height,
402                 position.Z + player_radius
403         );
404         core::aabbox3d<f32> playerbox_old(
405                 oldpos.X - player_radius,
406                 oldpos.Y - 0.0,
407                 oldpos.Z - player_radius,
408                 oldpos.X + player_radius,
409                 oldpos.Y + player_height,
410                 oldpos.Z + player_radius
411         );
412
413         /*
414                 If the player's feet touch the topside of any node, this is
415                 set to true.
416
417                 Player is allowed to jump when this is true.
418         */
419         touching_ground = false;
420         
421         /*std::cout<<"Checking collisions for ("
422                         <<oldpos_i.X<<","<<oldpos_i.Y<<","<<oldpos_i.Z
423                         <<") -> ("
424                         <<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z
425                         <<"):"<<std::endl;*/
426         
427         /*
428                 Go through every node around the player
429         */
430         for(s16 y = oldpos_i.Y - 1; y <= oldpos_i.Y + 2; y++)
431         for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++)
432         for(s16 x = oldpos_i.X - 1; x <= oldpos_i.X + 1; x++)
433         {
434                 try{
435                         // Player collides into walkable nodes
436                         if(content_walkable(map.getNode(v3s16(x,y,z)).d) == false)
437                                 continue;
438                 }
439                 catch(InvalidPositionException &e)
440                 {
441                         // Doing nothing here will block the player from
442                         // walking over map borders
443                 }
444
445                 core::aabbox3d<f32> nodebox = getNodeBox(v3s16(x,y,z), BS);
446                 
447                 /*
448                         See if the player is touching ground.
449
450                         Player touches ground if player's minimum Y is near node's
451                         maximum Y and player's X-Z-area overlaps with the node's
452                         X-Z-area.
453
454                         Use 0.15*BS so that it is easier to get on a node.
455                 */
456                 if(
457                                 //fabs(nodebox.MaxEdge.Y-playerbox.MinEdge.Y) < d
458                                 fabs(nodebox.MaxEdge.Y-playerbox.MinEdge.Y) < 0.15*BS
459                                 && nodebox.MaxEdge.X-d > playerbox.MinEdge.X
460                                 && nodebox.MinEdge.X+d < playerbox.MaxEdge.X
461                                 && nodebox.MaxEdge.Z-d > playerbox.MinEdge.Z
462                                 && nodebox.MinEdge.Z+d < playerbox.MaxEdge.Z
463                 ){
464                         touching_ground = true;
465                 }
466                 
467                 // If player doesn't intersect with node, ignore node.
468                 if(playerbox.intersectsWithBox(nodebox) == false)
469                         continue;
470                 
471                 /*
472                         Go through every axis
473                 */
474                 v3f dirs[3] = {
475                         v3f(0,0,1), // back-front
476                         v3f(0,1,0), // top-bottom
477                         v3f(1,0,0), // right-left
478                 };
479                 for(u16 i=0; i<3; i++)
480                 {
481                         /*
482                                 Calculate values along the axis
483                         */
484                         f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]);
485                         f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]);
486                         f32 playermax = playerbox.MaxEdge.dotProduct(dirs[i]);
487                         f32 playermin = playerbox.MinEdge.dotProduct(dirs[i]);
488                         f32 playermax_old = playerbox_old.MaxEdge.dotProduct(dirs[i]);
489                         f32 playermin_old = playerbox_old.MinEdge.dotProduct(dirs[i]);
490                         
491                         /*
492                                 Check collision for the axis.
493                                 Collision happens when player is going through a surface.
494                         */
495                         /*f32 neg_d = d;
496                         f32 pos_d = d;
497                         // Make it easier to get on top of a node
498                         if(i == 1)
499                                 neg_d = 0.15*BS;
500                         bool negative_axis_collides =
501                                 (nodemax > playermin && nodemax <= playermin_old + neg_d
502                                         && m_speed.dotProduct(dirs[i]) < 0);
503                         bool positive_axis_collides =
504                                 (nodemin < playermax && nodemin >= playermax_old - pos_d
505                                         && m_speed.dotProduct(dirs[i]) > 0);*/
506                         bool negative_axis_collides =
507                                 (nodemax > playermin && nodemax <= playermin_old + d
508                                         && m_speed.dotProduct(dirs[i]) < 0);
509                         bool positive_axis_collides =
510                                 (nodemin < playermax && nodemin >= playermax_old - d
511                                         && m_speed.dotProduct(dirs[i]) > 0);
512                         bool main_axis_collides =
513                                         negative_axis_collides || positive_axis_collides;
514                         
515                         /*
516                                 Check overlap of player and node in other axes
517                         */
518                         bool other_axes_overlap = true;
519                         for(u16 j=0; j<3; j++)
520                         {
521                                 if(j == i)
522                                         continue;
523                                 f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]);
524                                 f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]);
525                                 f32 playermax = playerbox.MaxEdge.dotProduct(dirs[j]);
526                                 f32 playermin = playerbox.MinEdge.dotProduct(dirs[j]);
527                                 if(!(nodemax - d > playermin && nodemin + d < playermax))
528                                 {
529                                         other_axes_overlap = false;
530                                         break;
531                                 }
532                         }
533                         
534                         /*
535                                 If this is a collision, revert the position in the main
536                                 direction.
537                         */
538                         if(other_axes_overlap && main_axis_collides)
539                         {
540                                 v3f old_speed = m_speed;
541
542                                 m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i];
543                                 position -= position.dotProduct(dirs[i]) * dirs[i];
544                                 position += oldpos.dotProduct(dirs[i]) * dirs[i];
545                                 
546                                 if(collision_info)
547                                 {
548                                         // Report fall collision
549                                         if(old_speed.Y < m_speed.Y - 0.1)
550                                         {
551                                                 CollisionInfo info;
552                                                 info.t = COLLISION_FALL;
553                                                 info.speed = m_speed.Y - old_speed.Y;
554                                                 collision_info->push_back(info);
555                                         }
556                                 }
557                         }
558                 
559                 }
560         } // xyz
561
562         /*
563                 Check the nodes under the player to see from which node the
564                 player is sneaking from, if any.
565         */
566         {
567                 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
568                 v2f player_p2df(position.X, position.Z);
569                 f32 min_distance_f = 100000.0*BS;
570                 // If already seeking from some node, compare to it.
571                 /*if(m_sneak_node_exists)
572                 {
573                         v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
574                         v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
575                         f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
576                         f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
577                         // Ignore if player is not on the same level (likely dropped)
578                         if(d_vert_f < 0.15*BS)
579                                 min_distance_f = d_horiz_f;
580                 }*/
581                 v3s16 new_sneak_node = m_sneak_node;
582                 for(s16 x=-1; x<=1; x++)
583                 for(s16 z=-1; z<=1; z++)
584                 {
585                         v3s16 p = pos_i_bottom + v3s16(x,0,z);
586                         v3f pf = intToFloat(p, BS);
587                         v2f node_p2df(pf.X, pf.Z);
588                         f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
589                         f32 max_axis_distance_f = MYMAX(
590                                         fabs(player_p2df.X-node_p2df.X),
591                                         fabs(player_p2df.Y-node_p2df.Y));
592                                         
593                         if(distance_f > min_distance_f ||
594                                         max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
595                                 continue;
596
597                         try{
598                                 // The node to be sneaked on has to be walkable
599                                 if(content_walkable(map.getNode(p).d) == false)
600                                         continue;
601                                 // And the node above it has to be nonwalkable
602                                 if(content_walkable(map.getNode(p+v3s16(0,1,0)).d) == true)
603                                         continue;
604                         }
605                         catch(InvalidPositionException &e)
606                         {
607                                 continue;
608                         }
609
610                         min_distance_f = distance_f;
611                         new_sneak_node = p;
612                 }
613                 
614                 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
615                 
616                 if(control.sneak && m_sneak_node_exists)
617                 {
618                         if(sneak_node_found)
619                                 m_sneak_node = new_sneak_node;
620                 }
621                 else
622                 {
623                         m_sneak_node = new_sneak_node;
624                         m_sneak_node_exists = sneak_node_found;
625                 }
626
627                 /*
628                         If sneaking, the player's collision box can be in air, so
629                         this has to be set explicitly
630                 */
631                 if(sneak_node_found && control.sneak)
632                         touching_ground = true;
633         }
634         
635         /*
636                 Set new position
637         */
638         setPosition(position);
639 }
640
641 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
642 {
643         move(dtime, map, pos_max_d, NULL);
644 }
645
646 void LocalPlayer::applyControl(float dtime)
647 {
648         // Clear stuff
649         swimming_up = false;
650
651         // Random constants
652         f32 walk_acceleration = 4.0 * BS;
653         f32 walkspeed_max = 4.0 * BS;
654         
655         setPitch(control.pitch);
656         setYaw(control.yaw);
657         
658         v3f move_direction = v3f(0,0,1);
659         move_direction.rotateXZBy(getYaw());
660         
661         v3f speed = v3f(0,0,0);
662
663         bool free_move = g_settings.getBool("free_move");
664         bool fast_move = g_settings.getBool("fast_move");
665         bool continuous_forward = g_settings.getBool("continuous_forward");
666
667         if(free_move)
668         {
669                 v3f speed = getSpeed();
670                 speed.Y = 0;
671                 setSpeed(speed);
672         }
673
674         // Whether superspeed mode is used or not
675         bool superspeed = false;
676         
677         // If free movement and fast movement, always move fast
678         if(free_move && fast_move)
679                 superspeed = true;
680         
681         // Auxiliary button 1 (E)
682         if(control.aux1)
683         {
684                 if(free_move)
685                 {
686                         // In free movement mode, aux1 descends
687                         v3f speed = getSpeed();
688                         if(fast_move)
689                                 speed.Y = -20*BS;
690                         else
691                                 speed.Y = -walkspeed_max;
692                         setSpeed(speed);
693                 }
694                 else
695                 {
696                         // If not free movement but fast is allowed, aux1 is
697                         // "Turbo button"
698                         if(fast_move)
699                                 superspeed = true;
700                 }
701         }
702
703         if(continuous_forward)
704                 speed += move_direction;
705
706         if(control.up)
707         {
708                 if(continuous_forward)
709                         superspeed = true;
710                 else
711                         speed += move_direction;
712         }
713         if(control.down)
714         {
715                 speed -= move_direction;
716         }
717         if(control.left)
718         {
719                 speed += move_direction.crossProduct(v3f(0,1,0));
720         }
721         if(control.right)
722         {
723                 speed += move_direction.crossProduct(v3f(0,-1,0));
724         }
725         if(control.jump)
726         {
727                 if(free_move)
728                 {
729                         v3f speed = getSpeed();
730                         if(fast_move)
731                                 speed.Y = 20*BS;
732                         else
733                                 speed.Y = walkspeed_max;
734                         setSpeed(speed);
735                 }
736                 else if(touching_ground)
737                 {
738                         v3f speed = getSpeed();
739                         /*
740                                 NOTE: The d value in move() affects jump height by
741                                 raising the height at which the jump speed is kept
742                                 at its starting value
743                         */
744                         speed.Y = 6.5*BS;
745                         setSpeed(speed);
746                 }
747                 // Use the oscillating value for getting out of water
748                 // (so that the player doesn't fly on the surface)
749                 else if(in_water)
750                 {
751                         v3f speed = getSpeed();
752                         speed.Y = 1.5*BS;
753                         setSpeed(speed);
754                         swimming_up = true;
755                 }
756         }
757
758         // The speed of the player (Y is ignored)
759         if(superspeed)
760                 speed = speed.normalize() * walkspeed_max * 5.0;
761         else if(control.sneak)
762                 speed = speed.normalize() * walkspeed_max / 3.0;
763         else
764                 speed = speed.normalize() * walkspeed_max;
765         
766         f32 inc = walk_acceleration * BS * dtime;
767         
768         // Faster acceleration if fast and free movement
769         if(free_move && fast_move)
770                 inc = walk_acceleration * BS * dtime * 10;
771         
772         // Accelerate to target speed with maximum increment
773         accelerate(speed, inc);
774 }
775 #endif
776