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