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