// ============================================================== // This file is part of Glest (www.glest.org) // // Copyright (C) 2001-2008 Martiņo Figueroa // // You can redistribute this code and/or modify it under // the terms of the GNU General Public License as published // by the Free Software Foundation; either version 2 of the // License, or (at your option) any later version // ============================================================== #include "path_finder.h" #include #include #include "config.h" #include "map.h" #include "unit.h" #include "unit_type.h" #include "leak_dumper.h" using namespace std; using namespace Shared::Graphics; using namespace Shared::Util; namespace Glest{ namespace Game{ // ===================================================== // class PathFinder // ===================================================== // ===================== PUBLIC ======================== const int PathFinder::maxFreeSearchRadius= 10; const int PathFinder::pathFindNodesMax= 400; const int PathFinder::pathFindRefresh= 10; PathFinder::PathFinder(){ nodePool= NULL; } PathFinder::PathFinder(const Map *map){ init(map); nodePool= NULL; } void PathFinder::init(const Map *map){ nodePool= new Node[pathFindNodesMax]; this->map= map; } PathFinder::~PathFinder(){ delete [] nodePool; } TravelState PathFinder::findPath(Unit *unit, const Vec2i &finalPos){ //route cache UnitPathInterface *path= unit->getPath(); if(finalPos==unit->getPos()) { //if arrived unit->setCurrSkill(scStop); return tsArrived; } else { if(path->isEmpty() == false) { if(dynamic_cast(path) != NULL) { //route cache UnitPathBasic *basicPath = dynamic_cast(path); Vec2i pos= basicPath->pop(); if(map->canMove(unit, unit->getPos(), pos)) { unit->setTargetPos(pos); return tsMoving; } } else if(dynamic_cast(path) != NULL) { UnitPath *advPath = dynamic_cast(path); //route cache Vec2i pos= advPath->peek(); if(map->canMove(unit, unit->getPos(), pos)) { advPath->pop(); unit->setTargetPos(pos); return tsMoving; } } else { throw runtime_error("unsupported or missing path finder detected!"); } } } //route cache miss TravelState ts= aStar(unit, finalPos); //post actions switch(ts){ case tsBlocked: case tsArrived: unit->setCurrSkill(scStop); break; case tsMoving: { if(dynamic_cast(path) != NULL) { UnitPathBasic *basicPath = dynamic_cast(path); Vec2i pos= basicPath->pop(); if(map->canMove(unit, unit->getPos(), pos)) { unit->setTargetPos(pos); } else { unit->setCurrSkill(scStop); return tsBlocked; } } else if(dynamic_cast(path) != NULL) { UnitPath *advPath = dynamic_cast(path); Vec2i pos= advPath->peek(); if(map->canMove(unit, unit->getPos(), pos)) { advPath->pop(); unit->setTargetPos(pos); } else { unit->setCurrSkill(scStop); return tsBlocked; } } else { throw runtime_error("unsupported or missing path finder detected!"); } } break; } return ts; } // ==================== PRIVATE ==================== //route a unit using A* algorithm TravelState PathFinder::aStar(Unit *unit, const Vec2i &targetPos){ nodePoolCount= 0; const Vec2i finalPos= computeNearestFreePos(unit, targetPos); //if arrived if(finalPos==unit->getPos()){ return tsArrived; } //path find algorithm //a) push starting pos into openNodes Node *firstNode= newNode(); assert(firstNode!=NULL);; firstNode->next= NULL; firstNode->prev= NULL; firstNode->pos= unit->getPos(); firstNode->heuristic= heuristic(unit->getPos(), finalPos); firstNode->exploredCell= true; openNodes.push_back(firstNode); //b) loop bool pathFound= true; bool nodeLimitReached= false; Node *node= NULL; while(!nodeLimitReached){ //b1) is open nodes is empty => failed to find the path if(openNodes.empty()){ pathFound= false; break; } //b2) get the minimum heuristic node Nodes::iterator it = minHeuristic(); node= *it; //b3) if minHeuristic is the finalNode, or the path is no more explored => path was found if(node->pos==finalPos || !node->exploredCell){ pathFound= true; break; } //b4) move this node from closedNodes to openNodes //add all succesors that are not in closedNodes or openNodes to openNodes closedNodes.push_back(node); openNodes.erase(it); for(int i=-1; i<=1 && !nodeLimitReached; ++i){ for(int j=-1; j<=1 && !nodeLimitReached; ++j){ Vec2i sucPos= node->pos + Vec2i(i, j); if(!openPos(sucPos) && map->aproxCanMove(unit, node->pos, sucPos)){ //if node is not open and canMove then generate another node Node *sucNode= newNode(); if(sucNode!=NULL){ sucNode->pos= sucPos; sucNode->heuristic= heuristic(sucNode->pos, finalPos); sucNode->prev= node; sucNode->next= NULL; sucNode->exploredCell= map->getSurfaceCell(Map::toSurfCoords(sucPos))->isExplored(unit->getTeam()); openNodes.push_back(sucNode); } else{ nodeLimitReached= true; } } } } }//while Node *lastNode= node; //if consumed all nodes find best node (to avoid strage behaviour) if(nodeLimitReached){ for(Nodes::iterator it= closedNodes.begin(); it!=closedNodes.end(); ++it){ if((*it)->heuristic < lastNode->heuristic){ lastNode= *it; } } } //check results of path finding TravelState ts; UnitPathInterface *path= unit->getPath(); if(pathFound==false || lastNode==firstNode){ //blocked ts= tsBlocked; path->incBlockCount(); } else { //on the way ts= tsMoving; //build next pointers Node *currNode= lastNode; while(currNode->prev!=NULL){ currNode->prev->next= currNode; currNode= currNode->prev; } //store path path->clear(); currNode= firstNode; for(int i=0; currNode->next!=NULL && inext, i++){ path->push(currNode->next->pos); } } //clean nodes openNodes.clear(); closedNodes.clear(); return ts; } PathFinder::Node *PathFinder::newNode(){ if(nodePoolCountgetPos(); int size= unit->getType()->getSize(); Field field= unit->getCurrField(); int teamIndex= unit->getTeam(); //if finalPos is free return it if(map->isAproxFreeCells(finalPos, size, field, teamIndex)){ return finalPos; } //find nearest pos Vec2i nearestPos= unitPos; float nearestDist= unitPos.dist(finalPos); for(int i= -maxFreeSearchRadius; i<=maxFreeSearchRadius; ++i){ for(int j= -maxFreeSearchRadius; j<=maxFreeSearchRadius; ++j){ Vec2i currPos= finalPos + Vec2i(i, j); if(map->isAproxFreeCells(currPos, size, field, teamIndex)){ float dist= currPos.dist(finalPos); //if nearer from finalPos if(distheuristic < (*minNodeIt)->heuristic){ minNodeIt= it; } } return minNodeIt; } bool PathFinder::openPos(const Vec2i &sucPos){ for(Nodes::reverse_iterator it= closedNodes.rbegin(); it!=closedNodes.rend(); ++it){ if(sucPos==(*it)->pos){ return true; } } //use reverse iterator to find a node faster for(Nodes::reverse_iterator it= openNodes.rbegin(); it!=openNodes.rend(); ++it){ if(sucPos==(*it)->pos){ return true; } } return false; } }} //end namespace