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