Line data Source code
1 : /*
2 : Minetest
3 : Copyright (C) 2010-2013 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 "event.h"
23 : #include "collision.h"
24 : #include "gamedef.h"
25 : #include "nodedef.h"
26 : #include "settings.h"
27 : #include "environment.h"
28 : #include "map.h"
29 : #include "util/numeric.h"
30 :
31 : /*
32 : LocalPlayer
33 : */
34 :
35 1 : LocalPlayer::LocalPlayer(IGameDef *gamedef, const char *name):
36 : Player(gamedef, name),
37 : parent(0),
38 : isAttached(false),
39 : overridePosition(v3f(0,0,0)),
40 : last_position(v3f(0,0,0)),
41 : last_speed(v3f(0,0,0)),
42 : last_pitch(0),
43 : last_yaw(0),
44 : last_keyPressed(0),
45 : last_animation(NO_ANIM),
46 : hotbar_image(""),
47 : hotbar_selected_image(""),
48 : light_color(255,255,255,255),
49 : m_sneak_node(32767,32767,32767),
50 : m_sneak_node_exists(false),
51 : m_old_node_below(32767,32767,32767),
52 : m_old_node_below_type("air"),
53 : m_need_to_get_new_sneak_node(true),
54 : m_can_jump(false),
55 1 : m_cao(NULL)
56 : {
57 : // Initialize hp to 0, so that no hearts will be shown if server
58 : // doesn't support health points
59 1 : hp = 0;
60 1 : eye_offset_first = v3f(0,0,0);
61 1 : eye_offset_third = v3f(0,0,0);
62 1 : }
63 :
64 2 : LocalPlayer::~LocalPlayer()
65 : {
66 2 : }
67 :
68 3748 : void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
69 : std::vector<CollisionInfo> *collision_info)
70 : {
71 3748 : Map *map = &env->getMap();
72 3748 : INodeDefManager *nodemgr = m_gamedef->ndef();
73 :
74 3748 : v3f position = getPosition();
75 :
76 : // Copy parent position if local player is attached
77 3748 : if(isAttached)
78 : {
79 0 : setPosition(overridePosition);
80 0 : m_sneak_node_exists = false;
81 0 : return;
82 : }
83 :
84 : // Skip collision detection if noclip mode is used
85 3748 : bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
86 18574 : bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
87 11078 : g_settings->getBool("noclip");
88 3748 : bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
89 3748 : if (free_move) {
90 0 : position += m_speed * dtime;
91 0 : setPosition(position);
92 0 : m_sneak_node_exists = false;
93 0 : return;
94 : }
95 :
96 : /*
97 : Collision detection
98 : */
99 :
100 : bool is_valid_position;
101 3748 : MapNode node;
102 3748 : v3s16 pp;
103 :
104 : /*
105 : Check if player is in liquid (the oscillating value)
106 : */
107 :
108 : // If in liquid, the threshold of coming out is at higher y
109 3748 : if (in_liquid)
110 : {
111 0 : pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
112 0 : node = map->getNodeNoEx(pp, &is_valid_position);
113 0 : if (is_valid_position) {
114 0 : in_liquid = nodemgr->get(node.getContent()).isLiquid();
115 0 : liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
116 : } else {
117 0 : in_liquid = false;
118 : }
119 : }
120 : // If not in liquid, the threshold of going in is at lower y
121 : else
122 : {
123 3748 : pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
124 3748 : node = map->getNodeNoEx(pp, &is_valid_position);
125 3748 : if (is_valid_position) {
126 3665 : in_liquid = nodemgr->get(node.getContent()).isLiquid();
127 3665 : liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
128 : } else {
129 83 : in_liquid = false;
130 : }
131 : }
132 :
133 :
134 : /*
135 : Check if player is in liquid (the stable value)
136 : */
137 3748 : pp = floatToInt(position + v3f(0,0,0), BS);
138 3748 : node = map->getNodeNoEx(pp, &is_valid_position);
139 3748 : if (is_valid_position) {
140 3665 : in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
141 : } else {
142 83 : in_liquid_stable = false;
143 : }
144 :
145 : /*
146 : Check if player is climbing
147 : */
148 :
149 :
150 3748 : pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
151 3748 : v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
152 3748 : node = map->getNodeNoEx(pp, &is_valid_position);
153 : bool is_valid_position2;
154 3748 : MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);
155 :
156 3748 : if (!(is_valid_position && is_valid_position2)) {
157 83 : is_climbing = false;
158 : } else {
159 3665 : is_climbing = (nodemgr->get(node.getContent()).climbable
160 3665 : || nodemgr->get(node2.getContent()).climbable) && !free_move;
161 : }
162 :
163 :
164 : /*
165 : Collision uncertainty radius
166 : Make it a bit larger than the maximum distance of movement
167 : */
168 : //f32 d = pos_max_d * 1.1;
169 : // A fairly large value in here makes moving smoother
170 3748 : f32 d = 0.15*BS;
171 :
172 : // This should always apply, otherwise there are glitches
173 3748 : sanity_check(d > pos_max_d);
174 :
175 : // Maximum distance over border for sneaking
176 3748 : f32 sneak_max = BS*0.4;
177 :
178 : /*
179 : If sneaking, keep in range from the last walked node and don't
180 : fall off from it
181 : */
182 14992 : if(control.sneak && m_sneak_node_exists &&
183 11244 : !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
184 0 : physics_override_sneak)
185 : {
186 0 : f32 maxd = 0.5*BS + sneak_max;
187 0 : v3f lwn_f = intToFloat(m_sneak_node, BS);
188 0 : position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
189 0 : position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
190 :
191 0 : if(!is_climbing)
192 : {
193 0 : f32 min_y = lwn_f.Y + 0.5*BS;
194 0 : if(position.Y < min_y)
195 : {
196 0 : position.Y = min_y;
197 :
198 0 : if(m_speed.Y < 0)
199 0 : m_speed.Y = 0;
200 : }
201 : }
202 : }
203 :
204 : // this shouldn't be hardcoded but transmitted from server
205 3748 : float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
206 :
207 : #ifdef __ANDROID__
208 : player_stepheight += (0.5 * BS);
209 : #endif
210 :
211 3748 : v3f accel_f = v3f(0,0,0);
212 :
213 : collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
214 : pos_max_d, m_collisionbox, player_stepheight, dtime,
215 7496 : position, m_speed, accel_f);
216 :
217 : /*
218 : If the player's feet touch the topside of any node, this is
219 : set to true.
220 :
221 : Player is allowed to jump when this is true.
222 : */
223 3748 : bool touching_ground_was = touching_ground;
224 3748 : touching_ground = result.touching_ground;
225 :
226 : //bool standing_on_unloaded = result.standing_on_unloaded;
227 :
228 : /*
229 : Check the nodes under the player to see from which node the
230 : player is sneaking from, if any. If the node from under
231 : the player has been removed, the player falls.
232 : */
233 3748 : v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
234 18656 : if(m_sneak_node_exists &&
235 18572 : nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
236 0 : m_old_node_below_type != "air")
237 : {
238 : // Old node appears to have been removed; that is,
239 : // it wasn't air before but now it is
240 0 : m_need_to_get_new_sneak_node = false;
241 0 : m_sneak_node_exists = false;
242 : }
243 3748 : else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
244 : {
245 : // We are on something, so make sure to recalculate the sneak
246 : // node.
247 3748 : m_need_to_get_new_sneak_node = true;
248 : }
249 3748 : if(m_need_to_get_new_sneak_node && physics_override_sneak)
250 : {
251 3748 : v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
252 3748 : v2f player_p2df(position.X, position.Z);
253 3748 : f32 min_distance_f = 100000.0*BS;
254 : // If already seeking from some node, compare to it.
255 : /*if(m_sneak_node_exists)
256 : {
257 : v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
258 : v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
259 : f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
260 : f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
261 : // Ignore if player is not on the same level (likely dropped)
262 : if(d_vert_f < 0.15*BS)
263 : min_distance_f = d_horiz_f;
264 : }*/
265 3748 : v3s16 new_sneak_node = m_sneak_node;
266 14992 : for(s16 x=-1; x<=1; x++)
267 44976 : for(s16 z=-1; z<=1; z++)
268 : {
269 33732 : v3s16 p = pos_i_bottom + v3s16(x,0,z);
270 33732 : v3f pf = intToFloat(p, BS);
271 33732 : v2f node_p2df(pf.X, pf.Z);
272 33732 : f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
273 33732 : f32 max_axis_distance_f = MYMAX(
274 : fabs(player_p2df.X-node_p2df.X),
275 : fabs(player_p2df.Y-node_p2df.Y));
276 :
277 49871 : if(distance_f > min_distance_f ||
278 16139 : max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
279 47253 : continue;
280 :
281 :
282 : // The node to be sneaked on has to be walkable
283 10479 : node = map->getNodeNoEx(p, &is_valid_position);
284 10479 : if (!is_valid_position || nodemgr->get(node).walkable == false)
285 747 : continue;
286 : // And the node above it has to be nonwalkable
287 9732 : node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
288 9732 : if (!is_valid_position || nodemgr->get(node).walkable) {
289 0 : continue;
290 : }
291 9732 : if (!physics_override_sneak_glitch) {
292 0 : node =map->getNodeNoEx(p + v3s16(0,2,0), &is_valid_position);
293 0 : if (!is_valid_position || nodemgr->get(node).walkable)
294 0 : continue;
295 : }
296 :
297 9732 : min_distance_f = distance_f;
298 9732 : new_sneak_node = p;
299 : }
300 :
301 3748 : bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
302 :
303 3748 : m_sneak_node = new_sneak_node;
304 3748 : m_sneak_node_exists = sneak_node_found;
305 :
306 : /*
307 : If sneaking, the player's collision box can be in air, so
308 : this has to be set explicitly
309 : */
310 3748 : if(sneak_node_found && control.sneak)
311 0 : touching_ground = true;
312 : }
313 :
314 : /*
315 : Set new position
316 : */
317 3748 : setPosition(position);
318 :
319 : /*
320 : Report collisions
321 : */
322 3748 : bool bouncy_jump = false;
323 : // Dont report if flying
324 3748 : if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
325 6963 : for(size_t i=0; i<result.collisions.size(); i++) {
326 3215 : const CollisionInfo &info = result.collisions[i];
327 3215 : collision_info->push_back(info);
328 6430 : if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
329 3215 : info.bouncy)
330 0 : bouncy_jump = true;
331 : }
332 : }
333 :
334 3748 : if(bouncy_jump && control.jump){
335 0 : m_speed.Y += movement_speed_jump*BS;
336 0 : touching_ground = false;
337 0 : MtEvent *e = new SimpleTriggerEvent("PlayerJump");
338 0 : m_gamedef->event()->put(e);
339 : }
340 :
341 3748 : if(!touching_ground_was && touching_ground){
342 1 : MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
343 1 : m_gamedef->event()->put(e);
344 :
345 : // Set camera impact value to be used for view bobbing
346 1 : camera_impact = getSpeed().Y * -1;
347 : }
348 :
349 : {
350 3748 : camera_barely_in_ceiling = false;
351 3748 : v3s16 camera_np = floatToInt(getEyePosition(), BS);
352 3748 : MapNode n = map->getNodeNoEx(camera_np);
353 3748 : if(n.getContent() != CONTENT_IGNORE){
354 3665 : if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
355 0 : camera_barely_in_ceiling = true;
356 : }
357 : }
358 : }
359 :
360 : /*
361 : Update the node last under the player
362 : */
363 3748 : m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
364 3748 : m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
365 :
366 : /*
367 : Check properties of the node on which the player is standing
368 : */
369 3748 : const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
370 : // Determine if jumping is possible
371 3748 : m_can_jump = touching_ground && !in_liquid;
372 3748 : if(itemgroup_get(f.groups, "disable_jump"))
373 0 : m_can_jump = false;
374 : }
375 :
376 0 : void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
377 : {
378 0 : move(dtime, env, pos_max_d, NULL);
379 0 : }
380 :
381 1186 : void LocalPlayer::applyControl(float dtime)
382 : {
383 : // Clear stuff
384 1186 : swimming_vertical = false;
385 :
386 1186 : setPitch(control.pitch);
387 1186 : setYaw(control.yaw);
388 :
389 : // Nullify speed and don't run positioning code if the player is attached
390 1186 : if(isAttached)
391 : {
392 0 : setSpeed(v3f(0,0,0));
393 0 : return;
394 : }
395 :
396 1186 : v3f move_direction = v3f(0,0,1);
397 1186 : move_direction.rotateXZBy(getYaw());
398 :
399 1186 : v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
400 1186 : v3f speedV = v3f(0,0,0); // Vertical (Y)
401 :
402 1186 : bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
403 1186 : bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
404 :
405 1186 : bool free_move = fly_allowed && g_settings->getBool("free_move");
406 1186 : bool fast_move = fast_allowed && g_settings->getBool("fast_move");
407 : // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
408 1186 : bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
409 1186 : bool continuous_forward = g_settings->getBool("continuous_forward");
410 1186 : bool always_fly_fast = g_settings->getBool("always_fly_fast");
411 :
412 : // Whether superspeed mode is used or not
413 1186 : bool superspeed = false;
414 :
415 1186 : if (always_fly_fast && free_move && fast_move)
416 0 : superspeed = true;
417 :
418 : // Old descend control
419 1186 : if(g_settings->getBool("aux1_descends"))
420 : {
421 : // If free movement and fast movement, always move fast
422 0 : if(free_move && fast_move)
423 0 : superspeed = true;
424 :
425 : // Auxiliary button 1 (E)
426 0 : if(control.aux1)
427 : {
428 0 : if(free_move)
429 : {
430 : // In free movement mode, aux1 descends
431 0 : if(fast_move)
432 0 : speedV.Y = -movement_speed_fast;
433 : else
434 0 : speedV.Y = -movement_speed_walk;
435 : }
436 0 : else if(in_liquid || in_liquid_stable)
437 : {
438 0 : speedV.Y = -movement_speed_walk;
439 0 : swimming_vertical = true;
440 : }
441 0 : else if(is_climbing)
442 : {
443 0 : speedV.Y = -movement_speed_climb;
444 : }
445 : else
446 : {
447 : // If not free movement but fast is allowed, aux1 is
448 : // "Turbo button"
449 0 : if(fast_move)
450 0 : superspeed = true;
451 : }
452 : }
453 : }
454 : // New minecraft-like descend control
455 : else
456 : {
457 : // Auxiliary button 1 (E)
458 1186 : if(control.aux1)
459 : {
460 0 : if(!is_climbing)
461 : {
462 : // aux1 is "Turbo button"
463 0 : if(fast_move)
464 0 : superspeed = true;
465 : }
466 : }
467 :
468 1186 : if(control.sneak)
469 : {
470 0 : if(free_move)
471 : {
472 : // In free movement mode, sneak descends
473 0 : if (fast_move && (control.aux1 || always_fly_fast))
474 0 : speedV.Y = -movement_speed_fast;
475 : else
476 0 : speedV.Y = -movement_speed_walk;
477 : }
478 0 : else if(in_liquid || in_liquid_stable)
479 : {
480 0 : if(fast_climb)
481 0 : speedV.Y = -movement_speed_fast;
482 : else
483 0 : speedV.Y = -movement_speed_walk;
484 0 : swimming_vertical = true;
485 : }
486 0 : else if(is_climbing)
487 : {
488 0 : if(fast_climb)
489 0 : speedV.Y = -movement_speed_fast;
490 : else
491 0 : speedV.Y = -movement_speed_climb;
492 : }
493 : }
494 : }
495 :
496 1186 : if(continuous_forward)
497 0 : speedH += move_direction;
498 :
499 1186 : if(control.up)
500 : {
501 405 : if(continuous_forward)
502 0 : superspeed = true;
503 : else
504 405 : speedH += move_direction;
505 : }
506 1186 : if(control.down)
507 : {
508 37 : speedH -= move_direction;
509 : }
510 1186 : if(control.left)
511 : {
512 148 : speedH += move_direction.crossProduct(v3f(0,1,0));
513 : }
514 1186 : if(control.right)
515 : {
516 214 : speedH += move_direction.crossProduct(v3f(0,-1,0));
517 : }
518 1186 : if(control.jump)
519 : {
520 0 : if (free_move) {
521 0 : if (g_settings->getBool("aux1_descends") || always_fly_fast) {
522 0 : if (fast_move)
523 0 : speedV.Y = movement_speed_fast;
524 : else
525 0 : speedV.Y = movement_speed_walk;
526 : } else {
527 0 : if(fast_move && control.aux1)
528 0 : speedV.Y = movement_speed_fast;
529 : else
530 0 : speedV.Y = movement_speed_walk;
531 : }
532 : }
533 0 : else if(m_can_jump)
534 : {
535 : /*
536 : NOTE: The d value in move() affects jump height by
537 : raising the height at which the jump speed is kept
538 : at its starting value
539 : */
540 0 : v3f speedJ = getSpeed();
541 0 : if(speedJ.Y >= -0.5 * BS)
542 : {
543 0 : speedJ.Y = movement_speed_jump * physics_override_jump;
544 0 : setSpeed(speedJ);
545 :
546 0 : MtEvent *e = new SimpleTriggerEvent("PlayerJump");
547 0 : m_gamedef->event()->put(e);
548 : }
549 : }
550 0 : else if(in_liquid)
551 : {
552 0 : if(fast_climb)
553 0 : speedV.Y = movement_speed_fast;
554 : else
555 0 : speedV.Y = movement_speed_walk;
556 0 : swimming_vertical = true;
557 : }
558 0 : else if(is_climbing)
559 : {
560 0 : if(fast_climb)
561 0 : speedV.Y = movement_speed_fast;
562 : else
563 0 : speedV.Y = movement_speed_climb;
564 : }
565 : }
566 :
567 : // The speed of the player (Y is ignored)
568 1186 : if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
569 0 : speedH = speedH.normalize() * movement_speed_fast;
570 1186 : else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
571 0 : speedH = speedH.normalize() * movement_speed_crouch;
572 : else
573 1186 : speedH = speedH.normalize() * movement_speed_walk;
574 :
575 : // Acceleration increase
576 1186 : f32 incH = 0; // Horizontal (X, Z)
577 1186 : f32 incV = 0; // Vertical (Y)
578 1186 : if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
579 : {
580 : // Jumping and falling
581 2 : if(superspeed || (fast_move && control.aux1))
582 0 : incH = movement_acceleration_fast * BS * dtime;
583 : else
584 2 : incH = movement_acceleration_air * BS * dtime;
585 2 : incV = 0; // No vertical acceleration in air
586 : }
587 1184 : else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb))
588 0 : incH = incV = movement_acceleration_fast * BS * dtime;
589 : else
590 1184 : incH = incV = movement_acceleration_default * BS * dtime;
591 :
592 : // Accelerate to target speed with maximum increment
593 1186 : accelerateHorizontal(speedH * physics_override_speed, incH * physics_override_speed);
594 1186 : accelerateVertical(speedV * physics_override_speed, incV * physics_override_speed);
595 : }
596 :
597 4914 : v3s16 LocalPlayer::getStandingNodePos()
598 : {
599 4914 : if(m_sneak_node_exists)
600 4831 : return m_sneak_node;
601 83 : return floatToInt(getPosition() - v3f(0, BS, 0), BS);
602 3 : }
603 :
|