]> git.lizzy.rs Git - minetest.git/blob - src/localplayer.cpp
Performance Improvement: Use a cache which caches result for getFacePositions.
[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::list<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         {
328                 for(size_t i=0; i<result.collisions.size(); i++){
329                         const CollisionInfo &info = result.collisions[i];
330                         collision_info->push_back(info);
331                         if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
332                                         info.bouncy)
333                                 bouncy_jump = true;
334                 }
335         }
336
337         if(bouncy_jump && control.jump){
338                 m_speed.Y += movement_speed_jump*BS;
339                 touching_ground = false;
340                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
341                 m_gamedef->event()->put(e);
342         }
343
344         if(!touching_ground_was && touching_ground){
345                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
346                 m_gamedef->event()->put(e);
347
348                 // Set camera impact value to be used for view bobbing
349                 camera_impact = getSpeed().Y * -1;
350         }
351
352         {
353                 camera_barely_in_ceiling = false;
354                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
355                 MapNode n = map->getNodeNoEx(camera_np);
356                 if(n.getContent() != CONTENT_IGNORE){
357                         if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
358                                 camera_barely_in_ceiling = true;
359                         }
360                 }
361         }
362
363         /*
364                 Update the node last under the player
365         */
366         m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
367         m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
368
369         /*
370                 Check properties of the node on which the player is standing
371         */
372         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
373         // Determine if jumping is possible
374         m_can_jump = touching_ground && !in_liquid;
375         if(itemgroup_get(f.groups, "disable_jump"))
376                 m_can_jump = false;
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
414         // Whether superspeed mode is used or not
415         bool superspeed = false;
416
417         if(g_settings->getBool("always_fly_fast") && free_move && fast_move)
418                 superspeed = true;
419
420         // Old descend control
421         if(g_settings->getBool("aux1_descends"))
422         {
423                 // If free movement and fast movement, always move fast
424                 if(free_move && fast_move)
425                         superspeed = true;
426
427                 // Auxiliary button 1 (E)
428                 if(control.aux1)
429                 {
430                         if(free_move)
431                         {
432                                 // In free movement mode, aux1 descends
433                                 if(fast_move)
434                                         speedV.Y = -movement_speed_fast;
435                                 else
436                                         speedV.Y = -movement_speed_walk;
437                         }
438                         else if(in_liquid || in_liquid_stable)
439                         {
440                                 speedV.Y = -movement_speed_walk;
441                                 swimming_vertical = true;
442                         }
443                         else if(is_climbing)
444                         {
445                                 speedV.Y = -movement_speed_climb;
446                         }
447                         else
448                         {
449                                 // If not free movement but fast is allowed, aux1 is
450                                 // "Turbo button"
451                                 if(fast_move)
452                                         superspeed = true;
453                         }
454                 }
455         }
456         // New minecraft-like descend control
457         else
458         {
459                 // Auxiliary button 1 (E)
460                 if(control.aux1)
461                 {
462                         if(!is_climbing)
463                         {
464                                 // aux1 is "Turbo button"
465                                 if(fast_move)
466                                         superspeed = true;
467                         }
468                 }
469
470                 if(control.sneak)
471                 {
472                         if(free_move)
473                         {
474                                 // In free movement mode, sneak descends
475                                 if(fast_move && (control.aux1 || g_settings->getBool("always_fly_fast")))
476                                         speedV.Y = -movement_speed_fast;
477                                 else
478                                         speedV.Y = -movement_speed_walk;
479                         }
480                         else if(in_liquid || in_liquid_stable)
481                         {
482                                 if(fast_climb)
483                                         speedV.Y = -movement_speed_fast;
484                                 else
485                                         speedV.Y = -movement_speed_walk;
486                                 swimming_vertical = true;
487                         }
488                         else if(is_climbing)
489                         {
490                                 if(fast_climb)
491                                         speedV.Y = -movement_speed_fast;
492                                 else
493                                         speedV.Y = -movement_speed_climb;
494                         }
495                 }
496         }
497
498         if(continuous_forward)
499                 speedH += move_direction;
500
501         if(control.up)
502         {
503                 if(continuous_forward)
504                         superspeed = true;
505                 else
506                         speedH += move_direction;
507         }
508         if(control.down)
509         {
510                 speedH -= move_direction;
511         }
512         if(control.left)
513         {
514                 speedH += move_direction.crossProduct(v3f(0,1,0));
515         }
516         if(control.right)
517         {
518                 speedH += move_direction.crossProduct(v3f(0,-1,0));
519         }
520         if(control.jump)
521         {
522                 if(free_move)
523                 {
524                         if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
525                         {
526                                 if(fast_move)
527                                         speedV.Y = movement_speed_fast;
528                                 else
529                                         speedV.Y = movement_speed_walk;
530                         } else {
531                                 if(fast_move && control.aux1)
532                                         speedV.Y = movement_speed_fast;
533                                 else
534                                         speedV.Y = movement_speed_walk;
535                         }
536                 }
537                 else if(m_can_jump)
538                 {
539                         /*
540                                 NOTE: The d value in move() affects jump height by
541                                 raising the height at which the jump speed is kept
542                                 at its starting value
543                         */
544                         v3f speedJ = getSpeed();
545                         if(speedJ.Y >= -0.5 * BS)
546                         {
547                                 speedJ.Y = movement_speed_jump * physics_override_jump;
548                                 setSpeed(speedJ);
549
550                                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
551                                 m_gamedef->event()->put(e);
552                         }
553                 }
554                 else if(in_liquid)
555                 {
556                         if(fast_climb)
557                                 speedV.Y = movement_speed_fast;
558                         else
559                                 speedV.Y = movement_speed_walk;
560                         swimming_vertical = true;
561                 }
562                 else if(is_climbing)
563                 {
564                         if(fast_climb)
565                                 speedV.Y = movement_speed_fast;
566                         else
567                                 speedV.Y = movement_speed_climb;
568                 }
569         }
570
571         // The speed of the player (Y is ignored)
572         if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
573                 speedH = speedH.normalize() * movement_speed_fast;
574         else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
575                 speedH = speedH.normalize() * movement_speed_crouch;
576         else
577                 speedH = speedH.normalize() * movement_speed_walk;
578
579         // Acceleration increase
580         f32 incH = 0; // Horizontal (X, Z)
581         f32 incV = 0; // Vertical (Y)
582         if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
583         {
584                 // Jumping and falling
585                 if(superspeed || (fast_move && control.aux1))
586                         incH = movement_acceleration_fast * BS * dtime;
587                 else
588                         incH = movement_acceleration_air * BS * dtime;
589                 incV = 0; // No vertical acceleration in air
590         }
591         else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
592                 incH = incV = movement_acceleration_fast * BS * dtime;
593         else
594                 incH = incV = movement_acceleration_default * BS * dtime;
595
596         // Accelerate to target speed with maximum increment
597         accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
598         accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
599 }
600
601 v3s16 LocalPlayer::getStandingNodePos()
602 {
603         if(m_sneak_node_exists)
604                 return m_sneak_node;
605         return floatToInt(getPosition() - v3f(0, BS, 0), BS);
606 }
607