]> git.lizzy.rs Git - minetest.git/blob - src/localplayer.cpp
m_active_object_messages is used like a queue. Use std::queue instead of std::list...
[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         eye_offset_first(v3f(0,0,0)),
46         eye_offset_third(v3f(0,0,0)),
47         last_animation(NO_ANIM),
48         hotbar_image(""),
49         hotbar_selected_image(""),
50         light_color(255,255,255,255),
51         m_sneak_node(32767,32767,32767),
52         m_sneak_node_exists(false),
53         m_old_node_below(32767,32767,32767),
54         m_old_node_below_type("air"),
55         m_need_to_get_new_sneak_node(true),
56         m_can_jump(false),
57         m_cao(NULL)
58 {
59         // Initialize hp to 0, so that no hearts will be shown if server
60         // doesn't support health points
61         hp = 0;
62 }
63
64 LocalPlayer::~LocalPlayer()
65 {
66 }
67
68 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
69                 std::vector<CollisionInfo> *collision_info)
70 {
71         Map *map = &env->getMap();
72         INodeDefManager *nodemgr = m_gamedef->ndef();
73
74         v3f position = getPosition();
75
76         // Copy parent position if local player is attached
77         if(isAttached)
78         {
79                 setPosition(overridePosition);
80                 m_sneak_node_exists = false;
81                 return;
82         }
83
84         // Skip collision detection if noclip mode is used
85         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
86         bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
87                 g_settings->getBool("noclip");
88         bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
89         if(free_move)
90         {
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         {
187                 f32 maxd = 0.5*BS + sneak_max;
188                 v3f lwn_f = intToFloat(m_sneak_node, BS);
189                 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
190                 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
191
192                 if(!is_climbing)
193                 {
194                         f32 min_y = lwn_f.Y + 0.5*BS;
195                         if(position.Y < min_y)
196                         {
197                                 position.Y = min_y;
198
199                                 if(m_speed.Y < 0)
200                                         m_speed.Y = 0;
201                         }
202                 }
203         }
204
205         // this shouldn't be hardcoded but transmitted from server
206         float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
207
208 #ifdef __ANDROID__
209         player_stepheight += (0.5 * BS);
210 #endif
211
212         v3f accel_f = v3f(0,0,0);
213
214         collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
215                         pos_max_d, m_collisionbox, player_stepheight, dtime,
216                         position, m_speed, accel_f);
217
218         /*
219                 If the player's feet touch the topside of any node, this is
220                 set to true.
221
222                 Player is allowed to jump when this is true.
223         */
224         bool touching_ground_was = touching_ground;
225         touching_ground = result.touching_ground;
226
227     //bool standing_on_unloaded = result.standing_on_unloaded;
228
229         /*
230                 Check the nodes under the player to see from which node the
231                 player is sneaking from, if any.  If the node from under
232                 the player has been removed, the player falls.
233         */
234         v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
235         if(m_sneak_node_exists &&
236            nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
237            m_old_node_below_type != "air")
238         {
239                 // Old node appears to have been removed; that is,
240                 // it wasn't air before but now it is
241                 m_need_to_get_new_sneak_node = false;
242                 m_sneak_node_exists = false;
243         }
244         else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
245         {
246                 // We are on something, so make sure to recalculate the sneak
247                 // node.
248                 m_need_to_get_new_sneak_node = true;
249         }
250         if(m_need_to_get_new_sneak_node && physics_override_sneak)
251         {
252                 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
253                 v2f player_p2df(position.X, position.Z);
254                 f32 min_distance_f = 100000.0*BS;
255                 // If already seeking from some node, compare to it.
256                 /*if(m_sneak_node_exists)
257                 {
258                         v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
259                         v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
260                         f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
261                         f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
262                         // Ignore if player is not on the same level (likely dropped)
263                         if(d_vert_f < 0.15*BS)
264                                 min_distance_f = d_horiz_f;
265                 }*/
266                 v3s16 new_sneak_node = m_sneak_node;
267                 for(s16 x=-1; x<=1; x++)
268                 for(s16 z=-1; z<=1; z++)
269                 {
270                         v3s16 p = pos_i_bottom + v3s16(x,0,z);
271                         v3f pf = intToFloat(p, BS);
272                         v2f node_p2df(pf.X, pf.Z);
273                         f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
274                         f32 max_axis_distance_f = MYMAX(
275                                         fabs(player_p2df.X-node_p2df.X),
276                                         fabs(player_p2df.Y-node_p2df.Y));
277
278                         if(distance_f > min_distance_f ||
279                                         max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
280                                 continue;
281
282
283                         // The node to be sneaked on has to be walkable
284                         node = map->getNodeNoEx(p, &is_valid_position);
285                         if (!is_valid_position || nodemgr->get(node).walkable == false)
286                                 continue;
287                         // And the node above it has to be nonwalkable
288                         node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
289                         if (!is_valid_position || nodemgr->get(node).walkable) {
290                                 continue;
291                         }
292                         if (!physics_override_sneak_glitch) {
293                                 node =map->getNodeNoEx(p + v3s16(0,2,0), &is_valid_position);
294                                 if (!is_valid_position || nodemgr->get(node).walkable)
295                                         continue;
296                         }
297
298                         min_distance_f = distance_f;
299                         new_sneak_node = p;
300                 }
301
302                 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
303
304                 m_sneak_node = new_sneak_node;
305                 m_sneak_node_exists = sneak_node_found;
306
307                 /*
308                         If sneaking, the player's collision box can be in air, so
309                         this has to be set explicitly
310                 */
311                 if(sneak_node_found && control.sneak)
312                         touching_ground = true;
313         }
314
315         /*
316                 Set new position
317         */
318         setPosition(position);
319
320         /*
321                 Report collisions
322         */
323         bool bouncy_jump = false;
324         // Dont report if flying
325         if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
326                 for(size_t i=0; i<result.collisions.size(); i++) {
327                         const CollisionInfo &info = result.collisions[i];
328                         collision_info->push_back(info);
329                         if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
330                                         info.bouncy)
331                                 bouncy_jump = true;
332                 }
333         }
334
335         if(bouncy_jump && control.jump){
336                 m_speed.Y += movement_speed_jump*BS;
337                 touching_ground = false;
338                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
339                 m_gamedef->event()->put(e);
340         }
341
342         if(!touching_ground_was && touching_ground){
343                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
344                 m_gamedef->event()->put(e);
345
346                 // Set camera impact value to be used for view bobbing
347                 camera_impact = getSpeed().Y * -1;
348         }
349
350         {
351                 camera_barely_in_ceiling = false;
352                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
353                 MapNode n = map->getNodeNoEx(camera_np);
354                 if(n.getContent() != CONTENT_IGNORE){
355                         if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
356                                 camera_barely_in_ceiling = true;
357                         }
358                 }
359         }
360
361         /*
362                 Update the node last under the player
363         */
364         m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
365         m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
366
367         /*
368                 Check properties of the node on which the player is standing
369         */
370         const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
371         // Determine if jumping is possible
372         m_can_jump = touching_ground && !in_liquid;
373         if(itemgroup_get(f.groups, "disable_jump"))
374                 m_can_jump = false;
375 }
376
377 void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
378 {
379         move(dtime, env, pos_max_d, NULL);
380 }
381
382 void LocalPlayer::applyControl(float dtime)
383 {
384         // Clear stuff
385         swimming_vertical = false;
386
387         setPitch(control.pitch);
388         setYaw(control.yaw);
389
390         // Nullify speed and don't run positioning code if the player is attached
391         if(isAttached)
392         {
393                 setSpeed(v3f(0,0,0));
394                 return;
395         }
396
397         v3f move_direction = v3f(0,0,1);
398         move_direction.rotateXZBy(getYaw());
399
400         v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
401         v3f speedV = v3f(0,0,0); // Vertical (Y)
402
403         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
404         bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
405
406         bool free_move = fly_allowed && g_settings->getBool("free_move");
407         bool fast_move = fast_allowed && g_settings->getBool("fast_move");
408         // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
409         bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
410         bool continuous_forward = g_settings->getBool("continuous_forward");
411
412         // Whether superspeed mode is used or not
413         bool superspeed = false;
414
415         if(g_settings->getBool("always_fly_fast") && free_move && fast_move)
416                 superspeed = true;
417
418         // Old descend control
419         if(g_settings->getBool("aux1_descends"))
420         {
421                 // If free movement and fast movement, always move fast
422                 if(free_move && fast_move)
423                         superspeed = true;
424
425                 // Auxiliary button 1 (E)
426                 if(control.aux1)
427                 {
428                         if(free_move)
429                         {
430                                 // In free movement mode, aux1 descends
431                                 if(fast_move)
432                                         speedV.Y = -movement_speed_fast;
433                                 else
434                                         speedV.Y = -movement_speed_walk;
435                         }
436                         else if(in_liquid || in_liquid_stable)
437                         {
438                                 speedV.Y = -movement_speed_walk;
439                                 swimming_vertical = true;
440                         }
441                         else if(is_climbing)
442                         {
443                                 speedV.Y = -movement_speed_climb;
444                         }
445                         else
446                         {
447                                 // If not free movement but fast is allowed, aux1 is
448                                 // "Turbo button"
449                                 if(fast_move)
450                                         superspeed = true;
451                         }
452                 }
453         }
454         // New minecraft-like descend control
455         else
456         {
457                 // Auxiliary button 1 (E)
458                 if(control.aux1)
459                 {
460                         if(!is_climbing)
461                         {
462                                 // aux1 is "Turbo button"
463                                 if(fast_move)
464                                         superspeed = true;
465                         }
466                 }
467
468                 if(control.sneak)
469                 {
470                         if(free_move)
471                         {
472                                 // In free movement mode, sneak descends
473                                 if(fast_move && (control.aux1 || g_settings->getBool("always_fly_fast")))
474                                         speedV.Y = -movement_speed_fast;
475                                 else
476                                         speedV.Y = -movement_speed_walk;
477                         }
478                         else if(in_liquid || in_liquid_stable)
479                         {
480                                 if(fast_climb)
481                                         speedV.Y = -movement_speed_fast;
482                                 else
483                                         speedV.Y = -movement_speed_walk;
484                                 swimming_vertical = true;
485                         }
486                         else if(is_climbing)
487                         {
488                                 if(fast_climb)
489                                         speedV.Y = -movement_speed_fast;
490                                 else
491                                         speedV.Y = -movement_speed_climb;
492                         }
493                 }
494         }
495
496         if(continuous_forward)
497                 speedH += move_direction;
498
499         if(control.up)
500         {
501                 if(continuous_forward)
502                         superspeed = true;
503                 else
504                         speedH += move_direction;
505         }
506         if(control.down)
507         {
508                 speedH -= move_direction;
509         }
510         if(control.left)
511         {
512                 speedH += move_direction.crossProduct(v3f(0,1,0));
513         }
514         if(control.right)
515         {
516                 speedH += move_direction.crossProduct(v3f(0,-1,0));
517         }
518         if(control.jump)
519         {
520                 if(free_move)
521                 {
522                         if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
523                         {
524                                 if(fast_move)
525                                         speedV.Y = movement_speed_fast;
526                                 else
527                                         speedV.Y = movement_speed_walk;
528                         } else {
529                                 if(fast_move && control.aux1)
530                                         speedV.Y = movement_speed_fast;
531                                 else
532                                         speedV.Y = movement_speed_walk;
533                         }
534                 }
535                 else if(m_can_jump)
536                 {
537                         /*
538                                 NOTE: The d value in move() affects jump height by
539                                 raising the height at which the jump speed is kept
540                                 at its starting value
541                         */
542                         v3f speedJ = getSpeed();
543                         if(speedJ.Y >= -0.5 * BS)
544                         {
545                                 speedJ.Y = movement_speed_jump * physics_override_jump;
546                                 setSpeed(speedJ);
547
548                                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
549                                 m_gamedef->event()->put(e);
550                         }
551                 }
552                 else if(in_liquid)
553                 {
554                         if(fast_climb)
555                                 speedV.Y = movement_speed_fast;
556                         else
557                                 speedV.Y = movement_speed_walk;
558                         swimming_vertical = true;
559                 }
560                 else if(is_climbing)
561                 {
562                         if(fast_climb)
563                                 speedV.Y = movement_speed_fast;
564                         else
565                                 speedV.Y = movement_speed_climb;
566                 }
567         }
568
569         // The speed of the player (Y is ignored)
570         if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
571                 speedH = speedH.normalize() * movement_speed_fast;
572         else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
573                 speedH = speedH.normalize() * movement_speed_crouch;
574         else
575                 speedH = speedH.normalize() * movement_speed_walk;
576
577         // Acceleration increase
578         f32 incH = 0; // Horizontal (X, Z)
579         f32 incV = 0; // Vertical (Y)
580         if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
581         {
582                 // Jumping and falling
583                 if(superspeed || (fast_move && control.aux1))
584                         incH = movement_acceleration_fast * BS * dtime;
585                 else
586                         incH = movement_acceleration_air * BS * dtime;
587                 incV = 0; // No vertical acceleration in air
588         }
589         else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
590                 incH = incV = movement_acceleration_fast * BS * dtime;
591         else
592                 incH = incV = movement_acceleration_default * BS * dtime;
593
594         // Accelerate to target speed with maximum increment
595         accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
596         accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
597 }
598
599 v3s16 LocalPlayer::getStandingNodePos()
600 {
601         if(m_sneak_node_exists)
602                 return m_sneak_node;
603         return floatToInt(getPosition() - v3f(0, BS, 0), BS);
604 }
605