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