view engine/core/pathfinder/routepather/routepather.cpp @ 336:16112ef84609

PyChan fixes: * Previous commits removed the ability to map events to unnamed widgets. Fixed.
author phoku@33b003aa-7bff-0310-803a-e67f0ece8222
date Mon, 24 Aug 2009 15:34:23 +0000
parents 8afb9b443f66
children
line wrap: on
line source

/***************************************************************************
 *   Copyright (C) 2005-2008 by the FIFE team                              *
 *   http://www.fifengine.de                                               *
 *   This file is part of FIFE.                                            *
 *                                                                         *
 *   FIFE is free software; you can redistribute it and/or                 *
 *   modify it under the terms of the GNU Lesser General Public            *
 *   License as published by the Free Software Foundation; either          *
 *   version 2.1 of the License, or (at your option) any later version.    *
 *                                                                         *
 *   This library is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 *   Lesser General Public License for more details.                       *
 *                                                                         *
 *   You should have received a copy of the GNU Lesser General Public      *
 *   License along with this library; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
 ***************************************************************************/

// Standard C++ library includes
#include <cassert>

// 3rd party library includes

// FIFE includes
// These includes are split up in two parts, separated by one empty line
// First block: files included from the FIFE root src directory
// Second block: files included from the same folder
#include "model/metamodel/grids/cellgrid.h"
#include "model/structures/instance.h"
#include "model/structures/layer.h"

#include "pathfinder/searchspace.h"

#include "routepather.h"
#include "routepathersearch.h"

namespace FIFE {
	void RoutePather::setMap(Map* map) {
		if(!map) {
			return;
		}
		m_map = map;
	}

	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)) {
			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) {
			if(m_sessions.empty()) {
				break;
			}
			RoutePatherSearch* priority_session = m_sessions.getPriorityElement().first;
			if(!sessionIdValid(priority_session->getSessionId())) {
				delete priority_session;
				m_sessions.popElement();
				continue;
			}
			priority_session->updateSearch();
			if(priority_session->getSearchStatus() == RoutePatherSearch::search_status_complete) {
				const int session_id = priority_session->getSessionId();
				Path newPath = priority_session->calcPath();
				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() == RoutePatherSearch::search_status_failed) {
				const int session_id = priority_session->getSessionId();
				invalidateSessionId(session_id);
				delete priority_session;
				m_sessions.popElement();
			}
			--ticksleft;
		}
	}
	
	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);
		facingPos.y = facingPos.y + (facingPos.y - instancePos.y);
		facingLocation = path.front();
		facingLocation.setMapCoordinates(facingPos);
		ExactModelCoordinate targetPos = path.front().getMapCoordinates();
		CellGrid* grid = instanceLoc.getLayer()->getCellGrid();
		double dx = (targetPos.x - instancePos.x) * grid->getXScale();
		double dy = (targetPos.y - instancePos.y) * grid->getYScale();
		double distance = sqrt(dx * dx + dy * dy);
		bool pop = false;
		if(speed > distance) {
			speed = distance;
			pop = true;
		}
		if(distance != 0) {
			instancePos.x += (dx / distance) * speed;
			instancePos.y += (dy / distance) * speed;
		} else {
			pop = true;
		}
		
		nextLocation.setMapCoordinates(instancePos);
		if(pop) {
			path.pop_front();
			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);
			}
		}
		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;
			}
		}
		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;
			}
		}
		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()) {
			return 0;
		}
		return i->second;
	}
}