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.base3d.worldview.object.Location;
20 import cz.cuni.amis.pogamut.ut2004.agent.navigation.navmesh.NavMesh;
21 import cz.cuni.amis.pogamut.ut2004.agent.navigation.navmesh.NavMeshConstants;
22 import cz.cuni.amis.pogamut.ut2004.agent.navigation.navmesh.NavMeshPolygon;
23 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPoint;
24 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPointNeighbourLink;
25 import cz.cuni.amis.pogamut.ut2004.utils.LinkFlag;
26 import cz.cuni.amis.pogamut.ut2004.utils.UnrealUtils;
27 import java.util.logging.Level;
28 import java.util.logging.Logger;
29 import javax.vecmath.Vector2d;
30 import math.geom2d.Point2D;
31 import math.geom2d.Vector2D;
32 import math.geom2d.line.Line2D;
33
34
35
36
37
38
39
40 public class JumpModule {
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 public static final double MAX_DOUBLE_JUMP_POWER = 755;
57 public static final double MAX_SINGLE_JUMP_POWER = 340;
58
59 private NavMesh navMesh;
60
61 private Logger log;
62
63 public static final double MAX_JUMP_HEIGHT = 130;
64 private static final double MAX_SINGLE_JUMP_HEIGHT = 60;
65 private static final double NAVMESH_Z_COORD_CORRECTION = 20;
66 private static final double SPEED_BOOST_DELAY = 0.100;
67 private static final double BOT_RADIUS = 70;
68 private static final double JUMP_PEEK_TIME = 0.39;
69
70 public JumpModule(NavMesh mesh, Logger log) {
71 this.navMesh = mesh;
72 this.log = log;
73 }
74
75
76
77
78
79
80
81
82
83
84 public JumpBoundaries computeJumpBoundaries(NavPointNeighbourLink jumpLink) {
85 if (jumpLink == null) {
86 return new JumpBoundaries(null);
87 }
88
89 NavPoint startNavPoint = jumpLink.getFromNavPoint();
90 NavPoint endNavPoint = jumpLink.getToNavPoint();
91 Location startLocation = startNavPoint.getLocation();
92 Location endLocation = endNavPoint.getLocation();
93
94
95 Location linkDirection3d = endLocation.sub(startLocation);
96 Vector2d linkDirection = new Vector2d(linkDirection3d.x, linkDirection3d.y);
97
98
99 BorderPoint startBorder = getBorderPoint(startLocation, endLocation);
100 Location startBorderPoint = startBorder.getPoint();
101
102
103 Vector2d negatedLinkDirection = new Vector2d(linkDirection);
104 negatedLinkDirection.negate();
105
106
107 BorderPoint endBorderPoint = getBorderPoint(endLocation, startLocation);
108 if (!endBorderPoint.getPoint().equals(endNavPoint.getLocation(), 1.0)) {
109
110 endBorderPoint.setPoint(endBorderPoint.getPoint().addZ(NAVMESH_Z_COORD_CORRECTION));
111 }
112
113
114 boolean borderToBorder = isJumpable(startBorderPoint, endBorderPoint.getPoint(), UnrealUtils.MAX_VELOCITY);
115 if (!borderToBorder) {
116
117 return new JumpBoundaries(jumpLink);
118 }
119
120
121 Location testBoundary = startLocation;
122 Location currentBoundary = startBorderPoint;
123 boolean boundaryFound = false;
124 double distanceToSearch = 0;
125 do {
126 boolean isJumpable = isJumpable(testBoundary, endBorderPoint.getPoint(), UnrealUtils.MAX_VELOCITY);
127 if (isJumpable && distanceToSearch < BOUNDARY_THRESHOLD) {
128 currentBoundary = testBoundary;
129 boundaryFound = true;
130 } else if (isJumpable) {
131
132 currentBoundary = testBoundary;
133 distanceToSearch /= 2;
134 testBoundary = getNavMeshPoint(testBoundary, startLocation, distanceToSearch);
135
136 } else if (distanceToSearch < BOUNDARY_THRESHOLD && distanceToSearch > 0) {
137
138 boundaryFound = true;
139 } else {
140
141 if (distanceToSearch == 0) {
142 distanceToSearch = testBoundary.getDistance2D(startBorderPoint);
143 }
144 distanceToSearch /= 2;
145 testBoundary = getNavMeshPoint(testBoundary, startBorderPoint, distanceToSearch);
146 }
147
148 } while (!boundaryFound);
149
150 return new JumpBoundaries(jumpLink, currentBoundary, startBorderPoint, startBorder.getDirection(), endBorderPoint.getPoint(), endBorderPoint.getDirection(), endLocation);
151 }
152 private static final int BOUNDARY_THRESHOLD = 50;
153
154
155
156
157
158
159
160
161 public BorderPoint getBorderPoint(Location start, Location end) {
162 int endPolygonId = navMesh.getPolygonId(end);
163 return getBorderPoint(start, end, -1, endPolygonId, start, 0);
164 }
165
166
167
168
169
170
171
172
173
174 private BorderPoint getBorderPoint(Location start, Location end, int pId, int endPolygonId, Location lastCross, int depth) {
175
176
177 Line2D ray = new Line2D(start.x, start.y, end.x, end.y);
178
179 if (pId < 0) {
180 pId = navMesh.getPolygonId(start);
181 }
182 if (pId < 0 || depth > 250) {
183 return new BorderPoint(start, null);
184 }
185 if (pId == endPolygonId) {
186 return new BorderPoint(end, null);
187 }
188
189
190
191
192
193
194 int currentPolygonId = pId;
195 int nextPolygonId = -1;
196
197
198 Point2D cross = null, cross2 = null;
199 int v1 = -1, v2 = -1;
200 int c2v1 = -1, c2v2 = -1;
201 double[] vertex1 = null;
202 double[] vertex2 = null;
203 int[] polygon = navMesh.getPolygon(currentPolygonId);
204 boolean foundFirstCross = false;
205 for (int i = 0; i < polygon.length; i++) {
206 v1 = polygon[i];
207 v2 = polygon[((i == polygon.length - 1) ? 0 : i + 1)];
208 vertex1 = navMesh.getVertex(v1);
209 vertex2 = navMesh.getVertex(v2);
210 Line2D edge = new Line2D(vertex1[0], vertex1[1], vertex2[0], vertex2[1]);
211 cross = ray.getIntersection(edge);
212
213 if (cross != null) {
214 if (((cross.x <= Math.max(edge.p1.x, edge.p2.x) && cross.x >= Math.min(edge.p1.x, edge.p2.x)) || Math.abs(cross.x - edge.p1.x) < 0.0001)
215 && ((cross.x <= Math.max(ray.p1.x, ray.p2.x) && cross.x >= Math.min(ray.p1.x, ray.p2.x)) || Math.abs(cross.x - ray.p1.x) < 0.0001)) {
216
217 if (foundFirstCross) {
218 break;
219 } else {
220 cross2 = cross;
221 c2v1 = v1;
222 c2v2 = v2;
223 foundFirstCross = true;
224 }
225 } else {
226
227 cross = null;
228 }
229 }
230 }
231
232
233 if (cross2 == null) {
234 return new BorderPoint(start, null);
235 } else if (cross == null) {
236 cross = cross2;
237 v1 = c2v1;
238 v2 = c2v2;
239 vertex1 = navMesh.getVertex(v1);
240 vertex2 = navMesh.getVertex(v2);
241 } else {
242 double distToCross = end.getDistance2D(new Location(cross.x, cross.y));
243 double distToCross2 = end.getDistance2D(new Location(cross2.x, cross2.y));
244 if (distToCross > distToCross2) {
245 cross = cross2;
246 v1 = c2v1;
247 v2 = c2v2;
248 vertex1 = navMesh.getVertex(v1);
249 vertex2 = navMesh.getVertex(v2);
250 }
251 }
252
253
254 nextPolygonId = navMesh.getNeighbourPolygon(currentPolygonId, v1, v2);
255
256 Location vertex1Loc = new Location(vertex1);
257 Location vertex2Loc = new Location(vertex2);
258 Location crossLocation = new Location(cross.x, cross.y);
259
260 double koef = vertex1Loc.getDistance2D(crossLocation) / vertex1Loc.getDistance2D(vertex2Loc);
261
262 Location border = vertex1Loc.interpolate(vertex2Loc, koef);
263
264 if (nextPolygonId == -1) {
265
266
267 return new BorderPoint(border.addZ(NavMeshConstants.liftPolygonLocation), vertex2Loc.sub(vertex1Loc));
268
269 } else {
270
271
272 if (border.getDistance2D(lastCross) < 2.0) {
273
274 Vector2D directionVector = new Vector2D(end.x - start.x, end.y - start.y);
275 directionVector.normalize();
276 border = border.addX(directionVector.getX() * 3);
277 border = border.addY(directionVector.getY() * 3);
278 }
279
280 log.log(Level.INFO, "getBorderPoint(): Border location: {0} Next polygon: {1}", new Object[]{border, nextPolygonId});
281 return getBorderPoint(border.addZ(NavMeshConstants.liftPolygonLocation), end, nextPolygonId, endPolygonId, border, ++depth);
282 }
283 }
284
285
286
287
288
289
290
291 public boolean needsJump(NavPointNeighbourLink link) {
292 if (link == null) {
293
294 return false;
295 } else {
296
297 if ((link.getFlags() & LinkFlag.JUMP.get()) != 0) {
298
299 return true;
300 }
301 if (link.isForceDoubleJump()) {
302
303 return true;
304 }
305 if (link.getNeededJump() != null) {
306
307 return true;
308 }
309 }
310
311 return false;
312 }
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328 public Double computeJump(Location start, JumpBoundaries boundaries, double velocity, double jumpAngleCos) {
329 Location end = boundaries.getLandingTarget();
330 double distance2d = getDistance2D(start, boundaries.getLandingTarget(), jumpAngleCos);
331 double targetZ = end.z - start.z;
332 debug("Jump {0} --(D3D:{1}|D2D:{2}|D2DC:{3}|DZ:{4})--> {5} [Velocity {6}]", new Object[]{start, end.getDistance(start), end.getDistance2D(start), distance2d, targetZ, end, velocity});
333
334
335 double timeToPassDistance = getTimeToPassDistance(distance2d, velocity) - 0.055;
336 Double force = computeJump(targetZ, timeToPassDistance, jumpAngleCos);
337
338 double originalTime = timeToPassDistance;
339 int jumpKoef = originalTime > JUMP_PEEK_TIME ? 2 : 1;
340
341 if (force.equals(Double.NaN) && timeToPassDistance < jumpKoef * JUMP_PEEK_TIME && targetZ > 0) {
342 force = getPowerForJumpByZDiff(targetZ);
343 }
344
345 if (force.equals(Double.NaN) || force < 0) {
346 return force;
347 }
348
349 if (log.isLoggable(Level.FINER)) {
350 debug("Computed force before collision adjustment: {0}", force);
351 }
352
353 Location collisionLocation = getCollisionLocation(boundaries);
354 if (collisionLocation == null) {
355 return force;
356 } else {
357 double collisionDistance = getDistance2D(start, collisionLocation, jumpAngleCos);
358 double timeToCollision = getTimeToPassDistance(collisionDistance, velocity);
359
360 double collidingTime = JUMP_PEEK_TIME * (force <= MAX_SINGLE_JUMP_POWER ? 1 : 2) - timeToCollision;
361 if (collidingTime > 0) {
362
363 Location edgeDirection = boundaries.getTargetEdgeDirection().setZ(0).getNormalized();
364 Location computedDirection = boundaries.getLandingTarget().sub(boundaries.getTakeOffMax()).setZ(0).getNormalized();
365 Location verticalDirection = new Location(edgeDirection.y, -edgeDirection.x);
366
367 double angleCos = verticalDirection.dot(computedDirection);
368
369
370 double collisionCoef = 1 + (collidingTime / JUMP_PEEK_TIME) * (1 - angleCos);
371 force = Math.min(MAX_DOUBLE_JUMP_POWER, force * collisionCoef);
372 debug("Possible jump collision detected, adjusting power. NEW POWER: {0}, Angle cos: {1}, Colliding time: {2}", new Object[]{force, angleCos, collidingTime});
373 }
374
375 return force;
376 }
377
378 }
379
380
381
382
383
384
385
386
387
388
389
390 public Double computeJump(double targetZ, double timeToPassDistance, double jumpAngleCos) {
391
392 if (!isJumpable(timeToPassDistance, targetZ)) {
393
394 debug("We are not able to jump there! Time: {0} Z: {1}", new Object[]{timeToPassDistance, targetZ});
395 return Double.NaN;
396 }
397
398
399 if (log.isLoggable(Level.FINER)) {
400 debug("Computing jump. Time to pass the distance: {0}", timeToPassDistance);
401 }
402
403 Double power = Double.NaN;
404
405 if (isSingleJumpable(timeToPassDistance, targetZ)) {
406 debug("Computing jump. Single jump should suffice.");
407
408 power = getSingleJumpPower(targetZ, timeToPassDistance);
409 }
410
411 if (power.equals(Double.NaN)) {
412 debug("Computing jump. Double jump will be needed.");
413 power = getDoubleJumpPower(targetZ, timeToPassDistance, UnrealUtils.FULL_DOUBLEJUMP_DELAY);
414 }
415
416 return power;
417 }
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433 public Double computeFall(Location start, JumpBoundaries boundaries, double velocity, double jumpAngleCos) {
434 Location end = boundaries.getLandingTarget();
435
436 double distance2D = getDistance2D(start, boundaries.getLandingTarget(), jumpAngleCos);
437
438 double targetZ = end.z - start.z;
439
440 debug("Fall {0} --(D3D:{1}|D2D:{2}|D2DC:{3}|DZ:{4})--> {5} [Velocity {6}]", new Object[]{start, end.getDistance(start), end.getDistance2D(start), distance2D, targetZ, end, velocity});
441
442 double fallSpeed = 430;
443 double fallTime = Math.abs(targetZ) / fallSpeed;
444 double fallDistance2D = velocity * fallTime;
445
446 double remainingDistance2D = distance2D - fallDistance2D;
447
448 if (remainingDistance2D < 0) {
449 debug("Remaining distance after fall " + ((int)remainingDistance2D) + " < 0 => perform only small jump");
450 return 110.0d;
451 }
452
453
454 double timeToPassDistance = getTimeToPassDistance(distance2D, velocity) - 0.055;
455 Double force = computeFall(targetZ, timeToPassDistance, jumpAngleCos);
456
457 double originalTime = timeToPassDistance;
458 int jumpKoef = originalTime > JUMP_PEEK_TIME ? 2 : 1;
459
460 if (force.equals(Double.NaN) && timeToPassDistance < jumpKoef * JUMP_PEEK_TIME && targetZ > 0) {
461 force = getPowerForJumpByZDiff(targetZ);
462 }
463
464 if (force.equals(Double.NaN) || force < 0) {
465 return force;
466 }
467
468 if (log.isLoggable(Level.FINER)) {
469 debug("Computed force before collision adjustment: {0}", force);
470 }
471
472 Location collisionLocation = getCollisionLocation(boundaries);
473 if (collisionLocation == null) {
474 return force;
475 } else {
476 double collisionDistance = getDistance2D(start, collisionLocation, jumpAngleCos);
477 double timeToCollision = getTimeToPassDistance(collisionDistance, velocity);
478
479 double collidingTime = JUMP_PEEK_TIME * (force <= MAX_SINGLE_JUMP_POWER ? 1 : 2) - timeToCollision;
480 if (collidingTime > 0) {
481
482 Location edgeDirection = boundaries.getTargetEdgeDirection().setZ(0).getNormalized();
483 Location computedDirection = boundaries.getLandingTarget().sub(boundaries.getTakeOffMax()).setZ(0).getNormalized();
484 Location verticalDirection = new Location(edgeDirection.y, -edgeDirection.x);
485
486 double angleCos = verticalDirection.dot(computedDirection);
487
488
489 double collisionCoef = 1 + (collidingTime / JUMP_PEEK_TIME) * (1 - angleCos);
490 force = Math.min(MAX_DOUBLE_JUMP_POWER, force * collisionCoef);
491 debug("Possible jump collision detected, adjusting power. NEW POWER: {0}, Angle cos: {1}, Colliding time: {2}", new Object[]{force, angleCos, collidingTime});
492 }
493
494 return force;
495 }
496
497 }
498
499
500
501
502
503
504
505
506
507
508
509 public Double computeFall(double targetZ, double timeToPassDistance, double jumpAngleCos) {
510
511 if (!isJumpable(timeToPassDistance, targetZ)) {
512
513 debug("We are not able to jump there! Time: {0} Z: {1}", new Object[]{timeToPassDistance, targetZ});
514 return Double.NaN;
515 }
516
517
518 if (log.isLoggable(Level.FINER)) {
519 debug("Computing jump. Time to pass the distance: {0}", timeToPassDistance);
520 }
521
522 Double power = Double.NaN;
523
524 if (isSingleJumpable(timeToPassDistance, targetZ)) {
525 debug("Computing jump. Single jump should suffice.");
526
527 power = getSingleJumpPower(targetZ, timeToPassDistance);
528 }
529
530 if (power.equals(Double.NaN)) {
531 debug("Computing jump. Double jump will be needed.");
532 power = getDoubleJumpPower(targetZ, timeToPassDistance, UnrealUtils.FULL_DOUBLEJUMP_DELAY);
533 }
534
535 return power;
536 }
537
538
539
540
541
542 private double getDistance2D(Location start, Location end, double jumpAngleCos) {
543
544 double distance2d = start.getDistance2D(end);
545
546 if (jumpAngleCos > 0) {
547 distance2d = start.getDistance2D(end) / jumpAngleCos;
548
549 } else {
550
551 }
552 return distance2d;
553 }
554
555
556
557
558
559
560
561
562
563 public boolean isJumpable(Location start, Location end, double velocity) {
564 if (start == null || end == null) {
565 return false;
566 }
567
568 if (end.z - start.z > MAX_JUMP_HEIGHT) {
569
570 return false;
571 }
572
573 double distance2d = start.getDistance2D(end);
574
575 return isJumpable(distance2d, velocity, end.z - start.z);
576 }
577
578 private boolean isJumpable(double distance2d, double velocity, double zDiff) {
579 double timeToPassDistance = getTimeToPassDistance(distance2d, velocity);
580 return isJumpable(timeToPassDistance, zDiff);
581 }
582
583 private boolean isJumpable(double timeToPassDistance, double zDiff) {
584 double computedZDiff;
585 double maxTime = 2 * JUMP_PEEK_TIME;
586 if (timeToPassDistance < maxTime) {
587 computedZDiff = MAX_JUMP_HEIGHT;
588 } else {
589 computedZDiff = getZDiffForJump(MAX_DOUBLE_JUMP_POWER, timeToPassDistance, true, 0.39);
590 }
591
592 return computedZDiff >= zDiff;
593 }
594
595 private double getTimeToPassDistance(double distance2d, double velocity) {
596
597
598 if (distance2d < velocity * SPEED_BOOST_DELAY) {
599 return distance2d / velocity;
600 } else {
601 return SPEED_BOOST_DELAY + (distance2d - velocity * SPEED_BOOST_DELAY) / (velocity * JUMP_SPEED_BOOST);
602 }
603 }
604
605 private static final double JUMP_SPEED_BOOST = 1.08959;
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621 private double getZDiffForJump(double power, double deltaTime, boolean isDoubleJump, double delay) {
622 double z = 0;
623
624
625 z += -475 * (deltaTime * deltaTime) + Math.min(power, MAX_SINGLE_JUMP_POWER) * deltaTime;
626 if (isDoubleJump) {
627
628 z += ((power - MAX_SINGLE_JUMP_POWER) * 1.066 + 2) * (deltaTime - delay);
629 }
630 return z;
631 }
632
633 private double getSingleJumpPower(double targetZ, double time) {
634
635 double power = (targetZ + 475 * time * time) / time;
636
637 if (power > MAX_SINGLE_JUMP_POWER) {
638 return Double.NaN;
639 }
640
641 return power;
642 }
643
644 private double getDoubleJumpPower(double targetZ, double time, double delay) {
645 double power = 0;
646
647 if (time < delay) {
648 debug("Computing double jump power. Time is lower than delay, postponing result.");
649 power = getPowerForJumpByZDiff(targetZ);
650 if (power < 0) {
651 return power;
652 }
653 return Double.NaN;
654 } else {
655
656
657 power = (targetZ + 475 * time * time + 20 * time - 140) / (1.066 * (time - delay));
658 debug("Computing double jump power. targetZ: {0}, Time: {1}, Delay: {2}, POWER: {3}", new Object[]{targetZ, time, delay, power});
659
660 }
661
662 if (power > MAX_DOUBLE_JUMP_POWER) {
663 return Double.NaN;
664 }
665
666 return power;
667 }
668
669 private Location getNavMeshPoint(Location from, Location direction, double distance) {
670
671 return from.interpolate(direction, distance / from.getDistance2D(direction));
672 }
673
674 private boolean isSingleJumpable(double timeToPassDistance, double zDiff) {
675 if (zDiff > MAX_SINGLE_JUMP_HEIGHT) {
676
677 return false;
678 }
679
680 if (timeToPassDistance < JUMP_PEEK_TIME) {
681 return true;
682 }
683
684 double computedZDiff = getZDiffForJump(MAX_SINGLE_JUMP_POWER, timeToPassDistance, false, 0);
685
686 return computedZDiff >= zDiff;
687 }
688
689 public Location getCollisionLocation(JumpBoundaries boundaries) {
690
691 if (!boundaries.isJumpUp()) {
692 return null;
693 }
694 if (boundaries.getTargetEdgeDirection() == null) {
695 return null;
696 }
697
698 Location edgeDirection = boundaries.getTargetEdgeDirection().setZ(0).getNormalized();
699 Location computedDirection = boundaries.getLandingTarget().sub(boundaries.getTakeOffMax()).setZ(0).getNormalized();
700 Location verticalDirection = new Location(edgeDirection.y, -edgeDirection.x);
701
702 double angleCos = verticalDirection.dot(computedDirection);
703
704 double xAngle = edgeDirection.dot(computedDirection);
705 if (Math.abs(xAngle) < Math.cos(Math.PI / 3)) {
706 debug("Computing collision. Ignoring collision. Edge to mesh angle cos: {0}", xAngle);
707 return null;
708 }
709 debug("Computing collision. Angle cos: {0}", angleCos);
710
711 double correctionDistance = (2 * BOT_RADIUS) / angleCos;
712
713 double koef = correctionDistance / boundaries.getLandingTarget().getDistance2D(boundaries.getTakeOffMax());
714
715 Location collisionLocation;
716 if (koef > 1.0) {
717 collisionLocation = boundaries.getTakeOffMax();
718 } else {
719 collisionLocation = boundaries.getLandingTarget().interpolate(boundaries.getTakeOffMax(), koef);
720 }
721
722 return collisionLocation;
723 }
724
725 public Location getNearestMeshDirection(Location location, Location direction) {
726
727
728 NavMeshPolygon poly = navMesh.getNearestPolygon(location);
729
730 Line2D ray = new Line2D(location.x, location.y, location.x + direction.x * 10000, location.y + direction.y * 10000);
731
732 int[] polygon = navMesh.getPolygon(poly.getPolygonId());
733 for (int i = 0; i < polygon.length; i++) {
734 int v1 = polygon[i];
735 int v2 = polygon[((i == polygon.length - 1) ? 0 : i + 1)];
736 double[] vertex1 = navMesh.getVertex(v1);
737 double[] vertex2 = navMesh.getVertex(v2);
738 Line2D edge = new Line2D(vertex1[0], vertex1[1], vertex2[0], vertex2[1]);
739 Point2D cross = ray.getIntersection(edge);
740 if (cross != null) {
741 if (((cross.x <= Math.max(edge.p1.x, edge.p2.x) && cross.x >= Math.min(edge.p1.x, edge.p2.x)) || Math.abs(cross.x - edge.p1.x) < 0.0001)
742 && ((cross.x <= Math.max(ray.p1.x, ray.p2.x) && cross.x >= Math.min(ray.p1.x, ray.p2.x)) || Math.abs(cross.x - ray.p1.x) < 0.0001)) {
743
744 Location vertex1Loc = new Location(vertex1);
745 Location vertex2Loc = new Location(vertex2);
746
747 return vertex2Loc.sub(vertex1Loc);
748 }
749 }
750 }
751 return null;
752 }
753
754 public double getCorrectedVelocity(double velocity, boolean isAccelerating) {
755 if (!isAccelerating) {
756
757 return velocity;
758 } else {
759 return Math.min(UnrealUtils.MAX_VELOCITY, 0.9788 * velocity + 111);
760 }
761 }
762
763 public double getCorrectedAngle(double angleCos, boolean isFirstStep) {
764 if (isFirstStep) {
765 return angleCos;
766 }
767 return Math.cos(Math.acos(angleCos) / 2);
768 }
769
770 private double getPowerForJumpByZDiff(double zDiff) {
771
772 zDiff += 5;
773
774 if (zDiff < MAX_SINGLE_JUMP_HEIGHT) {
775 return 3.87 * zDiff + 111;
776 } else {
777 return 3.136 * zDiff + 287;
778 }
779 }
780
781 private void debug(String msg) {
782 log.finer(" +-- " + msg);
783 }
784
785 private void debug(String msg, Object... objects) {
786 log.log(Level.FINER, " +-- " + msg, objects);
787 }
788
789 }