View Javadoc

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