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