Browse Source

- A complete solutions for reserved adventure objects
- AI won't get stuck at School of War / School of Magic when no money

DjWarmonger 13 years ago
parent
commit
81367ef6d5
3 changed files with 100 additions and 8 deletions
  1. 94 8
      AI/VCAI/VCAI.cpp
  2. 3 0
      AI/VCAI/VCAI.h
  3. 3 0
      lib/GameConstants.h

+ 94 - 8
AI/VCAI/VCAI.cpp

@@ -555,6 +555,8 @@ void VCAI::heroVisit(const CGHeroInstance *visitor, const CGObjectInstance *visi
 	{
 		visitedObject = const_cast<CGObjectInstance *>(visitedObj); // remember the object and wait for return
 		markObjectVisited (visitedObj);
+		remove_if_present(reservedObjs, visitedObj); //unreserve objects
+		remove_if_present(reservedHeroesMap[visitor], visitedObj);
 	}
 }
 
@@ -1114,6 +1116,9 @@ std::vector<const CGObjectInstance *> VCAI::getPossibleDestinations(const CGHero
 
 			if (!shouldVisit(h, obj))
 				return true;
+			
+			if (vstd::contains(reservedObjs, obj)) //does checking for our own reserved objects make sense? here?
+				return true;
 
 			return false;
 		}),possibleDestinations.end());
