1a59504ef85dc096e0a276f53e02d9cf4858e6f6
[oweals/minetest.git] / src / localplayer.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser 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 "localplayer.h"
21
22 #include "event.h"
23 #include "collision.h"
24 #include "gamedef.h"
25 #include "nodedef.h"
26 #include "settings.h"
27 #include "environment.h"
28 #include "map.h"
29 #include "util/numeric.h"
30
31 /*
32         LocalPlayer
33 */
34
35 LocalPlayer::LocalPlayer(IGameDef *gamedef, const char *name):
36         Player(gamedef, name),
37         parent(0),
38         isAttached(false),
39         overridePosition(v3f(0,0,0)),
40         last_position(v3f(0,0,0)),
41         last_speed(v3f(0,0,0)),
42         last_pitch(0),
43         last_yaw(0),
44         last_keyPressed(0),
45         last_animation(NO_ANIM),
46         hotbar_image(""),
47         hotbar_selected_image(""),
48         light_color(255,255,255,255),
49         m_sneak_node(32767,32767,32767),
50         m_sneak_node_exists(false),
51         m_old_node_below(32767,32767,32767),
52         m_old_node_below_type("air"),
53         m_need_to_get_new_sneak_node(true),
54         m_can_jump(false),
55         m_cao(NULL)
56 {
57         // Initialize hp to 0, so that no hearts will be shown if server
58         // doesn't support health points
59         hp = 0;
60         eye_offset_first = v3f(0,0,0);
61         eye_offset_third = v3f(0,0,0);
62 }
63
64 LocalPlayer::~LocalPlayer()
65 {
66 }
67
68 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
69                 std::vector<CollisionInfo> *collision_info)
70 {
71         Map *map = &env->getMap();
72         INodeDefManager *nodemgr = m_gamedef->ndef();
73
74         v3f position = getPosition();
75
76         // Copy parent position if local player is attached
77         if(isAttached)
78         {
79                 setPosition(overridePosition);
80                 m_sneak_node_exists = false;
81                 return;
82         }
83
84         // Skip collision detection if noclip mode is used
85         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
86         bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
87                 g_settings->getBool("noclip");
88         bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
89         if (free_move) {
90                 position += m_speed * dtime;
91                 setPosition(position);
92                 m_sneak_node_exists = false;
93                 return;
94         }
95
96         /*
97                 Collision detection
98         */
99
100         bool is_valid_position;
101         MapNode node;
102         v3s16 pp;
103
104         /*
105                 Check if player is in liquid (the oscillating value)
106         */
107
108         // If in liquid, the threshold of coming out is at higher y
109         if (in_liquid)
110         {
111                 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
112                 node = map->getNodeNoEx(pp, &is_valid_position);
113                 if (is_valid_position) {
114                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
115                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
116                 } else {
117                         in_liquid = false;
118                 }
119         }
120         // If not in liquid, the threshold of going in is at lower y
121         else
122         {
123                 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
124                 node = map->getNodeNoEx(pp, &is_valid_position);
125                 if (is_valid_position) {
126                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
127                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
128                 } else {
129                         in_liquid = false;
130                 }
131         }
132
133
134         /*
135                 Check if player is in liquid (the stable value)
136         */
137         pp = floatToInt(position + v3f(0,0,0), BS);
138         node = map->getNodeNoEx(pp, &is_valid_position);
139         if (is_valid_position) {
140                 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
141         } else {
142                 in_liquid_stable = false;
143         }
144
145         /*
146                 Check if player is climbing
147         */
148
149
150         pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
151         v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
152         node = map->getNodeNoEx(pp, &is_valid_position);
153         bool is_valid_position2;
154         MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
155
156         if (!(is_valid_position && is_valid_position2)) {
157                 is_climbing = false;
158         } else {
159                 is_climbing = (nodemgr->get(node.getContent()).climbable
160                                 || nodemgr->get(node2.getContent()).climbable) && !free_move;
161         }
162
163
164         /*
165                 Collision uncertainty radius
166                 Make it a bit larger than the maximum distance of movement
167         */
168         //f32 d = pos_max_d * 1.1;
169         // A fairly large value in here makes moving smoother
170         f32 d = 0.15*BS;
171
172         // This should always apply, otherwise there are glitches
173         sanity_check(d > pos_max_d);
174
175         // Maximum distance over border for sneaking
176         f32 sneak_max = BS*0.4;
177
178         /*
179                 If sneaking, keep in range from the last walked node and don't
180                 fall off from it
181         */
182         if(control.sneak && m_sneak_node_exists &&
183                         !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
184                         physics_override_sneak)
185         {
186                 f32 maxd = 0.5*BS + sneak_max;
187                 v3f lwn_f = intToFloat(m_sneak_node, BS);
188                 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
189                 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
190
191                 if(!is_climbing)
192                 {
193                         f32 min_y = lwn_f.Y + 0.5*BS;
194                         if(position.Y < min_y)
195                         {
196                                 position.Y = min_y;
197
198                                 if(m_speed.Y < 0)
199                                         m_speed.Y = 0;
200                         }
201                 }
202         }
203
204         // this shouldn't be hardcoded but transmitted from server
205         float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
206
207 #ifdef __ANDROID__
208         player_stepheight += (0.5 * BS);
209 #endif
210
211         v3f accel_f = v3f(0,0,0);
212
213         collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
214                         pos_max_d, m_collisionbox, player_stepheight, dtime,
215                         position, m_speed, accel_f);
216
217         /*
218                 If the player's feet touch the topside of any node, this is
219                 set to true.
220
221                 Player is allowed to jump when this is true.
222         */
223         bool touching_ground_was = touching_ground;
224         touching_ground = result.touching_ground;
225
226     //bool standing_on_unloaded = result.standing_on_unloaded;
227
228         /*
229                 Check the nodes under the player to see from which node the
230                 player is sneaking from, if any.  If the node from under
231                 the player has been removed, the player falls.
232         */
233         v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
234         if(m_sneak_node_exists &&
235            nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
236            m_old_node_below_type != "air")
237         {
238                 // Old node appears to have been removed; that is,
239                 // it wasn't air before but now it is
240                 m_need_to_get_new_sneak_node = false;
241                 m_sneak_node_exists = false;
242         }
243         else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
244         {
245                 // We are on something, so make sure to recalculate the sneak
246                 // node.
247                 m_need_to_get_new_sneak_node = true;
248         }
249         if(m_need_to_get_new_sneak_node && physics_override_sneak)
250         {
251                 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
252                 v2f player_p2df(position.X, position.Z);
253                 f32 min_distance_f = 100000.0*BS;
254                 // If already seeking from some node, compare to it.
255                 /*if(m_sneak_node_exists)
256                 {
257                         v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
258                         v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
259                         f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
260                         f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
261                         // Ignore if player is not on the same level (likely dropped)
262                         if(d_vert_f < 0.15*BS)
263                                 min_distance_f = d_horiz_f;
264                 }*/
265                 v3s16 new_sneak_node = m_sneak_node;
266                 for(s16 x=-1; x<=1; x++)
267                 for(s16 z=-1; z<=1; z++)
268                 {
269                         v3s16 p = pos_i_bottom + v3s16(x,0,z);
270                         v3f pf = intToFloat(p, BS);
271                         v2f node_p2df(pf.X, pf.Z);
272                         f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
273                         f32 max_axis_distance_f = MYMAX(
274                                         fabs(player_p2df.X-node_p2df.X),
275                                         fabs(player_p2df.Y-node_p2df.Y));
276
277                         if(distance_f > min_distance_f ||
278                                         max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
279                                 continue;
280
281
282                         // The node to be sneaked on has to be walkable
283                         node = map->getNodeNoEx(p, &is_valid_position);
284                         if (!is_valid_position || nodemgr->get(node).walkable == false)
285                                 continue;
286                         // And the node above it has to be nonwalkable
287                         node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
288                         if (!is_valid_position || nodemgr->get(node).walkable) {
289                                 continue;
290                         }
291                         if (!physics_override_sneak_glitch) {
292                                 node =map->getNodeNoEx(p + v3s16(0,2,0), &is_valid_position);
293                                 if (!is_valid_position || nodemgr->get(node).walkable)
294                                         continue;
295                         }
296
297                         min_distance_f = distance_f;
298                         new_sneak_node = p;
299                 }
300
301                 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
302
303                 m_sneak_node = new_sneak_node;
304                 m_sneak_node_exists = sneak_node_found;
305
306                 /*
307                         If sneaking, the player's collision box can be in air, so
308                         this has to be set explicitly
309                 */
310                 if(sneak_node_found && control.sneak)
311                         touching_ground = true;
312         }
313
314         /*
315                 Set new position
316         */
317         setPosition(position);
318
319         /*
320                 Report collisions
321         */
322
323         // Dont report if flying
324         if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
325                 for(size_t i=0; i<result.collisions.size(); i++) {
326                         const CollisionInfo &info = result.collisions[i];
327                         collision_info->push_back(info);
328                 }
329         }
330
331         if(!touching_ground_was && touching_ground){
332                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
333                 m_gamedef->event()->put(e);
334
335                 // Set camera impact value to be used for view bobbing
336                 camera_impact = getSpeed().Y * -1;
337         }
338
339         {
340                 camera_barely_in_ceiling = false;
341                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
342                 MapNode n = map->getNodeNoEx(camera_np);
343                 if(n.getContent() != CONTENT_IGNORE){
344                         if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
345                                 camera_barely_in_ceiling = true;
346                         }
347                 }
348         }
349
350         /*
351                 Update the node last under the player
352         */
353         m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
354         m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
355
356         /*
357                 Check properties of the node on which the player is standing
358         */
359         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
360         // Determine if jumping is possible
361         m_can_jump = touching_ground && !in_liquid;
362         if(itemgroup_get(f.groups, "disable_jump"))
363                 m_can_jump = false;
364         // Jump key pressed while jumping off from a bouncy block
365         if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
366                 m_speed.Y >= -0.5 * BS) {
367                 float jumpspeed = movement_speed_jump * physics_override_jump;
368                 if (m_speed.Y > 1) {
369                         // Reduce boost when speed already is high
370                         m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
371                 } else {
372                         m_speed.Y += jumpspeed;
373                 }
374                 setSpeed(m_speed);
375                 m_can_jump = false;
376         }
377 }
378
379 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
380 {
381         move(dtime, env, pos_max_d, NULL);
382 }
383
384 void LocalPlayer::applyControl(float dtime)
385 {
386         // Clear stuff
387         swimming_vertical = false;
388
389         setPitch(control.pitch);
390         setYaw(control.yaw);
391
392         // Nullify speed and don't run positioning code if the player is attached
393         if(isAttached)
394         {
395                 setSpeed(v3f(0,0,0));
396                 return;
397         }
398
399         v3f move_direction = v3f(0,0,1);
400         move_direction.rotateXZBy(getYaw());
401
402         v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
403         v3f speedV = v3f(0,0,0); // Vertical (Y)
404
405         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
406         bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
407
408         bool free_move = fly_allowed && g_settings->getBool("free_move");
409         bool fast_move = fast_allowed && g_settings->getBool("fast_move");
410         // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
411         bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
412         bool continuous_forward = g_settings->getBool("continuous_forward");
413         bool always_fly_fast = g_settings->getBool("always_fly_fast");
414
415         // Whether superspeed mode is used or not
416         bool superspeed = false;
417
418         if (always_fly_fast && free_move && fast_move)
419                 superspeed = true;
420
421         // Old descend control
422         if(g_settings->getBool("aux1_descends"))
423         {
424                 // If free movement and fast movement, always move fast
425                 if(free_move && fast_move)
426                         superspeed = true;
427
428                 // Auxiliary button 1 (E)
429                 if(control.aux1)
430                 {
431                         if(free_move)
432                         {
433                                 // In free movement mode, aux1 descends
434                                 if(fast_move)
435                                         speedV.Y = -movement_speed_fast;
436                                 else
437                                         speedV.Y = -movement_speed_walk;
438                         }
439                         else if(in_liquid || in_liquid_stable)
440                         {
441                                 speedV.Y = -movement_speed_walk;
442                                 swimming_vertical = true;
443                         }
444                         else if(is_climbing)
445                         {
446                                 speedV.Y = -movement_speed_climb;
447                         }
448                         else
449                         {
450                                 // If not free movement but fast is allowed, aux1 is
451                                 // "Turbo button"
452                                 if(fast_move)
453                                         superspeed = true;
454                         }
455                 }
456         }
457         // New minecraft-like descend control
458         else
459         {
460                 // Auxiliary button 1 (E)
461                 if(control.aux1)
462                 {
463                         if(!is_climbing)
464                         {
465                                 // aux1 is "Turbo button"
466                                 if(fast_move)
467                                         superspeed = true;
468                         }
469                 }
470
471                 if(control.sneak)
472                 {
473                         if(free_move)
474                         {
475                                 // In free movement mode, sneak descends
476                                 if (fast_move && (control.aux1 || always_fly_fast))
477                                         speedV.Y = -movement_speed_fast;
478                                 else
479                                         speedV.Y = -movement_speed_walk;
480                         }
481                         else if(in_liquid || in_liquid_stable)
482                         {
483                                 if(fast_climb)
484                                         speedV.Y = -movement_speed_fast;
485                                 else
486                                         speedV.Y = -movement_speed_walk;
487                                 swimming_vertical = true;
488                         }
489                         else if(is_climbing)
490                         {
491                                 if(fast_climb)
492                                         speedV.Y = -movement_speed_fast;
493                                 else
494                                         speedV.Y = -movement_speed_climb;
495                         }
496                 }
497         }
498
499         if(continuous_forward)
500                 speedH += move_direction;
501
502         if(control.up)
503         {
504                 if(continuous_forward)
505                         superspeed = true;
506                 else
507                         speedH += move_direction;
508         }
509         if(control.down)
510         {
511                 speedH -= move_direction;
512         }
513         if(control.left)
514         {
515                 speedH += move_direction.crossProduct(v3f(0,1,0));
516         }
517         if(control.right)
518         {
519                 speedH += move_direction.crossProduct(v3f(0,-1,0));
520         }
521         if(control.jump)
522         {
523                 if (free_move) {
524                         if (g_settings->getBool("aux1_descends") || always_fly_fast) {
525                                 if (fast_move)
526                                         speedV.Y = movement_speed_fast;
527                                 else
528                                         speedV.Y = movement_speed_walk;
529                         } else {
530                                 if(fast_move && control.aux1)
531                                         speedV.Y = movement_speed_fast;
532                                 else
533                                         speedV.Y = movement_speed_walk;
534                         }
535                 }
536                 else if(m_can_jump)
537                 {
538                         /*
539                                 NOTE: The d value in move() affects jump height by
540                                 raising the height at which the jump speed is kept
541                                 at its starting value
542                         */
543                         v3f speedJ = getSpeed();
544                         if(speedJ.Y >= -0.5 * BS)
545                         {
546                                 speedJ.Y = movement_speed_jump * physics_override_jump;
547                                 setSpeed(speedJ);
548
549                                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
550                                 m_gamedef->event()->put(e);
551                         }
552                 }
553                 else if(in_liquid)
554                 {
555                         if(fast_climb)
556                                 speedV.Y = movement_speed_fast;
557                         else
558                                 speedV.Y = movement_speed_walk;
559                         swimming_vertical = true;
560                 }
561                 else if(is_climbing)
562                 {
563                         if(fast_climb)
564                                 speedV.Y = movement_speed_fast;
565                         else
566                                 speedV.Y = movement_speed_climb;
567                 }
568         }
569
570         // The speed of the player (Y is ignored)
571         if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
572                 speedH = speedH.normalize() * movement_speed_fast;
573         else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
574                 speedH = speedH.normalize() * movement_speed_crouch;
575         else
576                 speedH = speedH.normalize() * movement_speed_walk;
577
578         // Acceleration increase
579         f32 incH = 0; // Horizontal (X, Z)
580         f32 incV = 0; // Vertical (Y)
581         if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
582         {
583                 // Jumping and falling
584                 if(superspeed || (fast_move && control.aux1))
585                         incH = movement_acceleration_fast * BS * dtime;
586                 else
587                         incH = movement_acceleration_air * BS * dtime;
588                 incV = 0; // No vertical acceleration in air
589         }
590         else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
591                 incH = incV = movement_acceleration_fast * BS * dtime;
592         else
593                 incH = incV = movement_acceleration_default * BS * dtime;
594
595         // Accelerate to target speed with maximum increment
596         accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
597         accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
598 }
599
600 v3s16 LocalPlayer::getStandingNodePos()
601 {
602         if(m_sneak_node_exists)
603                 return m_sneak_node;
604         return floatToInt(getPosition() - v3f(0, BS, 0), BS);
605 }
606