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