View Javadoc

1   package cz.cuni.amis.pogamut.emohawk.agent.module.sensomotoric;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   import java.util.logging.Level;
6   
7   import SteeringProperties.ObstacleAvoidanceProperties;
8   import SteeringProperties.PathFollowingProperties;
9   import SteeringProperties.PeopleAvoidanceProperties;
10  import SteeringProperties.StickToPathProperties;
11  import SteeringProperties.SteeringProperties.BehaviorType;
12  
13  import cz.cuni.amis.pogamut.base.agent.navigation.IPathExecutorState;
14  import cz.cuni.amis.pogamut.base.agent.navigation.IPathFuture;
15  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
16  import cz.cuni.amis.pogamut.base.utils.logging.LogCategory;
17  import cz.cuni.amis.pogamut.base.utils.math.DistanceUtils;
18  import cz.cuni.amis.pogamut.base3d.worldview.object.ILocated;
19  import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
20  import cz.cuni.amis.pogamut.ut2004.agent.navigation.IUT2004GetBackToNavGraph;
21  import cz.cuni.amis.pogamut.ut2004.agent.navigation.IUT2004PathExecutor;
22  import cz.cuni.amis.pogamut.ut2004.agent.navigation.IUT2004RunStraight;
23  import cz.cuni.amis.pogamut.ut2004.agent.navigation.floydwarshall.FloydWarshallMap;
24  import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
25  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.Stop;
26  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.EndMessage;
27  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Item;
28  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPoint;
29  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Player;
30  import cz.cuni.amis.utils.flag.FlagListener;
31  
32  /**
33   * Facade for navigation in UT2004. Method navigate() can be called both synchronously and asynchronously.
34   * 
35   * Uses {@link IUT2004PathExecutor}, {@link FloydWarshallMap}, {@link IUT2004RunStraight} and {@link IUT2004GetBackToNavGraph}
36   * to handle all possible navigation cases.
37   * 
38   * @author knight
39   * @author jimmy
40   */
41  public class EmohawkNavigation {
42  
43  	/** Location threshold for requesting of a new path or switching a path. */
44      protected static final int NEW_PATH_DISTANCE_THRESHOLD = 40;
45      /** Location threshold for checking whether we have arrived on target. For XY - 2D plane distance */
46      protected static final int ARRIVED_AT_LOCATION_XY_THRESHOLD = 50;
47      /** Location threshold for checking whether we have arrived on target. For Z distance. */
48      protected static final int ARRIVED_AT_LOCATION_Z_THRESHOLD = 100;
49      /** When PLAYER is further from currentTarget than this location, recompute the path */
50  	protected static final double PLAYER_DISTANCE_TRASHOLD = 600;
51  	/** We're managed to get to player */
52  	public static final double AT_PLAYER = 150;
53  	/** We're managed to get to location */
54  	public static final double AT_LOCATION = 150;
55  	
56  	/** Log used by this class. */
57  	protected LogCategory log;
58  	/** Steering used for navigation. */
59  	private Steering steering;
60  	/** FloydWarshallMap that is used for path planning. */
61      protected FloydWarshallMap fwMap;    
62      /** UT2004Bot reference. */
63      protected UT2004Bot bot;
64  	
65      protected IWorldEventListener<EndMessage> endMessageListener = new IWorldEventListener<EndMessage>() {		
66  		@Override
67  		public void notify(EndMessage event) {
68  			navigate();
69  		}
70  	};
71  	
72      // ===========
73      // CONSTRUCTOR
74      // ===========
75      
76      /**
77       * Here you may specify any custom UT2004Navigation parts.
78       * 
79       * @param bot
80       * @param ut2004PathExecutor
81       * @param fwMap
82       * @param getBackOnPath
83       * @param runStraight 
84       */
85      public EmohawkNavigation(UT2004Bot bot, Steering steering, FloydWarshallMap fwMap) {
86          this.log = bot.getLogger().getCategory(this.getClass().getSimpleName());
87      	this.bot = bot;
88      	
89      	this.steering = steering;
90      	this.fwMap = fwMap;
91      	
92          initListeners();
93      }
94      
95      private void initListeners() {
96      	bot.getWorldView().addEventListener(EndMessage.class, endMessageListener);
97  	}
98  	
99  	// ======================
100     // PUBLIC INTERFACE
101     // ======================
102         
103     public boolean isNavigating() {
104         return navigating;
105     }
106     
107     public void setFocus(ILocated located) {
108         throw new UnsupportedOperationException("Not implemented yet.");
109     }
110 
111     public void stopNavigation() {
112         reset(true);
113         bot.getAct().act(new Stop());
114     }
115     
116     public void navigate(ILocated target) {
117     	if (target == null) {
118     		if (log != null && log.isLoggable(Level.WARNING)) log.warning("Cannot navigate to NULL target!");
119     		stopNavigation();
120     		return;
121     	}
122     	
123     	if (target instanceof Player) {
124     		// USE DIFFERENT METHOD INSTEAD
125     		navigate((Player)target);
126     		return;
127     	}
128     	
129     	if (navigating) {
130     		if (currentTarget == target || currentTarget.getLocation().equals(target.getLocation())) {
131     			// just continue with current execution
132     			return;
133     		}    		
134     		// NEW TARGET!
135     		// => reset - stops pathExecutor as well, BUT DO NOT STOP getBackOnPath (we will need to do that eventually if needed, or it is not running)
136     		reset(false);
137     	}
138     	
139     	if (log != null && log.isLoggable(Level.FINE)) log.fine("Start navigating to: " + target);
140     	
141     	currentTarget = target;
142     	
143     	startNavigate();
144     }
145     
146     public void navigate(Player player) {
147     	if (player == null) {
148     		if (log != null && log.isLoggable(Level.WARNING)) log.warning("Cannot navigate to NULL player!");
149     		return;
150     	}
151     	
152     	if (navigating) {
153     		if (currentTarget == player) {
154     			// just continue with the execution
155     			return;
156     		}    		
157     		// NEW TARGET!
158     		// => reset - stops pathExecutor as well, BUT DO NOT STOP getBackOnPath (we will need to do that eventually if needed, or it is not running)
159     		reset(false);
160     	}
161     	
162     	if (log != null && log.isLoggable(Level.FINE)) log.fine("Start pursuing: " + player);
163     	    	
164     	currentTarget = player.getLocation();
165     	
166     	currentTargetPlayer = player;
167     	
168     	startNavigate();
169     }
170     
171     public NavPoint getNearestNavPoint(ILocated location) {
172     	if (location == null) return null;
173     	if (location instanceof NavPoint) return (NavPoint)location;
174     	if (location instanceof Item) {
175     		if (((Item)location).getNavPoint() != null) return ((Item)location).getNavPoint();
176     	}
177     	return DistanceUtils.getNearest(bot.getWorldView().getAll(NavPoint.class).values(), location);        
178     }
179     
180     public List<ILocated> getCurrentPathCopy() {
181         List<ILocated> result = new ArrayList();
182         if (currentFuturePath != null) {
183             result.addAll(currentFuturePath.get());
184         }
185         return result;
186     }
187 
188     public List<ILocated> getCurrentPathDirect() {
189         if (currentFuturePath != null) {
190             return currentFuturePath.get();
191         }
192         return null;
193     }
194     
195     public ILocated getCurrentTarget() {
196     	return currentTarget;
197     }
198     
199     public Player getCurrentTargetPlayer() {
200     	if (currentTarget instanceof Player) return (Player) currentTarget;
201     	return null;
202     }
203     
204     public NavPoint getCurrentTargetNavPoint() {
205     	if (currentTarget instanceof NavPoint) return (NavPoint) currentTarget;
206     	return null;
207     }
208 
209     
210     public ILocated getLastTarget() {
211     	return lastTarget;
212     }
213     
214     public NavPoint getLastTargetPlayer() {
215     	if (lastTarget instanceof Player) return (NavPoint)lastTarget;
216     	return null;
217     }
218     
219     public NavPoint getLastTargetNavPoint() {
220     	if (lastTarget instanceof NavPoint) return (NavPoint)lastTarget;
221     	return null;
222     }
223         
224     // ======================
225     // VARIABLES
226     // ======================
227     
228     /** Last location target. */
229     protected ILocated lastTarget = null;
230     /** Last location target. */
231     protected Player   lastTargetPlayer = null;
232     /** Current location target. */
233     protected ILocated currentTarget = null;
234     /** Current target is player (if not null) */
235     protected Player   currentTargetPlayer = null;
236     /** Navpoint we're running from (initial position when path executor has been triggered) */
237     protected NavPoint fromNavPoint;
238     /** Navpoint we're running to, nearest navpoint to currentTarget */
239 	protected NavPoint toNavPoint;    
240     /** Current path stored in IPathFuture object. */
241     protected IPathFuture currentFuturePath;
242     /** Whether navigation is running. */
243     protected boolean navigating = false;
244     /** We're running straight to the player. */
245 	protected boolean runningStraightToPlayer = false;
246 	/** Where run-straight failed. */
247 	protected Location runningStraightToPlayerFailedAt = null;
248 	
249     // ======================
250     // UTILITY METHODS
251     // ======================
252     
253 	protected void startNavigate() {
254 		if (!navigating) {
255 			if (steering.isNavigating()) steering.stopNavigation();
256 			steering.clearAllSteerings();
257 		}
258 		navigating = true;
259 		navigate();
260 	}
261 	
262     protected void navigate() {
263 		if (!navigating) return;
264 		
265 		if (log != null && log.isLoggable(Level.FINE)) {
266 			log.fine("NAVIGATING");
267 		}
268 		if (currentTargetPlayer != null) {
269 			if (log != null && log.isLoggable(Level.FINE)) log.fine("Pursuing " + currentTargetPlayer);
270 			navigatePlayer();
271 		} else {
272 			if (log != null && log.isLoggable(Level.FINE)) log.fine("Navigating to " + currentTarget);
273 			navigateLocation();
274 		}
275 	}
276     
277     private void navigateLocation() {
278     	if (steering.isNavigating()) {
279     		if (log != null && log.isLoggable(Level.FINE)) log.fine("Steering running");
280     		
281     		if (atLocation(currentTarget)) {
282     			reset(true);
283     		}
284     		
285     		return;
286     	}
287     	
288     	fromNavPoint = getNearestNavPoint(bot.getLocation());
289     	toNavPoint   = getNearestNavPoint(currentTarget);
290     	
291     	if (log != null && log.isLoggable(Level.FINE)) log.fine("Running from " + fromNavPoint.getId().getStringId() + " to " + toNavPoint.getId().getStringId());
292     	
293     	currentFuturePath = fwMap.computePath(fromNavPoint, toNavPoint);
294     	
295     	if (currentFuturePath == null || currentFuturePath.get() == null || currentFuturePath.get().size() == 0) {
296     		log.warning("NON EXISTING PATH BETWEEN: " + fromNavPoint.getId().getStringId() + " -> " + toNavPoint.getId().getStringId());
297     	}
298     	
299     	// FLOYD-WARSHAL HAS ALL PATHS PRECOMPUTED...
300     	// => path is already ready ;)
301     	
302     	// TINKER THE PATH
303     	processPathFuture(currentFuturePath);
304     	// LET'S START RUNNING!
305     	
306     	PathFollowingProperties pathFollowingProperties = new PathFollowingProperties();
307     	pathFollowingProperties.setBehaviorType(BehaviorType.ADVANCED);
308     	pathFollowingProperties.setTargetLocation(currentTarget.getLocation());
309     	pathFollowingProperties.setPath(currentFuturePath);
310     	steering.addPathFollowingSteering(pathFollowingProperties);
311     	
312     	PeopleAvoidanceProperties peopleAvoidanceProperties = new PeopleAvoidanceProperties();
313     	peopleAvoidanceProperties.setBehaviorType(BehaviorType.ADVANCED);
314     	peopleAvoidanceProperties.setAcceleration(true);
315     	peopleAvoidanceProperties.setCircumvention(true);
316     	peopleAvoidanceProperties.setDeceleration(true);
317     	steering.addPeopleAvoidanceSteering(peopleAvoidanceProperties);
318     	
319     	ObstacleAvoidanceProperties obstacleAvoidanceProperties = new ObstacleAvoidanceProperties();
320     	obstacleAvoidanceProperties.setBehaviorType(BehaviorType.ADVANCED);
321     	steering.addObstacleAvoidanceSteering(obstacleAvoidanceProperties);
322     	
323 //    	StickToPathProperties stickToPathProperties = new StickToPathProperties();
324 //    	stickToPathProperties.setPath(currentFuturePath);
325 //    	steering.addStickToPathSteering(stickToPathProperties);
326     	
327     	steering.startNavigation();
328 	}
329 
330 	private boolean atLocation(ILocated currentTarget) {
331 		return bot.getLocation().getDistance(currentTarget.getLocation()) < AT_LOCATION; 
332 	}
333 
334 	private void navigatePlayer() {
335 		throw new UnsupportedOperationException("Not implemented yet.");
336 	}
337 
338 	/**
339      * Checks if last path element is in close distance from our desired target and if not, we
340      * will add our desired target as the last path element.
341      * @param futurePath
342      */
343     protected void processPathFuture(IPathFuture futurePath) {
344         List<ILocated> pathList = futurePath.get();
345         
346         if (pathList == null) {
347         	// we failed to compute the path, e.g., path does not exist
348         	return;
349         }
350         
351         if (!pathList.isEmpty()) {
352             ILocated lastPathElement = pathList.get(pathList.size() - 1);
353             if (lastPathElement.getLocation().getDistance(currentTarget.getLocation()) > NEW_PATH_DISTANCE_THRESHOLD) {
354                 currentFuturePath.get().add(currentTarget);
355             }
356         } else {
357             currentFuturePath.get().add(currentTarget);
358         }
359     }
360         
361     protected void reset(boolean stopGetBackToNavGraph) {
362     	if (currentTarget != null) {
363     		lastTarget = currentTarget;
364     		lastTargetPlayer = currentTargetPlayer;
365     	}
366     	
367     	navigating = false;
368     	
369     	currentTarget = null;
370     	currentTargetPlayer = null;
371     	
372     	fromNavPoint = null;
373     	toNavPoint = null;
374     	
375     	currentFuturePath = null;
376     	
377     	runningStraightToPlayer = false;
378     	runningStraightToPlayerFailedAt = null;
379     	
380     	steering.stopNavigation();
381     	steering.clearAllSteerings();
382     }   
383     
384 }