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