1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package cz.cuni.amis.pogamut.ut2004.agent.navigation.navmesh.pathfollowing;
18
19 import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
20 import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObject;
21 import cz.cuni.amis.pogamut.base3d.worldview.object.ILocated;
22 import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
23 import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
24 import cz.cuni.amis.pogamut.ut2004.agent.module.sensor.AgentInfo;
25 import cz.cuni.amis.pogamut.ut2004.agent.navigation.IUT2004PathRunner;
26 import cz.cuni.amis.pogamut.ut2004.agent.navigation.navmesh.NavMesh;
27 import cz.cuni.amis.pogamut.ut2004.bot.command.AdvancedLocomotion;
28 import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
29 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.Move;
30 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPointNeighbourLink;
31 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Player;
32 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.WallCollision;
33 import cz.cuni.amis.pogamut.ut2004.utils.LinkFlag;
34 import cz.cuni.amis.pogamut.ut2004.utils.UnrealUtils;
35 import cz.cuni.amis.utils.NullCheck;
36 import java.util.logging.Level;
37 import java.util.logging.Logger;
38
39
40
41
42
43
44
45
46
47
48
49
50
51 public class NavMeshRunner implements IUT2004PathRunner {
52
53 private UT2004Bot bot;
54 private AgentInfo memory;
55 private AdvancedLocomotion body;
56 private Logger log;
57
58 private JumpBoundaries jumpBoundaries;
59
60
61
62
63 IWorldEventListener<WallCollision> myCollisionsListener = new IWorldEventListener<WallCollision>() {
64 @Override
65 public void notify(WallCollision event) {
66 lastCollidingEvent = event;
67 }
68
69 };
70
71 private JumpModule jumpModule;
72
73 private CollisionDetector collisionDetector;
74 private boolean inCollision = false;
75 private String collisionReason = null;
76
77
78
79
80
81 private int runnerStep = 0;
82
83
84
85
86 private int jumpStep = 0;
87
88
89
90
91 private int collisionNum = 0;
92
93
94
95
96 private Location collisionSpot = null;
97
98
99
100
101
102
103
104 private double distance;
105
106
107
108
109
110
111 private double distance2D;
112
113
114
115
116
117
118
119 private double distanceZ;
120
121
122
123
124
125
126 private double velocity;
127
128
129
130
131
132
133
134 private double velocityZ;
135
136
137
138
139
140
141 private boolean jumpRequired;
142
143
144
145
146 private String jumpReason;
147
148
149
150
151
152
153 private Location runningFrom;
154
155
156
157
158
159 private Location firstLocation;
160
161
162
163
164
165 private Location secondLocation;
166
167
168
169
170
171 private ILocated focus;
172
173
174
175
176
177 private NavPointNeighbourLink link;
178
179
180
181
182 private boolean forceNoJump;
183
184
185
186
187 private double runDeviation2DDot;
188
189
190
191
192 private double runAngle;
193
194
195
196
197 private boolean accelerating = false;
198
199
200
201
202 protected WallCollision lastCollidingEvent = null;
203
204
205
206 private static final int WALL_COLLISION_THRESHOLD_MILLIS = 500;
207
208
209
210
211
212
213
214
215
216
217 public NavMeshRunner(UT2004Bot bot, AgentInfo agentInfo, AdvancedLocomotion locomotion, Logger log, NavMesh navMesh) {
218
219 NullCheck.check(bot, "bot");
220 this.bot = bot;
221 NullCheck.check(agentInfo, "agentInfo");
222 this.memory = agentInfo;
223 NullCheck.check(locomotion, "locomotion");
224 this.body = locomotion;
225
226
227 bot.getWorldView().addEventListener(WallCollision.class, myCollisionsListener);
228
229 this.log = log;
230 if (this.log == null) {
231 this.log = bot.getLogger().getCategory(this.getClass().getSimpleName());
232 }
233
234 this.jumpModule = new JumpModule(navMesh, this.log);
235 this.collisionDetector = new CollisionDetector();
236
237 reset();
238 }
239
240 @Override
241 public void reset() {
242 log.finer("RUNNER RESET");
243
244 runnerStep = 0;
245
246 jumpRequired = false;
247 jumpBoundaries = null;
248 jumpReason = null;
249 jumpStep = 0;
250 forceNoJump = false;
251
252 collisionNum = 0;
253 collisionSpot = null;
254 lastCollidingEvent = null;
255 inCollision = false;
256 collisionReason = null;
257
258 distance = 0;
259 distance2D = 0;
260 distanceZ = 0;
261 velocity = 0;
262 velocityZ = 0;
263 runDeviation2DDot = 0;
264 accelerating = false;
265 lastVelocityZ = 0;
266 }
267
268 @Override
269 public boolean runToLocation(Location runningFrom, Location firstLocation, Location secondLocation, ILocated focus, NavPointNeighbourLink navPointsLink, boolean reachable, boolean forceNoJump) {
270
271 ++runnerStep;
272
273
274 this.runningFrom = runningFrom;
275 this.firstLocation = firstLocation;
276 this.secondLocation = secondLocation;
277 this.focus = focus;
278 this.link = navPointsLink;
279 this.forceNoJump = forceNoJump;
280
281
282 distance = memory.getLocation().getDistance(firstLocation);
283 distance2D = memory.getLocation().getDistance2D(firstLocation);
284 distanceZ = firstLocation.getDistanceZ(memory.getLocation());
285
286 double newVelocity = memory.getVelocity().size();
287 accelerating = isAccelerating(newVelocity, velocity);
288
289 velocity = newVelocity;
290 velocityZ = memory.getVelocity().z;
291
292 Location direction = Location.sub(firstLocation, memory.getLocation()).setZ(0);
293 direction = direction.getNormalized();
294 Location velocityDir = new Location(memory.getVelocity().asVector3d()).setZ(0);
295 velocityDir = velocityDir.getNormalized();
296 runDeviation2DDot = direction.dot(velocityDir);
297
298 runAngle = Math.atan2(distanceZ, distance2D);
299
300 if (jumpBoundaries == null || jumpBoundaries.getLink() != link) {
301 jumpBoundaries = jumpModule.computeJumpBoundaries(link);
302 }
303
304 checkJump();
305
306 checkCollision();
307
308 logIteration();
309
310
311
312
313 if (runnerStep <= 1) {
314 debug("FIRST STEP - Start running towards new location");
315 move(firstLocation, secondLocation, focus);
316 return true;
317 }
318
319
320 if (jumpStep > 0) {
321 debug("We're already jumping" + (collisionSpot != null ? " [COLLISION]" : ""));
322 return iterateJumpSequence();
323 }
324
325
326 if (inCollision) {
327
328 if (!forceNoJump) {
329
330 return resolveCollision();
331 }
332 } else {
333 if (collisionSpot != null || collisionNum != 0) {
334 debug("Collision waned...");
335 collisionNum = 0;
336 collisionSpot = null;
337 }
338 }
339
340
341 if (velocity < 5 && runnerStep > 5) {
342 debug("Velocity is (almost) zero and we're in the middle of running...");
343 if (forceNoJump) {
344 debug("But we will not jump as forceNoJump == true ...");
345 } else {
346 debug("So we're going to jump...");
347 return initJump(true, true);
348 }
349 }
350
351
352 if (jumpRequired) {
353
354 if (!forceNoJump) {
355
356 return decideJump();
357 }
358 }
359
360 debug("Keeping running to the target");
361 move(firstLocation, secondLocation, focus);
362
363 return true;
364 }
365
366
367
368
369
370
371 private boolean checkJump() {
372 if (jumpStep > 0) return true;
373
374 if (jumpModule.needsJump(link)) {
375
376 jumpRequired = true;
377 jumpReason = "LINK[";
378 boolean first = true;
379 if ((link.getFlags() & LinkFlag.JUMP.get()) != 0) {
380 first = false;
381 jumpReason += "JUMP-FLAG";
382 }
383 if (link.isForceDoubleJump()) {
384 if (!first) jumpReason += "|";
385 jumpReason += "DOUBLE-JUMP";
386 }
387 if (link.getNeededJump() != null) {
388 if (!first) jumpReason += "|";
389 int jumpDistance = (int) memory.getLocation().getDistance(new Location(link.getNeededJump()));
390 jumpReason += "DIST:" + jumpDistance;
391 }
392 jumpReason += "]";
393 } else {
394 jumpRequired = false;
395 jumpReason = null;
396 }
397 return jumpRequired;
398 }
399
400
401
402
403
404
405 private boolean checkCollision() {
406
407 if (collisionDetector.isColliding(memory.getLocation(), velocity, distance)) {
408 inCollision = true;
409 collisionReason = "COLLISION DETECTED";
410 return true;
411 }
412
413
414 if (lastCollidingEvent == null) {
415 inCollision = false;
416 collisionReason = null;
417 return false;
418 }
419
420 int collisionTimeMillis = (int)(memory.getTime() * 1000 - lastCollidingEvent.getSimTime());
421 if (collisionTimeMillis <= WALL_COLLISION_THRESHOLD_MILLIS) {
422 inCollision = true;
423 collisionReason = "WALL-EVENT[before " + collisionTimeMillis + "ms]";
424 return true;
425 }
426
427 inCollision = false;
428 collisionReason = null;
429 return false;
430 }
431
432 private void logIteration() {
433 Location curr = memory.getLocation();
434 double d1 = firstLocation == null ? -1 : curr.getDistance(firstLocation);
435 double d2 = firstLocation != null && secondLocation == null ? 0 : firstLocation.getDistance(secondLocation);
436
437
438 if (log != null && log.isLoggable(Level.FINER)) {
439 debug("RUNNER STEP " + runnerStep);
440 debug("running " + memory.getLocation() + " --(D3D:" + ((int)d1) + "|D2D:" + ((int)distance2D) + "|DZ:" + ((int)distanceZ) + ")--> " + firstLocation + (secondLocation == null ? "" : " --(D3D:" + ((int)d2) + ")--> " + secondLocation) + (focus == null ? "" : ", focusing to " + focus));
441 debug("velocity " + ((int)velocity) + " (z:" + ((int)velocityZ) + ")");
442 debug("deviation2D " + ((int)(Math.acos(runDeviation2DDot) * 180 / Math.PI)));
443 debug("runAngle " + ((int)(runAngle / Math.PI * 180)) + " degrees");
444 if (jumpRequired) {
445 debug("jump " + jumpReason);
446 }
447 if (inCollision) {
448 debug("collision " + collisionReason);
449 }
450 if (jumpBoundaries.getLink() != null) {
451 debug("jump-bounds " + (jumpBoundaries.isJumpable() ? "POSSIBLE" : "IMPOSSIBLE") + " X-" + jumpBoundaries.getTakeOffMin() + "<--(" + (jumpBoundaries.getTakeOffMin() != null && jumpBoundaries.getTakeOffMax() != null ? (int)(jumpBoundaries.getTakeOffMin().getDistance(jumpBoundaries.getTakeOffMax())) : "???") + ")-->" + jumpBoundaries.getTakeOffMax() + "-X--->" + jumpBoundaries.getLandingTarget());
452 }
453 if (jumpRequired || inCollision) {
454 if (forceNoJump) {
455 debug("jump forbidden [force-no-jump]");
456 }
457 }
458 }
459 }
460
461 private void move(ILocated firstLocation, ILocated secondLocation, ILocated focus) {
462 Move move = new Move();
463 if (firstLocation != null) {
464 move.setFirstLocation(firstLocation.getLocation());
465 if (secondLocation == null || secondLocation.equals(firstLocation)) {
466
467 move.setSecondLocation(firstLocation.getLocation());
468 } else {
469 double dist = firstLocation.getLocation().getDistance(secondLocation.getLocation());
470 if (dist < 50) {
471
472 double quantifier = 1 + (200 / dist);
473 Location extendedSecondLocation = firstLocation.getLocation().interpolate(secondLocation.getLocation(), quantifier);
474 move.setSecondLocation(extendedSecondLocation);
475 } else {
476 move.setSecondLocation(secondLocation.getLocation());
477 }
478 }
479 } else if (secondLocation != null) {
480
481 move.setSecondLocation(secondLocation.getLocation());
482 }
483
484 if (focus != null) {
485 if (focus instanceof Player) {
486 move.setFocusTarget((UnrealId) ((IWorldObject) focus).getId());
487 } else {
488 move.setFocusLocation(focus.getLocation());
489 }
490 }
491
492 debug(move.toString());
493 bot.getAct().act(move);
494 }
495
496 private boolean resolveCollision() {
497
498 if (
499 (collisionSpot == null)
500
501 || (memory.getLocation().getDistance2D(collisionSpot) > 120)
502 ) {
503
504 collisionSpot = memory.getLocation();
505 collisionNum = 1;
506 debug("COLLISION[" + collisionNum + "] => just moving...");
507
508 move(firstLocation, secondLocation, focus);
509 return true;
510 }
511
512
513
514
515 if (collisionNum > 8) {
516 debug("COLLISION[" + collisionNum + "] => JUMPING");
517 return initJump(true, true);
518 }
519
520
521 ++collisionNum;
522 debug("COLLISION[" + collisionNum + "] => just moving...");
523
524
525 move(firstLocation, secondLocation, focus);
526 return true;
527 }
528
529 private boolean decideJump() {
530 debug("decideJump(): called");
531
532 int jumpDistance2D = (int) distance2D;
533 debug(" ", "jumpDistance2D = " + jumpDistance2D);
534
535
536 boolean jumpIndicated = false;
537 boolean jumpForced = inCollision;
538 boolean doubleJumpRequired = false;
539
540
541 if (jumpModule.needsJump(link)) {
542 debug(" ", "JumpModule is indicating jump");
543 jumpIndicated = true;
544 } else {
545 debug(" ", "JumpModule is silent");
546 }
547
548
549 if (!jumpBoundaries.isJumpable()) {
550 if ((runnerStep > 1 && velocity > UnrealUtils.MAX_VELOCITY - 50 && runDeviation2DDot < 20 && jumpDistance2D < 500) || jumpDistance2D < 250) {
551 debug(" ", "jump-bounds impossible and we're heuristically in good position for jump => forcing double jump");
552 jumpForced = true;
553 doubleJumpRequired = true;
554 } else {
555 debug(" ", "jump-bounds indicating impossible jump => requiring double jump");
556 doubleJumpRequired = true;
557 }
558 }
559
560 if (!doubleJumpRequired && runnerStep > 1 && runAngle < -Math.PI/6) {
561 debug(" ", "we have to JUMP DOWN - indicating jump");
562 jumpIndicated = true;
563 }
564
565 if (jumpIndicated || jumpForced) {
566 return prepareJump(jumpForced, doubleJumpRequired);
567 } else {
568 debug(" ", "we do not need to jump right now, waiting to reach the right spot to jump from");
569 move(firstLocation, secondLocation, focus);
570 return true;
571 }
572 }
573
574
575
576
577
578
579
580
581
582
583 private boolean prepareJump(boolean jumpForced, boolean doubleJumpRequired) {
584 debug("prepareJump(): called");
585
586 double jumpVelocity = jumpModule.getCorrectedVelocity(velocity, accelerating);
587
588 Double jumpAngleDeviation = Math.acos(jumpModule.getCorrectedAngle(runDeviation2DDot, runnerStep <= 1));
589 boolean angleSuitable = !jumpAngleDeviation.isNaN() && jumpAngleDeviation < (Math.PI / 9);
590
591 debug(" ", "jumpVelocity " + ((int)jumpVelocity));
592 debug(" ", "jumpAngleDeviation " + ((int)(jumpAngleDeviation * 180 / Math.PI)) + " degress");
593 debug(" ", "angleSuitable " + angleSuitable);
594
595 if (!jumpForced && runnerStep > 1 && jumpBoundaries.isPastBoundaries(memory.getLocation())) {
596 debug(" ", "past jump-bounds => forcing the jump");
597 jumpForced = true;
598 }
599
600 if (!jumpForced && jumpAngleDeviation > Math.PI / 3) {
601 debug(" ", "impossible jump angle, postponing the jump...");
602 move(firstLocation, secondLocation, focus);
603 return true;
604 }
605
606 if (jumpForced) {
607 debug(" ", "jump is forced, bypassing jump checks");
608 } else {
609 debug(" ", "jump is not forced and we are within jump-bounds, checking jump conditions");
610
611 if (!jumpModule.isJumpable(memory.getLocation(), jumpBoundaries.getLandingTarget(), jumpVelocity)) {
612 debug(" ", "not jumpable! Start: " + memory.getLocation() + " Target: " + jumpBoundaries.getLandingTarget() + " Velocity: " + velocity + " Jump Velocity: " + jumpVelocity);
613 debug(" ", "proceeding with the straight movement to gain speed");
614 move(firstLocation, secondLocation, focus);
615 return true;
616 }
617
618 if (!angleSuitable) {
619 debug(" ", "angle is not suitable for jumping (angle > 20 degrees)");
620 debug(" ", "proceeding with the straight movement to gain speed");
621 move(firstLocation, secondLocation, focus);
622 return true;
623 }
624
625
626 if (jumpBoundaries.isJumpable()) {
627 if (jumpBoundaries.getTakeOffMax().getDistance2D(memory.getLocation()) > IDEAL_JUMP_RESERVE) {
628 boolean angleIdeal = !jumpAngleDeviation.isNaN() && jumpAngleDeviation < (Math.PI / 90);
629 if (!angleIdeal) {
630 debug(" ", "proceeding with the straight movement - waiting for IDEAL JUMP ANGLE");
631 move(firstLocation, secondLocation, focus);
632 return true;
633 }
634 if (velocity < UnrealUtils.MAX_VELOCITY - 50) {
635 debug(" ", "proceeding with the straight movement - waiting for IDEAL SPEED");
636 move(firstLocation, secondLocation, focus);
637 return true;
638 }
639 }
640 }
641
642 debug(" ", "passed ideal reserve spot, forcing the jump");
643 jumpForced = true;
644 }
645
646 return initJump(jumpForced, doubleJumpRequired);
647
648 }
649
650 private static final int IDEAL_JUMP_RESERVE = 80;
651
652
653
654
655
656
657
658
659
660
661
662 private boolean initJump(boolean jumpForced, boolean maxJumpRequired) {
663 debug("initJump(): called");
664
665 Boolean doubleJump = null;
666 Double jumpForce = Double.NaN;
667 Double jumpVelocity = jumpModule.getCorrectedVelocity(velocity, accelerating);
668 Double jumpAngleCos = jumpModule.getCorrectedAngle(runDeviation2DDot, runnerStep <= 1);
669
670 if (jumpForced) {
671
672 debug(" ", "Jump is forced!");
673
674
675 if (inCollision) {
676 debug(" ", "In collision, forcing MAX DOUBLE jump!");
677 inCollision = false;
678 jumpStep = 1;
679 return jump(true, UnrealUtils.FULL_DOUBLEJUMP_DELAY, UnrealUtils.FULL_DOUBLEJUMP_FORCE);
680 }
681
682
683 if (maxJumpRequired) {
684 debug(" ", "Forced MAX DOUBLE jump!");
685 jumpStep = 1;
686 return jump(true, UnrealUtils.FULL_DOUBLEJUMP_DELAY, UnrealUtils.FULL_DOUBLEJUMP_FORCE);
687 }
688
689
690 if (runAngle < 0 && distanceZ < -50) {
691 debug(" ", "We are going to fall down, runAngle = " + ((int)(runAngle/Math.PI*180)) + " < 0, distaceZ = " + ((int)distanceZ) + " < -50");
692
693 try {
694 jumpForce = Math.abs(jumpModule.computeFall(memory.getLocation(), jumpBoundaries, jumpVelocity, jumpAngleCos));
695 if (jumpForce < 110) jumpForce = 110.0d;
696 } catch (Exception e) {
697 debug(" ", "Failed to compute fall-jump force, setting 110.");
698 jumpForce = 110.0d;
699 }
700 } else {
701 debug(" ", "We have to jump normally, computing ideal jump force.");
702 try {
703 jumpForce = Math.abs(jumpModule.computeJump(memory.getLocation(), jumpBoundaries, jumpVelocity, jumpAngleCos));
704 } catch (Exception e) {
705 jumpForce = Double.NaN;
706 }
707 }
708
709 if (jumpForce == Double.NaN) {
710 debug(" ", "Could not compute jump force (NaN) => forcing MAX DOUBLE jump!");
711 jumpForce = (double)UnrealUtils.FULL_DOUBLEJUMP_FORCE;
712 }
713 doubleJump = jumpForce > UnrealUtils.FULL_JUMP_FORCE;
714
715 jumpStep = 1;
716 return jump(doubleJump, UnrealUtils.FULL_DOUBLEJUMP_DELAY, jumpForce);
717 }
718
719
720
721 if (!jumpBoundaries.isJumpable()) {
722 debug(" ", "Jump boundaries not present! We shouldn't be trying to JUMP!");
723 jumpForce = Double.NaN;
724 } else if (!jumpBoundaries.isInBoundaries(memory.getLocation())) {
725
726 if (runnerStep > 1 && jumpBoundaries.isPastBoundaries(memory.getLocation())) {
727 debug(" ", "Already passed max take-off point, forcing jump!");
728 jumpForced = true;
729 jumpForce = jumpModule.computeJump(memory.getLocation(), jumpBoundaries, jumpVelocity, jumpAngleCos);
730 } else {
731 debug(" ", "Not within jump boundaries! We shouldn't JUMP");
732 jumpForce = Double.NaN;
733 }
734 } else {
735 jumpForce = jumpModule.computeJump(memory.getLocation(), jumpBoundaries, jumpVelocity, jumpAngleCos);
736 if (jumpForce < UnrealUtils.FULL_JUMP_FORCE) {
737 doubleJump = false;
738 }
739 }
740
741 if (jumpForce.isNaN()) {
742 if (jumpForced) {
743 if (!accelerating) {
744 debug(" ", "Forcing jump but NOT accelerating, postpone!");
745 move(firstLocation, secondLocation, focus);
746 return true;
747 }
748
749 jumpStep = 1;
750 debug(" ", "Forcing jump - MAX!");
751 return jump(true, UnrealUtils.FULL_DOUBLEJUMP_DELAY, UnrealUtils.FULL_DOUBLEJUMP_FORCE);
752 }
753
754
755 debug(" ", "Jump failed to compute, continuing with move! Computed force: " + jumpForce);
756 move(firstLocation, secondLocation, focus);
757 return true;
758 } else if (jumpForce < 0) {
759
760 debug(" ", "We don't need to jump, continuing with move! Computed force: " + jumpForce);
761 if (jumpBoundaries.isJumpable() && (this.jumpBoundaries.getTakeoffEdgeDirection() != null)) {
762
763
764 Location movementDirection = jumpBoundaries.getLandingTarget().sub(jumpBoundaries.getTakeOffMax()).setZ(0).getNormalized();
765 Location meshDirection = jumpBoundaries.getTakeoffEdgeDirection();
766
767 double fallAngleCos = meshDirection.setZ(0).getNormalized().dot(movementDirection);
768 double takeOffDistance = jumpBoundaries.getLandingTarget().getDistance2D(jumpBoundaries.getTakeOffMax());
769 if (Math.abs(fallAngleCos) > Math.cos(Math.PI / 2.5)) {
770
771 debug(" ", "Not direct approach to fall, we should jump a little. Angle: " + Math.acos(fallAngleCos) * (180 / Math.PI));
772
773 jumpBoundaries.setLandingTarget(jumpBoundaries.getTakeOffMax().interpolate(jumpBoundaries.getLandingTarget(), (IDEAL_JUMP_RESERVE / takeOffDistance)).setZ(jumpBoundaries.getTakeOffMax().z));
774 return true;
775 } else {
776 debug(" ", "Fall solved by not jumping, as angle is suitable. AngleCos: " + fallAngleCos);
777 jumpStep = 1;
778 return true;
779 }
780 } else {
781 debug(" ", "Fall solved by not jumping, as angle is suitable. Boundaries not jumpable.");
782 jumpStep = 1;
783 return true;
784 }
785 } else {
786 jumpStep = 1;
787 return jump(doubleJump, UnrealUtils.FULL_DOUBLEJUMP_DELAY, jumpForce);
788 }
789
790 }
791
792
793
794
795
796 private boolean jump(boolean doubleJump, double delay, double force) {
797 if (doubleJump) {
798 debug("DOUBLE-JUMPING[delay=" + delay + ", force=" + force + "]");
799 } else {
800 debug("JUMPING[force=" + force + "]");
801 }
802 body.jump(doubleJump, delay, force);
803
804 return true;
805 }
806
807
808
809
810
811
812
813 private boolean iterateJumpSequence() {
814 debug("iterateJumpSequence(): called");
815
816 switch (jumpStep) {
817
818 case 1:
819
820 if (velocityZ > 100) {
821 debug("iterateJumpSequence(): jumping in progress (velocityZ > 100), increasing jumpStep");
822 jumpStep++;
823 }
824
825 debug("iterateJumpSequence(): issuing move command to the target (just to be sure)");
826 move(firstLocation, secondLocation, focus);
827 return true;
828
829
830 default:
831
832 jumpStep++;
833 if (velocityZ <= 0.01) {
834 if (velocityZ > lastVelocityZ) {
835 debug("iterateJumpSequence(): jump has ended");
836 lastVelocityZ = 0.02;
837 jumpStep = 0;
838 } else {
839 lastVelocityZ = velocityZ;
840 }
841
842 }
843 debug("iterateJumpSequence(): continuing movement to the target");
844 move(firstLocation, secondLocation, focus);
845 return true;
846 }
847
848 }
849
850
851
852
853
854 private double lastVelocityZ = 0.02;
855
856 private boolean isMaxVelocity(double newVelocity) {
857 return Math.abs(newVelocity - UnrealUtils.MAX_VELOCITY) < 5;
858 }
859
860 private boolean isAccelerating(double newVelocity, double oldVelocity) {
861 return velocity > 0 && (isMaxVelocity(newVelocity) || newVelocity > oldVelocity);
862 }
863
864 private void debug(String message) {
865 debug("", message);
866 }
867
868 private void debug(String prefix, String message) {
869 if (log.isLoggable(Level.FINER)) {
870 log.log(Level.FINER, prefix + " +-- {0}", message);
871 }
872 }
873
874 }
875