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