mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Merge pull request #143 from vcmi/feature/patrolSupport
Patrol support for AI heroes
This commit is contained in:
		| @@ -12,6 +12,7 @@ ADVETURE AI: | ||||
| * Fixed AI trying to go through underground rock | ||||
| * Fixed several cases causing AI wandering aimlessly | ||||
| * 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: | ||||
| * Changed fractalization algorithm so it can create cycles | ||||
|   | ||||
| @@ -38,7 +38,7 @@ CPathfinder::PathfinderOptions::PathfinderOptions() | ||||
| } | ||||
|  | ||||
| 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 == getHero(hero->id)); | ||||
| @@ -53,6 +53,7 @@ CPathfinder::CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstan | ||||
|  | ||||
| 	hlp = make_unique<CPathfinderHelper>(hero, options); | ||||
|  | ||||
| 	initializePatrol(); | ||||
| 	initializeGraph(); | ||||
| 	neighbourTiles.reserve(8); | ||||
| 	neighbours.reserve(16); | ||||
| @@ -96,8 +97,10 @@ void CPathfinder::calculatePaths() | ||||
| 	CGPathNode * initialNode = out.getNode(out.hpos, hero->boat ? ELayer::SAIL : ELayer::LAND); | ||||
| 	initialNode->turns = 0; | ||||
| 	initialNode->moveRemains = hero->movement; | ||||
| 	pq.push(initialNode); | ||||
| 	if(isHeroPatrolLocked()) | ||||
| 		return; | ||||
|  | ||||
| 	pq.push(initialNode); | ||||
| 	while(!pq.empty()) | ||||
| 	{ | ||||
| 		cp = pq.top(); | ||||
| @@ -120,6 +123,9 @@ void CPathfinder::calculatePaths() | ||||
| 		addNeighbours(); | ||||
| 		for(auto & neighbour : neighbours) | ||||
| 		{ | ||||
| 			if(!isPatrolMovementAllowed(neighbour)) | ||||
| 				continue; | ||||
|  | ||||
| 			dt = &gs->map->getTile(neighbour); | ||||
| 			dtObj = dt->topVisitableObj(); | ||||
| 			for(ELayer i = ELayer::LAND; i <= ELayer::AIR; i.advance(1)) | ||||
| @@ -216,7 +222,9 @@ void CPathfinder::addNeighbours() | ||||
| void CPathfinder::addTeleportExits() | ||||
| { | ||||
| 	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; | ||||
|  | ||||
| 	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 | ||||
| { | ||||
| 	/// 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; | ||||
| } | ||||
|  | ||||
| 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() | ||||
| { | ||||
| 	auto updateNode = [&](int3 pos, ELayer layer, const TerrainTile * tinfo) | ||||
|   | ||||
| @@ -161,6 +161,13 @@ private: | ||||
| 	const std::vector<std::vector<std::vector<ui8> > > &FoW; | ||||
| 	unique_ptr<CPathfinderHelper> hlp; | ||||
|  | ||||
| 	enum EPatrolState { | ||||
| 		PATROL_NONE = 0, | ||||
| 		PATROL_LOCKED = 1, | ||||
| 		PATROL_RADIUS | ||||
| 	} patrolState; | ||||
| 	std::unordered_set<int3, ShashInt3> patrolTiles; | ||||
|  | ||||
| 	struct NodeComparer | ||||
| 	{ | ||||
| 		bool operator()(const CGPathNode * lhs, const CGPathNode * rhs) const | ||||
| @@ -187,6 +194,9 @@ private: | ||||
| 	void addNeighbours(); | ||||
| 	void addTeleportExits(); | ||||
|  | ||||
| 	bool isHeroPatrolLocked() const; | ||||
| 	bool isPatrolMovementAllowed(const int3 & dst) const; | ||||
|  | ||||
| 	bool isLayerTransitionPossible(const ELayer dstLayer) const; | ||||
| 	bool isLayerTransitionPossible() const; | ||||
| 	bool isMovementToDestPossible() const; | ||||
| @@ -200,6 +210,7 @@ private: | ||||
| 	bool isDestinationGuarded(const bool ignoreAccessibility = true) const; | ||||
| 	bool isDestinationGuardian() const; | ||||
|  | ||||
| 	void initializePatrol(); | ||||
| 	void initializeGraph(); | ||||
|  | ||||
| 	CGPathNode::EAccessibility evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo, const ELayer layer) const; | ||||
|   | ||||
| @@ -27,7 +27,7 @@ | ||||
| #include "mapping/CCampaignHandler.h" //for CCampaignState | ||||
| #include "rmg/CMapGenerator.h" // for CMapGenOptions | ||||
|  | ||||
| const ui32 version = 754; | ||||
| const ui32 version = 755; | ||||
| const ui32 minSupportedVersion = 753; | ||||
|  | ||||
| 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) | ||||
| 	{ | ||||
| @@ -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++) | ||||
| 			{ | ||||
| 				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(!player | ||||
|   | ||||
| @@ -30,7 +30,7 @@ class DLL_LINKAGE CPrivilagedInfoCallback : public CGameInfoCallback | ||||
| public: | ||||
| 	CGameState * gameState(); | ||||
| 	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 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); | ||||
|   | ||||
| @@ -105,6 +105,11 @@ public: | ||||
| 	{ | ||||
| 		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 | ||||
| 	{ | ||||
|   | ||||
| @@ -72,12 +72,23 @@ public: | ||||
|  | ||||
| 	struct DLL_LINKAGE Patrol | ||||
| 	{ | ||||
| 		Patrol(){patrolling=false;patrolRadious=-1;}; | ||||
| 		Patrol(){patrolling=false;initialPos=int3();patrolRadious=-1;}; | ||||
| 		bool patrolling; | ||||
| 		int3 initialPos; | ||||
| 		ui32 patrolRadious; | ||||
| 		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; | ||||
|  | ||||
|   | ||||
| @@ -1060,7 +1060,7 @@ void CMapLoaderH3M::readObjects() | ||||
| 		case Obj::RANDOM_HERO: | ||||
| 		case Obj::PRISON: | ||||
| 			{ | ||||
| 				nobj = readHero(idToBeGiven); | ||||
| 				nobj = readHero(idToBeGiven, objPos); | ||||
| 				break; | ||||
| 			} | ||||
| 		case Obj::MONSTER:  //Monster | ||||
| @@ -1549,7 +1549,7 @@ void CMapLoaderH3M::readCreatureSet(CCreatureSet * out, int number) | ||||
| 	out->validTypes(true); | ||||
| } | ||||
|  | ||||
| CGObjectInstance * CMapLoaderH3M::readHero(ObjectInstanceID idToBeGiven) | ||||
| CGObjectInstance * CMapLoaderH3M::readHero(ObjectInstanceID idToBeGiven, const int3 & initialPos) | ||||
| { | ||||
| 	auto nhi = new CGHeroInstance(); | ||||
|  | ||||
| @@ -1658,6 +1658,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(ObjectInstanceID idToBeGiven) | ||||
| 	else | ||||
| 	{ | ||||
| 		nhi->patrol.patrolling = true; | ||||
| 		nhi->patrol.initialPos = CGHeroInstance::convertPosition(initialPos, false); | ||||
| 	} | ||||
|  | ||||
| 	if(map->version > EMapFormat::ROE) | ||||
|   | ||||
| @@ -172,7 +172,7 @@ private: | ||||
| 	 * @param idToBeGiven the object id which should be set for the hero | ||||
| 	 * @return a object instance | ||||
| 	 */ | ||||
| 	CGObjectInstance * readHero(ObjectInstanceID idToBeGiven); | ||||
| 	CGObjectInstance * readHero(ObjectInstanceID idToBeGiven, const int3 & initialPos); | ||||
|  | ||||
| 	/** | ||||
| 	 * Reads a seer hut. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user