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