]> git.lizzy.rs Git - minetest.git/blob - src/localplayer.cpp
Fix mapgen using unitialised height map values
[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 "main.h" // For g_settings
23 #include "event.h"
24 #include "collision.h"
25 #include "gamedef.h"
26 #include "nodedef.h"
27 #include "settings.h"
28 #include "environment.h"
29 #include "map.h"
30 #include "util/numeric.h"
31
32 /*
33         LocalPlayer
34 */
35
36 LocalPlayer::LocalPlayer(IGameDef *gamedef, const char *name):
37         Player(gamedef, name),
38         parent(0),
39         isAttached(false),
40         overridePosition(v3f(0,0,0)),
41         last_position(v3f(0,0,0)),
42         last_speed(v3f(0,0,0)),
43         last_pitch(0),
44         last_yaw(0),
45         last_keyPressed(0),
46         eye_offset_first(v3f(0,0,0)),
47         eye_offset_third(v3f(0,0,0)),
48         last_animation(NO_ANIM),
49         hotbar_image(""),
50         hotbar_selected_image(""),
51         light_color(255,255,255,255),
52         m_sneak_node(32767,32767,32767),
53         m_sneak_node_exists(false),
54         m_old_node_below(32767,32767,32767),
55         m_old_node_below_type("air"),
56         m_need_to_get_new_sneak_node(true),
57         m_can_jump(false),
58         m_cao(NULL)
59 {
60         // Initialize hp to 0, so that no hearts will be shown if server
61         // doesn't support health points
62         hp = 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         {
92         position += m_speed * dtime;
93                 setPosition(position);
94                 m_sneak_node_exists = false;
95                 return;
96         }
97
98         /*
99                 Collision detection
100         */
101
102         bool is_valid_position;
103         MapNode node;
104         v3s16 pp;
105
106         /*
107                 Check if player is in liquid (the oscillating value)
108         */
109
110         // If in liquid, the threshold of coming out is at higher y
111         if (in_liquid)
112         {
113                 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
114                 node = map->getNodeNoEx(pp, &is_valid_position);
115                 if (is_valid_position) {
116                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
117                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
118                 } else {
119                         in_liquid = false;
120                 }
121         }
122         // If not in liquid, the threshold of going in is at lower y
123         else
124         {
125                 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
126                 node = map->getNodeNoEx(pp, &is_valid_position);
127                 if (is_valid_position) {
128                         in_liquid = nodemgr->get(node.getContent()).isLiquid();
129                         liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
130                 } else {
131                         in_liquid = false;
132                 }
133         }
134
135
136         /*
137                 Check if player is in liquid (the stable value)
138         */
139         pp = floatToInt(position + v3f(0,0,0), BS);
140         node = map->getNodeNoEx(pp, &is_valid_position);
141         if (is_valid_position) {
142                 in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
143         } else {
144                 in_liquid_stable = false;
145         }
146
147         /*
148                 Check if player is climbing
149         */
150
151
152         pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
153         v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
154         node = map->getNodeNoEx(pp, &is_valid_position);
155         bool is_valid_position2;
156         MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
157
158         if (!(is_valid_position && is_valid_position2)) {
159                 is_climbing = false;
160         } else {
161                 is_climbing = (nodemgr->get(node.getContent()).climbable
162                                 || nodemgr->get(node2.getContent()).climbable) && !free_move;
163         }
164
165
166         /*
167                 Collision uncertainty radius
168                 Make it a bit larger than the maximum distance of movement
169         */
170         //f32 d = pos_max_d * 1.1;
171         // A fairly large value in here makes moving smoother
172         f32 d = 0.15*BS;
173
174         // This should always apply, otherwise there are glitches
175         assert(d > pos_max_d);
176
177         // Maximum distance over border for sneaking
178         f32 sneak_max = BS*0.4;
179
180         /*
181                 If sneaking, keep in range from the last walked node and don't
182                 fall off from it
183         */
184         if(control.sneak && m_sneak_node_exists &&
185                         !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
186                         physics_override_sneak)
187         {
188                 f32 maxd = 0.5*BS + sneak_max;
189                 v3f lwn_f = intToFloat(m_sneak_node, BS);
190                 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
191                 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
192
193                 if(!is_climbing)
194                 {
195                         f32 min_y = lwn_f.Y + 0.5*BS;
196                         if(position.Y < min_y)
197                         {
198                                 position.Y = min_y;
199
200                                 if(m_speed.Y < 0)
201                                         m_speed.Y = 0;
202                         }
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         v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
236         if(m_sneak_node_exists &&
237            nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
238            m_old_node_below_type != "air")
239         {
240                 // Old node appears to have been removed; that is,
241                 // it wasn't air before but now it is
242                 m_need_to_get_new_sneak_node = false;
243                 m_sneak_node_exists = false;
244         }
245         else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
246         {
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         if(m_need_to_get_new_sneak_node && physics_override_sneak)
252         {
253                 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
254                 v2f player_p2df(position.X, position.Z);
255                 f32 min_distance_f = 100000.0*BS;
256                 // If already seeking from some node, compare to it.
257                 /*if(m_sneak_node_exists)
258                 {
259                         v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
260                         v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
261                         f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
262                         f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
263                         // Ignore if player is not on the same level (likely dropped)
264                         if(d_vert_f < 0.15*BS)
265                                 min_distance_f = d_horiz_f;
266                 }*/
267                 v3s16 new_sneak_node = m_sneak_node;
268                 for(s16 x=-1; x<=1; x++)
269                 for(s16 z=-1; z<=1; z++)
270                 {
271                         v3s16 p = pos_i_bottom + v3s16(x,0,z);
272                         v3f pf = intToFloat(p, BS);
273                         v2f node_p2df(pf.X, pf.Z);
274                         f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
275                         f32 max_axis_distance_f = MYMAX(
276                                         fabs(player_p2df.X-node_p2df.X),
277                                         fabs(player_p2df.Y-node_p2df.Y));
278
279                         if(distance_f > min_distance_f ||
280                                         max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
281                                 continue;
282
283
284                         // The node to be sneaked on has to be walkable
285                         node = map->getNodeNoEx(p, &is_valid_position);
286                         if (!is_valid_position || nodemgr->get(node).walkable == false)
287                                 continue;
288                         // And the node above it has to be nonwalkable
289                         node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
290                         if (!is_valid_position || nodemgr->get(node).walkable) {
291                                 continue;
292                         }
293                         if (!physics_override_sneak_glitch) {
294                                 node =map->getNodeNoEx(p + v3s16(0,2,0), &is_valid_position);
295                                 if (!is_valid_position || nodemgr->get(node).walkable)
296                                         continue;
297                         }
298
299                         min_distance_f = distance_f;
300                         new_sneak_node = p;
301                 }
302
303                 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
304
305                 m_sneak_node = new_sneak_node;
306                 m_sneak_node_exists = sneak_node_found;
307
308                 /*
309                         If sneaking, the player's collision box can be in air, so
310                         this has to be set explicitly
311                 */
312                 if(sneak_node_found && control.sneak)
313                         touching_ground = true;
314         }
315
316         /*
317                 Set new position
318         */
319         setPosition(position);
320
321         /*
322                 Report collisions
323         */
324         bool bouncy_jump = false;
325         // Dont report if flying
326         if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
327                 for(size_t i=0; i<result.collisions.size(); i++) {
328                         const CollisionInfo &info = result.collisions[i];
329                         collision_info->push_back(info);
330                         if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
331                                         info.bouncy)
332                                 bouncy_jump = true;
333                 }
334         }
335
336         if(bouncy_jump && control.jump){
337                 m_speed.Y += movement_speed_jump*BS;
338                 touching_ground = false;
339                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
340                 m_gamedef->event()->put(e);
341         }
342
343         if(!touching_ground_was && touching_ground){
344                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
345                 m_gamedef->event()->put(e);
346
347                 // Set camera impact value to be used for view bobbing
348                 camera_impact = getSpeed().Y * -1;
349         }
350
351         {
352                 camera_barely_in_ceiling = false;
353                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
354                 MapNode n = map->getNodeNoEx(camera_np);
355                 if(n.getContent() != CONTENT_IGNORE){
356                         if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
357                                 camera_barely_in_ceiling = true;
358                         }
359                 }
360         }
361
362         /*
363                 Update the node last under the player
364         */
365         m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
366         m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
367
368         /*
369                 Check properties of the node on which the player is standing
370         */
371         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
372         // Determine if jumping is possible
373         m_can_jump = touching_ground && !in_liquid;
374         if(itemgroup_get(f.groups, "disable_jump"))
375                 m_can_jump = false;
376 }
377
378 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
379 {
380         move(dtime, env, pos_max_d, NULL);
381 }
382
383 void LocalPlayer::applyControl(float dtime)
384 {
385         // Clear stuff
386         swimming_vertical = false;
387
388         setPitch(control.pitch);
389         setYaw(control.yaw);
390
391         // Nullify speed and don't run positioning code if the player is attached
392         if(isAttached)
393         {
394                 setSpeed(v3f(0,0,0));
395                 return;
396         }
397
398         v3f move_direction = v3f(0,0,1);
399         move_direction.rotateXZBy(getYaw());
400
401         v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
402         v3f speedV = v3f(0,0,0); // Vertical (Y)
403
404         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
405         bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
406
407         bool free_move = fly_allowed && g_settings->getBool("free_move");
408         bool fast_move = fast_allowed && g_settings->getBool("fast_move");
409         // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
410         bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
411         bool continuous_forward = g_settings->getBool("continuous_forward");
412
413         // Whether superspeed mode is used or not
414         bool superspeed = false;
415
416         if(g_settings->getBool("always_fly_fast") && free_move && fast_move)
417                 superspeed = true;
418
419         // Old descend control
420         if(g_settings->getBool("aux1_descends"))
421         {
422                 // If free movement and fast movement, always move fast
423                 if(free_move && fast_move)
424                         superspeed = true;
425
426                 // Auxiliary button 1 (E)
427                 if(control.aux1)
428                 {
429                         if(free_move)
430                         {
431                                 // In free movement mode, aux1 descends
432                                 if(fast_move)
433                                         speedV.Y = -movement_speed_fast;
434                                 else
435                                         speedV.Y = -movement_speed_walk;
436                         }
437                         else if(in_liquid || in_liquid_stable)
438                         {
439                                 speedV.Y = -movement_speed_walk;
440                                 swimming_vertical = true;
441                         }
442                         else if(is_climbing)
443                         {
444                                 speedV.Y = -movement_speed_climb;
445                         }
446                         else
447                         {
448                                 // If not free movement but fast is allowed, aux1 is
449                                 // "Turbo button"
450                                 if(fast_move)
451                                         superspeed = true;
452                         }
453                 }
454         }
455         // New minecraft-like descend control
456         else
457         {
458                 // Auxiliary button 1 (E)
459                 if(control.aux1)
460                 {
461                         if(!is_climbing)
462                         {
463                                 // aux1 is "Turbo button"
464                                 if(fast_move)
465                                         superspeed = true;
466                         }
467                 }
468
469                 if(control.sneak)
470                 {
471                         if(free_move)
472                         {
473                                 // In free movement mode, sneak descends
474                                 if(fast_move && (control.aux1 || g_settings->getBool("always_fly_fast")))
475                                         speedV.Y = -movement_speed_fast;
476                                 else
477                                         speedV.Y = -movement_speed_walk;
478                         }
479                         else if(in_liquid || in_liquid_stable)
480                         {
481                                 if(fast_climb)
482                                         speedV.Y = -movement_speed_fast;
483                                 else
484                                         speedV.Y = -movement_speed_walk;
485                                 swimming_vertical = true;
486                         }
487                         else if(is_climbing)
488                         {
489                                 if(fast_climb)
490                                         speedV.Y = -movement_speed_fast;
491                                 else
492                                         speedV.Y = -movement_speed_climb;
493                         }
494                 }
495         }
496
497         if(continuous_forward)
498                 speedH += move_direction;
499
500         if(control.up)
501         {
502                 if(continuous_forward)
503                         superspeed = true;
504                 else
505                         speedH += move_direction;
506         }
507         if(control.down)
508         {
509                 speedH -= move_direction;
510         }
511         if(control.left)
512         {
513                 speedH += move_direction.crossProduct(v3f(0,1,0));
514         }
515         if(control.right)
516         {
517                 speedH += move_direction.crossProduct(v3f(0,-1,0));
518         }
519         if(control.jump)
520         {
521                 if(free_move)
522                 {
523                         if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
524                         {
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