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:
commit
9e7e5b81e4
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user