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