View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.agent.navigation.loquenavigator;
2   
3   import java.util.Iterator;
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.communication.worldview.IWorldView;
9   import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEventListener;
10  import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectUpdatedEvent;
11  import cz.cuni.amis.pogamut.base.utils.math.DistanceUtils;
12  import cz.cuni.amis.pogamut.base3d.worldview.object.ILocated;
13  import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
14  import cz.cuni.amis.pogamut.ut2004.agent.module.sensor.AgentInfo;
15  import cz.cuni.amis.pogamut.ut2004.agent.navigation.AbstractUT2004PathNavigator;
16  import cz.cuni.amis.pogamut.ut2004.agent.navigation.IUT2004PathRunner;
17  import cz.cuni.amis.pogamut.ut2004.bot.command.AdvancedLocomotion;
18  import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
19  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Mover;
20  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPoint;
21  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPointNeighbourLink;
22  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Self;
23  import cz.cuni.amis.pogamut.ut2004.utils.LinkFlag;
24  import cz.cuni.amis.pogamut.ut2004.utils.UnrealUtils;
25  import cz.cuni.amis.utils.NullCheck;
26  
27  /**
28   * Responsible for navigation to location.
29   *
30   * <p>This class navigates the agent along path. Silently handles all casual
31   * trouble with preparing next nodes, running along current nodes, switching
32   * between nodes at appropriate distances, etc. In other words, give me a
33   * destination and a path and you'll be there in no time.</p>
34   *
35   * <h4>Preparing ahead</h4>
36   *
37   * Nodes in the path are being prepared ahead, even before they are actually
38   * needed. The agent decides ahead, looks at the next nodes while still running
39   * to current ones, etc.
40   *
41   * <h4>Reachability checks</h4>
42   *
43   * Whenever the agent switches to the next node, reachcheck request is made to
44   * the engine. The navigation routine then informs the {@link KefikRunner}
45   * beneath about possible troubles along the way.
46   *
47   * <h4>Movers</h4>
48   *
49   * This class was originally supposed to contain handy (and fully working)
50   * navigation routines, including safe navigation along movers. However, the
51   * pogamut platform is not quite ready for movers yet. Especial when it comes
52   * to mover frames and correct mover links.
53   *
54   * <p>Thus, we rely completely on navigation points. Since the mover navigation
55   * points (LiftCenter ones) always travel with the associated mover, we do not
56   * try to look for movers at all. We simply compare navigation point location
57   * to agent's location and wait or move accordingly.</p>
58   *
59   * <h4>Future</h4>
60   *
61   * The bot could check from time to time, whether the target destination he is
62   * traveling to is not an empty pickup spot, since the memory is now capable of
63   * reporting empty pickups, when they are visible. The only pitfall to this is
64   * the way the agent might get <i>trapped</i> between two not-so-far-away items,
65   * each of them empty. The more players playe the same map, the bigger is the
66   * chance of pickup emptyness. The agent should implement a <i>fadeing memory
67   * of which items are empty</i> before this can be put safely into logic.
68   *
69   * @author Juraj Simlovic [jsimlo@matfyz.cz]
70   * @author Jimmy
71   */
72  public class LoqueNavigator<PATH_ELEMENT extends ILocated> extends AbstractUT2004PathNavigator<PATH_ELEMENT>
73  {
74      /**
75       * Current navigation destination.
76       */
77      private Location navigDestination = null;
78  
79      /**
80       * Current stage of the navigation.
81       */
82      private Stage navigStage = Stage.COMPLETED;
83  
84      /**
85       * Current focus of the bot, if null, provide default focus.
86       * <p><p>
87       * Filled at the beginning of the {@link LoqueNavigator#navigate(ILocated, int)}.
88       */
89  	private ILocated focus = null;
90  	
91  	/**
92  	 * {@link Self} listener.
93  	 */
94  	private class SelfListener implements IWorldObjectEventListener<Self, WorldObjectUpdatedEvent<Self>>
95  	{
96  		private IWorldView worldView;
97  
98  		/**
99  		 * Constructor. Registers itself on the given WorldView object.
100 		 * @param worldView WorldView object to listent to.
101 		 */
102 		public SelfListener(IWorldView worldView)
103 		{
104 			this.worldView = worldView;
105 			worldView.addObjectListener(Self.class, WorldObjectUpdatedEvent.class, this);
106 		}
107 
108 		@Override
109 		public void notify(WorldObjectUpdatedEvent<Self> event) {
110 			self = event.getObject();			
111 		}
112 	}
113 	
114 	/** {@link Self} listener */
115 	private SelfListener selfListener;
116 
117     /*========================================================================*/
118 
119     /**
120      * Distance, which is considered as close enough for considering the bot to be AT LOCATION/NAV POINT.
121      * 
122      * If greater than 50, navigation will failed on DM-Flux2 when navigating between health vials in one of map corners.
123      */
124     public static final int CLOSE_ENOUGH = 40;
125 
126     /*========================================================================*/
127     
128 	@Override
129 	protected void navigate(ILocated focus, int pathElementIndex) {
130 		if (log != null && log.isLoggable(Level.FINE)) log.fine("Current stage: " + navigStage);
131 		this.focus = focus;
132 		switch (navigStage = keepNavigating()) {
133 		case AWAITING_MOVER:
134 		case RIDING_MOVER:
135 			setBotWaiting(true);
136 			break;
137 		case TELEPORT:
138 		case NAVIGATING:
139 		case REACHING:
140 			setBotWaiting(false);
141 			break;
142 		
143 		case TIMEOUT:
144 		case CRASHED:
145 		case CANCELED:
146 			if (log != null && log.isLoggable(Level.WARNING)) log.warning("Navigation " + navigStage);
147 			executor.stuck();
148 			return;
149 		
150 		case COMPLETED:
151 			executor.targetReached();
152 			break;
153 		}
154 		if (log != null && log.isLoggable(Level.FINE)) log.fine("Next stage: " + navigStage);
155 	}
156 	
157 	@Override
158 	public void reset() {
159 		// reinitialize the navigator's values
160 		
161 		navigCurrentLocation = null;
162 		navigCurrentNode = null;
163         navigCurrentLink = null;
164 		navigDestination = null;
165 		navigIterator = null;
166 		navigLastLocation = null;
167 		navigLastNode = null;
168 		navigNextLocation = null;
169 		navigNextNode = null;
170 		navigNextLocationOffset = 0;
171 		navigStage = Stage.COMPLETED;
172 		setBotWaiting(false);
173 		
174 		resetNavigMoverVariables();
175 	}
176 
177 	@Override
178 	public void newPath(List<PATH_ELEMENT> path, ILocated focus) {
179 		// prepare for running along new path
180 		reset();
181 		
182 		// 1) obtain the destination
183 		Location dest = path.get(path.size()-1).getLocation();
184 				
185 		// 2) init the navigation
186 		initPathNavigation(dest, path);
187 		
188 		// 3) start navigation
189 		navigate(focus);
190 	}
191 	
192 	@Override
193 	public void pathExtended(List<PATH_ELEMENT> path, int currentPathIndex) {
194 		if (path == null || path.size() == 0) {
195 			throw new RuntimeException("path is null or 0-sized!");
196 		}
197 		navigDestination = path.get(path.size() - 1).getLocation();
198 		navigIterator = path.iterator();
199 		
200 		int newOffset = -currentPathIndex;
201 		for (int i = 0; i < path.size() && i < currentPathIndex+navigNextLocationOffset && navigIterator.hasNext(); ++i) {
202 			++newOffset;
203 			navigIterator.next();
204 		}
205 		log.fine("PATH EXTEND ... curr index " + currentPathIndex + ", old offset " + navigNextLocationOffset + ", new offset " + newOffset + ", path size " + path.size());
206 		navigNextLocationOffset = newOffset;
207 	}
208 	
209 	public NavPointNeighbourLink getCurrentLink() {
210 		return navigCurrentLink;
211 	}
212 	
213 	/*========================================================================*/
214 	
215 	/**
216      * Initializes direct navigation to the specified destination.
217      * @param dest Destination of the navigation.
218      * @param timeout Maximum timeout of the navigation. Use 0 to auto-timeout.
219      */
220     protected void initDirectNavigation (Location dest)
221     {
222         // calculate destination distance
223         int distance = (int) memory.getLocation().getDistance(dest);
224         // init the navigation
225         if (log != null && log.isLoggable(Level.FINE)) log.fine (
226             "LoqueNavigator.initDirectNavigation(): initializing direct navigation"
227             + ", distance " + distance
228         );
229         // init direct navigation
230         initDirectly (dest);
231     }
232 	
233 	/*========================================================================*/
234 
235     /**
236      * Initializes navigation to the specified destination along specified path.
237      * @param dest Destination of the navigation.
238      * @param path Navigation path to the destination.
239      */
240     protected void initPathNavigation(Location dest, List<PATH_ELEMENT> path)
241     {
242         // init the navigation
243         if (log != null && log.isLoggable(Level.FINE)) 
244         	log.fine (
245         			"LoqueNavigator.initPathNavigation(): initializing path navigation"
246         			+ ", nodes " + path.size ()
247         	);
248         // init path navigation
249         if (!initAlongPath(dest, path))
250         {
251             // do it directly then..
252             initDirectNavigation(dest);
253         }
254     }
255 
256     /*========================================================================*/
257 
258     /**
259      * Navigates with the current navigation request.
260      * @return Stage of the navigation progress.
261      */
262     protected Stage keepNavigating ()
263     {
264         // is there any point in navigating further?
265         if (navigStage.terminated)
266             return navigStage;
267       
268         if (log != null && log.isLoggable(Level.FINE)) {
269         	if (navigLastNode != null) {
270         		log.fine("LoqueNavigator.keepNavigating(): navigating from " + navigLastNode.getId().getStringId() + navigLastNode.getLocation() );    	
271         	} else
272         	if (navigLastLocation != null) {
273         		log.fine("LoqueNavigator.keepNavigating(): navigating from " + navigLastLocation + " (navpoint is unknown)" );
274         	}
275         	if (navigCurrentNode != null) {
276         		log.fine("LoqueNavigator.keepNavigating(): navigating to   " + navigCurrentNode.getId().getStringId() + navigCurrentNode.getLocation() );
277         	} else
278         	if (navigCurrentLocation != null) {
279         		log.fine("LoqueNavigator.keepNavigating(): navigating to       " + navigCurrentLocation + " (navpoint is unknown)" );
280         	}
281         	if (navigLastLocation != null && navigCurrentLocation != null) {
282         		log.fine("LoqueNavigator.keepNavigating(): distance in-between " + navigCurrentLocation.getDistance(navigLastLocation));
283         	}
284         }
285         
286         // try to navigate
287         switch (navigStage)
288         {
289             case REACHING:
290                 navigStage = navigDirectly();
291                 break;
292             default:
293                 navigStage = navigAlongPath();
294                 break;
295         }
296 
297         // return the stage
298         if (log != null && log.isLoggable(Level.FINEST)) log.finest ("Navigator.keepNavigating(): navigation stage " + navigStage);
299         return navigStage;
300     }
301 
302     /*========================================================================*/
303 
304     /**
305      * Initializes direct navigation to given destination.
306      * 
307      * @param dest Destination of the navigation.
308      * @return Next stage of the navigation progress.
309      */
310     private Stage initDirectly(Location dest)
311     {
312         // setup navigation info
313         navigDestination = dest;
314         // init runner
315         runner.reset();
316         // reset navigation stage
317         return navigStage = Stage.REACHING;
318     }
319 
320     /**
321      * Tries to navigate the agent directly to the navig destination.
322      * @return Next stage of the navigation progress.
323      */
324     private Stage navigDirectly ()
325     {    	
326         // get the distance from the target
327         int distance = (int) memory.getLocation().getDistance(navigDestination);
328 
329         // are we there yet?
330         if (distance <= CLOSE_ENOUGH)
331         {
332             if (log != null && log.isLoggable(Level.FINE)) log.fine ("LoqueNavigator.navigDirectly(): destination close enough: " + distance);
333             return Stage.COMPLETED;
334         }
335 
336         // run to that location..
337         if (!runner.runToLocation (navigLastLocation, navigDestination, null, (focus == null ? navigDestination : focus), null, true, false))
338         {
339             if (log != null && log.isLoggable(Level.FINE)) log.fine ("LoqueNavigator.navigDirectly(): direct navigation failed");
340             return Stage.CRASHED;
341         }
342 
343         // well, we're still running
344         if (log != null && log.isLoggable(Level.FINEST)) log.finer ("LoqueNavigator.navigDirectly(): traveling directly, distance = " + distance);
345         return navigStage;
346     }
347 
348     /*========================================================================*/
349 
350     /**
351      * Iterator through navigation path.
352      */
353     private Iterator<PATH_ELEMENT> navigIterator = null;
354     
355     /**
356      * How many path elements we have iterated over before selecting the current {@link LoqueNavigator#navigNextLocation}.
357      */
358     private int navigNextLocationOffset = 0;
359 
360     /**
361      * Last location in the path (the one the agent already reached).
362      */
363     private Location navigLastLocation = null;
364     
365     /**
366      * If {@link LoqueNavigator#navigLastLocation} is a {@link NavPoint} or has NavPoint near by, its instance
367      * is written here (null otherwise).
368      */
369     private NavPoint navigLastNode = null;
370 
371     /**
372      * Current node in the path (the one the agent is running to).
373      */
374     private Location navigCurrentLocation = null;
375     
376     /**
377      * If {@link LoqueNavigator#navigCurrentLocation} is a {@link NavPoint} or has NavPoint near by,
378      * its instance is written here (null otherwise).
379      */
380     private NavPoint navigCurrentNode = null;
381 
382     /**
383      * If moving between two NavPoints {@link NavPoint} the object {@link NeighbourLink} holding infomation
384      * about the link (if any) will be stored here (null otherwise).
385      */
386     private NavPointNeighbourLink navigCurrentLink = null;
387 
388     /**
389      * Next node in the path (the one being prepared).
390      */
391     private Location navigNextLocation = null;
392     
393     /**
394      * If {@link LoqueNavigator#navigNextLocation} is a {@link NavPoint} or has NavPoint near by,
395      * its instance is written here (null otherwise).
396      */
397     private NavPoint navigNextNode = null;
398     
399     /**
400      * Returns {@link NavPoint} instance for a given location. If there is no navpoint in the vicinity of {@link LoqueNavigator#CLOSE_ENOUGH}
401      * null is returned.
402      * 
403      * @param location
404      * @return
405      */
406     protected NavPoint getNavPoint(ILocated location) {
407     	if (location instanceof NavPoint) return (NavPoint) location;
408     	NavPoint np = DistanceUtils.getNearest(main.getWorldView().getAll(NavPoint.class).values(), location);
409     	if (np.getLocation().getDistance(location.getLocation()) < CLOSE_ENOUGH) return np;
410     	return null;
411     }
412 
413     /**
414      * Initializes navigation along path.
415      * @param dest Destination of the navigation.
416      * @param path Path of the navigation.
417      * @return True, if the navigation is successfuly initialized.
418      */
419     private boolean initAlongPath(Location dest, List<PATH_ELEMENT> path)
420     {
421         // setup navigation info
422         navigDestination = dest;
423         navigIterator = path.iterator();
424         // reset current node
425         navigCurrentLocation = bot.getLocation();
426         navigCurrentNode = DistanceUtils.getNearest(bot.getWorldView().getAll(NavPoint.class).values(), bot.getLocation(), 40);
427         // prepare next node
428         prepareNextNode();
429         // reset navigation stage
430         navigStage = Stage.NAVIGATING;
431         // reset node navigation info
432         return switchToNextNode ();
433     }
434 
435     /**
436      * Tries to navigate the agent safely along the navigation path.
437      * @return Next stage of the navigation progress.
438      */
439     private Stage navigAlongPath()
440     {
441         // get the distance from the destination
442         int totalDistance = (int) memory.getLocation().getDistance(navigDestination);
443 
444         // are we there yet?
445         if (totalDistance <= CLOSE_ENOUGH)
446         {
447             log.finest ("Navigator.navigAlongPath(): destination close enough: " + totalDistance);
448             return Stage.COMPLETED;
449         }
450         
451         // navigate
452         if (navigStage.mover) {
453         	log.fine("MOVER");
454         	return navigThroughMover();
455         } else
456         if (navigStage.teleport) {
457         	log.fine("TELEPORT");
458         	return navigThroughTeleport();
459         } else {
460         	log.fine("STANDARD");
461             return navigToCurrentNode(true); // USE FOCUS, normal navigation
462         }
463     }
464 
465     /*========================================================================*/
466 
467     /**
468      * Prepares next navigation node in path.
469      * <p><p>
470      * If necessary just recalls {@link LoqueNavigator#prepareNextNodeTeleporter()}.
471      */
472     private void prepareNextNode ()
473     {
474     	if (navigCurrentNode != null && navigCurrentNode.isTeleporter()) {
475     		// current node is a teleporter! ...
476     		prepareNextNodeTeleporter();
477     		return;
478     	}
479     	
480         // retreive the next node, if there are any left
481         // note: there might be null nodes along the path!
482     	ILocated located = null;
483         navigNextLocation = null;
484         navigNextLocationOffset = 0;
485         while ((located == null) && navigIterator.hasNext ())
486         {
487             // get next node in the path
488         	located = navigIterator.next();
489         	navigNextLocationOffset += 1;
490         	if (located == null) {
491         		continue;            
492         	}
493         }
494 
495         // did we get the next node?
496         if (located == null) {
497         	navigNextLocationOffset = 0;
498         	return;
499         }
500         
501         if (executor.getPathElementIndex() + navigNextLocationOffset >= executor.getPath().size()) {
502         	navigNextLocationOffset = 0; // WTF?
503         }
504        
505         // obtain next location
506         navigNextLocation = located.getLocation();
507         // obtain navpoint instance for a given location
508         navigNextNode = getNavPoint(located);
509     }
510     
511     /**
512      * Prepares next node in the path assuming the currently pursued node is a teleporter.
513      */
514     private void prepareNextNodeTeleporter() {
515     	// Retrieve the next node, if there are any left
516         // note: there might be null nodes along the path!
517     	ILocated located = null;
518         navigNextLocation = null;
519         navigNextLocationOffset = 0;
520         boolean nextTeleporterFound = false;
521         while ((located == null) && navigIterator.hasNext ())
522         {
523             // get next node in the path
524         	located = navigIterator.next();
525         	navigNextLocationOffset += 1;
526         	if (located == null) {
527         		continue;            
528         	}
529         	navigNextNode = getNavPoint(located);
530         	if (navigNextNode != null && navigNextNode.isTeleporter()) {
531         		// next node is 
532         		if (!nextTeleporterFound) {
533         			// ignore first teleporter as it is the other end of the teleporter we're currently trying to enter
534         			located = null;
535         		}
536         		nextTeleporterFound = true;
537         	} else {
538         		break;
539         	}
540         }
541         
542         // did we get the next node?
543         if (located == null) {
544         	navigNextLocationOffset = 0;
545         	return;
546         }
547         
548         if (executor.getPathElementIndex() + navigNextLocationOffset >= executor.getPath().size()) {
549         	navigNextLocationOffset = 0; // WTF?
550         }
551        
552         // obtain next location
553         navigNextLocation = located.getLocation();
554         // obtain navpoint instance for a given location
555         navigNextNode = getNavPoint(located);        
556     }
557 
558     /**
559      * Initializes next navigation node in path.
560      * @return True, if the navigation node is successfully switched.
561      */
562     private boolean switchToNextNode ()
563     {    	
564     	if (log != null && log.isLoggable(Level.FINER)) log.finer ("Navigator.switchToNextNode(): switching!");
565     	
566         // move the current node into last node
567         navigLastLocation = navigCurrentLocation;
568         navigLastNode = navigCurrentNode;
569 
570         // get the next prepared node
571         if (null == (navigCurrentLocation = navigNextLocation))
572         {
573             // no nodes left there..
574             if (log != null && log.isLoggable(Level.FINER)) log.finer ("Navigator.switchToNextNode(): no nodes left");
575             navigCurrentNode = null;
576             return false;
577         }
578         // rewrite the navpoint as well
579         navigCurrentNode = navigNextNode;
580 
581         // store current NavPoint link
582         navigCurrentLink = getNavPointsLink(navigLastNode, navigCurrentNode);
583         
584         if (navigCurrentLink == null) {
585         	getNavPointsLink(navigLastNode, navigCurrentNode);
586         	if (log.isLoggable(Level.INFO)) {
587         		log.info("No link information...");
588         	}
589         }
590         
591         // ensure that the last node is not null
592         if (navigLastLocation == null) {
593             navigLastLocation = bot.getLocation();
594             navigLastNode = navigCurrentNode;
595         }
596 
597         // get next node distance
598         int localDistance = (int) memory.getLocation().getDistance(navigCurrentLocation.getLocation());
599 
600         if (navigCurrentNode == null) {
601         	// we do not have extra information about the location we're going to reach
602         	runner.reset();
603         	if (log != null && log.isLoggable(Level.FINE)) 
604         		log.fine (
605                     "LoqueNavigator.switchToNextNode(): switch to next location " + navigCurrentLocation
606                     + ", distance " + localDistance
607                     + ", mover " + navigStage.mover
608                 );
609         } else {
610         	// is this next node a teleporter?
611         	if (navigCurrentNode.isTeleporter()) {
612         		navigStage = Stage.TeleporterStage();
613         	} else
614 	        // is this next node a mover?
615 	        if (navigCurrentNode.isLiftCenter())
616 	        {
617 	            // setup mover sequence
618 	            navigStage = Stage.FirstMoverStage();	            
619 	            resetNavigMoverVariables();
620 	            
621 	            // AREN'T WE ALREADY ON THE LIFT CENTER?
622 	            if (memory.getLocation().getDistance(navigCurrentNode.getLocation()) < CLOSE_ENOUGH) {
623 	            	// YES WE ARE!
624 	            	navigStage = navigStage.next();
625 	            }	            
626 	        } else
627 	        // are we still moving on mover?
628 	        if (navigStage.mover)
629 	        {
630 	        	navigStage = navigStage.next();
631 	            // init the runner
632 	            runner.reset();
633 	        } else
634 	        if (navigStage.teleport) {
635 	        	navigStage = navigStage.next();
636 	            // init the runner
637 	            runner.reset();
638 	        } else
639 	        // no movers & teleports
640 	        {
641 	            // init the runner
642 	            runner.reset();
643 	        }
644 	
645 	        // switch to next node
646 	        if (log != null && log.isLoggable(Level.FINE)) 
647 	        	log.fine (
648 		            "LoqueNavigator.switchToNextNode(): switch to next node " + navigCurrentNode.getId().getStringId()
649 		            + ", distance " + localDistance
650 		            + ", reachable " + isReachable(navigCurrentNode)
651 		            + ", mover " + navigStage.mover
652 		        );
653         }
654 
655         // tell the executor that we have moved in the path to the next element
656         if (executor.getPathElementIndex() < 0) {
657         	executor.switchToAnotherPathElement(0);
658         } else {
659         	if (navigNextLocationOffset > 0) {
660         		executor.switchToAnotherPathElement(executor.getPathElementIndex()+navigNextLocationOffset);
661         	} else {
662         		executor.switchToAnotherPathElement(executor.getPathElementIndex());
663         	}
664         }
665         navigNextLocationOffset = 0;
666         
667         prepareNextNode();
668         
669         if (localDistance < 20) {
670         	return switchToNextNode();
671         }
672         
673         return true;
674     }
675 
676     protected boolean isReachable(NavPoint node) {
677     	if (node == null) return true;
678 		int hDistance = (int) memory.getLocation().getDistance2D(node.getLocation());
679 		int vDistance = (int) node.getLocation().getDistanceZ(memory.getLocation());
680 		double angle; 
681 		if (hDistance == 0) {
682 			angle = vDistance == 0 ? 0 : (vDistance > 0 ? Math.PI/2 : -Math.PI/2);
683 		} else {
684 			angle = Math.atan(vDistance / hDistance);
685 		}
686 		return Math.abs(vDistance) < 30 && Math.abs(angle) < Math.PI / 4;
687 	}
688 
689 	//
690     // NAVIG MOVER VARIABLES
691     //
692     
693     private int navigMoverRideUpCount;
694 
695 	private int navigMoverRideDownCount;
696 
697 	private Boolean navigMoverIsRidingUp;
698 
699 	private Boolean navigMoverIsRidingDown;
700 	
701 	private void resetNavigMoverVariables() {
702     	navigMoverIsRidingUp = null;
703     	navigMoverIsRidingDown = null;
704         navigMoverRideUpCount = 0;
705         navigMoverRideDownCount = 0;
706 	}
707 	
708 	private void checkMoverMovement(Mover mover) {
709 		// ASSUMING THAT MOVER IS ALWAYS ... riding fully UP, riding fully DOWN (or vice versa) and passing all possible exits
710 		if (mover.getVelocity().z > 0) {
711 			// mover is riding UP
712 			if (navigMoverIsRidingUp == null) {
713 				navigMoverIsRidingUp = true;
714 				navigMoverIsRidingDown = false;
715 				navigMoverRideUpCount = 1;
716 				navigMoverRideDownCount = 0;
717 			} else
718 			if (navigMoverIsRidingDown) {
719 				navigMoverIsRidingUp = true;
720 				navigMoverIsRidingDown = false;
721 				++navigMoverRideUpCount;
722 			}
723 		} else 
724 		if (mover.getVelocity().z < 0) {
725 			// mover is riding DOWN
726 			if (navigMoverIsRidingDown == null) {
727 				navigMoverIsRidingUp = false;
728 				navigMoverIsRidingDown = true;
729 				navigMoverRideUpCount = 0;
730 				navigMoverRideDownCount = 1;
731 			} else 
732 			if (navigMoverIsRidingUp) {
733 				navigMoverIsRidingUp = false;
734 				navigMoverIsRidingDown = true;
735 				++navigMoverRideDownCount;
736 			}
737 		}
738 	}
739 
740 	/*========================================================================*/
741 
742     /**
743      * Gets the link with movement information between two navigation points. Holds
744      * information about how we can traverse from the start to the end navigation
745      * point.
746      * 
747      * @return NavPointNeighbourLink or null
748      */
749     private NavPointNeighbourLink getNavPointsLink(NavPoint start, NavPoint end) {
750         if (start == null) {
751             //if start NavPoint is not provided, we try to find some
752             NavPoint tmp = getNavPoint(memory.getLocation());
753             if (tmp != null)
754                 start = tmp;
755             else
756                 return null;
757         }
758         if (end == null)
759             return null;
760 
761         if (end.getIncomingEdges().containsKey(start.getId()))
762             return end.getIncomingEdges().get(start.getId());
763         
764         return null;
765     }
766 
767     /*========================================================================*/
768 
769     /**
770      * Tries to navigate the agent safely to the current navigation node.
771      * @return Next stage of the navigation progress.
772      */
773     private Stage navigToCurrentNode (boolean useFocus)
774     {
775     	if (navigCurrentNode != null) {
776     		// update location of the current place we're reaching ... it might be Mover after all
777     		navigCurrentLocation = navigCurrentNode.getLocation();
778        	}
779     	if (navigNextNode != null) {
780     		// update location of the next place we're reaching ... it might be Mover after all
781     		navigNextLocation = navigNextNode.getLocation();
782     	}
783     	
784         // get the distance from the current node
785         int localDistance = (int) memory.getLocation().getDistance(navigCurrentLocation.getLocation());
786         // get the distance from the current node (neglecting jumps)
787         int localDistance2 = (int) memory.getLocation().getDistance(
788             Location.add(navigCurrentLocation.getLocation(), new Location (0,0,100))
789         );
790         int distanceZ = (int) memory.getLocation().getDistanceZ(navigCurrentLocation);
791 
792         // where are we going to run to
793         Location firstLocation = navigCurrentLocation.getLocation();
794         // where we're going to continue
795         Location secondLocation = (navigNextNode != null ?
796         		 						(navigNextNode.isLiftCenter() || navigNextNode.isLiftExit() ? 
797         		 								null // let navigThroughMover() to handle these cases with care!
798         		 							:	navigNextNode.getLocation())
799         		 						: navigNextLocation);
800         // and what are we going to look at
801         ILocated focus = (this.focus == null || !useFocus ?
802         						((navigNextLocation == null) ? firstLocation : navigNextLocation.getLocation())
803         						:
804         						this.focus
805         				 );
806 
807         // run to the current node..
808         if (!runner.runToLocation (navigLastLocation, firstLocation, secondLocation, focus, navigCurrentLink, (navigCurrentNode == null ? true : isReachable(navigCurrentNode)), false)) {
809             if (log != null && log.isLoggable(Level.FINE)) log.fine ("LoqueNavigator.navigToCurrentNode(): navigation to current node failed");
810             return Stage.CRASHED;
811         }
812 
813         // we're still running
814         if (log != null && log.isLoggable(Level.FINEST)) log.finest ("LoqueNavigator.navigToCurrentNode(): traveling to current node, distance = " + localDistance);
815 
816         int testDistance = 125; // default constant suitable for default running 
817         if (navigCurrentNode != null && (navigCurrentNode.isLiftCenter() || navigCurrentNode.isLiftExit())) {
818         	// if we should get to lift exit or the lift center, we must use more accurate constants
819         	testDistance = 100;
820         }
821         if (navigCurrentLink != null && (navigCurrentLink.getFlags() & LinkFlag.JUMP.get()) != 0) {
822         	// we need to jump to reach the destination ... do not switch based on localDistance2
823         	localDistance2 = 10000;
824         }
825         
826         if (navigCurrentLocation != null && navigCurrentLocation.equals(executor.getPath().get(executor.getPath().size()-1))
827         	|| (!navigIterator.hasNext() && (navigNextLocation == null || navigCurrentLocation == navigNextLocation))) {
828         	// if we're going to the LAST POINT ... be sure to get to the exact location in order to ensure pick up of the item
829         	testDistance = 2 * ((int) UnrealUtils.CHARACTER_COLLISION_RADIUS);
830         }
831             
832         // are we close enough to switch to the next node? (mind the distanceZ particularly!)
833         if ( distanceZ < 40 && ((localDistance < testDistance) || (localDistance2 < testDistance) ))
834         {
835             // switch navigation to the next node
836             if (!switchToNextNode ())
837             {
838                 // switch to the direct navigation
839                 if (log != null && log.isLoggable(Level.FINE)) log.fine("Navigator.navigToCurrentNode(): switching to direct navigation");
840                 return initDirectly(navigDestination);
841             }
842         }
843 
844         // well, we're still running
845         return navigStage;
846     }
847 
848     /*========================================================================*/
849 
850     /**
851      * Tries to navigate the agent safely along mover navigation nodes.
852      *
853      * <h4>Pogamut troubles</h4>
854      *
855      * Since the engine does not send enough reasonable info about movers and
856      * their frames, the agent relies completely and only on the associated
857      * navigation points. Fortunatelly, LiftCenter navigation points move with
858      * movers.
859      *
860      * <p>Well, do not get too excited. Pogamut seems to update the position of
861      * LiftCenter navpoint from time to time, but it's not frequent enough for
862      * correct and precise reactions while leaving lifts.</p>
863      *
864      * @return Next stage of the navigation progress.
865      */
866     private Stage navigThroughMover ()
867     {
868     	Stage stage = navigStage;
869     	        
870         if (navigCurrentNode == null) {
871         	if (log != null && log.isLoggable(Level.WARNING)) log.warning("LoqueNavigator.navigThroughMover("+stage+"): can't navigate through the mover without the navpoint instance (navigCurrentNode == null)");
872         	return Stage.CRASHED;
873         }
874         
875         Mover mover = (Mover) bot.getWorldView().get(navigCurrentNode.getMover());
876         if (mover == null) {
877         	if (log != null && log.isLoggable(Level.WARNING)) log.warning("LoqueNavigator.navigThroughMover("+stage+"): can't navigate through the mover as current node does not represent a mover (moverId == null): " + navigCurrentNode);
878         	return Stage.CRASHED;
879         }
880         
881         // update navigCurrentLocation as the mover might have moved
882         navigCurrentLocation = navigCurrentNode.getLocation();
883         
884         if (navigNextNode != null) {
885         	// update navigNextLocation as the mover might have moved
886         	navigNextLocation = navigNextNode.getLocation();
887         }
888                 
889         log.info("navig-curr: " + navigCurrentNode);
890         log.info("navig-next: " + navigNextNode);
891         
892         // get horizontal distance from the mover center node ... always POSITIVE
893         int hDistance = (int) memory.getLocation().getDistance2D(navigCurrentLocation.getLocation());
894         // get vertical distance from the mover center node ... +/- ... negative -> mover is below us, positive -> mover is above us
895         int zDistance = (int) navigCurrentLocation.getLocation().getDistanceZ(memory.getLocation());
896         // whether mover is riding UP
897         boolean moverRidingUp = mover.getVelocity().z > 0;
898         // whether mover is riding DOWN
899 		boolean moverRidingDown = mover.getVelocity().z < 0;
900 		// whether mover is standing still
901 		boolean moverStandingStill = Math.abs(mover.getVelocity().z) < Location.DISTANCE_ZERO;
902         
903     	if (navigStage == Stage.AWAITING_MOVER) {   
904     		// Aren't we under the mover?
905     		if (zDistance > 0 && moverRidingUp) {
906     			// we're under the mover and the mover is riding up...
907     			if (log != null && log.isLoggable(Level.FINER)) {
908 	            	log.finer (
909 		                "LoqueNavigator.navigThroughMover("+stage+"): we are UNDER the mover and mover is RIDING UP ... getting back to waiting position"
910 		                + ", zDistance " + zDistance + ", mover.velocity.z " + mover.getVelocity().z + ", mover " + (moverRidingUp ? "riding UP" : (moverRidingDown ? "riding DOWN" : moverStandingStill ? "standing STILL" : " movement unknown"))
911 		            );
912     			}
913 	            // run to the last node, the one we need to be waiting on for the mover to come
914 	        	// WE MUST NOT USE FOCUS! Because we need to see the mover TODO: provide turning behavior, i.e., if focus is set, once in a time turn to then direction
915     			if (!runner.runToLocation(memory.getLocation(), navigLastLocation, null, navigCurrentLocation, null, (navigLastNode == null ? true : isReachable(navigLastNode)), false))
916 	            {
917 	                if (log != null && log.isLoggable(Level.FINE)) log.fine ("LoqueNavigator.navigThroughMover("+stage+"): navigation to wait-for-mover node failed");
918 	                return Stage.CRASHED;
919 	            }
920     			return navigStage;
921     		}
922     		
923 	        // wait for the current node to come close in both, vert and horiz
924 	        // the horizontal distance can be quite long.. the agent will hop on
925 	        // TODO: There may be problem when LiftExit is more than 400 ut units far from LiftCenter!
926     		
927     		if (hDistance > 400) {
928     			if (log != null && log.isLoggable(Level.WARNING)) log.warning("LoqueNavigator.navigThroughMover("+stage+"): failed to get onto the mover as its 2D distance is > 400, hDistance " + hDistance + ", unsupported!");
929                 return Stage.CRASHED;
930     		}
931     		
932 	        if (zDistance > 30 && moverRidingUp) // mover is riding UP and is already above us, we won't make it...
933 	        {
934 	            // run to the last node, the one we need to be waiting on for the mover to come
935 	        	if (log != null && log.isLoggable(Level.FINER)) {
936 	            	log.finer (
937 		                "LoqueNavigator.navigThroughMover("+stage+"): waiting for the mover to come"
938 		                + " | zDistance " + zDistance + ", hDistance " + hDistance + ", mover " + (moverRidingUp ? "riding UP" : (moverRidingDown ? "riding DOWN" : moverStandingStill ? "standing STILL" : " movement unknown"))
939 		                + ", node " + navigCurrentNode.getId().getStringId()
940 		            );
941 	        	}
942 	        	// WE MUST NOT USE FOCUS! Because we need to see the mover TODO: provide turning behavior, i.e., if focus is set, once in a time turn to then direction
943 	            if (!runner.runToLocation(memory.getLocation(), navigLastLocation, null, navigCurrentLocation, navigCurrentLink, (navigLastNode == null ? true : isReachable(navigLastNode)), false))  {
944 	                if (log != null && log.isLoggable(Level.FINE)) log.fine ("LoqueNavigator.navigThroughMover("+stage+"): navigation to last node failed");
945 	                return Stage.CRASHED;
946 	            }
947 	
948 	            return navigStage;
949 	        }
950 	        
951 	        // MOVER HAS ARRIVED (at least that what we're thinking so...)
952 	        if (log != null && log.isLoggable(Level.FINER)) 
953 		        log.finer (
954 		            "Navigator.navigThroughMover("+stage+"): mover arrived"		            
955 		            + " | zDistance " + zDistance + ", hDistance " + hDistance + ", mover " + (moverRidingUp ? "riding UP" : (moverRidingDown ? "riding DOWN" : moverStandingStill ? "standing STILL" : " movement unknown"))
956 		            + ", node " + navigCurrentNode.getId().getStringId()
957 		        );
958 	        
959 	        // LET'S MOVE TO THE LIFT CENTER (do not use focus)
960 	        return navigToCurrentNode(false);
961     	} else
962     	if (navigStage == Stage.RIDING_MOVER) {
963     		checkMoverMovement(mover);
964     		
965     		if (navigMoverRideDownCount > 2 || navigMoverRideUpCount > 2) {
966     			// we're riding up & down without any effect ... failure :(
967     			if (log != null && log.isLoggable(Level.FINE)) log.fine ("LoqueNavigator.navigThroughMover("+stage+"): navigation to mover exit node failed, we've rided twice up & down and there was no place suitable to exit the mover in order to get to get to " + navigCurrentNode);
968 	            return Stage.CRASHED;
969     		}
970     		
971     		if (hDistance > 400) {
972     			if (log != null && log.isLoggable(Level.WARNING)) log.warning("LoqueNavigator.navigThroughMover("+stage+"): navigation to mover exit node failed, the node is too far, hDistance " + hDistance + " > 400, unsupported (wiered navigation graph link)");
973 	            return Stage.CRASHED;
974     		}
975     		
976     		// wait for the mover to ride us up/down
977     		if ( Math.abs(zDistance) > 50 ) {
978  	            // run to the last node, the one we're waiting on
979     			if (log != null && log.isLoggable(Level.FINER)) 
980  	            	log.finer (
981  		                "LoqueNavigator.navigThroughMover("+stage+"): riding the mover"               
982  		                + " | zDistance " + zDistance + ", hDistance " + hDistance + ", mover " + (moverRidingUp ? "riding UP" : (moverRidingDown ? "riding DOWN" : moverStandingStill ? "standing STILL" : " movement unknown"))
983  		                + ", node " + navigCurrentNode.getId().getStringId()
984  		            ); 	
985     			
986     			// WE MUST NOT USE FOCUS! We have to see the mover. TODO: provide turning behavior, i.e., turn to desired focus once in a time
987  	            if (!runner.runToLocation(memory.getLocation(), navigLastLocation, null, navigCurrentLocation, navigCurrentLink, (navigLastNode == null ? true : isReachable(navigLastNode)), false))
988  	            {
989  	                if (log != null && log.isLoggable(Level.FINE)) log.fine ("LoqueNavigator.navigThroughMover("+stage+"): navigation to last node failed");
990  	                return Stage.CRASHED;
991  	            }
992  	            // and keep waiting for the mover to go to the correct position
993  	            
994  	            return navigStage;
995  	        }
996     		
997     		// MOVER HAS ARRIVED TO POSITION FOR EXIST (at least that what we're thinking so...)
998 	        if (log != null && log.isLoggable(Level.FINER)) 
999 		        log.finer (
1000 		            "Navigator.navigThroughMover("+stage+"): exiting the mover"
1001 		            + " | zDistance " + zDistance + ", hDistance " + hDistance + ", mover " + (moverRidingUp ? "riding UP" : (moverRidingDown ? "riding DOWN" : moverStandingStill ? "standing STILL" : " movement unknown"))
1002 		            + ", node " + navigCurrentNode.getId().getStringId()		            
1003 		        );
1004 	        
1005 	        // LET'S MOVE TO THE LIFT EXIT (do not use focus)
1006 	        return navigToCurrentNode(false);    		
1007     	} else {
1008     		if (log != null && log.isLoggable(Level.WARNING)) {
1009     			log.warning("Navigator.navigThroughMover("+stage+"): invalid stage, neither AWAITING_MOVER nor RIDING MOVER");
1010     		}
1011     		return Stage.CRASHED;
1012     	}
1013 
1014     }
1015     
1016     /*========================================================================*/
1017     
1018     /**
1019      * Tries to navigate the agent safely to the current navigation node.
1020      * @return Next stage of the navigation progress.
1021      */
1022     private Stage navigThroughTeleport()
1023     {
1024     	if (navigCurrentNode != null) {
1025     		// update location of the current place we're reaching
1026     		navigCurrentLocation = navigCurrentNode.getLocation();
1027     	}
1028     	
1029     	if (navigNextNode != null) {
1030     		// update location of the Next place we're reaching
1031     		navigNextLocation = navigNextNode.getLocation();
1032     	}
1033     	
1034     	// now we have to compute whether we should switch to another navpoint
1035         // it has two flavours, we should switch if:
1036         //			1. we're too near to teleport, we should run into
1037         //          2. we're at the other end of the teleport, i.e., we've already got through the teleport
1038         
1039         // 1. DISTANCE TO THE TELEPORT
1040         // get the distance from the current node
1041         int localDistance1_1 = (int) memory.getLocation().getDistance(navigCurrentLocation.getLocation());
1042         // get the distance from the current node (neglecting jumps)
1043         int localDistance1_2 = (int) memory.getLocation().getDistance(
1044             Location.add(navigCurrentLocation.getLocation(), new Location (0,0,100))
1045         );        
1046         
1047         // 2. DISTANCE TO THE OTHER END OF THE TELEPORT
1048         // ---[[ WARNING ]]--- we're assuming that there is only ONE end of the teleport
1049         int localDistance2_1 = Integer.MAX_VALUE;
1050         int localDistance2_2 = Integer.MAX_VALUE;
1051         for (NavPointNeighbourLink link : navigCurrentNode.getOutgoingEdges().values()) {
1052         	if (link.getToNavPoint().isTeleporter()) {
1053         		localDistance2_1 = (int)memory.getLocation().getDistance(link.getToNavPoint().getLocation());
1054         		localDistance2_2 = (int) memory.getLocation().getDistance(
1055         	            Location.add(link.getToNavPoint().getLocation(), new Location (0,0,100))
1056                 );        
1057         		break;
1058         	}
1059         }
1060                 
1061         boolean switchedToNextNode = false;
1062         // are we close enough to switch to the OTHER END of the teleporter?
1063         if ( (localDistance2_1 < 200) || (localDistance2_2 < 200))
1064         {
1065         	// yes we are! we already passed the teleporter, so DO NOT APPEAR DUMB and DO NOT TRY TO RUN BACK 
1066             // ... better to switch navigation to the next node
1067             if (!switchToNextNode ())
1068             {
1069                 // switch to the direct navigation
1070                 if (log != null && log.isLoggable(Level.FINE)) log.fine ("Navigator.navigToCurrentNode(): switch to direct navigation");
1071                 return initDirectly(navigDestination);
1072             }
1073             switchedToNextNode = true;
1074         }
1075     	
1076         // where are we going to run to
1077         Location firstLocation = navigCurrentLocation.getLocation();
1078         // where we're going to continue
1079         Location secondLocation = (navigNextNode != null && !navigNextNode.isLiftCenter() && !navigNextNode.isLiftCenter() ? 
1080         		                  	navigNextNode.getLocation() :
1081         		                  	navigNextLocation);
1082         // and what are we going to look at
1083         ILocated focus = (this.focus == null ? 
1084         						((navigNextLocation == null) ? firstLocation : navigNextLocation.getLocation())
1085         						:
1086         						this.focus
1087         				 );
1088 
1089         // run to the current node..
1090         if (!runner.runToLocation(navigLastLocation, firstLocation, secondLocation, focus, navigCurrentLink, (navigCurrentNode == null ? true : isReachable(navigCurrentNode)), false)) {
1091             if (log != null && log.isLoggable(Level.FINE)) log.fine ("LoqueNavigator.navigToCurrentNode(): navigation to current node failed");
1092             return Stage.CRASHED;
1093         }
1094 
1095         // we're still running
1096         if (log != null && log.isLoggable(Level.FINEST)) log.finest ("LoqueNavigator.navigToCurrentNode(): traveling to current node");        
1097         
1098         // now as we've tested the first node ... test the second one
1099         if ( !switchedToNextNode && ((localDistance1_1 < 200) || (localDistance1_2 < 200)) )
1100         {
1101             // switch navigation to the next node
1102             if (!switchToNextNode ())
1103             {
1104                 // switch to the direct navigation
1105                 if (log != null && log.isLoggable(Level.FINE)) log.fine ("Navigator.navigToCurrentNode(): switch to direct navigation");
1106                 return initDirectly(navigDestination);
1107             }
1108         }
1109 
1110         // well, we're still running
1111         return navigStage;
1112     }
1113 
1114     /*========================================================================*/
1115 
1116     /**
1117      * Enum of types of terminating navigation stages.
1118      */
1119     private enum TerminatingStageType {
1120         /** Terminating with success. */
1121         SUCCESS (false),
1122         /** Terminating with failure. */
1123         FAILURE (true);
1124 
1125         /** Whether the terminating with failure. */
1126         public boolean failure;
1127 
1128         /**
1129          * Constructor.
1130          * @param failure Whether the terminating with failure.
1131          */
1132         private TerminatingStageType (boolean failure)
1133         {
1134             this.failure = failure;
1135         }
1136     };
1137 
1138     /**
1139      * Enum of types of mover navigation stages.
1140      */
1141     private enum MoverStageType {
1142         /** Waiting for mover. */
1143         WAITING,
1144         /** Riding mover. */
1145         RIDING;
1146     };
1147     
1148     /**
1149      * Enum of types of mover navigation stages.
1150      */
1151     private enum TeleportStageType {
1152         /** Next navpoint is a teleport */
1153         GOING_THROUGH;
1154     };
1155 
1156     /**
1157      * All stages the navigation can come to.
1158      */
1159     public enum Stage
1160     {
1161         /**
1162          * Running directly to the destination.
1163          */
1164         REACHING ()
1165         {
1166             protected Stage next () { return this; }
1167         },
1168         /**
1169          * Navigating along the path.
1170          */
1171         NAVIGATING ()
1172         {
1173             protected Stage next () { return this; }
1174         },
1175         /**
1176          * Waiting for a mover to arrive.
1177          */
1178         AWAITING_MOVER (MoverStageType.WAITING)
1179         {
1180             protected Stage next () { return RIDING_MOVER; }
1181         },
1182         /**
1183          * Waiting for a mover to ferry.
1184          */
1185         RIDING_MOVER (MoverStageType.RIDING)
1186         {
1187             protected Stage next () { return NAVIGATING; }
1188         },
1189         /**
1190          * Navigation cancelled by outer force.
1191          */
1192         CANCELED (TerminatingStageType.FAILURE)
1193         {
1194             protected Stage next () { return this; }
1195         },
1196         /**
1197          * Navigation timeout reached.
1198          */
1199         TIMEOUT (TerminatingStageType.FAILURE)
1200         {
1201             protected Stage next () { return this; }
1202         },
1203         /**
1204          * Navigation failed because of troublesome obstacles.
1205          */
1206         CRASHED (TerminatingStageType.FAILURE)
1207         {
1208             protected Stage next () { return this; }
1209         },
1210         /**
1211          * Navigation finished successfully.
1212          */
1213         COMPLETED (TerminatingStageType.SUCCESS)
1214         {
1215             protected Stage next () { return this; }
1216         },
1217         /**
1218          * We're going through the teleport.
1219          */
1220         TELEPORT (TeleportStageType.GOING_THROUGH) {
1221         	protected Stage next() { return NAVIGATING; };
1222         };
1223         
1224 
1225         /*====================================================================*/
1226 
1227         /**
1228          * Running through the mover.
1229          */
1230         private boolean mover;
1231         /**
1232          * Whether the nagivation is terminated.
1233          */
1234         public boolean terminated;
1235         /**
1236          * Whether the navigation has failed.
1237          */
1238         public boolean failure;
1239         /**
1240          * We're going through the teleport.
1241          */
1242         public boolean teleport;
1243 
1244         /*====================================================================*/
1245 
1246         /**
1247          * Constructor: Not finished, not failed
1248          */
1249         private Stage ()
1250         {
1251             this.mover = false;
1252             this.teleport = false;
1253             this.terminated = false;
1254             this.failure = false;
1255         }
1256 
1257         private Stage(TeleportStageType type) {
1258         	this.mover = false;
1259         	this.teleport = true;
1260         	this.failure = false;
1261         	this.terminated = false;
1262         }
1263         
1264         /**
1265          * Constructor: mover.
1266          * @param type Type of mover navigation stage.
1267          */
1268         private Stage (MoverStageType type)
1269         {
1270             this.mover = true;
1271             this.teleport = false;
1272             this.terminated = false;
1273             this.failure = false;
1274         }
1275 
1276         /**
1277          * Constructor: terminating.
1278          * @param type Type of terminating navigation stage.
1279          */
1280         private Stage (TerminatingStageType type)
1281         {
1282             this.mover = false;
1283             this.teleport = false;
1284             this.terminated = true;
1285             this.failure = type.failure;
1286         }
1287 
1288         /*====================================================================*/
1289 
1290         /**
1291          * Retreives the next step of navigation sequence the stage belongs to.
1292          * @return The next step of navigation sequence. Note: Some stages are
1293          * not part of any logical navigation sequence. In such cases, this
1294          * method simply returns the same stage.
1295          */
1296         protected abstract Stage next ();
1297 
1298         /*====================================================================*/
1299 
1300         /**
1301          * Returns the first step of mover sequence.
1302          * @return The first step of mover sequence.
1303          */
1304         protected static Stage FirstMoverStage ()
1305         {
1306             return AWAITING_MOVER;
1307         }
1308         
1309         /**
1310          * Returns the first step of the teleporter sequence.
1311          * @return
1312          */
1313         protected static Stage TeleporterStage() {
1314         	return Stage.TELEPORT;
1315         }
1316     }
1317 
1318     /*========================================================================*/
1319 
1320     /**
1321      * Default: Loque Runner.
1322      */
1323     private IUT2004PathRunner runner;
1324 
1325     /*========================================================================*/
1326 
1327     /** Agent's main. */
1328     protected UT2004Bot main;
1329     /** Loque memory. */
1330     protected AgentInfo memory;
1331     /** Agent's body. */
1332     protected AdvancedLocomotion body;
1333     /** Agent's log. */
1334     protected Logger log;
1335 
1336     /*========================================================================*/
1337 
1338     /**
1339      * Constructor.
1340      * @param main Agent's main.
1341      * @param memory Loque memory.
1342      */
1343     public LoqueNavigator (UT2004Bot bot, AgentInfo info, AdvancedLocomotion move,  Logger log)
1344     {
1345     	this(bot, info, move, new KefikRunner(bot, info, move, log),log);
1346     }
1347 
1348     /**
1349      * Constructor.
1350      * @param main Agent's main.
1351      * @param memory Loque memory.
1352      */
1353     public LoqueNavigator (UT2004Bot bot, AgentInfo info, AdvancedLocomotion move, IUT2004PathRunner runner, Logger log)
1354     {
1355         // setup reference to agent
1356         this.main = bot;
1357         this.memory = info;
1358         this.body = move;
1359         this.log = log;
1360         
1361         this.selfListener = new SelfListener( bot.getWorldView() );
1362 
1363         // save runner object
1364         this.runner = runner;
1365         NullCheck.check(this.runner, "runner");
1366     }
1367 
1368 	public Logger getLog() {
1369 		return log;
1370 	}
1371         
1372 }