1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-26 22:57:00 +02:00

Merge pull request #143 from vcmi/feature/patrolSupport

Patrol support for AI heroes
This commit is contained in:
ArseniyShestakov 2015-12-05 03:11:07 +03:00
commit 9e7e5b81e4
10 changed files with 88 additions and 12 deletions

View File

@ -12,6 +12,7 @@ ADVETURE AI:
* Fixed AI trying to go through underground rock * Fixed AI trying to go through underground rock
* Fixed several cases causing AI wandering aimlessly * Fixed several cases causing AI wandering aimlessly
* AI can again pick best artifacts and exchange artifacts between heroes * AI can again pick best artifacts and exchange artifacts between heroes
* AI heroes with patrol enabled won't leave patrol area anymore
RANDOM MAP GENERATOR: RANDOM MAP GENERATOR:
* Changed fractalization algorithm so it can create cycles * Changed fractalization algorithm so it can create cycles

View File

@ -38,7 +38,7 @@ CPathfinder::PathfinderOptions::PathfinderOptions()
} }
CPathfinder::CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstance * _hero) CPathfinder::CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstance * _hero)
: CGameInfoCallback(_gs, boost::optional<PlayerColor>()), out(_out), hero(_hero), FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap) : CGameInfoCallback(_gs, boost::optional<PlayerColor>()), out(_out), hero(_hero), FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap), patrolTiles({})
{ {
assert(hero); assert(hero);
assert(hero == getHero(hero->id)); assert(hero == getHero(hero->id));
@ -53,6 +53,7 @@ CPathfinder::CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstan
hlp = make_unique<CPathfinderHelper>(hero, options); hlp = make_unique<CPathfinderHelper>(hero, options);
initializePatrol();
initializeGraph(); initializeGraph();
neighbourTiles.reserve(8); neighbourTiles.reserve(8);
neighbours.reserve(16); neighbours.reserve(16);
@ -96,8 +97,10 @@ void CPathfinder::calculatePaths()
CGPathNode * initialNode = out.getNode(out.hpos, hero->boat ? ELayer::SAIL : ELayer::LAND); CGPathNode * initialNode = out.getNode(out.hpos, hero->boat ? ELayer::SAIL : ELayer::LAND);
initialNode->turns = 0; initialNode->turns = 0;
initialNode->moveRemains = hero->movement; initialNode->moveRemains = hero->movement;
pq.push(initialNode); if(isHeroPatrolLocked())
return;
pq.push(initialNode);
while(!pq.empty()) while(!pq.empty())
{ {
cp = pq.top(); cp = pq.top();
@ -120,6 +123,9 @@ void CPathfinder::calculatePaths()
addNeighbours(); addNeighbours();
for(auto & neighbour : neighbours) for(auto & neighbour : neighbours)
{ {
if(!isPatrolMovementAllowed(neighbour))
continue;
dt = &gs->map->getTile(neighbour); dt = &gs->map->getTile(neighbour);
dtObj = dt->topVisitableObj(); dtObj = dt->topVisitableObj();
for(ELayer i = ELayer::LAND; i <= ELayer::AIR; i.advance(1)) for(ELayer i = ELayer::LAND; i <= ELayer::AIR; i.advance(1))
@ -216,7 +222,9 @@ void CPathfinder::addNeighbours()
void CPathfinder::addTeleportExits() void CPathfinder::addTeleportExits()
{ {
neighbours.clear(); neighbours.clear();
if(!isSourceVisitableObj()) /// For now we disable teleports usage for patrol movement
/// VCAI not aware about patrol and may stuck while attempt to use teleport
if(!isSourceVisitableObj() || patrolState == PATROL_RADIUS)
return; return;
const CGTeleport * objTeleport = dynamic_cast<const CGTeleport *>(ctObj); const CGTeleport * objTeleport = dynamic_cast<const CGTeleport *>(ctObj);
@ -257,6 +265,22 @@ void CPathfinder::addTeleportExits()
} }
} }
bool CPathfinder::isHeroPatrolLocked() const
{
return patrolState == PATROL_LOCKED;
}
bool CPathfinder::isPatrolMovementAllowed(const int3 & dst) const
{
if(patrolState == PATROL_RADIUS)
{
if(!vstd::contains(patrolTiles, dst))
return false;
}
return true;
}
bool CPathfinder::isLayerTransitionPossible(const ELayer destLayer) const bool CPathfinder::isLayerTransitionPossible(const ELayer destLayer) const
{ {
/// No layer transition allowed when previous node action is BATTLE /// No layer transition allowed when previous node action is BATTLE
@ -565,6 +589,23 @@ bool CPathfinder::isDestinationGuardian() const
return gs->guardingCreaturePosition(cp->coord) == dp->coord; return gs->guardingCreaturePosition(cp->coord) == dp->coord;
} }
void CPathfinder::initializePatrol()
{
auto state = PATROL_NONE;
if(hero->patrol.patrolling && !getPlayer(hero->tempOwner)->human)
{
if(hero->patrol.patrolRadious)
{
state = PATROL_RADIUS;
gs->getTilesInRange(patrolTiles, hero->patrol.initialPos, hero->patrol.patrolRadious, boost::optional<PlayerColor>(), 0, true);
}
else
state = PATROL_LOCKED;
}
patrolState = state;
}
void CPathfinder::initializeGraph() void CPathfinder::initializeGraph()
{ {
auto updateNode = [&](int3 pos, ELayer layer, const TerrainTile * tinfo) auto updateNode = [&](int3 pos, ELayer layer, const TerrainTile * tinfo)

View File

@ -161,6 +161,13 @@ private:
const std::vector<std::vector<std::vector<ui8> > > &FoW; const std::vector<std::vector<std::vector<ui8> > > &FoW;
unique_ptr<CPathfinderHelper> hlp; unique_ptr<CPathfinderHelper> hlp;
enum EPatrolState {
PATROL_NONE = 0,
PATROL_LOCKED = 1,
PATROL_RADIUS
} patrolState;
std::unordered_set<int3, ShashInt3> patrolTiles;
struct NodeComparer struct NodeComparer
{ {
bool operator()(const CGPathNode * lhs, const CGPathNode * rhs) const bool operator()(const CGPathNode * lhs, const CGPathNode * rhs) const
@ -187,6 +194,9 @@ private:
void addNeighbours(); void addNeighbours();
void addTeleportExits(); void addTeleportExits();
bool isHeroPatrolLocked() const;
bool isPatrolMovementAllowed(const int3 & dst) const;
bool isLayerTransitionPossible(const ELayer dstLayer) const; bool isLayerTransitionPossible(const ELayer dstLayer) const;
bool isLayerTransitionPossible() const; bool isLayerTransitionPossible() const;
bool isMovementToDestPossible() const; bool isMovementToDestPossible() const;
@ -200,6 +210,7 @@ private:
bool isDestinationGuarded(const bool ignoreAccessibility = true) const; bool isDestinationGuarded(const bool ignoreAccessibility = true) const;
bool isDestinationGuardian() const; bool isDestinationGuardian() const;
void initializePatrol();
void initializeGraph(); void initializeGraph();
CGPathNode::EAccessibility evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo, const ELayer layer) const; CGPathNode::EAccessibility evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo, const ELayer layer) const;

View File

@ -27,7 +27,7 @@
#include "mapping/CCampaignHandler.h" //for CCampaignState #include "mapping/CCampaignHandler.h" //for CCampaignState
#include "rmg/CMapGenerator.h" // for CMapGenOptions #include "rmg/CMapGenerator.h" // for CMapGenOptions
const ui32 version = 754; const ui32 version = 755;
const ui32 minSupportedVersion = 753; const ui32 minSupportedVersion = 753;
class CISer; class CISer;

View File

@ -47,7 +47,7 @@ void CPrivilagedInfoCallback::getFreeTiles (std::vector<int3> &tiles) const
} }
} }
void CPrivilagedInfoCallback::getTilesInRange( std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, boost::optional<PlayerColor> player/*=uninit*/, int mode/*=0*/ ) const void CPrivilagedInfoCallback::getTilesInRange( std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, boost::optional<PlayerColor> player/*=uninit*/, int mode/*=0*/, bool patrolDistance/*=false*/) const
{ {
if(!!player && *player >= PlayerColor::PLAYER_LIMIT) if(!!player && *player >= PlayerColor::PLAYER_LIMIT)
{ {
@ -63,7 +63,13 @@ void CPrivilagedInfoCallback::getTilesInRange( std::unordered_set<int3, ShashInt
{ {
for (int yd = std::max<int>(pos.y - radious, 0); yd <= std::min<int>(pos.y + radious, gs->map->height - 1); yd++) for (int yd = std::max<int>(pos.y - radious, 0); yd <= std::min<int>(pos.y + radious, gs->map->height - 1); yd++)
{ {
double distance = pos.dist2d(int3(xd,yd,pos.z)) - 0.5; int3 tilePos(xd,yd,pos.z);
double distance;
if(patrolDistance)
distance = pos.mandist2d(tilePos);
else
distance = pos.dist2d(tilePos) - 0.5;
if(distance <= radious) if(distance <= radious)
{ {
if(!player if(!player

View File

@ -30,7 +30,7 @@ class DLL_LINKAGE CPrivilagedInfoCallback : public CGameInfoCallback
public: public:
CGameState * gameState(); CGameState * gameState();
void getFreeTiles (std::vector<int3> &tiles) const; //used for random spawns void getFreeTiles (std::vector<int3> &tiles) const; //used for random spawns
void getTilesInRange(std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, boost::optional<PlayerColor> player = boost::optional<PlayerColor>(), int mode=0) const; //mode 1 - only unrevealed tiles; mode 0 - all, mode -1 - only unrevealed void getTilesInRange(std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, boost::optional<PlayerColor> player = boost::optional<PlayerColor>(), int mode = 0, bool patrolDistance = false) const; //mode 1 - only unrevealed tiles; mode 0 - all, mode -1 - only unrevealed
void getAllTiles (std::unordered_set<int3, ShashInt3> &tiles, boost::optional<PlayerColor> player = boost::optional<PlayerColor>(), int level=-1, int surface=0) const; //returns all tiles on given level (-1 - both levels, otherwise number of level); surface: 0 - land and water, 1 - only land, 2 - only water void getAllTiles (std::unordered_set<int3, ShashInt3> &tiles, boost::optional<PlayerColor> player = boost::optional<PlayerColor>(), int level=-1, int surface=0) const; //returns all tiles on given level (-1 - both levels, otherwise number of level); surface: 0 - land and water, 1 - only land, 2 - only water
void pickAllowedArtsSet(std::vector<const CArtifact*> &out); //gives 3 treasures, 3 minors, 1 major -> used by Black Market and Artifact Merchant void pickAllowedArtsSet(std::vector<const CArtifact*> &out); //gives 3 treasures, 3 minors, 1 major -> used by Black Market and Artifact Merchant
void getAllowedSpells(std::vector<SpellID> &out, ui16 level); void getAllowedSpells(std::vector<SpellID> &out, ui16 level);

View File

@ -105,6 +105,11 @@ public:
{ {
return std::sqrt((double)dist2dSQ(o)); return std::sqrt((double)dist2dSQ(o));
} }
//manhattan distance used for patrol radius (z coord is not used)
double mandist2d(const int3 & o) const
{
return abs(o.x - x) + abs(o.y - y);
}
bool areNeighbours(const int3 & o) const bool areNeighbours(const int3 & o) const
{ {

View File

@ -72,12 +72,23 @@ public:
struct DLL_LINKAGE Patrol struct DLL_LINKAGE Patrol
{ {
Patrol(){patrolling=false;patrolRadious=-1;}; Patrol(){patrolling=false;initialPos=int3();patrolRadious=-1;};
bool patrolling; bool patrolling;
int3 initialPos;
ui32 patrolRadious; ui32 patrolRadious;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & patrolling & patrolRadious; h & patrolling;
if(version >= 755)
{
h & initialPos;
}
else if(!h.saving)
{
patrolling = false;
initialPos = int3();
}
h & patrolRadious;
} }
} patrol; } patrol;

View File

@ -1060,7 +1060,7 @@ void CMapLoaderH3M::readObjects()
case Obj::RANDOM_HERO: case Obj::RANDOM_HERO:
case Obj::PRISON: case Obj::PRISON:
{ {
nobj = readHero(idToBeGiven); nobj = readHero(idToBeGiven, objPos);
break; break;
} }
case Obj::MONSTER: //Monster case Obj::MONSTER: //Monster
@ -1549,7 +1549,7 @@ void CMapLoaderH3M::readCreatureSet(CCreatureSet * out, int number)
out->validTypes(true); out->validTypes(true);
} }
CGObjectInstance * CMapLoaderH3M::readHero(ObjectInstanceID idToBeGiven) CGObjectInstance * CMapLoaderH3M::readHero(ObjectInstanceID idToBeGiven, const int3 & initialPos)
{ {
auto nhi = new CGHeroInstance(); auto nhi = new CGHeroInstance();
@ -1658,6 +1658,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(ObjectInstanceID idToBeGiven)
else else
{ {
nhi->patrol.patrolling = true; nhi->patrol.patrolling = true;
nhi->patrol.initialPos = CGHeroInstance::convertPosition(initialPos, false);
} }
if(map->version > EMapFormat::ROE) if(map->version > EMapFormat::ROE)

View File

@ -172,7 +172,7 @@ private:
* @param idToBeGiven the object id which should be set for the hero * @param idToBeGiven the object id which should be set for the hero
* @return a object instance * @return a object instance
*/ */
CGObjectInstance * readHero(ObjectInstanceID idToBeGiven); CGObjectInstance * readHero(ObjectInstanceID idToBeGiven, const int3 & initialPos);
/** /**
* Reads a seer hut. * Reads a seer hut.