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