]> git.lizzy.rs Git - dragonfireclient.git/blob - src/localplayer.cpp
Add disable_jump and fall_damage_add_percent node groups
[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         if(collision_info)
286         {
287                 // Report fall collision
288                 if(old_speed.Y < m_speed.Y - 0.1 && !standing_on_unloaded)
289                 {
290                         CollisionInfo info;
291                         info.t = COLLISION_FALL;
292                         info.speed = m_speed.Y - old_speed.Y;
293                         collision_info->push_back(info);
294                 }
295         }
296
297         if(!touching_ground_was && touching_ground){
298                 MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
299                 m_gamedef->event()->put(e);
300         }
301
302         {
303                 camera_barely_in_ceiling = false;
304                 v3s16 camera_np = floatToInt(getEyePosition(), BS);
305                 MapNode n = map.getNodeNoEx(camera_np);
306                 if(n.getContent() != CONTENT_IGNORE){
307                         if(nodemgr->get(n).walkable){
308                                 camera_barely_in_ceiling = true;
309                         }
310                 }
311         }
312
313         /*
314                 Update the node last under the player
315         */
316         m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
317         m_old_node_below_type = nodemgr->get(map.getNodeNoEx(m_old_node_below)).name;
318         
319         /*
320                 Check properties of the node on which the player is standing
321         */
322         const ContentFeatures &f = nodemgr->get(map.getNodeNoEx(getStandingNodePos()));
323         // Determine if jumping is possible
324         m_can_jump = touching_ground;
325         if(itemgroup_get(f.groups, "disable_jump"))
326                 m_can_jump = false;
327 }
328
329 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
330 {
331         move(dtime, map, pos_max_d, NULL);
332 }
333
334 void LocalPlayer::applyControl(float dtime)
335 {
336         // Clear stuff
337         swimming_up = false;
338
339         // Random constants
340         f32 walk_acceleration = 4.0 * BS;
341         f32 walkspeed_max = 4.0 * BS;
342         
343         setPitch(control.pitch);
344         setYaw(control.yaw);
345         
346         v3f move_direction = v3f(0,0,1);
347         move_direction.rotateXZBy(getYaw());
348         
349         v3f speed = v3f(0,0,0);
350         
351         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
352         bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
353
354         bool free_move = fly_allowed && g_settings->getBool("free_move");
355         bool fast_move = fast_allowed && g_settings->getBool("fast_move");
356         bool continuous_forward = g_settings->getBool("continuous_forward");
357
358         if(free_move || is_climbing)
359         {
360                 v3f speed = getSpeed();
361                 speed.Y = 0;
362                 setSpeed(speed);
363         }
364
365         // Whether superspeed mode is used or not
366         bool superspeed = false;
367         
368         // If free movement and fast movement, always move fast
369         if(free_move && fast_move)
370                 superspeed = true;
371         
372         // Old descend control
373         if(g_settings->getBool("aux1_descends"))
374         {
375                 // Auxiliary button 1 (E)
376                 if(control.aux1)
377                 {
378                         if(free_move)
379                         {
380                                 // In free movement mode, aux1 descends
381                                 v3f speed = getSpeed();
382                                 if(fast_move)
383                                         speed.Y = -20*BS;
384                                 else
385                                         speed.Y = -walkspeed_max;
386                                 setSpeed(speed);
387                         }
388                         else if(is_climbing)
389                         {
390                                         v3f speed = getSpeed();
391                                 speed.Y = -3*BS;
392                                 setSpeed(speed);
393                         }
394                         else
395                         {
396                                 // If not free movement but fast is allowed, aux1 is
397                                 // "Turbo button"
398                                 if(fast_move)
399                                         superspeed = true;
400                         }
401                 }
402         }
403         // New minecraft-like descend control
404         else
405         {
406                 // Auxiliary button 1 (E)
407                 if(control.aux1)
408                 {
409                         if(!free_move && !is_climbing)
410                         {
411                                 // If not free movement but fast is allowed, aux1 is
412                                 // "Turbo button"
413                                 if(fast_move)
414                                         superspeed = true;
415                         }
416                 }
417
418                 if(control.sneak)
419                 {
420                         if(free_move)
421                         {
422                                 // In free movement mode, sneak descends
423                                 v3f speed = getSpeed();
424                                 if(fast_move)
425                                         speed.Y = -20*BS;
426                                 else
427                                         speed.Y = -walkspeed_max;
428                                         setSpeed(speed);
429                         }
430                         else if(is_climbing)
431                         {
432                                 v3f speed = getSpeed();
433                                 speed.Y = -3*BS;
434                                 setSpeed(speed);
435                         }
436                 }
437         }
438
439         if(continuous_forward)
440                 speed += move_direction;
441
442         if(control.up)
443         {
444                 if(continuous_forward)
445                         superspeed = true;
446                 else
447                         speed += move_direction;
448         }
449         if(control.down)
450         {
451                 speed -= move_direction;
452         }
453         if(control.left)
454         {
455                 speed += move_direction.crossProduct(v3f(0,1,0));
456         }
457         if(control.right)
458         {
459                 speed += move_direction.crossProduct(v3f(0,-1,0));
460         }
461         if(control.jump)
462         {
463                 if(free_move)
464                 {
465                         v3f speed = getSpeed();
466                         if(fast_move)
467                                 speed.Y = 20*BS;
468                         else
469                                 speed.Y = walkspeed_max;
470                         setSpeed(speed);
471                 }
472                 else if(m_can_jump)
473                 {
474                         /*
475                                 NOTE: The d value in move() affects jump height by
476                                 raising the height at which the jump speed is kept
477                                 at its starting value
478                         */
479                         v3f speed = getSpeed();
480                         if(speed.Y >= -0.5*BS)
481                         {
482                                 speed.Y = 6.5*BS;
483                                 setSpeed(speed);
484                                 
485                                 MtEvent *e = new SimpleTriggerEvent("PlayerJump");
486                                 m_gamedef->event()->put(e);
487                         }
488                 }
489                 // Use the oscillating value for getting out of water
490                 // (so that the player doesn't fly on the surface)
491                 else if(in_water)
492                 {
493                         v3f speed = getSpeed();
494                         speed.Y = 1.5*BS;
495                         setSpeed(speed);
496                         swimming_up = true;
497                 }
498                 else if(is_climbing)
499                 {
500                         v3f speed = getSpeed();
501                         speed.Y = 3*BS;
502                         setSpeed(speed);
503                 }
504         }
505
506         // The speed of the player (Y is ignored)
507         if(superspeed)
508                 speed = speed.normalize() * walkspeed_max * 5.0;
509         else if(control.sneak)
510                 speed = speed.normalize() * walkspeed_max / 3.0;
511         else
512                 speed = speed.normalize() * walkspeed_max;
513         
514         f32 inc = walk_acceleration * BS * dtime;
515         
516         // Faster acceleration if fast and free movement
517         if(free_move && fast_move)
518                 inc = walk_acceleration * BS * dtime * 10;
519         
520         // Accelerate to target speed with maximum increment
521         accelerate(speed, inc);
522 }
523
524 v3s16 LocalPlayer::getStandingNodePos()
525 {
526         if(m_sneak_node_exists)
527                 return m_sneak_node;
528         return floatToInt(getPosition(), BS);
529 }
530