]> git.lizzy.rs Git - minetest.git/blob - src/localplayer.cpp
Make m_media_fetch_threads to contain MediaFetchThread* instead of MediaFetchThread
[minetest.git] / src / localplayer.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2012 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 "map.h"
29 #include "util/numeric.h"
30
31 /*
32         LocalPlayer
33 */
34
35 LocalPlayer::LocalPlayer(IGameDef *gamedef):
36         Player(gamedef),
37         isAttached(false),
38         overridePosition(v3f(0,0,0)),
39         last_position(v3f(0,0,0)),
40         last_speed(v3f(0,0,0)),
41         last_pitch(0),
42         last_yaw(0),
43         last_keyPressed(0),
44         m_sneak_node(32767,32767,32767),
45         m_sneak_node_exists(false),
46         m_old_node_below(32767,32767,32767),
47         m_old_node_below_type("air"),
48         m_need_to_get_new_sneak_node(true),
49         m_can_jump(false)
50 {
51         // Initialize hp to 0, so that no hearts will be shown if server
52         // doesn't support health points
53         hp = 0;
54 }
55
56 LocalPlayer::~LocalPlayer()
57 {
58 }
59
60 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
61                 core::list<CollisionInfo> *collision_info)
62 {
63         INodeDefManager *nodemgr = m_gamedef->ndef();
64
65         v3f position = getPosition();
66
67         v3f old_speed = m_speed;
68
69         // Copy parent position if local player is attached
70         if(isAttached)
71         {
72                 setPosition(overridePosition);
73                 return;
74         }
75
76         // Skip collision detection if noclip mode is used
77         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
78         bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
79                 g_settings->getBool("noclip");
80         bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
81         if(free_move)
82         {
83         position += m_speed * dtime;
84                 setPosition(position);
85                 return;
86         }
87
88         /*
89                 Collision detection
90         */
91         
92         /*
93                 Check if player is in water (the oscillating value)
94         */
95         try{
96                 // If in water, the threshold of coming out is at higher y
97                 if(in_water)
98                 {
99                         v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
100                         in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
101                 }
102                 // If not in water, the threshold of going in is at lower y
103                 else
104                 {
105                         v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
106                         in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
107                 }
108         }
109         catch(InvalidPositionException &e)
110         {
111                 in_water = false;
112         }
113
114         /*
115                 Check if player is in water (the stable value)
116         */
117         try{
118                 v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
119                 in_water_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
120         }
121         catch(InvalidPositionException &e)
122         {
123                 in_water_stable = false;
124         }
125
126         /*
127                 Check if player is climbing
128         */
129
130         try {
131                 v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
132                 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
133                 is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable ||
134                 nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move);
135         }
136         catch(InvalidPositionException &e)
137         {
138                 is_climbing = false;
139         }
140
141         /*
142                 Collision uncertainty radius
143                 Make it a bit larger than the maximum distance of movement
144         */
145         //f32 d = pos_max_d * 1.1;
146         // A fairly large value in here makes moving smoother
147         f32 d = 0.15*BS;
148
149         // This should always apply, otherwise there are glitches
150         assert(d > pos_max_d);
151
152         float player_radius = BS*0.30;
153         float player_height = BS*1.55;
154         
155         // Maximum distance over border for sneaking
156         f32 sneak_max = BS*0.4;
157
158         /*
159                 If sneaking, keep in range from the last walked node and don't
160                 fall off from it
161         */
162         if(control.sneak && m_sneak_node_exists && !g_settings->getBool("free_move"))
163         {
164                 f32 maxd = 0.5*BS + sneak_max;
165                 v3f lwn_f = intToFloat(m_sneak_node, BS);
166                 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
167                 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
168                 
169                 if(!is_climbing)
170                 {
171                         f32 min_y = lwn_f.Y + 0.5*BS;
172                         if(position.Y < min_y)
173                         {
174                                 position.Y = min_y;
175
176                                 if(m_speed.Y < 0)
177                                         m_speed.Y = 0;
178                         }
179                 }
180         }
181
182         /*
183                 Calculate player collision box (new and old)
184         */
185         core::aabbox3d<f32> playerbox(
186                 -player_radius,
187                 0.0,
188                 -player_radius,
189                 player_radius,
190                 player_height,
191                 player_radius
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(&map, m_gamedef,
199                         pos_max_d, playerbox, 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)
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                         }
274                         catch(InvalidPositionException &e)
275                         {
276                                 continue;
277                         }
278
279                         min_distance_f = distance_f;
280                         new_sneak_node = p;
281                 }
282                 
283                 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
284
285                 m_sneak_node = new_sneak_node;
286                 m_sneak_node_exists = sneak_node_found;
287
288                 /*
289                         If sneaking, the player's collision box can be in air, so
290                         this has to be set explicitly
291                 */
292                 if(sneak_node_found && control.sneak)
293                         touching_ground = true;
294         }
295         
296         /*
297                 Set new position
298         */
299         setPosition(position);
300         
301         /*
302                 Report collisions
303         */
304         bool bouncy_jump = false;
305         // Dont report if flying
306         if(collision_info && !g_settings->getBool("free_move"))
307         {
308                 for(size_t i=0; i<result.collisions.size(); i++){
309                         const CollisionInfo &info = result.collisions[i];
310                         collision_info->push_back(info);
311                         if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
312                                         info.bouncy)
313                                 bouncy_jump = true;
314                 }
315         }
316
317         if(bouncy_jump && control.jump){
318                 m_speed.Y += 6.5*BS;
319                 touching_ground = false;
320                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
321                 m_gamedef->event()->put(e);
322         }
323
324         if(!touching_ground_was && touching_ground){
325                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
326                 m_gamedef->event()->put(e);
327         }
328
329         {
330                 camera_barely_in_ceiling = false;
331                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
332                 MapNode n = map.getNodeNoEx(camera_np);
333                 if(n.getContent() != CONTENT_IGNORE){
334                         if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
335                                 camera_barely_in_ceiling = true;
336                         }
337                 }
338         }
339
340         /*
341                 Update the node last under the player
342         */
343         m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
344         m_old_node_below_type = nodemgr->get(map.getNodeNoEx(m_old_node_below)).name;
345         
346         /*
347                 Check properties of the node on which the player is standing
348         */
349         const ContentFeatures &f = nodemgr->get(map.getNodeNoEx(getStandingNodePos()));
350         // Determine if jumping is possible
351         m_can_jump = touching_ground;
352         if(itemgroup_get(f.groups, "disable_jump"))
353                 m_can_jump = false;
354 }
355
356 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
357 {
358         move(dtime, map, pos_max_d, NULL);
359 }
360
361 void LocalPlayer::applyControl(float dtime)
362 {
363         // Clear stuff
364         swimming_up = false;
365
366         // Random constants
367         f32 walk_acceleration = 4.0 * BS;
368         f32 walkspeed_max = 4.0 * BS;
369         
370         setPitch(control.pitch);
371         setYaw(control.yaw);
372
373         // Nullify speed and don't run positioning code if the player is attached
374         if(isAttached)
375         {
376                 setSpeed(v3f(0,0,0));
377                 return;
378         }
379
380         v3f move_direction = v3f(0,0,1);
381         move_direction.rotateXZBy(getYaw());
382         
383         v3f speed = v3f(0,0,0);
384         
385         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
386         bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
387
388         bool free_move = fly_allowed && g_settings->getBool("free_move");
389         bool fast_move = fast_allowed && g_settings->getBool("fast_move");
390         bool continuous_forward = g_settings->getBool("continuous_forward");
391
392         if(free_move || is_climbing)
393         {
394                 v3f speed = getSpeed();
395                 speed.Y = 0;
396                 setSpeed(speed);
397         }
398
399         // Whether superspeed mode is used or not
400         bool superspeed = false;
401         
402         // If free movement and fast movement, always move fast
403         if(free_move && fast_move)
404                 superspeed = true;
405         
406         // Old descend control
407         if(g_settings->getBool("aux1_descends"))
408         {
409                 // Auxiliary button 1 (E)
410                 if(control.aux1)
411                 {
412                         if(free_move)
413                         {
414                                 // In free movement mode, aux1 descends
415                                 v3f speed = getSpeed();
416                                 if(fast_move)
417                                         speed.Y = -20*BS;
418                                 else
419                                         speed.Y = -walkspeed_max;
420                                 setSpeed(speed);
421                         }
422                         else if(is_climbing)
423                         {
424                                         v3f speed = getSpeed();
425                                 speed.Y = -3*BS;
426                                 setSpeed(speed);
427                         }
428                         else
429                         {
430                                 // If not free movement but fast is allowed, aux1 is
431                                 // "Turbo button"
432                                 if(fast_move)
433                                         superspeed = true;
434                         }
435                 }
436         }
437         // New minecraft-like descend control
438         else
439         {
440                 // Auxiliary button 1 (E)
441                 if(control.aux1)
442                 {
443                         if(!free_move && !is_climbing)
444                         {
445                                 // If not free movement but fast is allowed, aux1 is
446                                 // "Turbo button"
447                                 if(fast_move)
448                                         superspeed = true;
449                         }
450                 }
451
452                 if(control.sneak)
453                 {
454                         if(free_move)
455                         {
456                                 // In free movement mode, sneak descends
457                                 v3f speed = getSpeed();
458                                 if(fast_move)
459                                         speed.Y = -20*BS;
460                                 else
461                                         speed.Y = -walkspeed_max;
462                                         setSpeed(speed);
463                         }
464                         else if(is_climbing)
465                         {
466                                 v3f speed = getSpeed();
467                                 speed.Y = -3*BS;
468                                 setSpeed(speed);
469                         }
470                 }
471         }
472
473         if(continuous_forward)
474                 speed += move_direction;
475
476         if(control.up)
477         {
478                 if(continuous_forward)
479                         superspeed = true;
480                 else
481                         speed += move_direction;
482         }
483         if(control.down)
484         {
485                 speed -= move_direction;
486         }
487         if(control.left)
488         {
489                 speed += move_direction.crossProduct(v3f(0,1,0));
490         }
491         if(control.right)
492         {
493                 speed += move_direction.crossProduct(v3f(0,-1,0));
494         }
495         if(control.jump)
496         {
497                 if(free_move)
498                 {
499                         v3f speed = getSpeed();
500                         if(fast_move)
501                                 speed.Y = 20*BS;
502                         else
503                                 speed.Y = walkspeed_max;
504                         setSpeed(speed);
505                 }
506                 else if(m_can_jump)
507                 {
508                         /*
509                                 NOTE: The d value in move() affects jump height by
510                                 raising the height at which the jump speed is kept
511                                 at its starting value
512                         */
513                         v3f speed = getSpeed();
514                         if(speed.Y >= -0.5*BS)
515                         {
516                                 speed.Y = 6.5*BS;
517                                 setSpeed(speed);
518                                 
519                                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
520                                 m_gamedef->event()->put(e);
521                         }
522                 }
523                 // Use the oscillating value for getting out of water
524                 // (so that the player doesn't fly on the surface)
525                 else if(in_water)
526                 {
527                         v3f speed = getSpeed();
528                         speed.Y = 1.5*BS;
529                         setSpeed(speed);
530                         swimming_up = true;
531                 }
532                 else if(is_climbing)
533                 {
534                         v3f speed = getSpeed();
535                         speed.Y = 3*BS;
536                         setSpeed(speed);
537                 }
538         }
539
540         // The speed of the player (Y is ignored)
541         if(superspeed)
542                 speed = speed.normalize() * walkspeed_max * 5.0;
543         else if(control.sneak && !free_move)
544                 speed = speed.normalize() * walkspeed_max / 3.0;
545         else
546                 speed = speed.normalize() * walkspeed_max;
547         
548         f32 inc = walk_acceleration * BS * dtime;
549         
550         // Faster acceleration if fast and free movement
551         if(free_move && fast_move)
552                 inc = walk_acceleration * BS * dtime * 10;
553         
554         // Accelerate to target speed with maximum increment
555         accelerate(speed, inc);
556 }
557
558 v3s16 LocalPlayer::getStandingNodePos()
559 {
560         if(m_sneak_node_exists)
561                 return m_sneak_node;
562         return floatToInt(getPosition(), BS);
563 }
564