From 3d4ccb460cf79d01e7251d31d951cd6c265d0852 Mon Sep 17 00:00:00 2001 From: Mark Vejvoda Date: Sun, 17 Oct 2010 08:50:27 +0000 Subject: [PATCH] - added a little extra logic for units when pathfinding. We keep temporary lists of bad cells of harvest resources where a unit simply cannot get to. This is kept for small periods of time and then the bad cell list is purged when data gets old. This tremendously helps units to be more active when getting stuck in challenging places on maps. --- source/glest_game/ai/path_finder.cpp | 33 +++++++---- source/glest_game/type_instances/unit.cpp | 72 +++++++++++++++++++++++ source/glest_game/type_instances/unit.h | 21 ++++++- source/glest_game/world/map.cpp | 19 +++--- source/glest_game/world/map.h | 2 +- source/glest_game/world/unit_updater.cpp | 48 ++++++++------- 6 files changed, 153 insertions(+), 42 deletions(-) diff --git a/source/glest_game/ai/path_finder.cpp b/source/glest_game/ai/path_finder.cpp index 4049498f..cb93738d 100644 --- a/source/glest_game/ai/path_finder.cpp +++ b/source/glest_game/ai/path_finder.cpp @@ -173,10 +173,18 @@ TravelState PathFinder::findPath(Unit *unit, const Vec2i &finalPos) { throw runtime_error("unsupported or missing path finder detected!"); } } + else if(ts == tsArrived) { + ts = aStar(unit, finalPos); + break; + } } } } unit->setInBailOutAttempt(false); + + if(ts == tsArrived) { + ts = tsBlocked; + } } unit->setCurrSkill(scStop); break; @@ -230,17 +238,20 @@ TravelState PathFinder::aStar(Unit *unit, const Vec2i &targetPos){ //if arrived if(finalPos == unit->getPos()) { - if(SystemFlags::getSystemSettingType(SystemFlags::debugPathFinder).enabled == true) { - string commandDesc = "none"; - Command *command= unit->getCurrCommand(); - if(command != NULL && command->getCommandType() != NULL) { - commandDesc = command->getCommandType()->toString(); + Command *command= unit->getCurrCommand(); + if(command == NULL || command->getPos() != unit->getPos()) { + if(SystemFlags::getSystemSettingType(SystemFlags::debugPathFinder).enabled == true) { + string commandDesc = "none"; + Command *command= unit->getCurrCommand(); + if(command != NULL && command->getCommandType() != NULL) { + commandDesc = command->getCommandType()->toString(); + } + char szBuf[1024]=""; + sprintf(szBuf,"State: arrived#2 at pos: %s, command [%s]",targetPos.getString().c_str(),commandDesc.c_str()); + unit->setCurrentUnitTitle(szBuf); } - char szBuf[1024]=""; - sprintf(szBuf,"State: arrived#2 at pos: %s, command [%s]",targetPos.getString().c_str(),commandDesc.c_str()); - unit->setCurrentUnitTitle(szBuf); + return tsArrived; } - return tsArrived; } //path find algorithm @@ -338,8 +349,10 @@ TravelState PathFinder::aStar(Unit *unit, const Vec2i &targetPos){ commandDesc = command->getCommandType()->toString(); } + std::pair lastHarvest = unit->getLastHarvestResourceTarget(); + char szBuf[1024]=""; - sprintf(szBuf,"State: blocked, cmd [%s] pos: %s, dest pos: %s, reason A= %d, B= %d, C= %d, D= %d, E= %d, F = %d",commandDesc.c_str(),unit->getPos().getString().c_str(), targetPos.getString().c_str(),pathFound,(lastNode == firstNode),path->getBlockCount(), path->isBlocked(), nodeLimitReached,path->isStuck()); + sprintf(szBuf,"State: blocked, cmd [%s] pos: [%s], dest pos: [%s], lastHarvest = [%s - %lld], reason A= %d, B= %d, C= %d, D= %d, E= %d, F = %d",commandDesc.c_str(),unit->getPos().getString().c_str(), targetPos.getString().c_str(),lastHarvest.first.getString().c_str(),lastHarvest.second.getMillis(), pathFound,(lastNode == firstNode),path->getBlockCount(), path->isBlocked(), nodeLimitReached,path->isStuck()); unit->setCurrentUnitTitle(szBuf); } diff --git a/source/glest_game/type_instances/unit.cpp b/source/glest_game/type_instances/unit.cpp index 42ee3d31..1583d2c9 100644 --- a/source/glest_game/type_instances/unit.cpp +++ b/source/glest_game/type_instances/unit.cpp @@ -183,6 +183,7 @@ Unit::Unit(int id, UnitPathInterface *unitpath, const Vec2i &pos, const UnitType this->retryCurrCommandCount=0; this->screenPos = Vec3f(0.0); this->inBailOutAttempt = false; + this->lastHarvestResourceTarget.first = Vec2i(0); level= NULL; loadType= NULL; @@ -245,6 +246,8 @@ Unit::Unit(int id, UnitPathInterface *unitpath, const Vec2i &pos, const UnitType Unit::~Unit(){ SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] delete unitid = %d\n",__FILE__,__FUNCTION__,__LINE__,id); + badHarvestPosList.clear(); + //Just to be sure, should already be removed if (livingUnits.erase(id)) { SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); @@ -1678,6 +1681,75 @@ void Unit::logSynchData(string source) { } } +void Unit::addBadHarvestPos(const Vec2i &value) { + Chrono chron; + chron.start(); + badHarvestPosList.push_back(std::pair(value,chron)); + cleanupOldBadHarvestPos(); +} + +void Unit::removeBadHarvestPos(const Vec2i &value) { + for(int i = 0; i < badHarvestPosList.size(); ++i) { + const std::pair &item = badHarvestPosList[i]; + if(item.first == value) { + badHarvestPosList.erase(badHarvestPosList.begin() + i); + break; + } + } + cleanupOldBadHarvestPos(); +} + +bool Unit::isBadHarvestPos(const Vec2i &value) { + cleanupOldBadHarvestPos(); + + bool result = false; + for(int i = 0; i < badHarvestPosList.size(); ++i) { + const std::pair &item = badHarvestPosList[i]; + if(item.first == value) { + result = true; + break; + } + } + + return result; +} + +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 + // seconds remove it so the unit could potentially try it again + if(item.second.getMillis() >= 1200000) { + badHarvestPosList.erase(badHarvestPosList.begin() + i); + } + } +} + +void Unit::setLastHarvestResourceTarget(const Vec2i *pos) { + if(pos == NULL) { + lastHarvestResourceTarget.first = Vec2i(0); + //lastHarvestResourceTarget.second = 0; + } + else { + const Vec2i resourceLocation = *pos; + if(resourceLocation != lastHarvestResourceTarget.first) { + lastHarvestResourceTarget.first = resourceLocation; + + Chrono chron; + chron.start(); + lastHarvestResourceTarget.second = chron; + } + else { + // If we cannot harvest for > 10 seconds tag the position + // as a bad one + if(lastHarvestResourceTarget.second.getMillis() > 10000) { + addBadHarvestPos(resourceLocation); + } + } + } +} + 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 d168016b..1b58aff2 100644 --- a/source/glest_game/type_instances/unit.h +++ b/source/glest_game/type_instances/unit.h @@ -18,9 +18,10 @@ #include "skill_type.h" #include "game_constants.h" #include +#include "platform_common.h" #include "leak_dumper.h" -namespace Glest{ namespace Game{ +namespace Glest { namespace Game { using Shared::Graphics::ParticleSystem; using Shared::Graphics::UnitParticleSystem; @@ -29,6 +30,7 @@ using Shared::Graphics::Vec2f; using Shared::Graphics::Vec3f; using Shared::Graphics::Vec2i; using Shared::Graphics::Model; +using Shared::PlatformCommon::Chrono; using std::set; @@ -281,6 +283,13 @@ private: string currentUnitTitle; bool inBailOutAttempt; + // This buffer stores a list of bad harvest cells, along with the start + // time of when it was detected. Typically this may be due to a unit + // constantly getting blocked from getting to the resource so this + // list may be used to tell areas of the game to ignore those cells for a + // period of time + std::vector > badHarvestPosList; + std::pair lastHarvestResourceTarget; static Game *game; @@ -426,6 +435,16 @@ public: bool getInBailOutAttempt() const { return inBailOutAttempt; } void setInBailOutAttempt(bool value) { inBailOutAttempt = value; } + std::vector > getBadHarvestPosList() const { return badHarvestPosList; } + void setBadHarvestPosList(std::vector > value) { badHarvestPosList = value; } + void addBadHarvestPos(const Vec2i &value); + void removeBadHarvestPos(const Vec2i &value); + bool isBadHarvestPos(const Vec2i &value); + void cleanupOldBadHarvestPos(); + + void setLastHarvestResourceTarget(const Vec2i *pos); + std::pair getLastHarvestResourceTarget() const { return lastHarvestResourceTarget;} + std::string toString() const; private: diff --git a/source/glest_game/world/map.cpp b/source/glest_game/world/map.cpp index 2ee54c6a..7e50fa66 100644 --- a/source/glest_game/world/map.cpp +++ b/source/glest_game/world/map.cpp @@ -249,15 +249,18 @@ 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) const{ - for(int i=-1; i<=size; ++i){ - for(int j=-1; j<=size; ++j){ - if(isInside(pos.x+i, pos.y+j)){ - Resource *r= getSurfaceCell(toSurfCoords(Vec2i(pos.x+i, pos.y+j)))->getResource(); - if(r!=NULL){ - if(r->getType()==rt){ +bool Map::isResourceNear(const Vec2i &pos, const ResourceType *rt, Vec2i &resourcePos, int size, Unit *unit) const { + for(int i = -1; i <= size; ++i) { + for(int j = -1; j <= size; ++j) { + if(isInside(pos.x + i, pos.y + j)) { + Resource *r= getSurfaceCell(toSurfCoords(Vec2i(pos.x + i, pos.y + j)))->getResource(); + if(r != NULL){ + if(r->getType() == rt) { resourcePos= pos + Vec2i(i,j); - return true; + + if(unit == NULL || unit->isBadHarvestPos(resourcePos) == false) { + return true; + } } } } diff --git a/source/glest_game/world/map.h b/source/glest_game/world/map.h index 486f30ec..88b71918 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) const; + bool isResourceNear(const Vec2i &pos, const ResourceType *rt, Vec2i &resourcePos, int size, Unit *unit=NULL) 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 54b74ac2..63192a55 100644 --- a/source/glest_game/world/unit_updater.cpp +++ b/source/glest_game/world/unit_updater.cpp @@ -572,7 +572,7 @@ void UnitUpdater::updateBuild(Unit *unit) { // ==================== updateHarvest ==================== -void UnitUpdater::updateHarvest(Unit *unit){ +void UnitUpdater::updateHarvest(Unit *unit) { Chrono chrono; chrono.start(); @@ -585,17 +585,17 @@ void UnitUpdater::updateHarvest(Unit *unit){ if(unit->getCurrSkill()->getClass() != scHarvest) { //if not working - if(unit->getLoadCount()==0){ + if(unit->getLoadCount() == 0) { //if not loaded go for resources Resource *r= map->getSurfaceCell(Map::toSurfCoords(command->getPos()))->getResource(); - if(r!=NULL && hct->canHarvest(r->getType())){ + if(r != NULL && hct->canHarvest(r->getType())) { //if can harvest dest. pos bool canHarvestDestPos = false; switch(this->game->getGameSettings()->getPathFinderType()) { case pfBasic: - canHarvestDestPos = (unit->getPos().dist(command->getPos())isResourceNear(unit->getPos(), r->getType(), targetPos,unit->getType()->getSize())); + canHarvestDestPos = (unit->getPos().dist(command->getPos()) < harvestDistance && + map->isResourceNear(unit->getPos(), r->getType(), targetPos,unit->getType()->getSize(),unit)); break; case pfRoutePlanner: canHarvestDestPos = map->isResourceNear(unit->getPos(), unit->getType()->getSize(), r->getType(), targetPos); @@ -605,22 +605,24 @@ void UnitUpdater::updateHarvest(Unit *unit){ } if (canHarvestDestPos == true) { - //if it finds resources it starts harvesting - unit->setCurrSkill(hct->getHarvestSkillType()); - unit->setTargetPos(targetPos); - command->setPos(targetPos); - unit->setLoadCount(0); + unit->setLastHarvestResourceTarget(&targetPos); - 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 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!"); + } } else { @@ -652,7 +654,7 @@ void UnitUpdater::updateHarvest(Unit *unit){ } } } - else{ + else { //if loaded, return to store Unit *store= world->nearestStore(unit->getPos(), unit->getFaction()->getIndex(), unit->getLoadType()); if(store!=NULL) { @@ -702,8 +704,10 @@ void UnitUpdater::updateHarvest(Unit *unit){ } } } - else{ + else { //if working + //unit->setLastHarvestResourceTarget(NULL); + SurfaceCell *sc= map->getSurfaceCell(Map::toSurfCoords(unit->getTargetPos())); Resource *r= sc->getResource();