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