View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.agent.navigation.navmesh.pathfollowing;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   import java.util.logging.Level;
6   import java.util.logging.Logger;
7   
8   import cz.cuni.amis.pogamut.base.agent.navigation.IPathExecutorState;
9   import cz.cuni.amis.pogamut.base.agent.navigation.IPathFuture;
10  import cz.cuni.amis.pogamut.base.agent.navigation.IPathPlanner;
11  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
12  import cz.cuni.amis.pogamut.base.utils.logging.LogCategory;
13  import cz.cuni.amis.pogamut.base.utils.math.DistanceUtils;
14  import cz.cuni.amis.pogamut.base3d.worldview.object.ILocated;
15  import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
16  import cz.cuni.amis.pogamut.ut2004.agent.module.sensor.AgentInfo;
17  import cz.cuni.amis.pogamut.ut2004.agent.navigation.IUT2004GetBackToNavGraph;
18  import cz.cuni.amis.pogamut.ut2004.agent.navigation.IUT2004Navigation;
19  import cz.cuni.amis.pogamut.ut2004.agent.navigation.IUT2004PathExecutor;
20  import cz.cuni.amis.pogamut.ut2004.agent.navigation.IUT2004RunStraight;
21  import cz.cuni.amis.pogamut.ut2004.agent.navigation.NavigationState;
22  import cz.cuni.amis.pogamut.ut2004.agent.navigation.UT2004GetBackToNavGraph;
23  import cz.cuni.amis.pogamut.ut2004.agent.navigation.UT2004Navigation;
24  import cz.cuni.amis.pogamut.ut2004.agent.navigation.UT2004RunStraight;
25  import cz.cuni.amis.pogamut.ut2004.agent.navigation.navmesh.NavMesh;
26  import cz.cuni.amis.pogamut.ut2004.agent.navigation.navmesh.NavMeshModule;
27  import cz.cuni.amis.pogamut.ut2004.agent.navigation.stuckdetector.AccUT2004DistanceStuckDetector;
28  import cz.cuni.amis.pogamut.ut2004.agent.navigation.stuckdetector.AccUT2004PositionStuckDetector;
29  import cz.cuni.amis.pogamut.ut2004.agent.navigation.stuckdetector.AccUT2004TimeStuckDetector;
30  import cz.cuni.amis.pogamut.ut2004.bot.command.AdvancedLocomotion;
31  import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
32  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.Stop;
33  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.BotKilled;
34  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.EndMessage;
35  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Item;
36  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPoint;
37  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Player;
38  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Self;
39  import cz.cuni.amis.utils.flag.Flag;
40  import cz.cuni.amis.utils.flag.FlagListener;
41  
42  /**
43   * Facade for navigation in UT2004. Method navigate() can be called both
44   * synchronously and asynchronously.
45   * <br/><br/>
46   * Almost identical to {@link UT2004Navigation} but uses {@link NavMesh} under the hood and thus requires navmesh files to be present
47   * in the root of the project!
48   *
49   * @author knight
50   * @author jimmy
51   * @author Bogo
52   */
53  public class NavMeshNavigation implements IUT2004Navigation {
54  
55      public static double EXTEND_PATH_THRESHOLD = 500;
56      /**
57       * Log used by this class.
58       */
59      protected LogCategory log;
60      /**
61       * UT2004PathExecutor that is used for the navigation.
62       */
63      protected IUT2004PathExecutor<ILocated> pathExecutor;
64      /**
65       * NavMeshModule that is used for path planning, its {@link NavMeshModule#getNavMesh()} is saved within {@link #pathPlanner}. 
66       */
67      protected NavMeshModule navMeshModule;
68      /**
69       * NavMesh that is used for path planning.
70       */
71      protected NavMesh pathPlanner;
72      /**
73       * UT2004Bot reference.
74       */
75      protected UT2004Bot bot;
76      /**
77       * UT2004GetBackToNavGraph for returning bot back to the navigation graph.
78       */
79      protected IUT2004GetBackToNavGraph getBackToNavGraph;
80      /**
81       * UT2004RunStraight is used to run directly to player at some moment.
82       */
83      protected IUT2004RunStraight runStraight;
84      /**
85       * From which distance we should use
86       * {@link IUT2004PathExecutor#extendPath(List)}.
87       */
88      protected double extendPathThreshold;
89      /**
90       * Location threshold for requesting of a new path or switching a path.
91       */
92      protected static final int NEW_PATH_DISTANCE_THRESHOLD = 40;
93      /**
94       * Location threshold for checking whether we have arrived on target. For XY
95       * - 2D plane distance
96       */
97      protected static final int ARRIVED_AT_LOCATION_XY_THRESHOLD = 50;
98      /**
99       * Location threshold for checking whether we have arrived on target. For Z
100      * distance.
101      */
102     protected static final int ARRIVED_AT_LOCATION_Z_THRESHOLD = 100;
103     /**
104      * When PLAYER is further from currentTarget than this location, recompute
105      * the path
106      */
107     protected static final double PLAYER_DISTANCE_TRASHOLD = 600;
108     /**
109      * We're managed to get to player
110      */
111     public static final double AT_PLAYER = 100;
112     /**
113      * State of UT2004Navigation
114      */
115     protected Flag<NavigationState> state = new Flag<NavigationState>(NavigationState.STOPPED);
116 
117     /**
118      * Listener for UT2004PathExecutor state.
119      */
120     FlagListener<IPathExecutorState> myUT2004PathExecutorStateListener = new FlagListener<IPathExecutorState>() {
121 
122         @Override
123         public void flagChanged(IPathExecutorState changedValue) {
124             switch (changedValue.getState()) {
125                 case TARGET_REACHED:
126                     targetReached();
127                     break;
128                 case PATH_COMPUTATION_FAILED:
129                     noPath();
130                     break;
131                 case STUCK:
132                     if (log != null && log.isLoggable(Level.WARNING)) {
133                         log.warning("stuck() - Path executor reported stuck!");
134                     }
135                     stuck();
136                     break;
137             }
138         }
139     };
140     protected IWorldEventListener<EndMessage> endMessageListener = new IWorldEventListener<EndMessage>() {
141         @Override
142         public void notify(EndMessage event) {
143             navigate();
144         }
145     };
146 
147     protected IWorldEventListener<BotKilled> botKilledMessageListener = new IWorldEventListener<BotKilled>() {
148         @Override
149         public void notify(BotKilled event) {
150             reset(true, NavigationState.STOPPED);
151         }
152     };
153 
154     // ===========
155     // CONSTRUCTOR
156     // ===========
157 
158 
159     /**
160      * Setup navigation for using {@link NavMesh}. Currently in utilizes {@link UT2004AcceleratedPathExecutor} and {@link NavMeshRunner}.
161      *
162      * @param bot
163      * @param info
164      * @param move
165      */
166     public NavMeshNavigation(UT2004Bot bot, AgentInfo info, AdvancedLocomotion move, NavMeshModule navMeshModule) {
167         this.log = bot.getLogger().getCategory(this.getClass().getSimpleName());
168         this.bot = bot;
169         this.navMeshModule = navMeshModule;
170         this.pathPlanner = navMeshModule.getNavMesh();
171         
172         this.pathExecutor = new UT2004AcceleratedPathExecutor<ILocated>(
173                 bot, info, move,
174                 //TODO: Switch runners by parameter. For testing new runner.
175                 new NavMeshNavigator<ILocated>(bot, info, move, new NavMeshRunner(bot, info, move, log, navMeshModule.getNavMesh()), log));
176         
177         pathExecutor.addStuckDetector(new AccUT2004TimeStuckDetector(bot, 3000, 100000));
178         // if the bot does not move for 3 seconds, considered that it is stuck
179         
180         pathExecutor.addStuckDetector(new AccUT2004PositionStuckDetector(bot));
181     	// watch over the position history of the bot, if the bot does not move
182         // sufficiently enough, consider that it is stuck
183         
184         pathExecutor.addStuckDetector(new AccUT2004DistanceStuckDetector(bot));
185          // watch over distances to target
186 
187         this.getBackToNavGraph = new UT2004GetBackToNavGraph(bot, info, move);
188         this.runStraight = new UT2004RunStraight(bot, info, move);
189 
190         initListeners();
191     }
192 
193     @Override
194     public Logger getLog() {
195         return log;
196     }
197     
198     public void setLogLevel(Level level) {
199     	getLog().setLevel(level);
200     	pathExecutor.getLog().setLevel(level);
201     	getBackToNavGraph.getLog().setLevel(level);
202     	runStraight.getLog().setLevel(level);
203     }
204 
205     private void initListeners() {
206         this.pathExecutor.getState().addListener(myUT2004PathExecutorStateListener);
207         bot.getWorldView().addEventListener(EndMessage.class, endMessageListener);
208         bot.getWorldView().addEventListener(BotKilled.class, botKilledMessageListener);
209     }
210 
211     // ============================
212     // TWEAKING / LISTENERS
213     // ============================
214 
215     @Override
216     public void addStrongNavigationListener(FlagListener<NavigationState> listener) {
217         state.addStrongListener(listener);
218     }
219 
220     @Override
221     public void removeStrongNavigationListener(FlagListener<NavigationState> listener) {
222         state.removeListener(listener);
223     }
224 
225     @Override
226     public IUT2004PathExecutor<ILocated> getPathExecutor() {
227         return pathExecutor;
228     }
229     
230     @Override
231    	public IPathPlanner<ILocated> getPathPlanner() {
232    		return pathPlanner;
233    	}
234 
235     @Override
236     public IUT2004GetBackToNavGraph getBackToNavGraph() {
237         return getBackToNavGraph;
238     }
239 
240     @Override
241     public IUT2004RunStraight getRunStraight() {
242         return runStraight;
243     }
244 
245     // ======================
246     // PUBLIC INTERFACE
247     // ======================
248     
249     /**
250      * Returns true if the instance is ready to be used (has its navmesh successfully loaded and ready). 
251      * @return
252      */
253     public boolean isAvailable() {
254     	return navMeshModule.isInitialized();
255     }
256 
257     @Override
258     public boolean isNavigating() {
259         return navigating;
260     }
261 
262     @Override
263     public boolean isNavigatingToNavPoint() {
264         return isNavigating() && getCurrentTarget() instanceof NavPoint;
265     }
266 
267     @Override
268     public boolean isNavigatingToItem() {
269         return isNavigating() && getCurrentTarget() instanceof Item;
270     }
271 
272     @Override
273     public boolean isNavigatingToPlayer() {
274         return isNavigating() && getCurrentTarget() instanceof Player;
275     }
276 
277     @Override
278     public boolean isTryingToGetBackToNav() {
279         return getBackToNavGraph.isExecuting();
280     }
281 
282     @Override
283     public boolean isPathExecuting() {
284         return pathExecutor.isExecuting();
285     }
286 
287     @Override
288     public boolean isRunningStraight() {
289         return runStraight.isExecuting();
290     }
291 
292     @Override
293     public ILocated getFocus() {
294         return pathExecutor.getFocus();
295     }
296 
297     @Override
298     public void setFocus(ILocated located) {
299         pathExecutor.setFocus(located);
300         getBackToNavGraph.setFocus(located);
301         runStraight.setFocus(located);
302     }
303 
304     @Override
305     public void stopNavigation() {
306         reset(true, NavigationState.STOPPED);
307         bot.getAct().act(new Stop());
308     }
309 
310     @Override
311     public void navigate(ILocated target) {
312     	if (!isAvailable()) {
313     		log.severe("Cannot navigate as NavMesh was not successfully loaded! Check log during bot initialization to obtain more details.");
314     		return;
315     	}
316     	
317         if (target == null) {
318             if (log != null && log.isLoggable(Level.WARNING)) {
319                 log.warning("Cannot navigate to NULL target!");
320             }
321             reset(true, NavigationState.STOPPED);
322             return;
323         }
324 
325         if (target instanceof Player) {
326             // USE DIFFERENT METHOD INSTEAD
327             navigate((Player) target);
328             return;
329         }
330 
331         if (navigating) {
332             if (currentTarget == target || currentTarget.getLocation().equals(target.getLocation())) {
333                 // just continue with current execution
334                 return;
335             }
336             // NEW TARGET!
337             // => reset - stops pathExecutor as well, BUT DO NOT STOP getBackOnPath (we will need to do that eventually if needed, or it is not running)
338             reset(false, null);
339         }
340 
341         if (log != null && log.isLoggable(Level.FINE)) {
342             log.fine("Start navigating to: " + target);
343         }
344 
345         navigating = true;
346         switchState(NavigationState.NAVIGATING);
347 
348         currentTarget = target;
349 
350         navigate();
351     }
352 
353     @Override
354     public void navigate(Player player) {
355     	if (!isAvailable()) {
356     		log.severe("Cannot navigate as NavMesh was not successfully loaded! Check log during bot initialization to obtain more details.");
357     		return;
358     	}
359     	
360         if (player == null) {
361             if (log != null && log.isLoggable(Level.WARNING)) {
362                 log.warning("Cannot navigate to NULL player!");
363             }
364             return;
365         }
366 
367         if (navigating) {
368             if (currentTarget == player) {
369                 // just continue with the execution
370                 return;
371             }
372             // NEW TARGET!
373             // => reset - stops pathExecutor as well, BUT DO NOT STOP getBackOnPath (we will need to do that eventually if needed, or it is not running)
374             reset(false, null);
375         }
376 
377         if (log != null && log.isLoggable(Level.FINE)) {
378             log.fine("Start pursuing: " + player);
379         }
380 
381         navigating = true;
382         switchState(NavigationState.NAVIGATING);
383 
384         // Current target and currentTarget player should refer to the same object.
385         // Current target is used by navigatePlayer to compute new destination and 
386         // by this method to see if the taret has changed.
387         currentTarget = player;
388         currentTargetPlayer = player;
389 
390         navigate();
391     }
392 
393     @Override
394     public void navigate(IPathFuture<ILocated> pathHandle) {
395     	if (!isAvailable()) {
396     		log.severe("Cannot navigate as NavMesh was not successfully loaded! Check log during bot initialization to obtain more details.");
397     		return;
398     	}
399     	
400         if (pathHandle == null) {
401             if (log != null && log.isLoggable(Level.WARNING)) {
402                 log.warning("Cannot navigate to NULL pathHandle!");
403             }
404             return;
405         }
406 
407         if (navigating) {
408             if (currentPathFuture == pathHandle) {
409                 // just continue with the execution
410                 return;
411             }
412             // NEW TARGET!
413             // => reset - stops pathExecutor as well, BUT DO NOT STOP getBackOnPath (we will need to do that eventually if needed, or it is not running)
414             reset(false, null);
415         }
416 
417         if (log != null && log.isLoggable(Level.FINE)) {
418             log.fine("Start running along the path to target: " + pathHandle.getPathTo());
419         }
420 
421         navigating = true;
422         switchState(NavigationState.NAVIGATING);
423 
424         currentTarget = pathHandle.getPathTo();
425         currentPathFuture = pathHandle;
426 
427         navigate();
428     }
429 
430 
431     @Override
432     public NavPoint getNearestNavPoint(ILocated location) {
433         if (location == null) {
434             return null;
435         }
436         if (location instanceof NavPoint) {
437             return (NavPoint) location;
438         }
439         if (location instanceof Item) {
440             if (((Item) location).getNavPoint() != null) {
441                 return ((Item) location).getNavPoint();
442             }
443         }
444         return DistanceUtils.getNearest(bot.getWorldView().getAll(NavPoint.class).values(), location);
445     }
446 
447     public ILocated getContinueTo() {
448         return continueTo;
449     }
450 
451     public void setContinueTo(ILocated continueTo) {
452         if (!isNavigating()) {
453             log.warning("Cannot continueTo(" + continueTo + ") as navigation is not navigating!");
454             return;
455         }
456         if (isNavigatingToPlayer()) {
457             log.warning("Cannot continueTo(" + continueTo + ") as we're navigating to player!");
458             return;
459         }
460         this.continueTo = continueTo;
461 
462         NavPoint from = getNearestNavPoint(currentTarget);
463         NavPoint to = getNearestNavPoint(continueTo);
464 
465         this.continueToPath = pathPlanner.computePath(from, to);
466 
467         checkExtendPath();
468     }
469 
470     @Override
471     public List<ILocated> getCurrentPathCopy() {
472         List<ILocated> result = new ArrayList();
473         if (currentPathFuture != null) {
474             result.addAll(currentPathFuture.get());
475         }
476         return result;
477     }
478 
479     @Override
480     public List<ILocated> getCurrentPathDirect() {
481         if (currentPathFuture != null) {
482             return currentPathFuture.get();
483         }
484         return null;
485     }
486 
487     @Override
488     public ILocated getCurrentTarget() {
489         return currentTarget;
490     }
491 
492     @Override
493     public Player getCurrentTargetPlayer() {
494         return currentTargetPlayer;
495     }
496 
497     @Override
498     public Item getCurrentTargetItem() {
499         if (currentTarget instanceof Item) {
500             return (Item) currentTarget;
501         }
502         return null;
503     }
504 
505     @Override
506     public NavPoint getCurrentTargetNavPoint() {
507         if (currentTarget instanceof NavPoint) {
508             return (NavPoint) currentTarget;
509         }
510         return null;
511     }
512 
513     @Override
514     public ILocated getLastTarget() {
515         return lastTarget;
516     }
517 
518     @Override
519     public Player getLastTargetPlayer() {
520         return lastTargetPlayer;
521     }
522 
523     @Override
524     public Item getLastTargetItem() {
525         if (lastTarget instanceof Item) {
526             return (Item) lastTarget;
527         }
528         return null;
529     }
530 
531     @Override
532     public double getRemainingDistance() {
533         if (!isNavigating()) {
534             return 0;
535         }
536         if (isNavigatingToPlayer()) {
537             if (isPathExecuting()) {
538                 return pathExecutor.getRemainingDistance() + pathExecutor.getPathTo().getLocation().getDistance(currentTargetPlayer.getLocation());
539             } else {
540                 // TODO: HOW TO GET TRUE DISTANCE, IF YOU MAY HAVE ASYNC PATH-PLANNER?
541                 NavPoint from = getNearestNavPoint(bot.getLocation());
542                 NavPoint to = getNearestNavPoint(currentTargetPlayer.getLocation());
543                 IPathFuture<ILocated> pathFuture = pathPlanner.computePath(from, to);
544                 if (pathFuture.isDone()) {
545                     return bot.getLocation().getDistance(from.getLocation()) + getPathDistance(pathFuture.get()) + to.getLocation().getDistance(currentTargetPlayer.getLocation());
546                 } else {
547                     // CANNOT BE COMPUTED DUE TO ASYNC PATH-PLANNER
548                     return -1;
549                 }
550             }
551         } else {
552             if (isPathExecuting()) {
553                 return pathExecutor.getRemainingDistance();
554             } else {
555                 // TODO: HOW TO GET TRUE DISTANCE, IF YOU MAY HAVE ASYNC PATH-PLANNER?
556                 NavPoint from = getNearestNavPoint(bot.getLocation());
557                 NavPoint to = getNearestNavPoint(currentTarget.getLocation());
558                 IPathFuture<ILocated> pathFuture = pathPlanner.computePath(from, to);
559                 if (pathFuture.isDone()) {
560                     return bot.getLocation().getDistance(from.getLocation()) + getPathDistance(pathFuture.get()) + to.getLocation().getDistance(currentTarget.getLocation());
561                 } else {
562                     // CANNOT BE COMPUTED DUE TO ASYNC PATH-PLANNER
563                     return -1;
564                 }
565             }
566         }
567     }
568 
569     /**
570      * Careful here - although the input is List&lt;NavPoint&gt;, there may be
571      * location added to the end of the list when no close navpoint to final
572      * destination exists. Just treat everything in the list as ILocated and you
573      * should be fine. 
574      *
575      */
576     private double getPathDistance(List list) {
577         if (list == null || list.size() <= 0) {
578             return 0;
579         }
580         double distance = 0;
581         ILocated curr = (ILocated) list.get(0);
582         for (int i = 1; i < list.size(); ++i) {
583             ILocated next = (ILocated) list.get(i);
584             distance += curr.getLocation().getDistance(next.getLocation());
585             curr = next;
586         }
587         return distance;
588     }
589 
590 
591     // ======================
592     // VARIABLES
593     // ======================
594     /**
595      * Last location target.
596      */
597     protected ILocated lastTarget = null;
598     /**
599      * Last location target.
600      */
601     protected Player lastTargetPlayer = null;
602     /**
603      * Current location target.
604      */
605     protected ILocated currentTarget = null;
606     /**
607      * Current target is player (if not null)
608      */
609     protected Player currentTargetPlayer = null;
610     /**
611      * Navpoint we're running from (initial position when path executor has been
612      * triggered)
613      */
614     protected NavPoint fromNavPoint;
615     /**
616      * Navpoint we're running to, nearest navpoint to currentTarget
617      */
618     protected NavPoint toNavPoint;
619     /**
620      * Current path stored in IPathFuture object.
621      */
622     protected IPathFuture currentPathFuture;
623     /**
624      * Whether navigation is running.
625      */
626     protected boolean navigating = false;
627     /**
628      * We're running straight to the player.
629      */
630     protected boolean runningStraightToPlayer = false;
631     /**
632      * Where run-straight failed.
633      */
634     protected Location runningStraightToPlayerFailedAt = null;
635     /**
636      * Whether we're using {@link UT2004Navigation#getBackToNavGraph}.
637      */
638     protected boolean usingGetBackToNavGraph = false;
639     /**
640      * Where the bot will continue to.
641      */
642     protected ILocated continueTo;
643     /**
644      * Path to prolong.
645      */
646     protected IPathFuture<ILocated> continueToPath;
647 
648     // ======================
649     // UTILITY METHODS
650     // ======================
651 
652     protected void navigate() {
653         if (!navigating) {
654             return;
655         }
656 
657         if (log != null && log.isLoggable(Level.FINE)) {
658             log.fine("NAVIGATING");
659         }
660         if (currentTargetPlayer != null) {
661             if (log != null && log.isLoggable(Level.FINE)) {
662                 log.log(Level.FINE, "Pursuing {0}", currentTargetPlayer);
663             }
664             navigatePlayer();
665         } else {
666             if (log != null && log.isLoggable(Level.FINE)) {
667                 log.log(Level.FINE, "Navigating to {0}", currentTarget);
668             }
669             navigateLocation();
670         }
671     }
672 
673     private void navigateLocation() {
674         if (isPathExecuting()) {
675             // Navigation is driven by Path Executor already...	
676             // => check continueTo
677             checkExtendPath();
678             if (log != null && log.isLoggable(Level.FINE)) {
679                 log.fine("Path executor running");
680             }
681             return;
682         }
683 
684         // PATH EXECUTOR IS NOT RUNNING
685         // => we have not started to run along path yet
686         
687         // ARE WE ON NAV MESH?
688         if (!usingGetBackToNavGraph && navMeshModule.getNavMesh().getPolygonId(bot.getSelf().getLocation()) >= 0) {
689         	// YES, WE ARE!
690         } else {
691 	        // ARE WE ON NAV-GRAPH?
692 	        if (!getBackToNavGraph.isOnNavGraph()) {
693 	            // NO!
694 	            // => get back to navigation graph
695 	            if (log != null && log.isLoggable(Level.FINE)) {
696 	                log.fine("Getting back to navigation graph");
697 	            }
698 	            if (getBackToNavGraph.isExecuting()) {
699 	                // already running
700 	                return;
701 	            }
702 	            if (usingGetBackToNavGraph) {
703 	                // GetBackToNavGraph was already called && stopped && we're still not on nav graph
704 	                // => stuck
705 	                if (log != null && log.isLoggable(Level.WARNING)) {
706 	                    log.warning("stuck() - GetBackToNavGraph was already called && stopped && we're still not on nav graph.");
707 	                }
708 	                stuck();
709 	                return;
710 	            }
711 	            getBackToNavGraph.backToNavGraph();
712 	            // => mark that we're using GetBackToNavGraph
713 	            usingGetBackToNavGraph = true;
714 	            return;
715 	        } else {
716 	            usingGetBackToNavGraph = false;
717 	        }
718 	        // YES!    	
719 	        // ... getBackToNavGraph will auto-terminate itself when we manage to get back to graph
720         }
721 
722         if (currentPathFuture == null) {
723             fromNavPoint = getNearestNavPoint(bot.getLocation());
724             toNavPoint = getNearestNavPoint(currentTarget);
725 
726             if (log != null && log.isLoggable(Level.FINE)) {
727                 log.fine("Computing path from " + fromNavPoint.getId().getStringId() + " to " + toNavPoint.getId().getStringId());
728             }
729 
730             currentPathFuture = pathPlanner.computePath(fromNavPoint, toNavPoint);
731         }
732 
733         switch (currentPathFuture.getStatus()) {
734             case FUTURE_IS_READY:
735                 // ALL OK!
736                 break;
737             case FUTURE_IS_BEING_COMPUTED:
738                 if (log != null && log.isLoggable(Level.FINE)) {
739                     log.fine("Waiting for the path to be computed...");
740                 }
741                 return;
742             case CANCELED:
743                 if (log != null && log.isLoggable(Level.WARNING)) {
744                     log.warning("Path computation has been canceled.");
745                 }
746                 noPath();
747                 return;
748             case COMPUTATION_EXCEPTION:
749                 if (log != null && log.isLoggable(Level.WARNING)) {
750                     log.warning("Path computation has failed with an exception.");
751                 }
752                 noPath();
753                 return;
754         }
755 
756         // PATH IS READY!
757         // => tinker the path
758         if (!processPathFuture(currentPathFuture, currentTarget)) {
759             noPath();
760             return;
761         }
762         // => let's start running
763         pathExecutor.followPath(currentPathFuture);
764     }
765 
766     private void checkExtendPath() {
767         if (continueTo == null) {
768             return;
769         }
770         if (continueToPath == null) {
771             log.severe("continueTo specified, but continueToPath is NULL!");
772             return;
773         }
774         if (isNavigatingToPlayer()) {
775             log.warning("continueTo specified, but navigating to Player, INVALID!");
776             return;
777         }
778         if (isPathExecuting()) {
779             double remainingDistance = getRemainingDistance();
780             if (remainingDistance < extendPathThreshold) {
781                 if (!continueToPath.isDone()) {
782                     log.warning("Should extend path, remainingDistance = " + remainingDistance + " < " + extendPathThreshold + " = extendPathThreshold, but continueToPath.isDone() == false, cannot extend path!");
783                     return;
784                 }
785                 log.info("Extending path to continue to " + continueTo);
786 
787                 pathExecutor.extendPath(((List) continueToPath.get()));
788 
789                 // ADJUST INTERNALS
790                 currentPathFuture = pathExecutor.getPathFuture();
791                 lastTarget = currentTarget;
792                 currentTarget = continueTo;
793                 fromNavPoint = getNearestNavPoint(((IPathFuture<ILocated>) currentPathFuture).get().get(0).getLocation());
794                 toNavPoint = getNearestNavPoint(((IPathFuture<ILocated>) currentPathFuture).get().get(currentPathFuture.get().size() - 1).getLocation());
795 
796                 continueTo = null;
797                 continueToPath = null;
798             }
799         }
800 
801     }
802 
803 
804     private void navigatePlayer() {
805         double vDistance = bot.getLocation().getDistanceZ(currentTargetPlayer.getLocation());
806         double hDistance = bot.getLocation().getDistance2D(currentTargetPlayer.getLocation());
807 
808         if (hDistance < AT_PLAYER && vDistance < 50) {
809             // player reached
810             if (log != null && log.isLoggable(Level.FINE)) {
811                 log.fine("Player reached");
812             }
813             if (pathExecutor.isExecuting()) {
814                 pathExecutor.getPath().set(pathExecutor.getPath().size() - 1, bot.getLocation());
815             } else {
816                 targetReached();
817             }
818             return;
819         }
820 
821         if (hDistance < 400 && Math.abs(vDistance) < 50) {
822             // RUN STRAIGHT			
823             if (runningStraightToPlayer) {
824                 if (runStraight.isFailed()) {
825                     runningStraightToPlayer = false;
826                     runningStraightToPlayerFailedAt = bot.getLocation();
827                 }
828             } else {
829                 if (runningStraightToPlayerFailedAt == null || // we have not failed previously
830                         bot.getLocation().getDistance(runningStraightToPlayerFailedAt) > 500 // or place where we have failed is too distant
831                         ) {
832                     if (getBackToNavGraph.isExecuting()) {
833                         getBackToNavGraph.stop();
834                         usingGetBackToNavGraph = false;
835                     }
836                     if (pathExecutor.isExecuting()) {
837                         pathExecutor.stop();
838                     }
839                     runningStraightToPlayer = true;
840                     runningStraightToPlayerFailedAt = null;
841                     runStraight.runStraight(currentTargetPlayer);
842                 }
843             }
844             if (runningStraightToPlayer) {
845                 if (log != null && log.isLoggable(Level.FINE)) {
846                     log.fine("Running straight to player");
847                 }
848                 return;
849             }
850         } else {
851             if (runningStraightToPlayer) {
852                 runningStraightToPlayer = false;
853                 runStraight.stop(false);
854             }
855         }
856 
857         if (pathExecutor.isExecuting()) {
858             // Navigation is driven by Path Executor already...			
859             if (log != null && log.isLoggable(Level.FINE)) {
860                 log.fine("Path executor running");
861             }
862             // check distance between point we're navigating to and current player's location
863             double distance = currentTarget.getLocation().getDistance(currentTargetPlayer.getLocation());
864 
865             if (distance < PLAYER_DISTANCE_TRASHOLD) {
866                 // PLAYER DID NOT MOVED TOO MUCH FROM ITS ORIGINAL POSITION
867                 // => continue running using pathExecutor
868                 return;
869             }
870 
871             if (log != null && log.isLoggable(Level.FINE)) {
872                 log.fine("Player moved " + distance + " from its original location, checking path...");
873             }
874             // WE NEED TO CHECK ON PATH!					
875             NavPoint newToNavPoint = getNearestNavPoint(currentTargetPlayer);
876             if (newToNavPoint != toNavPoint) {
877                 // WE NEED TO ALTER THE PATH!
878                 if (log != null && log.isLoggable(Level.FINE)) {
879                     log.fine("Replanning path to get to " + currentTargetPlayer);
880                 }
881                 pathExecutor.stop();
882                 currentPathFuture = null;
883             } else {
884                 if (log != null && log.isLoggable(Level.FINE)) {
885                     log.fine("Path remains the same");
886                 }
887                 return;
888             }
889         }
890 
891         // PATH EXECUTOR IS NOT RUNNING
892         // => we have not started to run along path yet
893 
894         // ARE WE ON NAV-GRAPH?
895 
896         if (!getBackToNavGraph.isOnNavGraph()) {
897             // NO!
898             // => get back to navigation graph
899             if (log != null && log.isLoggable(Level.FINE)) {
900                 log.fine("Getting back to navigation graph");
901             }
902             if (getBackToNavGraph.isExecuting()) {
903                 // nothing to see go along
904                 return;
905             }
906             if (usingGetBackToNavGraph) {
907                 // GetBackToNavGraph was already called && stopped && we're still not on nav graph
908                 // => stuck
909                 if (log != null && log.isLoggable(Level.WARNING)) {
910                     log.warning("stuck() - GetBackToNavGraph was already called && stopped && we're still not on nav graph.");
911                 }
912                 stuck();
913                 return;
914             }
915             getBackToNavGraph.backToNavGraph();
916             // => mark that we're using GetBackToNavGraph
917             usingGetBackToNavGraph = true;
918             return;
919         } else {
920             usingGetBackToNavGraph = false;
921         }
922         // YES, WE'RE ON NAV-GRAPH!  	
923         // ... getBackToNavGraph will auto-terminate itself when we manage to get back to graph
924 
925         if (currentPathFuture == null) {
926             fromNavPoint = getNearestNavPoint(bot.getLocation());
927             toNavPoint = getNearestNavPoint(currentTarget);
928 
929             if (log != null && log.isLoggable(Level.FINE)) {
930                 log.fine("Computing path from " + fromNavPoint.getId().getStringId() + " to " + toNavPoint.getId().getStringId());
931             }
932 
933             currentPathFuture = pathPlanner.computePath(fromNavPoint, toNavPoint);
934         }
935 
936         switch (currentPathFuture.getStatus()) {
937             case FUTURE_IS_READY:
938                 // ALL OK!
939                 break;
940             case FUTURE_IS_BEING_COMPUTED:
941                 if (log != null && log.isLoggable(Level.FINE)) {
942                     log.fine("Waiting for the path to be computed...");
943                 }
944                 return;
945             case CANCELED:
946                 if (log != null && log.isLoggable(Level.WARNING)) {
947                     log.warning("Path computation has been canceled.");
948                 }
949                 noPath();
950                 return;
951             case COMPUTATION_EXCEPTION:
952                 if (log != null && log.isLoggable(Level.WARNING)) {
953                     log.warning("Path computation has failed with an exception.");
954                 }
955                 noPath();
956                 return;
957         }
958 
959         // PATH IS READY!
960         // => tinker the path
961         if (!processPathFuture(currentPathFuture, currentTarget)) {
962             noPath();
963             return;
964         }
965         // => let's start running
966         pathExecutor.followPath(currentPathFuture);
967     }
968 
969     /**
970      * Checks if last path element is in close distance from our desired target
971      * and if not, we will add our desired target as the last path element.
972      *
973      * @param futurePath
974      */
975     protected boolean processPathFuture(IPathFuture futurePath, ILocated currentTarget) {
976         List<ILocated> pathList = futurePath.get();
977 
978         if (pathList == null) {
979             // we failed to compute the path, e.g., path does not exist
980             return false;
981         }
982 
983         if (currentTarget == null) {
984 
985 
986 
987             if (pathList.size() == 0) {
988                 return false;
989             }
990             currentTarget = pathList.get(pathList.size() - 1);
991         } else if (pathList.size() == 0) {
992             currentPathFuture.get().add(currentTarget);
993         } else {
994             ILocated lastPathElement = pathList.get(pathList.size() - 1);
995             if (lastPathElement.getLocation().getDistance(currentTarget.getLocation()) > NEW_PATH_DISTANCE_THRESHOLD) {
996                 currentPathFuture.get().add(currentTarget);
997             }
998         }
999         return true;
1000     }
1001 
1002     protected void switchState(NavigationState newState) {
1003         state.setFlag(newState);
1004     }
1005 
1006     protected void noPath() {
1007         // DAMN ...
1008         reset(true, NavigationState.PATH_COMPUTATION_FAILED);
1009     }
1010 
1011 
1012     protected void stuck() {
1013         // DAMN ...
1014         reset(true, NavigationState.STUCK);
1015     }
1016 
1017     protected void targetReached() {
1018         // COOL !!!
1019         reset(true, NavigationState.TARGET_REACHED);
1020     }
1021 
1022     protected void reset(boolean stopGetBackToNavGraph, NavigationState resultState) {
1023         if (currentTarget != null) {
1024             lastTarget = currentTarget;
1025             lastTargetPlayer = currentTargetPlayer;
1026         }
1027 
1028         navigating = false;
1029 
1030         currentTarget = null;
1031         currentTargetPlayer = null;
1032 
1033         fromNavPoint = null;
1034         toNavPoint = null;
1035 
1036         currentPathFuture = null;
1037 
1038         runningStraightToPlayer = false;
1039         runningStraightToPlayerFailedAt = null;
1040 
1041         continueTo = null;
1042         continueToPath = null;
1043 
1044         pathExecutor.stop();
1045         runStraight.stop(false);
1046         if (stopGetBackToNavGraph) {
1047             getBackToNavGraph.stop();
1048             usingGetBackToNavGraph = false;
1049         }
1050 
1051 
1052         if (resultState == null) {
1053             return;
1054         }
1055         switchState(resultState);
1056     }
1057 
1058     @Override
1059     public Flag<NavigationState> getState() {
1060         return state.getImmutable();
1061     }
1062 }