]> git.lizzy.rs Git - dragonfireclient.git/blob - src/localplayer.cpp
Don't send player position from client to server if the player hasn't moved
[dragonfireclient.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 a special movement mode is used
77         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
78         bool free_move = fly_allowed && g_settings->getBool("free_move");
79         if(free_move)
80         {
81         position += m_speed * dtime;
82                 setPosition(position);
83                 return;
84         }
85
86         /*
87                 Collision detection
88         */
89         
90         /*
91                 Check if player is in water (the oscillating value)
92         */
93         try{
94                 // If in water, the threshold of coming out is at higher y
95                 if(in_water)
96                 {
97                         v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
98                         in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
99                 }
100                 // If not in water, the threshold of going in is at lower y
101                 else
102                 {
103                         v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
104                         in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
105                 }
106         }
107         catch(InvalidPositionException &e)
108         {
109                 in_water = false;
110         }
111
112         /*
113                 Check if player is in water (the stable value)
114         */
115         try{
116                 v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
117                 in_water_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
118         }
119         catch(InvalidPositionException &e)
120         {
121                 in_water_stable = false;
122         }
123
124         /*
125                 Check if player is climbing
126         */
127
128         try {
129                 v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
130                 v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
131                 is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable ||
132                 nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move);
133         }
134         catch(InvalidPositionException &e)
135         {
136                 is_climbing = false;
137         }
138
139         /*
140                 Collision uncertainty radius
141                 Make it a bit larger than the maximum distance of movement
142         */
143         //f32 d = pos_max_d * 1.1;
144         // A fairly large value in here makes moving smoother
145         f32 d = 0.15*BS;
146
147         // This should always apply, otherwise there are glitches
148         assert(d > pos_max_d);
149
150         float player_radius = BS*0.30;
151         float player_height = BS*1.55;
152         
153         // Maximum distance over border for sneaking
154         f32 sneak_max = BS*0.4;
155
156         /*
157                 If sneaking, keep in range from the last walked node and don't
158                 fall off from it
159         */
160         if(control.sneak && m_sneak_node_exists)
161         {
162                 f32 maxd = 0.5*BS + sneak_max;
163                 v3f lwn_f = intToFloat(m_sneak_node, BS);
164                 position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
165                 position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
166                 
167                 if(!is_climbing)
168                 {
169                         f32 min_y = lwn_f.Y + 0.5*BS;
170                         if(position.Y < min_y)
171                         {
172                                 position.Y = min_y;
173
174                                 if(m_speed.Y < 0)
175                                         m_speed.Y = 0;
176                         }
177                 }
178         }
179
180         /*
181                 Calculate player collision box (new and old)
182         */
183         core::aabbox3d<f32> playerbox(
184                 -player_radius,
185                 0.0,
186                 -player_radius,
187                 player_radius,
188                 player_height,
189                 player_radius
190         );
191
192         float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
193
194         v3f accel_f = v3f(0,0,0);
195
196         collisionMoveResult result = collisionMoveSimple(&map, m_gamedef,
197                         pos_max_d, playerbox, player_stepheight, dtime,
198                         position, m_speed, accel_f);
199
200         /*
201                 If the player's feet touch the topside of any node, this is
202                 set to true.
203
204                 Player is allowed to jump when this is true.
205         */
206         bool touching_ground_was = touching_ground;
207         touching_ground = result.touching_ground;
208     
209     //bool standing_on_unloaded = result.standing_on_unloaded;
210
211         /*
212                 Check the nodes under the player to see from which node the
213                 player is sneaking from, if any.  If the node from under
214                 the player has been removed, the player falls.
215         */
216         v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
217         if(m_sneak_node_exists &&
218            nodemgr->get(map.getNodeNoEx(m_old_node_below)).name == "air" &&
219            m_old_node_below_type != "air")
220         {
221                 // Old node appears to have been removed; that is,
222                 // it wasn't air before but now it is
223                 m_need_to_get_new_sneak_node = false;
224                 m_sneak_node_exists = false;
225         }
226         else if(nodemgr->get(map.getNodeNoEx(current_node)).name != "air")
227         {
228                 // We are on something, so make sure to recalculate the sneak
229                 // node.
230                 m_need_to_get_new_sneak_node = true;
231         }
232         if(m_need_to_get_new_sneak_node)
233         {
234                 v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
235                 v2f player_p2df(position.X, position.Z);
236                 f32 min_distance_f = 100000.0*BS;
237                 // If already seeking from some node, compare to it.
238                 /*if(m_sneak_node_exists)
239                 {
240                         v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
241                         v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
242                         f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
243                         f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
244                         // Ignore if player is not on the same level (likely dropped)
245                         if(d_vert_f < 0.15*BS)
246                                 min_distance_f = d_horiz_f;
247                 }*/
248                 v3s16 new_sneak_node = m_sneak_node;
249                 for(s16 x=-1; x<=1; x++)
250                 for(s16 z=-1; z<=1; z++)
251                 {
252                         v3s16 p = pos_i_bottom + v3s16(x,0,z);
253                         v3f pf = intToFloat(p, BS);
254                         v2f node_p2df(pf.X, pf.Z);
255                         f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
256                         f32 max_axis_distance_f = MYMAX(
257                                         fabs(player_p2df.X-node_p2df.X),
258                                         fabs(player_p2df.Y-node_p2df.Y));
259                                         
260                         if(distance_f > min_distance_f ||
261                                         max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
262                                 continue;
263
264                         try{
265                                 // The node to be sneaked on has to be walkable
266                                 if(nodemgr->get(map.getNode(p)).walkable == false)
267                                         continue;
268                                 // And the node above it has to be nonwalkable
269                                 if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true)
270                                         continue;
271                         }
272                         catch(InvalidPositionException &e)
273                         {
274                                 continue;
275                         }
276
277                         min_distance_f = distance_f;
278                         new_sneak_node = p;
279                 }
280                 
281                 bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
282
283                 m_sneak_node = new_sneak_node;
284                 m_sneak_node_exists = sneak_node_found;
285
286                 /*
287                         If sneaking, the player's collision box can be in air, so
288                         this has to be set explicitly
289                 */
290                 if(sneak_node_found && control.sneak)
291                         touching_ground = true;
292         }
293         
294         /*
295                 Set new position
296         */
297         setPosition(position);
298         
299         /*
300                 Report collisions
301         */
302         bool bouncy_jump = false;
303         if(collision_info)
304         {
305                 for(size_t i=0; i<result.collisions.size(); i++){
306                         const CollisionInfo &info = result.collisions[i];
307                         collision_info->push_back(info);
308                         if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
309                                         info.bouncy)
310                                 bouncy_jump = true;
311                 }
312         }
313
314         if(bouncy_jump && control.jump){
315                 m_speed.Y += 6.5*BS;
316                 touching_ground = false;
317                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
318                 m_gamedef->event()->put(e);
319         }
320
321         if(!touching_ground_was && touching_ground){
322                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
323                 m_gamedef->event()->put(e);
324         }
325
326         {
327                 camera_barely_in_ceiling = false;
328                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
329                 MapNode n = map.getNodeNoEx(camera_np);
330                 if(n.getContent() != CONTENT_IGNORE){
331                         if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
332                                 camera_barely_in_ceiling = true;
333                         }
334                 }
335         }
336
337         /*
338                 Update the node last under the player
339         */
340         m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
341         m_old_node_below_type = nodemgr->get(map.getNodeNoEx(m_old_node_below)).name;
342         
343         /*
344                 Check properties of the node on which the player is standing
345         */
346         const ContentFeatures &f = nodemgr->get(map.getNodeNoEx(getStandingNodePos()));
347         // Determine if jumping is possible
348         m_can_jump = touching_ground;
349         if(itemgroup_get(f.groups, "disable_jump"))
350                 m_can_jump = false;
351 }
352
353 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
354 {
355         move(dtime, map, pos_max_d, NULL);
356 }
357
358 void LocalPlayer::applyControl(float dtime)
359 {
360         // Clear stuff
361         swimming_up = false;
362
363         // Random constants
364         f32 walk_acceleration = 4.0 * BS;
365         f32 walkspeed_max = 4.0 * BS;
366         
367         setPitch(control.pitch);
368         setYaw(control.yaw);
369
370         // Nullify speed and don't run positioning code if the player is attached
371         if(isAttached)
372         {
373                 setSpeed(v3f(0,0,0));
374                 return;
375         }
376
377         v3f move_direction = v3f(0,0,1);
378         move_direction.rotateXZBy(getYaw());
379         
380         v3f speed = v3f(0,0,0);
381         
382         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
383         bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
384
385         bool free_move = fly_allowed && g_settings->getBool("free_move");
386         bool fast_move = fast_allowed && g_settings->getBool("fast_move");
387         bool continuous_forward = g_settings->getBool("continuous_forward");
388
389         if(free_move || is_climbing)
390         {
391                 v3f speed = getSpeed();
392                 speed.Y = 0;
393                 setSpeed(speed);
394         }
395
396         // Whether superspeed mode is used or not
397         bool superspeed = false;
398         
399         // If free movement and fast movement, always move fast
400         if(free_move && fast_move)
401                 superspeed = true;
402         
403         // Old descend control
404         if(g_settings->getBool("aux1_descends"))
405         {
406                 // Auxiliary button 1 (E)
407                 if(control.aux1)
408                 {
409                         if(free_move)
410                         {
411                                 // In free movement mode, aux1 descends
412                                 v3f speed = getSpeed();
413                                 if(fast_move)
414                                         speed.Y = -20*BS;
415                                 else
416                                         speed.Y = -walkspeed_max;
417                                 setSpeed(speed);
418                         }
419                         else if(is_climbing)
420                         {
421                                         v3f speed = getSpeed();
422                                 speed.Y = -3*BS;
423                                 setSpeed(speed);
424                         }
425                         else
426                         {
427                                 // If not free movement but fast is allowed, aux1 is
428                                 // "Turbo button"
429                                 if(fast_move)
430                                         superspeed = true;
431                         }
432                 }
433         }
434         // New minecraft-like descend control
435         else
436         {
437                 // Auxiliary button 1 (E)
438                 if(control.aux1)
439                 {
440                         if(!free_move && !is_climbing)
441                         {
442                                 // If not free movement but fast is allowed, aux1 is
443                                 // "Turbo button"
444                                 if(fast_move)
445                                         superspeed = true;
446                         }
447                 }
448
449                 if(control.sneak)
450                 {
451                         if(free_move)
452                         {
453                                 // In free movement mode, sneak descends
454                                 v3f speed = getSpeed();
455                                 if(fast_move)
456                                         speed.Y = -20*BS;
457                                 else
458                                         speed.Y = -walkspeed_max;
459                                         setSpeed(speed);
460                         }
461                         else if(is_climbing)
462                         {
463                                 v3f speed = getSpeed();
464                                 speed.Y = -3*BS;
465                                 setSpeed(speed);
466                         }
467                 }
468         }
469
470         if(continuous_forward)
471                 speed += move_direction;
472
473         if(control.up)
474         {
475                 if(continuous_forward)
476                         superspeed = true;
477                 else
478                         speed += move_direction;
479         }
480         if(control.down)
481         {
482                 speed -= move_direction;
483         }
484         if(control.left)
485         {
486                 speed += move_direction.crossProduct(v3f(0,1,0));
487         }
488         if(control.right)
489         {
490                 speed += move_direction.crossProduct(v3f(0,-1,0));
491         }
492         if(control.jump)
493         {
494                 if(free_move)
495                 {
496                         v3f speed = getSpeed();
497                         if(fast_move)
498                                 speed.Y = 20*BS;
499                         else
500                                 speed.Y = walkspeed_max;
501                         setSpeed(speed);
502                 }
503                 else if(m_can_jump)
504                 {
505                         /*
506                                 NOTE: The d value in move() affects jump height by
507                                 raising the height at which the jump speed is kept
508                                 at its starting value
509                         */
510                         v3f speed = getSpeed();
511                         if(speed.Y >= -0.5*BS)
512                         {
513                                 speed.Y = 6.5*BS;
514                                 setSpeed(speed);
515                                 
516                                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
517                                 m_gamedef->event()->put(e);
518                         }
519                 }
520                 // Use the oscillating value for getting out of water
521                 // (so that the player doesn't fly on the surface)
522                 else if(in_water)
523                 {
524                         v3f speed = getSpeed();
525                         speed.Y = 1.5*BS;
526                         setSpeed(speed);
527                         swimming_up = true;
528                 }
529                 else if(is_climbing)
530                 {
531                         v3f speed = getSpeed();
532                         speed.Y = 3*BS;
533                         setSpeed(speed);
534                 }
535         }
536
537         // The speed of the player (Y is ignored)
538         if(superspeed)
539                 speed = speed.normalize() * walkspeed_max * 5.0;
540         else if(control.sneak)
541                 speed = speed.normalize() * walkspeed_max / 3.0;
542         else
543                 speed = speed.normalize() * walkspeed_max;
544         
545         f32 inc = walk_acceleration * BS * dtime;
546         
547         // Faster acceleration if fast and free movement
548         if(free_move && fast_move)
549                 inc = walk_acceleration * BS * dtime * 10;
550         
551         // Accelerate to target speed with maximum increment
552         accelerate(speed, inc);
553 }
554
555 v3s16 LocalPlayer::getStandingNodePos()
556 {
557         if(m_sneak_node_exists)
558                 return m_sneak_node;
559         return floatToInt(getPosition(), BS);
560 }
561