| 
									
										
										
										
											2021-05-15 19:22:44 +03:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * AIUtility.cpp, part of VCMI engine | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Authors: listed in file AUTHORS in main folder | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * License: GNU General Public License v2.0 or later | 
					
						
							|  |  |  |  * Full text of license available in license.txt file, in main folder | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #include "StdInc.h"
 | 
					
						
							|  |  |  | #include "AIUtility.h"
 | 
					
						
							| 
									
										
										
										
											2021-05-16 15:39:38 +03:00
										 |  |  | #include "AIGateway.h"
 | 
					
						
							| 
									
										
										
										
											2021-05-15 19:22:44 +03:00
										 |  |  | #include "Goals/Goals.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "../../lib/UnlockGuard.h"
 | 
					
						
							|  |  |  | #include "../../lib/CConfigHandler.h"
 | 
					
						
							|  |  |  | #include "../../lib/CHeroHandler.h"
 | 
					
						
							| 
									
										
										
										
											2020-05-04 18:58:43 +03:00
										 |  |  | #include "../../lib/mapObjects/MapObjects.h"
 | 
					
						
							| 
									
										
										
										
											2021-05-15 19:22:44 +03:00
										 |  |  | #include "../../lib/mapping/CMapDefines.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-16 14:56:27 +03:00
										 |  |  | #include "../../lib/CModHandler.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-16 15:39:38 +03:00
										 |  |  | extern boost::thread_specific_ptr<AIGateway> ai; | 
					
						
							| 
									
										
										
										
											2021-05-15 19:22:44 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | //extern static const int3 dirs[8];
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const CGObjectInstance * ObjectIdRef::operator->() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return cb->getObj(id, false); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ObjectIdRef::operator const CGObjectInstance *() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return cb->getObj(id, false); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ObjectIdRef::operator bool() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return cb->getObj(id, false); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ObjectIdRef::ObjectIdRef(ObjectInstanceID _id) | 
					
						
							|  |  |  | 	: id(_id) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ObjectIdRef::ObjectIdRef(const CGObjectInstance * obj) | 
					
						
							|  |  |  | 	: id(obj->id) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool ObjectIdRef::operator<(const ObjectIdRef & rhs) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return id < rhs.id; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | HeroPtr::HeroPtr(const CGHeroInstance * H) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if(!H) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		//init from nullptr should equal to default init
 | 
					
						
							|  |  |  | 		*this = HeroPtr(); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	h = H; | 
					
						
							|  |  |  | 	name = h->name; | 
					
						
							|  |  |  | 	hid = H->id; | 
					
						
							|  |  |  | //	infosCount[ai->playerID][hid]++;
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | HeroPtr::HeroPtr() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	h = nullptr; | 
					
						
							|  |  |  | 	hid = ObjectInstanceID(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | HeroPtr::~HeroPtr() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | //	if(hid >= 0)
 | 
					
						
							|  |  |  | //		infosCount[ai->playerID][hid]--;
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool HeroPtr::operator<(const HeroPtr & rhs) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return hid < rhs.hid; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const CGHeroInstance * HeroPtr::get(bool doWeExpectNull) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	//TODO? check if these all assertions every time we get info about hero affect efficiency
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//behave terribly when attempting unauthorized access to hero that is not ours (or was lost)
 | 
					
						
							|  |  |  | 	assert(doWeExpectNull || h); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if(h) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		auto obj = cb->getObj(hid); | 
					
						
							| 
									
										
										
										
											2021-05-15 21:57:36 +03:00
										 |  |  | 		//const bool owned = obj && obj->tempOwner == ai->playerID;
 | 
					
						
							| 
									
										
										
										
											2021-05-15 19:22:44 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-15 21:57:36 +03:00
										 |  |  | 		if(doWeExpectNull && !obj) | 
					
						
							| 
									
										
										
										
											2021-05-15 19:22:44 +03:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			return nullptr; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			assert(obj); | 
					
						
							| 
									
										
										
										
											2021-05-15 21:57:36 +03:00
										 |  |  | 			//assert(owned);
 | 
					
						
							| 
									
										
										
										
											2021-05-15 19:22:44 +03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return h; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-16 15:08:56 +03:00
										 |  |  | const CGHeroInstance * HeroPtr::get(CCallback * cb, bool doWeExpectNull) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	//TODO? check if these all assertions every time we get info about hero affect efficiency
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//behave terribly when attempting unauthorized access to hero that is not ours (or was lost)
 | 
					
						
							|  |  |  | 	assert(doWeExpectNull || h); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if(h) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		auto obj = cb->getObj(hid); | 
					
						
							|  |  |  | 		//const bool owned = obj && obj->tempOwner == ai->playerID;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(doWeExpectNull && !obj) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			return nullptr; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			assert(obj); | 
					
						
							|  |  |  | 			//assert(owned);
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return h; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-15 19:22:44 +03:00
										 |  |  | const CGHeroInstance * HeroPtr::operator->() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return get(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool HeroPtr::validAndSet() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return get(true); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const CGHeroInstance * HeroPtr::operator*() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return get(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool HeroPtr::operator==(const HeroPtr & rhs) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return h == rhs.get(true); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	const CGPathNode * ln = ai->myCb->getPathsInfo(hero)->getPathInfo(lhs->visitablePos()); | 
					
						
							|  |  |  | 	const CGPathNode * rn = ai->myCb->getPathsInfo(hero)->getPathInfo(rhs->visitablePos()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-02 20:16:24 +03:00
										 |  |  | 	return ln->getCost() < rn->getCost(); | 
					
						
							| 
									
										
										
										
											2021-05-15 19:22:44 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-15 21:01:48 +03:00
										 |  |  | bool isSafeToVisit(HeroPtr h, const CCreatureSet * heroArmy, uint64_t dangerStrength) | 
					
						
							| 
									
										
										
										
											2021-05-15 19:22:44 +03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-03-17 23:27:09 +02:00
										 |  |  | 	const ui64 heroStrength = h->getFightingStrength() * heroArmy->getArmyStrength(); | 
					
						
							| 
									
										
										
										
											2021-05-15 19:22:44 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if(dangerStrength) | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2019-04-07 07:31:47 +03:00
										 |  |  | 		return heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength; | 
					
						
							| 
									
										
										
										
											2021-05-15 19:22:44 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return true; //there's no danger
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-15 21:01:48 +03:00
										 |  |  | bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return isSafeToVisit(h, h.get(), dangerStrength); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-15 19:22:44 +03:00
										 |  |  | bool isObjectRemovable(const CGObjectInstance * obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	//FIXME: move logic to object property!
 | 
					
						
							|  |  |  | 	switch (obj->ID) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 	case Obj::MONSTER: | 
					
						
							|  |  |  | 	case Obj::RESOURCE: | 
					
						
							|  |  |  | 	case Obj::CAMPFIRE: | 
					
						
							|  |  |  | 	case Obj::TREASURE_CHEST: | 
					
						
							|  |  |  | 	case Obj::ARTIFACT: | 
					
						
							|  |  |  | 	case Obj::BORDERGUARD: | 
					
						
							|  |  |  | 	case Obj::FLOTSAM: | 
					
						
							|  |  |  | 	case Obj::PANDORAS_BOX: | 
					
						
							|  |  |  | 	case Obj::OCEAN_BOTTLE: | 
					
						
							|  |  |  | 	case Obj::SEA_CHEST: | 
					
						
							|  |  |  | 	case Obj::SHIPWRECK_SURVIVOR: | 
					
						
							|  |  |  | 	case Obj::SPELL_SCROLL: | 
					
						
							|  |  |  | 		return true; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	// TODO: Such information should be provided by pathfinder
 | 
					
						
							|  |  |  | 	// Tile must be free or with unoccupied boat
 | 
					
						
							|  |  |  | 	if(!t->blocked) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		return true; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else if(!fromWater) // do not try to board when in water sector
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		if(t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT) | 
					
						
							|  |  |  | 			return true; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-16 15:08:56 +03:00
										 |  |  | bool isObjectPassable(const Nullkiller * ai, const CGObjectInstance * obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return isObjectPassable(obj, ai->playerID, ai->cb->getPlayerRelations(obj->tempOwner, ai->playerID)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-16 14:45:30 +03:00
										 |  |  | bool isObjectPassable(const CGObjectInstance * obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return isObjectPassable(obj, ai->playerID, cb->getPlayerRelations(obj->tempOwner, ai->playerID)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Pathfinder internal helper
 | 
					
						
							|  |  |  | bool isObjectPassable(const CGObjectInstance * obj, PlayerColor playerColor, PlayerRelations::PlayerRelations objectRelations) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if((obj->ID == Obj::GARRISON || obj->ID == Obj::GARRISON2) | 
					
						
							|  |  |  | 		&& objectRelations != PlayerRelations::ENEMIES) | 
					
						
							|  |  |  | 		return true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if(obj->ID == Obj::BORDER_GATE) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		auto quest = dynamic_cast<const CGKeys *>(obj); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(quest->passableFor(playerColor)) | 
					
						
							|  |  |  | 			return true; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-15 19:22:44 +03:00
										 |  |  | bool isBlockVisitObj(const int3 & pos) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if(auto obj = cb->getTopObj(pos)) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		if(obj->blockVisit) //we can't stand on that object
 | 
					
						
							|  |  |  | 			return true; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | creInfo infoFromDC(const dwellingContent & dc) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	creInfo ci; | 
					
						
							|  |  |  | 	ci.count = dc.first; | 
					
						
							|  |  |  | 	ci.creID = dc.second.size() ? dc.second.back() : CreatureID(-1); //should never be accessed
 | 
					
						
							|  |  |  | 	if (ci.creID != -1) | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2021-07-18 20:44:37 +03:00
										 |  |  | 		ci.cre = VLC->creh->objects[ci.creID].get(); | 
					
						
							| 
									
										
										
										
											2021-05-15 19:22:44 +03:00
										 |  |  | 		ci.level = ci.cre->level; //this is cretaure tier, while tryRealize expects dwelling level. Ignore.
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		ci.cre = nullptr; | 
					
						
							|  |  |  | 		ci.level = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ci; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool compareHeroStrength(HeroPtr h1, HeroPtr h2) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return h1->getTotalStrength() < h2->getTotalStrength(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return a1->getArmyStrength() < a2->getArmyStrength(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	auto art1 = a1->artType; | 
					
						
							|  |  |  | 	auto art2 = a2->artType; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if(art1->price == art2->price) | 
					
						
							|  |  |  | 		return art1->valOfBonuses(Bonus::PRIMARY_SKILL) > art2->valOfBonuses(Bonus::PRIMARY_SKILL); | 
					
						
							|  |  |  | 	else | 
					
						
							| 
									
										
										
										
											2019-04-07 07:31:47 +03:00
										 |  |  | 		return art1->price > art2->price; | 
					
						
							| 
									
										
										
										
											2021-05-15 19:22:44 +03:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-05-04 18:58:43 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | bool isWeeklyRevisitable(const CGObjectInstance * obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	//TODO: allow polling of remaining creatures in dwelling
 | 
					
						
							|  |  |  | 	if(dynamic_cast<const CGVisitableOPW *>(obj)) // ensures future compatibility, unlike IDs
 | 
					
						
							|  |  |  | 		return true; | 
					
						
							|  |  |  | 	if(dynamic_cast<const CGDwelling *>(obj)) | 
					
						
							|  |  |  | 		return true; | 
					
						
							|  |  |  | 	if(dynamic_cast<const CBank *>(obj)) //banks tend to respawn often in mods
 | 
					
						
							|  |  |  | 		return true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch(obj->ID) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 	case Obj::STABLES: | 
					
						
							|  |  |  | 	case Obj::MAGIC_WELL: | 
					
						
							|  |  |  | 	case Obj::HILL_FORT: | 
					
						
							|  |  |  | 		return true; | 
					
						
							|  |  |  | 	case Obj::BORDER_GATE: | 
					
						
							|  |  |  | 	case Obj::BORDERGUARD: | 
					
						
							|  |  |  | 		return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(ai->playerID); //FIXME: they could be revisited sooner than in a week
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false; | 
					
						
							| 
									
										
										
										
											2021-05-16 14:56:13 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-23 11:56:06 +03:00
										 |  |  | uint64_t timeElapsed(std::chrono::time_point<std::chrono::high_resolution_clock> start) | 
					
						
							| 
									
										
										
										
											2021-05-16 14:56:13 +03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-11-23 09:41:03 +03:00
										 |  |  | 	auto end = std::chrono::high_resolution_clock::now(); | 
					
						
							| 
									
										
										
										
											2021-05-16 14:56:13 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-23 09:41:03 +03:00
										 |  |  | 	return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count(); | 
					
						
							| 
									
										
										
										
											2021-05-16 14:56:27 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // todo: move to obj manager
 | 
					
						
							| 
									
										
										
										
											2021-05-16 15:08:56 +03:00
										 |  |  | bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObjectInstance * obj) | 
					
						
							| 
									
										
										
										
											2021-05-16 14:56:27 +03:00
										 |  |  | { | 
					
						
							|  |  |  | 	switch(obj->ID) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 	case Obj::TOWN: | 
					
						
							|  |  |  | 	case Obj::HERO: //never visit our heroes at random
 | 
					
						
							|  |  |  | 		return obj->tempOwner != h->tempOwner; //do not visit our towns at random
 | 
					
						
							|  |  |  | 	case Obj::BORDER_GATE: | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2021-05-16 15:08:56 +03:00
										 |  |  | 		for(auto q : ai->cb->getMyQuests()) | 
					
						
							| 
									
										
										
										
											2021-05-16 14:56:27 +03:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			if(q.obj == obj) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				return false; // do not visit guards or gates when wandering
 | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return true; //we don't have this quest yet
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	case Obj::BORDERGUARD: //open borderguard if possible
 | 
					
						
							|  |  |  | 		return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(ai->playerID); | 
					
						
							|  |  |  | 	case Obj::SEER_HUT: | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2021-05-16 15:08:56 +03:00
										 |  |  | 		for(auto q : ai->cb->getMyQuests()) | 
					
						
							| 
									
										
										
										
											2021-05-16 14:56:27 +03:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			if(q.obj == obj) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				if(q.quest->checkQuest(h)) | 
					
						
							|  |  |  | 					return true; //we completed the quest
 | 
					
						
							|  |  |  | 				else | 
					
						
							|  |  |  | 					return false; //we can't complete this quest
 | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return true; //we don't have this quest yet
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	case Obj::CREATURE_GENERATOR1: | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		if(obj->tempOwner != h->tempOwner) | 
					
						
							|  |  |  | 			return true; //flag just in case
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const CGDwelling * d = dynamic_cast<const CGDwelling *>(obj); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for(auto level : d->creatures) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			for(auto c : level.second) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				if(level.first | 
					
						
							|  |  |  | 					&& h->getSlotFor(CreatureID(c)) != SlotID() | 
					
						
							| 
									
										
										
										
											2021-05-16 15:08:56 +03:00
										 |  |  | 					&& ai->cb->getResourceAmount().canAfford(c.toCreature()->cost)) | 
					
						
							| 
									
										
										
										
											2021-05-16 14:56:27 +03:00
										 |  |  | 				{ | 
					
						
							|  |  |  | 					return true; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	case Obj::HILL_FORT: | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		for(auto slot : h->Slots()) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			if(slot.second->type->upgrades.size()) | 
					
						
							|  |  |  | 				return true; //TODO: check price?
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	case Obj::MONOLITH_ONE_WAY_ENTRANCE: | 
					
						
							|  |  |  | 	case Obj::MONOLITH_ONE_WAY_EXIT: | 
					
						
							|  |  |  | 	case Obj::MONOLITH_TWO_WAY: | 
					
						
							|  |  |  | 	case Obj::WHIRLPOOL: | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 	case Obj::SCHOOL_OF_MAGIC: | 
					
						
							|  |  |  | 	case Obj::SCHOOL_OF_WAR: | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2021-05-16 15:39:38 +03:00
										 |  |  | 		if(ai->getFreeGold() < 1000) | 
					
						
							| 
									
										
										
										
											2021-05-16 14:56:27 +03:00
										 |  |  | 			return false; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	case Obj::LIBRARY_OF_ENLIGHTENMENT: | 
					
						
							|  |  |  | 		if(h->level < 12) | 
					
						
							|  |  |  | 			return false; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case Obj::TREE_OF_KNOWLEDGE: | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2021-05-16 15:08:56 +03:00
										 |  |  | 		if(ai->heroManager->getHeroRole(h) == HeroRole::SCOUT) | 
					
						
							| 
									
										
										
										
											2021-05-16 14:56:27 +03:00
										 |  |  | 			return false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-16 15:39:38 +03:00
										 |  |  | 		TResources myRes = ai->getFreeResources(); | 
					
						
							| 
									
										
										
										
											2021-05-16 14:56:27 +03:00
										 |  |  | 		if(myRes[Res::GOLD] < 2000 || myRes[Res::GEMS] < 10) | 
					
						
							|  |  |  | 			return false; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	case Obj::MAGIC_WELL: | 
					
						
							|  |  |  | 		return h->mana < h->manaLimit(); | 
					
						
							|  |  |  | 	case Obj::PRISON: | 
					
						
							| 
									
										
										
										
											2021-05-16 15:08:56 +03:00
										 |  |  | 		return ai->cb->getHeroesInfo().size() < VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER; | 
					
						
							| 
									
										
										
										
											2021-05-16 14:56:27 +03:00
										 |  |  | 	case Obj::TAVERN: | 
					
						
							| 
									
										
										
										
											2021-05-16 15:39:38 +03:00
										 |  |  | 	case Obj::EYE_OF_MAGI: | 
					
						
							| 
									
										
										
										
											2021-05-16 14:56:27 +03:00
										 |  |  | 	case Obj::BOAT: | 
					
						
							| 
									
										
										
										
											2021-05-16 15:39:38 +03:00
										 |  |  | 	case Obj::SIGN: | 
					
						
							| 
									
										
										
										
											2021-05-16 14:56:27 +03:00
										 |  |  | 		return false; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if(obj->wasVisited(h)) //it must pointer to hero instance, heroPtr calls function wasVisited(ui8 player);
 | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return true; | 
					
						
							| 
									
										
										
										
											2021-11-23 09:41:03 +03:00
										 |  |  | } |