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