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