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