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