diff --git a/source/glest_game/ai/path_finder.cpp b/source/glest_game/ai/path_finder.cpp index 62d59720..f69a5609 100644 --- a/source/glest_game/ai/path_finder.cpp +++ b/source/glest_game/ai/path_finder.cpp @@ -20,6 +20,7 @@ #include "unit_type.h" #include "platform_common.h" #include "command.h" +#include "faction.h" #include "leak_dumper.h" using namespace std; @@ -95,6 +96,7 @@ TravelState PathFinder::findPath(Unit *unit, const Vec2i &finalPos, bool *wasStu unit->setCurrentUnitTitle(szBuf); } + unit->getFaction()->addCachedPath(finalPos,unit); return tsArrived; } else { @@ -105,6 +107,7 @@ TravelState PathFinder::findPath(Unit *unit, const Vec2i &finalPos, bool *wasStu Vec2i pos= basicPath->pop(); if(map->canMove(unit, unit->getPos(), pos)) { unit->setTargetPos(pos); + unit->addCurrentTargetPathTakenCell(finalPos,pos); return tsMoving; } } @@ -124,13 +127,29 @@ TravelState PathFinder::findPath(Unit *unit, const Vec2i &finalPos, bool *wasStu } } - //route cache miss - TravelState ts= aStar(unit, finalPos, false); + TravelState ts = tsImpossible; + std::vector cachedPath = unit->getFaction()->findCachedPath(finalPos, unit); + if(cachedPath.size() > 0) { + path->clear(); + + for(int i=0; i < cachedPath.size() && i < pathFindRefresh; ++i) { + path->add(cachedPath[i]); + } + ts = tsMoving; + } + else { + //route cache miss + ts = aStar(unit, finalPos, false); + } //post actions switch(ts) { case tsBlocked: case tsArrived: + + if(ts == tsArrived) { + unit->getFaction()->addCachedPath(finalPos,unit); + } // The unit is stuck (not only blocked but unable to go anywhere for a while) // We will try to bail out of the immediate area if( ts == tsBlocked && unit->getInBailOutAttempt() == false && @@ -204,6 +223,7 @@ TravelState PathFinder::findPath(Unit *unit, const Vec2i &finalPos, bool *wasStu Vec2i pos= basicPath->pop(); if(map->canMove(unit, unit->getPos(), pos)) { unit->setTargetPos(pos); + unit->addCurrentTargetPathTakenCell(finalPos,pos); } else { unit->setCurrSkill(scStop); diff --git a/source/glest_game/type_instances/faction.cpp b/source/glest_game/type_instances/faction.cpp index 7e291407..394073bc 100644 --- a/source/glest_game/type_instances/faction.cpp +++ b/source/glest_game/type_instances/faction.cpp @@ -584,6 +584,105 @@ void Faction::resetResourceAmount(const ResourceType *rt){ assert(false); } +std::vector Faction::findCachedPath(const Vec2i &target, Unit *unit) { + std::vector result; + if(successfulPathFinderTargetList.find(target) == successfulPathFinderTargetList.end()) { + // Lets find the shortest and most successful path already taken by a + // similar sized unit + + bool foundCachedPath = false; + + std::vector &cacheList = successfulPathFinderTargetList[target]; + int unitSize = unit->getType()->getSize(); + for(int i = 0; i < cacheList.size(); ++i) { + FactionPathSuccessCache &cache = cacheList[i]; + if(cache.unitSize <= unitSize) { + vector, int> > &pathQueue = cache.pathQueue; + + for(int j = 0; j < pathQueue.size(); ++j) { + // Now start at the end of the path and see how many nodes + // until we reach a cell near the unit's current position + std::pair, int> &path = pathQueue[j]; + + for(int k = path.first.size() - 1; k >= 0; --k) { + if(world->getMap()->canMove(unit, unit->getPos(), path.first[k]) == true) { + if(foundCachedPath == false) { + for(int l = k; l < path.first.size(); ++l) { + result.push_back(path.first[l]); + } + } + else { + if(result.size() > (path.first.size() - k)) { + for(int l = k; l < path.first.size(); ++l) { + result.push_back(path.first[l]); + } + } + } + foundCachedPath = true; + break; + } + } + } + } + } + } + return result; +} + +void Faction::addCachedPath(const Vec2i &target, Unit *unit) { + if(successfulPathFinderTargetList.find(target) == successfulPathFinderTargetList.end()) { + FactionPathSuccessCache cache; + cache.unitSize = unit->getType()->getSize(); + cache.pathQueue.push_back(make_pair, int>(unit->getCurrentTargetPathTaken().second,1)); + successfulPathFinderTargetList[target].push_back(cache); + } + else { + bool finishedAdd = false; + std::pair > currentTargetPathTaken = unit->getCurrentTargetPathTaken(); + std::vector &cacheList = successfulPathFinderTargetList[target]; + int unitSize = unit->getType()->getSize(); + for(int i = 0; i < cacheList.size() && finishedAdd == false; ++i) { + FactionPathSuccessCache &cache = cacheList[i]; + if(cache.unitSize <= unitSize) { + vector, int> > &pathQueue = cache.pathQueue; + + for(int j = 0; j < pathQueue.size() && finishedAdd == false; ++j) { + // Now start at the end of the path and see how many nodes are the same + std::pair, int> &path = pathQueue[j]; + int minPathSize = std::min(path.first.size(),currentTargetPathTaken.second.size()); + int intersectIndex = -1; + + for(int k = 0; k < minPathSize; ++k) { + if(path.first[path.first.size() - k - 1] != currentTargetPathTaken.second[currentTargetPathTaken.second.size() - k - 1]) { + intersectIndex = k; + break; + } + } + + // New path is same or longer than old path so replace + // old path with new + if(intersectIndex + 1 == path.first.size()) { + path.first = currentTargetPathTaken.second; + path.second++; + finishedAdd = true; + } + // Old path is same or longer than new path so + // do nothing + else if(intersectIndex + 1 == currentTargetPathTaken.second.size()) { + path.second++; + finishedAdd = true; + } + } + + // If new path is >= 10 cells add it + if(finishedAdd == false && currentTargetPathTaken.second.size() >= 10) { + pathQueue.push_back(make_pair, int>(currentTargetPathTaken.second,1)); + } + } + } + } +} + std::string Faction::toString() const { std::string result = ""; diff --git a/source/glest_game/type_instances/faction.h b/source/glest_game/type_instances/faction.h index d07744d7..d6352017 100644 --- a/source/glest_game/type_instances/faction.h +++ b/source/glest_game/type_instances/faction.h @@ -45,7 +45,15 @@ class World; /// Each of the game players // ===================================================== -class Faction{ +class FactionPathSuccessCache { +public: + // The unit Size for the path findings + int unitSize; + // a List of paths with their # success counts + vector, int> > pathQueue; +}; + +class Faction { private: typedef vector Resources; typedef vector Store; @@ -64,7 +72,6 @@ private: World *world; ScriptManager *scriptManager; - ControlType control; Texture2D *texture; @@ -76,6 +83,8 @@ private: bool thisFaction; + std::map > successfulPathFinderTargetList; + public: Faction(); ~Faction(); @@ -143,6 +152,9 @@ public: void setControlType(ControlType value) { control = value; } + std::vector findCachedPath(const Vec2i &target, Unit *unit); + void addCachedPath(const Vec2i &target, Unit *unit); + std::string toString() const; private: diff --git a/source/glest_game/type_instances/unit.cpp b/source/glest_game/type_instances/unit.cpp index 1583d2c9..e0537a0e 100644 --- a/source/glest_game/type_instances/unit.cpp +++ b/source/glest_game/type_instances/unit.cpp @@ -1699,8 +1699,8 @@ void Unit::removeBadHarvestPos(const Vec2i &value) { cleanupOldBadHarvestPos(); } -bool Unit::isBadHarvestPos(const Vec2i &value) { - cleanupOldBadHarvestPos(); +bool Unit::isBadHarvestPos(const Vec2i &value, bool checkPeerUnits) const { + //cleanupOldBadHarvestPos(); bool result = false; for(int i = 0; i < badHarvestPosList.size(); ++i) { @@ -1711,6 +1711,21 @@ bool Unit::isBadHarvestPos(const Vec2i &value) { } } + if(result == false && checkPeerUnits == true) { + // Check if any other units of similar type have this position tagged + // as bad? + for(int i = 0; i < this->getFaction()->getUnitCount(); ++i) { + Unit *peerUnit = this->getFaction()->getUnit(i); + if( peerUnit != NULL && peerUnit->getId() != this->getId() && + peerUnit->getType()->getSize() <= this->getType()->getSize()) { + if(peerUnit->isBadHarvestPos(value,false) == true) { + result = true; + break; + } + } + } + } + return result; } @@ -1718,9 +1733,9 @@ void Unit::cleanupOldBadHarvestPos() { for(int i = badHarvestPosList.size() - 1; i >= 0; --i) { const std::pair &item = badHarvestPosList[i]; - // If this position has been is the list for longer than 120 + // If this position has been is the list for longer than 240 // seconds remove it so the unit could potentially try it again - if(item.second.getMillis() >= 1200000) { + if(item.second.getMillis() >= 2400000) { badHarvestPosList.erase(badHarvestPosList.begin() + i); } } @@ -1750,6 +1765,14 @@ void Unit::setLastHarvestResourceTarget(const Vec2i *pos) { } } +void Unit::addCurrentTargetPathTakenCell(const Vec2i &target,const Vec2i &cell) { + if(currentTargetPathTaken.first != target) { + currentTargetPathTaken.second.clear(); + } + currentTargetPathTaken.first = target; + currentTargetPathTaken.second.push_back(cell); +} + std::string Unit::toString() const { std::string result = ""; diff --git a/source/glest_game/type_instances/unit.h b/source/glest_game/type_instances/unit.h index 1b58aff2..629bd07a 100644 --- a/source/glest_game/type_instances/unit.h +++ b/source/glest_game/type_instances/unit.h @@ -116,6 +116,8 @@ public: virtual int getBlockCount() const = 0; virtual int getQueueCount() const = 0; + virtual vector getQueue() const = 0; + virtual std::string toString() const = 0; }; @@ -141,6 +143,8 @@ public: virtual int getBlockCount() const { return blockCount; } virtual int getQueueCount() const { return pathQueue.size(); } + virtual vector getQueue() const { return pathQueue; } + virtual std::string toString() const; }; @@ -185,6 +189,14 @@ public: virtual int getBlockCount() const { return blockCount; } virtual int getQueueCount() const { return this->size(); } + virtual vector getQueue() const { + vector result; + for(list::const_iterator iter = this->begin(); iter != this->end(); ++iter) { + result.push_back(*iter); + } + return result; + } + virtual std::string toString() const; }; @@ -291,6 +303,8 @@ private: std::vector > badHarvestPosList; std::pair lastHarvestResourceTarget; + std::pair > currentTargetPathTaken; + static Game *game; public: @@ -439,12 +453,15 @@ public: void setBadHarvestPosList(std::vector > value) { badHarvestPosList = value; } void addBadHarvestPos(const Vec2i &value); void removeBadHarvestPos(const Vec2i &value); - bool isBadHarvestPos(const Vec2i &value); + bool isBadHarvestPos(const Vec2i &value,bool checkPeerUnits=true) const; void cleanupOldBadHarvestPos(); void setLastHarvestResourceTarget(const Vec2i *pos); std::pair getLastHarvestResourceTarget() const { return lastHarvestResourceTarget;} + std::pair > getCurrentTargetPathTaken() const { return currentTargetPathTaken; } + void addCurrentTargetPathTakenCell(const Vec2i &target,const Vec2i &cell); + std::string toString() const; private: diff --git a/source/glest_game/world/map.cpp b/source/glest_game/world/map.cpp index 7e50fa66..9d3fb675 100644 --- a/source/glest_game/world/map.cpp +++ b/source/glest_game/world/map.cpp @@ -24,6 +24,8 @@ #include "game_settings.h" #include "platform_util.h" #include "pos_iterator.h" +#include "faction.h" +#include "command.h" #include "leak_dumper.h" using namespace Shared::Graphics; @@ -249,7 +251,7 @@ bool Map::isInsideSurface(const Vec2i &sPos) const{ } //returns if there is a resource next to a unit, in "resourcePos" is stored the relative position of the resource -bool Map::isResourceNear(const Vec2i &pos, const ResourceType *rt, Vec2i &resourcePos, int size, Unit *unit) const { +bool Map::isResourceNear(const Vec2i &pos, const ResourceType *rt, Vec2i &resourcePos, int size, Unit *unit, bool fallbackToPeersHarvestingSameResource) const { for(int i = -1; i <= size; ++i) { for(int j = -1; j <= size; ++j) { if(isInside(pos.x + i, pos.y + j)) { @@ -266,6 +268,29 @@ bool Map::isResourceNear(const Vec2i &pos, const ResourceType *rt, Vec2i &resour } } } + + if(fallbackToPeersHarvestingSameResource == true && unit != NULL) { + for(int i = 0; i < unit->getFaction()->getUnitCount(); ++i) { + Unit *peerUnit = unit->getFaction()->getUnit(i); + if( peerUnit != NULL && peerUnit->getId() != unit->getId() && + peerUnit->getType()->getSize() <= unit->getType()->getSize()) { + if( peerUnit->getCurrSkill()->getClass() == scHarvest && + peerUnit->getLoadType() == rt && + peerUnit->getCurrCommand() != NULL) { + + if(unit->getPos().dist(peerUnit->getCurrCommand()->getPos()) <= 30) { + if(i == 0 || (unit->getPos().dist(peerUnit->getCurrCommand()->getPos()) < unit->getPos().dist(resourcePos))) { + resourcePos = peerUnit->getCurrCommand()->getPos(); + } + if(unit->getPos().dist(peerUnit->getCurrCommand()->getPos()) <= 5) { + resourcePos = peerUnit->getCurrCommand()->getPos(); + return true; + } + } + } + } + } + } return false; } @@ -384,23 +409,26 @@ bool Map::canOccupy(const Vec2i &pos, Field field, const UnitType *ut, CardinalD // ==================== unit placement ==================== //checks if a unit can move from between 2 cells -bool Map::canMove(const Unit *unit, const Vec2i &pos1, const Vec2i &pos2) const{ +bool Map::canMove(const Unit *unit, const Vec2i &pos1, const Vec2i &pos2) const { int size= unit->getType()->getSize(); - for(int i=pos2.x; igetUnit(unit->getCurrField())!=unit){ - if(!isFreeCell(Vec2i(i, j), unit->getCurrField())){ + for(int i=pos2.x; igetUnit(unit->getCurrField()) != unit) { + if(isFreeCell(Vec2i(i, j), unit->getCurrField()) == false) { return false; } } } - else{ + else { return false; } } } + if(unit == NULL || unit->isBadHarvestPos(pos2) == true) { + return false; + } return true; } @@ -411,37 +439,47 @@ bool Map::aproxCanMove(const Unit *unit, const Vec2i &pos1, const Vec2i &pos2) c Field field= unit->getCurrField(); //single cell units - if(size==1){ - if(!isAproxFreeCell(pos2, field, teamIndex)){ + if(size==1) { + if(isAproxFreeCell(pos2, field, teamIndex) == false){ return false; } - if(pos1.x!=pos2.x && pos1.y!=pos2.y){ - if(!isAproxFreeCell(Vec2i(pos1.x, pos2.y), field, teamIndex)){ + if(pos1.x != pos2.x && pos1.y != pos2.y){ + if(isAproxFreeCell(Vec2i(pos1.x, pos2.y), field, teamIndex) == false) { return false; } - if(!isAproxFreeCell(Vec2i(pos2.x, pos1.y), field, teamIndex)){ + if(isAproxFreeCell(Vec2i(pos2.x, pos1.y), field, teamIndex) == false) { return false; } } + + if(unit == NULL || unit->isBadHarvestPos(pos2) == true) { + return false; + } + return true; } //multi cell units - else{ - for(int i=pos2.x; igetUnit(unit->getCurrField())!=unit){ - if(!isAproxFreeCell(Vec2i(i, j), field, teamIndex)){ + if(getCell(i, j)->getUnit(unit->getCurrField())!=unit) { + if(isAproxFreeCell(Vec2i(i, j), field, teamIndex) == false) { return false; } } } - else{ + else { return false; } } } + + if(unit == NULL || unit->isBadHarvestPos(pos2) == true) { + return false; + } + return true; } } diff --git a/source/glest_game/world/map.h b/source/glest_game/world/map.h index 88b71918..4c8cfc4a 100755 --- a/source/glest_game/world/map.h +++ b/source/glest_game/world/map.h @@ -197,7 +197,7 @@ public: bool isInside(const Vec2i &pos) const; bool isInsideSurface(int sx, int sy) const; bool isInsideSurface(const Vec2i &sPos) const; - bool isResourceNear(const Vec2i &pos, const ResourceType *rt, Vec2i &resourcePos, int size, Unit *unit=NULL) const; + bool isResourceNear(const Vec2i &pos, const ResourceType *rt, Vec2i &resourcePos, int size, Unit *unit=NULL,bool fallbackToPeersHarvestingSameResource=false) const; bool isResourceNear(const Vec2i &pos, int size, const ResourceType *rt, Vec2i &resourcePos) const; //free cells diff --git a/source/glest_game/world/unit_updater.cpp b/source/glest_game/world/unit_updater.cpp index 7b17afe7..4e82efbe 100644 --- a/source/glest_game/world/unit_updater.cpp +++ b/source/glest_game/world/unit_updater.cpp @@ -591,6 +591,8 @@ void UnitUpdater::updateHarvest(Unit *unit) { if(r != NULL && hct->canHarvest(r->getType())) { //if can harvest dest. pos bool canHarvestDestPos = false; + targetPos.x = -1; + targetPos.y = -1; switch(this->game->getGameSettings()->getPathFinderType()) { case pfBasic: @@ -607,24 +609,27 @@ void UnitUpdater::updateHarvest(Unit *unit) { if (canHarvestDestPos == true) { unit->setLastHarvestResourceTarget(NULL); - //if it finds resources it starts harvesting - unit->setCurrSkill(hct->getHarvestSkillType()); - unit->setTargetPos(targetPos); - command->setPos(targetPos); - unit->setLoadCount(0); + canHarvestDestPos == (map->getSurfaceCell(Map::toSurfCoords(targetPos))->getResource() != NULL); + if(canHarvestDestPos == true) { + //if it finds resources it starts harvesting + unit->setCurrSkill(hct->getHarvestSkillType()); + unit->setTargetPos(targetPos); + command->setPos(targetPos); + unit->setLoadCount(0); - switch(this->game->getGameSettings()->getPathFinderType()) { - case pfBasic: - unit->setLoadType(map->getSurfaceCell(Map::toSurfCoords(unit->getTargetPos()))->getResource()->getType()); - break; - case pfRoutePlanner: - unit->setLoadType(r->getType()); - break; - default: - throw runtime_error("detected unsupported pathfinder type!"); + switch(this->game->getGameSettings()->getPathFinderType()) { + case pfBasic: + unit->setLoadType(map->getSurfaceCell(Map::toSurfCoords(unit->getTargetPos()))->getResource()->getType()); + break; + case pfRoutePlanner: + unit->setLoadType(r->getType()); + break; + default: + throw runtime_error("detected unsupported pathfinder type!"); + } } } - else { + if(canHarvestDestPos == false) { unit->setLastHarvestResourceTarget(&targetPos); //if not continue walking @@ -648,10 +653,75 @@ void UnitUpdater::updateHarvest(Unit *unit) { } if(wasStuck == true) { - //if can't harvest, search for another resource - unit->setCurrSkill(scStop); - if(searchForResource(unit, hct) == false) { - unit->finishCommand(); + switch(this->game->getGameSettings()->getPathFinderType()) { + case pfBasic: + canHarvestDestPos = (unit->getPos().dist(command->getPos()) < harvestDistance && + map->isResourceNear(unit->getPos(), r->getType(), targetPos,unit->getType()->getSize(),unit, true)); + break; + case pfRoutePlanner: + canHarvestDestPos = map->isResourceNear(unit->getPos(), unit->getType()->getSize(), r->getType(), targetPos); + break; + default: + throw runtime_error("detected unsupported pathfinder type!"); + } + + if (canHarvestDestPos == true) { + unit->setLastHarvestResourceTarget(NULL); + + canHarvestDestPos == (map->getSurfaceCell(Map::toSurfCoords(targetPos))->getResource() != NULL); + if(canHarvestDestPos == true) { + //if it finds resources it starts harvesting + unit->setCurrSkill(hct->getHarvestSkillType()); + unit->setTargetPos(targetPos); + command->setPos(targetPos); + unit->setLoadCount(0); + + switch(this->game->getGameSettings()->getPathFinderType()) { + case pfBasic: + unit->setLoadType(map->getSurfaceCell(Map::toSurfCoords(unit->getTargetPos()))->getResource()->getType()); + break; + case pfRoutePlanner: + unit->setLoadType(r->getType()); + break; + default: + throw runtime_error("detected unsupported pathfinder type!"); + } + } + } + if(canHarvestDestPos == false) { + unit->setLastHarvestResourceTarget(&targetPos); + + if(targetPos.x >= 0) { + //if not continue walking + wasStuck = false; + TravelState tsValue = tsImpossible; + switch(this->game->getGameSettings()->getPathFinderType()) { + case pfBasic: + tsValue = pathFinder->findPath(unit, targetPos, &wasStuck); + if (tsValue == tsMoving) { + unit->setCurrSkill(hct->getMoveSkillType()); + command->setPos(targetPos); + } + break; + case pfRoutePlanner: + tsValue = routePlanner->findPathToResource(unit, targetPos, r->getType()); + if (tsValue == tsMoving) { + unit->setCurrSkill(hct->getMoveSkillType()); + command->setPos(targetPos); + } + break; + default: + throw runtime_error("detected unsupported pathfinder type!"); + } + } + + if(wasStuck == true) { + //if can't harvest, search for another resource + unit->setCurrSkill(scStop); + if(searchForResource(unit, hct) == false) { + unit->finishCommand(); + } + } } } }