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