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