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, info, move,
166         		new LoqueNavigator<ILocated>(bot,info, move, 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 ILocated getFocus() {
263         return pathExecutor.getFocus();
264     }
265     
266     @Override
267     public void setFocus(ILocated located) {
268         pathExecutor.setFocus(located);
269         getBackToNavGraph.setFocus(located);
270         runStraight.setFocus(located);
271     }
272 
273     @Override
274     public void stopNavigation() {
275         reset(true, NavigationState.STOPPED);
276         bot.getAct().act(new Stop());
277     }
278     
279     @Override
280     public void navigate(ILocated target) {
281     	if (target == null) {
282     		if (log != null && log.isLoggable(Level.WARNING)) log.warning("Cannot navigate to NULL target!");
283     		reset(true, NavigationState.STOPPED);
284     		return;
285     	}
286     	
287     	if (target instanceof Player) {
288     		// USE DIFFERENT METHOD INSTEAD
289     		navigate((Player)target);
290     		return;
291     	}
292     	
293     	if (navigating) {
294     		if (currentTarget == target || currentTarget.getLocation().equals(target.getLocation())) {
295     			// just continue with current execution
296     			return;
297     		}    		
298     		// NEW TARGET!
299     		// => reset - stops pathExecutor as well, BUT DO NOT STOP getBackOnPath (we will need to do that eventually if needed, or it is not running)
300     		reset(false, null);
301     	}
302     	
303     	if (log != null && log.isLoggable(Level.FINE)) log.fine("Start navigating to: " + target);
304     	
305     	navigating = true;
306     	switchState(NavigationState.NAVIGATING);
307     	
308     	currentTarget = target;
309     	
310     	navigate();
311     }
312     
313     @Override
314     public void navigate(Player player) {
315     	if (player == null) {
316     		if (log != null && log.isLoggable(Level.WARNING)) log.warning("Cannot navigate to NULL player!");
317     		return;
318     	}
319     	
320     	if (navigating) {
321     		if (currentTarget == player) {
322     			// just continue with the execution
323     			return;
324     		}    		
325     		// NEW TARGET!
326     		// => reset - stops pathExecutor as well, BUT DO NOT STOP getBackOnPath (we will need to do that eventually if needed, or it is not running)
327     		reset(false, null);
328     	}
329     	
330     	if (log != null && log.isLoggable(Level.FINE)) log.fine("Start pursuing: " + player);
331     	
332     	navigating = true;
333     	switchState(NavigationState.NAVIGATING);
334     	
335     	// Current target and currentTarget player should refer to the same object.
336     	// Current target is used by navigatePlayer to compute new destination and 
337     	// by this method to see if the taret has changed.
338     	currentTarget = player;
339     	currentTargetPlayer = player;
340     	
341     	navigate();
342     }
343     
344 	@Override
345 	public void navigate(IPathFuture<ILocated> pathHandle) {
346 		if (pathHandle == null) {
347     		if (log != null && log.isLoggable(Level.WARNING)) log.warning("Cannot navigate to NULL pathHandle!");
348     		return;
349     	}
350     	
351     	if (navigating) {
352     		if (currentPathFuture == pathHandle) {
353     			// just continue with the execution
354     			return;
355     		}    		
356     		// NEW TARGET!
357     		// => reset - stops pathExecutor as well, BUT DO NOT STOP getBackOnPath (we will need to do that eventually if needed, or it is not running)
358     		reset(false, null);
359     	}
360     	
361     	if (log != null && log.isLoggable(Level.FINE)) log.fine("Start running along the path to target: " + pathHandle.getPathTo());
362     	
363     	navigating = true;
364     	switchState(NavigationState.NAVIGATING);
365     	
366     	currentTarget = pathHandle.getPathTo();
367     	currentPathFuture = pathHandle;
368     	
369     	navigate();
370 	}
371 
372     
373     @Override
374     public NavPoint getNearestNavPoint(ILocated location) {
375     	if (location == null) return null;
376     	if (location instanceof NavPoint) return (NavPoint)location;
377     	if (location instanceof Item) {
378     		if (((Item)location).getNavPoint() != null) return ((Item)location).getNavPoint();
379     	}
380     	return DistanceUtils.getNearest(bot.getWorldView().getAll(NavPoint.class).values(), location);        
381     }
382     
383     public ILocated getContinueTo() {
384     	return continueTo;
385     }
386     
387     public void setContinueTo(ILocated continueTo) {
388     	if (!isNavigating()) {
389     		log.warning("Cannot continueTo(" + continueTo + ") as navigation is not navigating!");
390     		return;
391     	}
392     	if (isNavigatingToPlayer()) {
393     		log.warning("Cannot continueTo(" + continueTo + ") as we're navigating to player!");
394     		return;
395     	}
396     	this.continueTo = continueTo;
397     	
398     	NavPoint from = getNearestNavPoint(currentTarget);
399     	NavPoint to = getNearestNavPoint(continueTo);
400     	
401     	this.continueToPath = pathPlanner.computePath(from, to);
402     	
403     	checkExtendPath();
404     }
405         
406 	@Override
407     public List<ILocated> getCurrentPathCopy() {
408         List<ILocated> result = new ArrayList();
409         if (currentPathFuture != null) {
410             result.addAll(currentPathFuture.get());
411         }
412         return result;
413     }
414 
415     @Override
416     public List<ILocated> getCurrentPathDirect() {
417         if (currentPathFuture != null) {
418             return currentPathFuture.get();
419         }
420         return null;
421     }
422     
423     @Override
424     public ILocated getCurrentTarget() {
425     	return currentTarget;
426     }
427     
428     @Override
429     public Player getCurrentTargetPlayer() {
430     	return currentTargetPlayer;
431     }
432     
433     @Override
434     public Item getCurrentTargetItem() {
435     	if (currentTarget instanceof Item) return (Item) currentTarget;
436     	return null;
437     }
438     
439     @Override
440     public NavPoint getCurrentTargetNavPoint() {
441     	if (currentTarget instanceof NavPoint) return (NavPoint) currentTarget;
442     	return null;
443     }
444 
445     
446     @Override
447     public ILocated getLastTarget() {
448     	return lastTarget;
449     }
450     
451     @Override
452     public Player getLastTargetPlayer() {
453     	return lastTargetPlayer;
454     }
455     
456     @Override
457     public Item getLastTargetItem() {
458     	if (lastTarget instanceof Item) return (Item)lastTarget;
459     	return null;
460     }
461     
462     @Override
463     public double getRemainingDistance() {
464     	if (!isNavigating()) return 0;
465     	if (isNavigatingToPlayer()) {
466     		if (isPathExecuting()) {
467     			return pathExecutor.getRemainingDistance() + pathExecutor.getPathTo().getLocation().getDistance(currentTargetPlayer.getLocation());
468     		} else {
469     			// TODO: HOW TO GET TRUE DISTANCE, IF YOU MAY HAVE ASYNC PATH-PLANNER?
470     			NavPoint from = getNearestNavPoint(bot.getLocation());
471     			NavPoint to = getNearestNavPoint(currentTargetPlayer.getLocation());
472     			IPathFuture<NavPoint> pathFuture = pathPlanner.computePath(from, to);
473     			if (pathFuture.isDone()) {
474     				return bot.getLocation().getDistance(from.getLocation()) + getPathDistance(pathFuture.get()) + to.getLocation().getDistance(currentTargetPlayer.getLocation());
475     			} else {
476     				// CANNOT BE COMPUTED DUE TO ASYNC PATH-PLANNER
477     				return -1;
478     			}
479     		}
480     	} else {
481     		if (isPathExecuting()) {
482     			return pathExecutor.getRemainingDistance();
483     		} else {
484     			// TODO: HOW TO GET TRUE DISTANCE, IF YOU MAY HAVE ASYNC PATH-PLANNER?
485     			NavPoint from = getNearestNavPoint(bot.getLocation());
486     			NavPoint to = getNearestNavPoint(currentTarget.getLocation());
487     			IPathFuture<NavPoint> pathFuture = pathPlanner.computePath(from, to);
488     			if (pathFuture.isDone()) {    				
489     				return bot.getLocation().getDistance(from.getLocation()) + getPathDistance(pathFuture.get()) + to.getLocation().getDistance(currentTarget.getLocation());
490     			} else {
491     				// CANNOT BE COMPUTED DUE TO ASYNC PATH-PLANNER
492     				return -1;
493     			}
494     		}
495     	}
496     }
497 
498     /**
499      * Careful here - although the input is List&lt;NavPoint&gt;, there may be location added to the end
500      * of the list when no close navpoint to final destination exists. Just treat everything in the list
501      * as ILocated and you should be fine. 
502      * */
503     private double getPathDistance(List list) {
504 		if (list == null || list.size() <= 0) return 0;
505     	double distance = 0;
506     	ILocated curr = (ILocated) list.get(0);
507     	for (int i = 1; i < list.size(); ++i) {
508     		ILocated next = (ILocated) list.get(i);
509     		distance += curr.getLocation().getDistance(next.getLocation());
510     		curr = next;
511     	}		
512 		return distance;
513 	}    
514     
515 
516 	// ======================
517     // VARIABLES
518     // ======================
519     
520     /** Last location target. */
521     protected ILocated lastTarget = null;
522     /** Last location target. */
523     protected Player   lastTargetPlayer = null;
524     /** Current location target. */
525     protected ILocated currentTarget = null;
526     /** Current target is player (if not null) */
527     protected Player   currentTargetPlayer = null;
528     /** Navpoint we're running from (initial position when path executor has been triggered) */
529     protected NavPoint fromNavPoint;
530     /** Navpoint we're running to, nearest navpoint to currentTarget */
531 	protected NavPoint toNavPoint;    
532     /** Current path stored in IPathFuture object. */
533     protected IPathFuture currentPathFuture;
534     /** Whether navigation is running. */
535     protected boolean navigating = false;
536     /** We're running straight to the player. */
537 	protected boolean runningStraightToPlayer = false;
538 	/** Where run-straight failed. */
539 	protected Location runningStraightToPlayerFailedAt = null;
540 	/** Whether we're using {@link UT2004Navigation#getBackToNavGraph}. */
541 	protected boolean usingGetBackToNavGraph = false;	
542 	/** Where the bot will continue to. */
543 	protected ILocated continueTo;
544 	/** Path to prolong. */
545 	protected IPathFuture<NavPoint> continueToPath;
546 	
547     // ======================
548     // UTILITY METHODS
549     // ======================
550     
551     protected void navigate() {
552 		if (!navigating) return;
553 		
554 		if (log != null && log.isLoggable(Level.FINE)) {
555 			log.fine("NAVIGATING");
556 		}
557 		if (currentTargetPlayer != null) {
558 			if (log != null && log.isLoggable(Level.FINE)) log.fine("Pursuing " + currentTargetPlayer);
559 			navigatePlayer();
560 		} else {
561 			if (log != null && log.isLoggable(Level.FINE)) log.fine("Navigating to " + currentTarget);
562 			navigateLocation();
563 		}
564 	}
565     
566     private void navigateLocation() {
567     	if (isPathExecuting()) {
568 			// Navigation is driven by Path Executor already...	
569     		// => check continueTo
570     		checkExtendPath();
571     		if (log != null && log.isLoggable(Level.FINE)) log.fine("Path executor running");			
572 			return;
573 		}
574 		
575 		// PATH EXECUTOR IS NOT RUNNING
576 		// => we have not started to run along path yet
577 
578 		// ARE WE ON NAV-GRAPH?
579 		if (!getBackToNavGraph.isOnNavGraph()) {
580 			// NO!
581 			// => get back to navigation graph
582 			if (log != null && log.isLoggable(Level.FINE)) log.fine("Getting back to navigation graph");
583     		if (getBackToNavGraph.isExecuting()) {
584     			// already running
585     			return;
586     		}
587     		if (usingGetBackToNavGraph) {
588     			// GetBackToNavGraph was already called && stopped && we're still not on nav graph
589     			// => stuck
590     			if (log != null && log.isLoggable(Level.WARNING)) log.warning("UT2004Navigation:stuck(). GetBackToNavGraph was already called && stopped && we're still not on nav graph.");
591     			stuck();
592     			return;
593     		}
594     		getBackToNavGraph.backToNavGraph();
595     		// => mark that we're using GetBackToNavGraph
596     		usingGetBackToNavGraph = true;			
597     		return;
598     	} else {
599     		usingGetBackToNavGraph = false;
600     	}
601 		// YES!    	
602     	// ... getBackToNavGraph will auto-terminate itself when we manage to get back to graph
603     	
604 		if (currentPathFuture == null) {
605 			fromNavPoint = getNearestNavPoint(bot.getLocation());
606 			toNavPoint   = getNearestNavPoint(currentTarget);
607     	
608 			if (log != null && log.isLoggable(Level.FINE)) log.fine("Computing path from " + fromNavPoint.getId().getStringId() + " to " + toNavPoint.getId().getStringId());
609     	
610 			currentPathFuture = pathPlanner.computePath(fromNavPoint, toNavPoint);
611 		}
612 		
613 		switch(currentPathFuture.getStatus()) {
614 		case FUTURE_IS_READY:
615 			// ALL OK!
616 			break;
617 		case FUTURE_IS_BEING_COMPUTED:
618 			if (log != null && log.isLoggable(Level.FINE)) log.fine("Waiting for the path to be computed...");
619 			return;
620 		case CANCELED:
621 			if (log != null && log.isLoggable(Level.WARNING)) log.warning("Path computation has been canceled.");
622 			noPath();
623 			return;
624 		case COMPUTATION_EXCEPTION:
625 			if (log != null && log.isLoggable(Level.WARNING)) log.warning("Path computation has failed with an exception.");
626 			noPath();
627 			return;		
628 		}
629     	
630 		// PATH IS READY!
631 		// => tinker the path
632     	if (!processPathFuture(currentPathFuture, currentTarget)) {
633     		noPath();
634     		return;
635     	}
636     	// => let's start running
637     	pathExecutor.followPath(currentPathFuture);	
638 	}
639     
640     private void checkExtendPath() {
641     	if (continueTo == null) return;
642     	if (continueToPath == null) {
643     		log.severe("continueTo specified, but continueToPath is NULL!");
644     		return;
645     	}
646     	if (isNavigatingToPlayer()) {
647     		log.warning("continueTo specified, but navigating to Player, INVALID!");
648     		return;
649     	}
650     	if (isPathExecuting()) {
651     		double remainingDistance = getRemainingDistance();
652     		if (remainingDistance < extendPathThreshold) {
653     			if (!continueToPath.isDone()) {
654     				log.warning("Should extend path, remainingDistance = " + remainingDistance + " < " + extendPathThreshold + " = extendPathThreshold, but continueToPath.isDone() == false, cannot extend path!");
655     				return;
656     			}
657     			log.info("Extending path to continue to " + continueTo);
658     			
659     			pathExecutor.extendPath(((List)continueToPath.get()));
660     			
661     			// ADJUST INTERNALS
662     			currentPathFuture = pathExecutor.getPathFuture();
663     			lastTarget = currentTarget;
664     			currentTarget = continueTo;    			
665     			fromNavPoint = getNearestNavPoint(((IPathFuture<ILocated>)currentPathFuture).get().get(0).getLocation());
666     			toNavPoint = getNearestNavPoint(((IPathFuture<ILocated>)currentPathFuture).get().get(currentPathFuture.get().size()-1).getLocation());    			
667     			
668     			continueTo = null;
669     			continueToPath = null;
670     		}
671     	}
672     	
673 	}
674 
675 
676 	private void navigatePlayer() {
677 		double vDistance = bot.getLocation().getDistanceZ(currentTargetPlayer.getLocation());
678 		double hDistance = bot.getLocation().getDistance2D(currentTargetPlayer.getLocation());
679 		
680 		if (hDistance < AT_PLAYER && vDistance < 50) {
681 			// player reached
682 			if (log != null && log.isLoggable(Level.FINE)) log.fine("Player reached");	
683 			if (pathExecutor.isExecuting()) {
684 				pathExecutor.getPath().set(pathExecutor.getPath().size()-1, bot.getLocation());
685 			} else {
686 				targetReached();
687 			}
688 			return;
689 		}
690 		
691 		if (hDistance < 400 && Math.abs(vDistance) < 50) {
692 			// RUN STRAIGHT			
693 			if (runningStraightToPlayer) {
694 				if (runStraight.isFailed()) {
695 					runningStraightToPlayer = false;
696 					runningStraightToPlayerFailedAt = bot.getLocation();
697 				}
698 			} else {
699 				if (runningStraightToPlayerFailedAt == null ||                           // we have not failed previously
700 					bot.getLocation().getDistance(runningStraightToPlayerFailedAt) > 500 // or place where we have failed is too distant
701 				) {
702 					if (getBackToNavGraph.isExecuting()) {
703 						getBackToNavGraph.stop();
704 						usingGetBackToNavGraph = false;
705 					}
706 					if (pathExecutor.isExecuting()) {
707 						pathExecutor.stop();
708 					}
709 					runningStraightToPlayer = true;
710 					runningStraightToPlayerFailedAt = null;
711 					runStraight.runStraight(currentTargetPlayer);
712 				}				
713 			}
714 			if (runningStraightToPlayer) {
715 				if (log != null && log.isLoggable(Level.FINE)) log.fine("Running straight to player");
716 				return;
717 			}
718 		} else {
719 			if (runningStraightToPlayer) {
720 				runningStraightToPlayer = false;
721 				runStraight.stop(false);				
722 			}
723 		}
724 		
725 		if (pathExecutor.isExecuting()) {
726 			// Navigation is driven by Path Executor already...			
727 			if (log != null && log.isLoggable(Level.FINE)) log.fine("Path executor running");
728 			// check distance between point we're navigating to and current player's location
729 			double distance = currentTarget.getLocation().getDistance(currentTargetPlayer.getLocation());
730 			
731 			if (distance < PLAYER_DISTANCE_TRASHOLD) {
732 				// PLAYER DID NOT MOVED TOO MUCH FROM ITS ORIGINAL POSITION
733 				// => continue running using pathExecutor
734 				return;
735 			}
736 			
737 			if (log != null && log.isLoggable(Level.FINE)) log.fine("Player moved " + distance + " from its original location, checking path...");
738 			// WE NEED TO CHECK ON PATH!					
739 			NavPoint newToNavPoint = getNearestNavPoint(currentTargetPlayer);
740 			if (newToNavPoint != toNavPoint) {
741 				// WE NEED TO ALTER THE PATH!
742 				if (log != null && log.isLoggable(Level.FINE)) log.fine("Replanning path to get to " + currentTargetPlayer);					
743 				pathExecutor.stop();
744 				currentPathFuture = null;
745 			} else {
746 				if (log != null && log.isLoggable(Level.FINE)) log.fine("Path remains the same");
747 				return;
748 			}
749 		}
750 		
751 		// PATH EXECUTOR IS NOT RUNNING
752 		// => we have not started to run along path yet
753 
754 		// ARE WE ON NAV-GRAPH?
755 		
756 		if (!getBackToNavGraph.isOnNavGraph()) {
757 			// NO!
758 			// => get back to navigation graph
759 			if (log != null && log.isLoggable(Level.FINE)) log.fine("Getting back to navigation graph");
760 			if (getBackToNavGraph.isExecuting()) {
761 				// nothing to see go along
762 				return;
763 			}
764 			if (usingGetBackToNavGraph) {
765     			// GetBackToNavGraph was already called && stopped && we're still not on nav graph
766     			// => stuck
767 				if (log != null && log.isLoggable(Level.WARNING)) log.warning("UT2004Navigation:stuck(). GetBackToNavGraph was already called && stopped && we're still not on nav graph.");
768     			stuck();
769     			return;
770     		}
771     		getBackToNavGraph.backToNavGraph();
772     		// => mark that we're using GetBackToNavGraph
773     		usingGetBackToNavGraph = true;			
774     		return;
775     	} else {
776     		usingGetBackToNavGraph = false;
777     	}
778 		// YES, WE'RE ON NAV-GRAPH!  	
779     	// ... getBackToNavGraph will auto-terminate itself when we manage to get back to graph
780     	
781 		if (currentPathFuture == null) {
782 			fromNavPoint = getNearestNavPoint(bot.getLocation());
783 			toNavPoint   = getNearestNavPoint(currentTarget);
784     	
785 			if (log != null && log.isLoggable(Level.FINE)) log.fine("Computing path from " + fromNavPoint.getId().getStringId() + " to " + toNavPoint.getId().getStringId());
786     	
787 			currentPathFuture = pathPlanner.computePath(fromNavPoint, toNavPoint);
788 		}
789 		
790 		switch(currentPathFuture.getStatus()) {
791 		case FUTURE_IS_READY:
792 			// ALL OK!
793 			break;
794 		case FUTURE_IS_BEING_COMPUTED:
795 			if (log != null && log.isLoggable(Level.FINE)) log.fine("Waiting for the path to be computed...");
796 			return;
797 		case CANCELED:
798 			if (log != null && log.isLoggable(Level.WARNING)) log.warning("Path computation has been canceled.");
799 			noPath();
800 			return;
801 		case COMPUTATION_EXCEPTION:
802 			if (log != null && log.isLoggable(Level.WARNING)) log.warning("Path computation has failed with an exception.");
803 			noPath();
804 			return;		
805 		}
806 		
807 		// PATH IS READY!
808     	// => tinker the path
809 		if (!processPathFuture(currentPathFuture, currentTarget)) {
810     		noPath();
811     		return;
812     	}
813     	// => let's start running
814     	pathExecutor.followPath(currentPathFuture);	
815 	}
816 
817 	/**
818      * Checks if last path element is in close distance from our desired target and if not, we
819      * will add our desired target as the last path element.
820      * @param futurePath
821      */
822     protected boolean processPathFuture(IPathFuture futurePath, ILocated currentTarget) {
823         List<ILocated> pathList = futurePath.get();
824         
825         if (pathList == null) {
826         	// we failed to compute the path, e.g., path does not exist
827         	return false;
828         }
829 
830         if (currentTarget == null) {
831         	if (pathList.size() == 0) return false;
832         	currentTarget = pathList.get(pathList.size()-1);
833         } else
834         if (pathList.size() == 0) {
835         	currentPathFuture.get().add(currentTarget);
836         } else {
837             ILocated lastPathElement = pathList.get(pathList.size() - 1);
838             if (lastPathElement.getLocation().getDistance(currentTarget.getLocation()) > NEW_PATH_DISTANCE_THRESHOLD) {
839                 currentPathFuture.get().add(currentTarget);
840             }
841         }
842         return true;
843     }
844     
845     protected void switchState(NavigationState newState) {
846     	state.setFlag(newState);    	    	
847     }
848     
849     protected void noPath() {
850 		// DAMN ...
851 		reset(true, NavigationState.PATH_COMPUTATION_FAILED);			
852 	}
853 
854         
855     protected void stuck() {
856     	// DAMN ...
857     	reset(true, NavigationState.STUCK);
858 	}
859 
860 	protected void targetReached() {
861 		// COOL !!!
862 		reset(true, NavigationState.TARGET_REACHED);
863 	}
864     
865     protected void reset(boolean stopGetBackToNavGraph, NavigationState resultState) {    	
866     	if (currentTarget != null) {
867     		lastTarget = currentTarget;
868     		lastTargetPlayer = currentTargetPlayer;    			
869     	}
870     	
871     	navigating = false;
872     	
873     	currentTarget = null;
874     	currentTargetPlayer = null;
875     	
876     	fromNavPoint = null;
877     	toNavPoint = null;
878     	
879     	currentPathFuture = null;
880     	
881     	runningStraightToPlayer = false;
882     	runningStraightToPlayerFailedAt = null;
883     	
884     	continueTo = null;
885     	continueToPath = null;
886     	
887     	pathExecutor.stop();
888     	runStraight.stop(false);
889     	if (stopGetBackToNavGraph) {
890     		getBackToNavGraph.stop();
891     		usingGetBackToNavGraph = false;
892     	}
893     	
894     	
895     	if (resultState == null) return;
896     	switchState(resultState);    		
897     }
898 
899 	@Override
900 	public Flag<NavigationState> getState() {
901 		return state.getImmutable();
902 	}
903     
904 }