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