changeset 190:f970f7dab2dd

Allow routepather to re-plan when the plan becomes invalid: - one of the steps in the plan is blocked - the target of the plan is changed
author icelus@33b003aa-7bff-0310-803a-e67f0ece8222
date Sat, 28 Feb 2009 12:15:04 +0000
parents 3d0cc4545938
children c725ecb2ce4c
files engine/core/pathfinder/routepather/routepather.cpp engine/core/pathfinder/routepather/routepather.h
diffstat 2 files changed, 136 insertions(+), 56 deletions(-) [+]
line wrap: on
line diff
--- a/engine/core/pathfinder/routepather/routepather.cpp	Thu Feb 26 23:33:25 2009 +0000
+++ b/engine/core/pathfinder/routepather/routepather.cpp	Sat Feb 28 12:15:04 2009 +0000
@@ -45,43 +45,84 @@
 		m_map = map;
 	}
 
-	int RoutePather::getNextLocation(const Instance* instance, const Location& target, 
-				double distance_to_travel, Location& nextLocation,
-				Location& facingLocation, int session_id, int priority) {
-		assert(instance);
-		assert(instance->getLocation().getLayer() == target.getLayer());
-		if(session_id != -1) {
-			PathMap::iterator j = m_paths.find(session_id);
-			if(j != m_paths.end()) {
-				if(j->second.empty()) {
-					m_paths.erase(j);
-					return -1;
-				} else {
-					followPath(instance, j->second, distance_to_travel, nextLocation, facingLocation);
-					return session_id;
-				}
-			} else {
-				if(!sessionIdValid(session_id)) {
-					//Session id is invalid.
-					return -1;
-				}
-			}
-			return session_id;
-		}
+	int RoutePather::makeSessionId() {
+		return m_nextFreeSessionId++;
+	}
+	
+	void RoutePather::makePlan(const Instance *instance, const Location& target, int session_id, int priority) {
 		SearchSpace* searchspace = getSearchSpace(target.getLayer());
 		if(!searchspace) {
 			searchspace = new SearchSpace(target.getLayer());
 			addSearchSpace(searchspace);
 		}
 		if(searchspace->isInSearchSpace(target)) {
-			session_id = m_nextFreeSessionId++;
 			RoutePatherSearch* newSearch = new RoutePatherSearch(session_id, instance->getLocation(), target, searchspace);
 			m_sessions.pushElement(SessionQueue::value_type(newSearch, priority));
 			addSessionId(session_id);
+			m_path_targets.insert(LocationMap::value_type(session_id,target));
+		}
+	}
+	
+	bool RoutePather::locationsEqual(const Location &a, const Location &b) {
+		
+		const ModelCoordinate a_coord = a.getLayerCoordinates();
+		const ModelCoordinate b_coord = b.getLayerCoordinates();
+		
+		return a_coord == b_coord;
+	}
+	
+	bool RoutePather::testStep(const Instance *instance, Path& path) {
+		Location instanceLoc = instance->getLocation();
+		if(!path.empty() && 
+		   !locationsEqual(path.front(), instanceLoc) &&
+		   instanceLoc.getLayer()->cellContainsBlockingInstance(path.front().getLayerCoordinates())) {
+			const bool last_step = path.front() == path.back();
+			path.clear();
+			return last_step;
+		}
+		return true;
+	}
+	
+	int RoutePather::getNextLocation(const Instance* instance, const Location& target, 
+									 double distance_to_travel, Location& nextLocation,
+									 Location& facingLocation, int session_id, int priority) {
+		assert(instance);
+		assert(instance->getLocation().getLayer() == target.getLayer());
+		bool plan_needed = true;
+		
+		if(session_id != -1) {
+			plan_needed = false;
+			PathMap::iterator path_itor = m_paths.find(session_id);
+			if(path_itor != m_paths.end()) {
+				LocationMap::iterator location_itor = m_path_targets.find(session_id);
+				assert(location_itor != m_path_targets.end());
+				
+				if(path_itor->second.empty()) {
+					m_paths.erase(path_itor);
+					m_path_targets.erase(location_itor);
+					return -1;
+				}
+				
+				if(!followPath(instance, path_itor->second, distance_to_travel, nextLocation, facingLocation) 
+				   || !locationsEqual(location_itor->second, target)) {
+					m_paths.erase(path_itor);
+					m_path_targets.erase(location_itor);
+					plan_needed = true;
+				}
+			} else if(!sessionIdValid(session_id)) {
+				//Session id is invalid.
+				return -1;
+			}
+		}
+		if(plan_needed) {
+			if(session_id == -1) {
+				session_id = makeSessionId();
+			}
+			makePlan(instance, target, session_id, priority);
 		}
 		return session_id;
 	}
-
+	
 	void RoutePather::update() {
 		int ticksleft = m_maxticks;
 		while(ticksleft >= 0) {
@@ -96,22 +137,33 @@
 			}
 			priority_session->updateSearch();
 			if(priority_session->getSearchStatus() == Search::search_status_complete) {
+				const int session_id = priority_session->getSessionId();
 				Path newPath = priority_session->calcPath();
-				m_paths.insert(PathMap::value_type(priority_session->getSessionId(), newPath));
-				invalidateSessionId(priority_session->getSessionId());
+				newPath.erase(newPath.begin());
+				m_paths.insert(PathMap::value_type(session_id, newPath));
+				invalidateSessionId(session_id);
 				delete priority_session;
 				m_sessions.popElement();
 			} else if(priority_session->getSearchStatus() == Search::search_status_failed) {
-				invalidateSessionId(priority_session->getSessionId());
+				const int session_id = priority_session->getSessionId();
+				invalidateSessionId(session_id);
 				delete priority_session;
 				m_sessions.popElement();
 			}
 			--ticksleft;
 		}
 	}
