View Javadoc

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