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