-
-	void RoutePather::followPath(const Instance* instance, Path& path, double speed, Location& nextLocation, Location& facingLocation) {
-		Location instanceLoc = instance->getLocation();
+	
+	bool RoutePather::followPath(const Instance* instance, Path& path, double speed, Location& nextLocation, Location& facingLocation) {
+		Location instanceLoc = instance->getLocation();                      
+		if(!testStep(instance, path)) {
+			return false;
+		}
+		
+		if(path.empty()) {
+			return true;
+		}
+		
 		ExactModelCoordinate instancePos = instanceLoc.getMapCoordinates();
 		ExactModelCoordinate facingPos = path.front().getMapCoordinates();
 		facingPos.x = facingPos.x + (facingPos.x - instancePos.x);
@@ -128,32 +180,31 @@
 			speed = distance;
 			pop = true;
 		}
-		if(distance != 0)
-		{
+		if(distance != 0) {
 			instancePos.x += (dx / distance) * speed;
 			instancePos.y += (dy / distance) * speed;
-
 		} else {
 			pop = true;
 		}
+		
 		nextLocation.setMapCoordinates(instancePos);
-		if(pop)
-		{
+		if(pop) {
 			path.pop_front();
-			if(!path.empty() && instanceLoc.getLayer()->cellContainsBlockingInstance(path.front().getLayerCoordinates()))
-			{
-				//TODO: Determine if we're close enough to stop, otherwise find alternate route.
-				path.clear();
-				return;
+			if(!testStep(instance, path)) {
+				return false;
 			}
 		}
+		return true;
 	}
 	
 	bool RoutePather::cancelSession(const int session_id) {
 		if(session_id >= 0) {
 			PathMap::iterator i = m_paths.find(session_id);
 			if(i != m_paths.end()) {
+				LocationMap::iterator j = m_path_targets.find(session_id);
+				assert(j != m_path_targets.end());
 				m_paths.erase(i);
+				m_path_targets.erase(j);
 				return true;
 			} else {
 				invalidateSessionId(session_id);
@@ -161,40 +212,40 @@
 		}
 		return false;
 	}
-
+	
 	void RoutePather::addSessionId(const int sessionId) {
 		m_registeredSessionIds.push_back(sessionId);
 	}
-
+	
 	bool RoutePather::sessionIdValid(const int sessionId) {
 		for(SessionList::const_iterator i = m_registeredSessionIds.begin();
 			i != m_registeredSessionIds.end();
 			++i) {
-				if((*i) == sessionId) {
-					return true;
-				}
+			if((*i) == sessionId) {
+				return true;
+			}
 		}
 		return false;
 	}
-
+	
 	bool RoutePather::invalidateSessionId(const int sessionId) {
 		for(SessionList::iterator i = m_registeredSessionIds.begin();
 			i != m_registeredSessionIds.end();
 			++i) {
-				if((*i) == sessionId) {
-					m_registeredSessionIds.erase(i);
-					return true;
-				}
+			if((*i) == sessionId) {
+				m_registeredSessionIds.erase(i);
+				return true;
+			}
 		}
 		return false;
 	}
-
+	
 	bool RoutePather::addSearchSpace(SearchSpace* search_space) {
 		std::pair<SearchSpaceMap::iterator, bool> res = m_searchspaces.insert(SearchSpaceMap::value_type(search_space->getLayer(), search_space));
 		
 		return res.second;
 	}
-
+	
 	SearchSpace* RoutePather::getSearchSpace(Layer * const layer) {
 		SearchSpaceMap::iterator i = m_searchspaces.find(layer);
 		if(i == m_searchspaces.end()) {
--- a/engine/core/pathfinder/routepather/routepather.h	Thu Feb 26 23:33:25 2009 +0000
+++ b/engine/core/pathfinder/routepather/routepather.h	Sat Feb 28 12:15:04 2009 +0000
@@ -96,6 +96,7 @@
 		typedef std::list<int> SessionList;
 		typedef std::map<int, Path> PathMap;
 		typedef std::map<Layer*, SearchSpace*> SearchSpaceMap;
+		typedef std::map<int, Location> LocationMap;
 		/** Makes the instance follow the given path.
 		 *
 		 * Calculates the next position the instance should move to given the 
@@ -105,8 +106,9 @@
 		 * @param speed The speed to move the instance.
 		 * @param nextLocation An out variable which will store the instances next location.
 		 * @param facingLocation An out variable which will store the instances facing location.
+                 * @return true if it was possible to follow the path, false if it was not
 		 */
-		void followPath(const Instance* instance, Path& path, double speed, Location& nextLocation, Location& facingLocation);
+		bool followPath(const Instance* instance, Path& path, double speed, Location& nextLocation, Location& facingLocation);
 
 		/** Adds a session id to the session map.
 		 *
@@ -115,8 +117,32 @@
 		 * @param sessionId The session id to store.
 		 */
 		void addSessionId(const int sessionId);
-
-
+		
+		/** Schedules a plan to be created for the given instance to reach the given
+		 * target; the session id is where the plan should be stored 
+		 *
+		 * @param instance is the instance to pathfind for
+		 * @param target is where the instance is going
+		 * @param session_id is which pathfinding slot to put the plan in
+		 * @param priority is the priority of the request
+		 */
+		void makePlan(const Instance *instance, const Location& target, int session_id, int priority);
+		
+		/** make a new session id 
+			@return the new session id
+		*/
+		int makeSessionId();
+		
+		/** are two locations equivalent from the perspective of pathing */
+		bool locationsEqual(const Location &a, const Location &b);
+		
+		/** check whether it's safe to continue moving down the path
+			@param instance is the instance following the path
+			@param path is the path to step through
+			@return true if the path could be followed, false if blocked
+		*/
+		bool testStep(const Instance *instance, Path& path);
+		
 		/** Determines if the given session Id is valid.
 		 *
 		 * Searches the session list to determine if a search with the given session id
@@ -145,6 +171,9 @@
 		//Calculated paths for the movement phase.
 		PathMap		   m_paths;
 
+		//The endpoints for which those paths were calculated
+		LocationMap        m_path_targets;
+		
 		//A map of searchspaces.
 		SearchSpaceMap m_searchspaces;