@@ -1125,18 +1130,74 @@ void VCAI::wander(const CGHeroInstance * h)
 {
 	while(1)
 	{
-		auto dests = getPossibleDestinations(h);
+		std::vector <const CGObjectInstance *> dests (reservedHeroesMap[h].begin(), reservedHeroesMap[h].end()); //copy constructor
+		if (!dests.size())
+			dests = getPossibleDestinations(h);
 		if(!dests.size())
 		{
-			PNLOG("Nowhere more to go...\n");
-			setGoal (h, INVALID);
-			break;
+			auto compareReinforcements = [h](const CGTownInstance *lhs, const CGTownInstance *rhs) -> bool
+	        {
+				return howManyReinforcementsCanGet(h, lhs) < howManyReinforcementsCanGet(h, rhs);
+	        };
+	
+	        std::vector<const CGTownInstance *> townsReachable;
+	        std::vector<const CGTownInstance *> townsNotReachable;
+	        BOOST_FOREACH(const CGTownInstance *t, cb->getTownsInfo())
+	        {
+	            if(!t->visitingHero && howManyReinforcementsCanGet(h,t) && !vstd::contains(townVisitsThisWeek[h], t))
+	            {
+	                if(isReachable(t))
+						townsReachable.push_back(t);
+	                else
+						townsNotReachable.push_back(t);
+	            }
+			}
+            if(townsReachable.size())
+            {
+				boost::sort(townsReachable, compareReinforcements);
+				dests.push_back(townsReachable.back());
+			}
+			else if(townsNotReachable.size())
+			{
+				boost::sort(townsNotReachable, compareReinforcements);
+	            //TODO pick the truly best
+	            const CGTownInstance *t = townsNotReachable.back();
+	            BNLOG("%s can't reach any town, we'll try to make our way to %s at %s", h->name % t->name % t->visitablePos());
+				int3 pos1 = h->pos;
+				striveToGoal(CGoal(CLEAR_WAY_TO).settile(t->visitablePos()).sethero(h));
+				if (pos1 == h->pos && h == primaryHero()) //hero can't move
+				{
+					if(cb->getResourceAmount(Res::GOLD) >= HERO_GOLD_COST && cb->getHeroesInfo().size() < ALLOWED_ROAMING_HEROES && cb->getAvailableHeroes(t).size())
+					recruitHero(t);
+				}
+	            break;
+			}
+			else if(cb->getResourceAmount(Res::GOLD) >= HERO_GOLD_COST)
+	        {
+				std::vector<const CGTownInstance *> towns = cb->getTownsInfo();
+				erase_if(towns, [](const CGTownInstance *t) -> bool
+				{
+					BOOST_FOREACH(const CGHeroInstance *h, cb->getHeroesInfo())
+					if(!t->getArmyStrength() || howManyReinforcementsCanGet(h, t))
+						return true;
+					return false;
+				});
+				boost::sort(towns, compareArmyStrength);
+	            if(towns.size())
+	                    recruitHero(towns.back());
+	            break;
+	        }
+            else
+            {
+				PNLOG("Nowhere more to go...\n");
+				break;
+			}
 		}
 		const CGObjectInstance * obj = dests.front();
 		if(!goVisitObj(obj, h))
 		{
 			BNLOG("Hero %s apparently used all MPs (%d left)\n", h->name % h->movement);
-			markObjectVisited(obj); //reserve that object - we predict it will be reached soon
+			reserveObject(h, obj); //reserve that object - we predict it will be reached soon
 			setGoal(h, CGoal(VISIT_TILE).sethero(h).settile(obj->visitablePos()));
 			break;
 		}
@@ -1194,6 +1255,12 @@ void VCAI::markObjectVisited (const CGObjectInstance *obj)
 	alreadyVisited.push_back(obj);
 }
 
+void VCAI::reserveObject (const CGHeroInstance * h, const CGObjectInstance *obj)
+{
+	reservedObjs.push_back(obj);
+	reservedHeroesMap[h].insert(obj);
+}
+
 void VCAI::validateVisitableObjs()
 {
 	std::vector<const CGObjectInstance *> hlp;
@@ -1355,9 +1422,15 @@ bool VCAI::moveHeroToTile(int3 dst, const CGHeroInstance * h)
 			cb->moveHero(h,CGHeroInstance::convertPosition(endpos, true));
 			waitTillFree(); //movement may cause battle or blocking dialog
 			boost::this_thread::interruption_point();
-			if(h->tempOwner != playerID) //we lost hero
+			if(h->tempOwner != playerID) //we lost hero - remove all tasks assigned to him/her
 			{
 				remove_if_present(lockedHeroes, h);
+				BOOST_FOREACH (auto obj, reservedHeroesMap[h])
+				{
+					remove_if_present(reservedObjs, obj); //unreserve all objects for that hero
+				}
+				remove_if_present(reservedHeroesMap, h);
+
 				throw std::runtime_error("Hero was lost!"); //we need to throw, otherwise hero will be assigned to sth again
 				break;
 			}
@@ -1366,7 +1439,9 @@ bool VCAI::moveHeroToTile(int3 dst, const CGHeroInstance * h)
 		ret = !i;
 	}
 	if (visitedObject) //we step into something interesting
+	{
 		performObjectInteraction (visitedObject, h);
+	}
 
 	if(h->tempOwner == playerID) //lost hero after last move
 		cb->recalculatePaths();
@@ -2524,8 +2599,6 @@ bool isWeeklyRevisitable (const CGObjectInstance * obj)
 
 bool shouldVisit (const CGHeroInstance * h, const CGObjectInstance * obj)
 {
-	if (obj->wasVisited(h))
-		return false;
 	switch (obj->ID)
 	{
 		case Obj::CREATURE_GENERATOR1:
@@ -2548,7 +2621,20 @@ bool shouldVisit (const CGHeroInstance * h, const CGObjectInstance * obj)
 		case Obj::WHIRLPOOL:
 			//TODO: mehcanism for handling monoliths
 			return false;
+		case Obj::SCHOOL_OF_MAGIC:
+		case Obj::SCHOOL_OF_WAR:
+			{
+				TResources myRes = ai->myCb->getResourceAmount();
+				if (myRes[Res::GOLD] - GOLD_RESERVE < 1000)
+					return false;
+			}
+		case Obj::LIBRARY_OF_ENLIGHTENMENT:
+			return h->level >= 12;
 	}
+
+	if (obj->wasVisited(h))
+		return false;
+
 	return true;
 }
 

+ 3 - 0
AI/VCAI/VCAI.h

@@ -157,9 +157,11 @@ public:
 	std::map<const CGHeroInstance *, std::vector<const CGTownInstance *> > townVisitsThisWeek;
 
 	std::map<const CGHeroInstance *, CGoal> lockedHeroes; //TODO: allow non-elementar objectives
+	std::map<const CGHeroInstance *, std::set<const CGObjectInstance *> > reservedHeroesMap; //objects reserved by specific heroes
 
 	std::vector<const CGObjectInstance *> visitableObjs;
 	std::vector<const CGObjectInstance *> alreadyVisited;
+	std::vector<const CGObjectInstance *> reservedObjs; //to be visited by specific hero
 
 	TResources saving;
 
@@ -266,6 +268,7 @@ public:
 
 	void addVisitableObj(const CGObjectInstance *obj);
 	void markObjectVisited (const CGObjectInstance *obj);
+	void reserveObject (const CGHeroInstance * h, const CGObjectInstance *obj);
 	//void removeVisitableObj(const CGObjectInstance *obj);
 	void validateVisitableObjs();
 	void retreiveVisitableObjs(std::vector<const CGObjectInstance *> &out, bool includeOwned = false) const;

+ 3 - 0
lib/GameConstants.h

@@ -180,9 +180,11 @@ namespace Obj
 		DERELICT_SHIP = 24,
 		DRAGON_UTOPIA = 25,
 		GARRISON = 33,
+		LIBRARY_OF_ENLIGHTENMENT = 41,
 		MONOLITH1 = 43,
 		MONOLITH2 = 44,
 		MONOLITH3 = 45,
+		SCHOOL_OF_MAGIC = 47,
 		MINE = 53,
 		MONSTER = 54,
 		OBELISK = 57,
@@ -192,6 +194,7 @@ namespace Obj
 		STABLES = 94,
 		TRADING_POST = 99,
 		SUBTERRANEAN_GATE = 103,
+		SCHOOL_OF_WAR = 107,
 		WHIRLPOOL = 111,
 		BORDER_GATE = 212,
 		GARRISON2 = 219,