瀏覽代碼

- Restored correct specialty serialization (#1599 and all its children)
- Fixed rare AI crash
- Fixed AI visiting some objects many times
- Some cleanup and refactoring

DjWarmonger 11 年之前
父節點
當前提交
a3cad2883f
共有 10 個文件被更改,包括 71 次插入57 次删除
  1. 1 1
      AI/VCAI/AIUtility.cpp
  2. 2 0
      AI/VCAI/Fuzzy.cpp
  3. 10 43
      AI/VCAI/Goals.cpp
  4. 40 7
      AI/VCAI/VCAI.cpp
  5. 3 2
      AI/VCAI/VCAI.h
  6. 4 4
      lib/CObjectHandler.cpp
  7. 4 0
      lib/IGameCallback.cpp
  8. 1 0
      lib/IGameCallback.h
  9. 5 0
      lib/mapping/CMap.cpp
  10. 1 0
      lib/mapping/CMap.h

+ 1 - 1
AI/VCAI/AIUtility.cpp

@@ -347,7 +347,7 @@ int3 whereToExplore(HeroPtr h)
 	catch(cannotFulfillGoalException &e)
 	{
 		//perform exhaustive search
-		return ai->explorationNewPoint(radius, h);
+		return ai->explorationNewPoint(h);
 	}
 }
 

+ 2 - 0
AI/VCAI/Fuzzy.cpp

@@ -467,6 +467,8 @@ float FuzzyHelper::evaluate (Goals::VisitTile & g)
 float FuzzyHelper::evaluate (Goals::VisitHero & g)
 {
 	auto obj = cb->getObj(ObjectInstanceID(g.objid)); //we assume for now that these goals are similiar
+	if (!obj)
+		return -100; //hero died in the meantime
 	//TODO: consider direct copy (constructor?)
 	g.setpriority(Goals::VisitTile(obj->visitablePos()).sethero(g.hero).setisAbstract(g.isAbstract).accept(this));
 	return g.priority;	

+ 10 - 43
AI/VCAI/Goals.cpp

@@ -418,7 +418,7 @@ TGoalVec ClearWayTo::getAllPossibleSubgoals()
 			ret.push_back  (sptr (Goals::FindObj (Obj::KEYMASTER, cb->getTile(tileToHit)->visitableObjects.back()->subID)));
 		}
 
-		auto topObj = backOrNull(cb->getVisitableObjs(tileToHit));
+		auto topObj = cb->getTopObj(tileToHit);
 		if(topObj)
 		{
 			if (topObj->ID == Obj::HERO && cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
@@ -516,18 +516,7 @@ TGoalVec Explore::getAllPossibleSubgoals()
 		for (auto obj : objs) //double loop, performance risk?
 		{
 			auto t = sm.firstTileToGet(h, obj->visitablePos()); //we assume that no more than one tile on the way is guarded
-			if (t.valid())
-			{
-				if (isSafeToVisit(h, t))
-				{
-					ret.push_back (sptr (Goals::VisitTile(t).sethero(h)));
-				}
-				else
-				{
-					ret.push_back (sptr (Goals::GatherArmy(evaluateDanger(t, h)*SAFE_ATTACK_CONSTANT).
-						sethero(h).setisAbstract(true)));
-				}
-			}
+			ai->whatToDoToReachTile(h, t, ret);
 		}
 
 		int3 t = whereToExplore(h);
@@ -537,19 +526,8 @@ TGoalVec Explore::getAllPossibleSubgoals()
 		}
 		else if (hero.h == h || (!hero && h == ai->primaryHero().h)) //check this only ONCE, high cost
 		{
-			t = ai->explorationDesperate(h->getSightRadious(), h);
-			if (t.valid())
-			{
-				if (isSafeToVisit(h, t))
-				{
-					ret.push_back (sptr (Goals::VisitTile(t).sethero(h)));
-				}
-				else
-				{
-					ret.push_back (sptr (Goals::GatherArmy(evaluateDanger(t, h)*SAFE_ATTACK_CONSTANT).
-						sethero(h).setisAbstract(true)));
-				}
-			}
+			t = ai->explorationDesperate(h);
+			ai->whatToDoToReachTile(h, t, ret);
 		}
 	}
 	//we either don't have hero yet or none of heroes can explore
@@ -560,7 +538,7 @@ TGoalVec Explore::getAllPossibleSubgoals()
 	//{
 	//	for (auto h : heroes) //this is costly function, use only when there is no other way
 	//	{
-	//		auto t = ai->explorationDesperate (h->getSightRadious(), h); //we assume that no more than one tile on the way is guarded
+	//		auto t = ai->explorationDesperate (h); //we assume that no more than one tile on the way is guarded
 	//		if (t.valid())
 	//		{
 	//			if (isSafeToVisit(h, t))
@@ -744,7 +722,7 @@ TSubgoal CollectRes::whatToDoToAchieve()
 
 		if(howManyCanWeBuy + cb->getResourceAmount(static_cast<Res::ERes>(resID)) >= value)
 		{
-			auto backObj = backOrNull(cb->getVisitableObjs(m->o->visitablePos())); //it'll be a hero if we have one there; otherwise marketplace
+			auto backObj = cb->getTopObj(m->o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace
 			assert(backObj);
 			if (backObj->tempOwner != ai->playerID)
 			{
@@ -858,21 +836,7 @@ TGoalVec Conquer::getAllPossibleSubgoals()
 		for (auto obj : ourObjs) //double loop, performance risk?
 		{
 			auto t = sm.firstTileToGet(h, obj->visitablePos()); //we assume that no more than one tile on the way is guarded
-			if (t.valid())
-			{
-				if (isSafeToVisit(h, t))
-				{
-					if (obj->ID == Obj::HERO)
-						ret.push_back (sptr (Goals::VisitHero(obj->id.getNum()).sethero(h).setisAbstract(true)));
-						//track enemy hero
-					else
-						ret.push_back (sptr (Goals::VisitTile(t).sethero(h)));
-				}
-				else
-				{
-					ret.push_back (sptr (Goals::GatherArmy(evaluateDanger(t,h)*SAFE_ATTACK_CONSTANT).sethero(h).setisAbstract(true)));
-				}
-			}
+			ai->whatToDoToReachTile (h, t, ret);
 		}
 	}
 	if (!objs.empty() && ai->canRecruitAnyHero()) //probably no point to recruit hero if we see no objects to capture
@@ -978,6 +942,9 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
 				ret.push_back (sptr (Goals::VisitTile(pos).sethero(h)));
 		}
 	}
+	if (ai->canRecruitAnyHero()) //this is not stupid in early phase of game
+		ret.push_back (sptr(Goals::RecruitHero()));
+
 	if (ret.empty())
 	{
 		if (hero == ai->primaryHero() || value >= 1.1f)

+ 40 - 7
AI/VCAI/VCAI.cpp

@@ -742,7 +742,6 @@ void VCAI::makeTurnInternal()
 			std::vector<std::pair<HeroPtr, Goals::TSubgoal> > safeCopy;
 			for (auto mission : lockedHeroes)
 			{
-				//FIXME: for some reason when called here, priority is always the same
 				fh->setPriority (mission.second); //re-evaluate
 				if (canAct(mission.first))
 				{
@@ -1241,6 +1240,26 @@ std::vector<const CGObjectInstance *> VCAI::getPossibleDestinations(HeroPtr h)
 	return possibleDestinations;
 }
 
+void VCAI::whatToDoToReachTile (const CGHeroInstance * h, int3 t, Goals::TGoalVec& vec)
+///TODO: possibly merge with Goals::ClearWayTo
+{
+	if (t.valid())
+	{
+		auto obj = cb->getTopObj(t);
+		if (obj && vstd::contains(ai->reservedObjs, obj) && !vstd::contains(reservedHeroesMap[h], obj))
+				return; //do not capture object reserved by another hero
+		if (isSafeToVisit(h, t))
+		{
+			vec.push_back (sptr (Goals::VisitTile(t).sethero(h)));
+		}
+		else
+		{
+			vec.push_back (sptr (Goals::GatherArmy(evaluateDanger(t, h)*SAFE_ATTACK_CONSTANT).
+				sethero(h).setisAbstract(true)));
+		}
+	}
+}
+
 bool VCAI::canRecruitAnyHero (const CGTownInstance * t) const
 {
 	//TODO: make gathering gold, building tavern or conquering town (?) possible subgoals
@@ -2205,10 +2224,11 @@ int3 VCAI::explorationBestNeighbour(int3 hpos, int radius, HeroPtr h)
 	throw cannotFulfillGoalException("No neighbour will bring new discoveries!");
 }
 
-int3 VCAI::explorationNewPoint(int radius, HeroPtr h)
+int3 VCAI::explorationNewPoint(HeroPtr h)
 {
     //logAi->debugStream() << "Looking for an another place for exploration...";
 	cb->setSelection(h.h);
+	int radius = h->getSightRadious();
 
 	std::vector<std::vector<int3> > tiles; //tiles[distance_to_fow]
 	tiles.resize(radius);
@@ -2288,10 +2308,11 @@ int3 VCAI::explorationNewPoint(int radius, HeroPtr h)
 	return bestTile;
 }
 
-int3 VCAI::explorationDesperate(int radius, HeroPtr h)
+int3 VCAI::explorationDesperate(HeroPtr h)
 {
     //logAi->debugStream() << "Looking for an another place for exploration...";
 	SectorMap sm(h);
+	int radius = h->getSightRadious();
 	
 	std::vector<std::vector<int3> > tiles; //tiles[distance_to_fow]
 	tiles.resize(radius);
@@ -3049,7 +3070,7 @@ For ship construction etc, another function (goal?) is needed
 		int3 curtile = dst;
 		while(curtile != h->visitablePos())
 		{
-			auto topObj = backOrNull(cb->getVisitableObjs(curtile));
+			auto topObj = cb->getTopObj(curtile);
 			if (topObj && topObj->ID == Obj::HERO && topObj != h.h)
 			{
 				logAi->warnStream() << ("Another allied hero stands in our way");
@@ -3095,8 +3116,7 @@ void SectorMap::makeParentBFS(crint3 source)
 		ui8 &sec = retreiveTile(curPos);
 		assert(sec == mySector); //consider only tiles from the same sector
 		UNUSED(sec);
-
-		//const TerrainTile *t = cb->getTile(curPos);
+	
 		foreach_neighbour(curPos, [&](crint3 neighPos)
 		{
 			if(retreiveTile(neighPos) == mySector && !vstd::contains(parent, neighPos))
@@ -3107,7 +3127,20 @@ void SectorMap::makeParentBFS(crint3 source)
 					parent[neighPos] = curPos;
 				}
 			}
-		});
+		});
+		//this code is unused, as tiles on both sides of game are different sectors
+
+		//const TerrainTile *t = cb->getTile(curPos);
+		//if(t->topVisitableId() == Obj::SUBTERRANEAN_GATE)
+		//{
+		//	//try finding the exit gate
+		//	auto it = ai->knownSubterraneanGates.find(t->topVisitableObj());
+		//	if (it != ai->knownSubterraneanGates.end())
+		//	{
+		//		const int3 outPos = it->second->visitablePos();
+		//		parent[outPos] = curPos; //TODO: is it only one tile?
+		//	}
+		//}
 	}
 }
 

+ 3 - 2
AI/VCAI/VCAI.h

@@ -178,8 +178,9 @@ public:
 	void tryRealize(Goals::AbstractGoal & g);
 
 	int3 explorationBestNeighbour(int3 hpos, int radius, HeroPtr h);
-	int3 explorationNewPoint(int radius, HeroPtr h);
-	int3 explorationDesperate(int radius, HeroPtr h);
+	int3 explorationNewPoint(HeroPtr h);
+	int3 explorationDesperate(HeroPtr h);
+	void whatToDoToReachTile (const CGHeroInstance * h, int3 t, Goals::TGoalVec& vec);
 	void recruitHero();
 
 	virtual std::string getBattleAIName() const override;

+ 4 - 4
lib/CObjectHandler.cpp

@@ -1567,10 +1567,10 @@ void CGHeroInstance::deserializationFix()
 {
 	artDeserializationFix(this);
 
-	//for (auto hs : specialty)
-	//{
-	//	attachTo (hs);
-	//}
+	for (auto hs : specialty)
+	{
+		attachTo (hs);
+	}
 }
 
 CBonusSystemNode * CGHeroInstance::whereShouldBeAttached(CGameState *gs)

+ 4 - 0
lib/IGameCallback.cpp

@@ -521,6 +521,10 @@ std::vector <const CGObjectInstance * > CGameInfoCallback::getVisitableObjs(int3
 
 	return ret;
 }
+const CGObjectInstance * CGameInfoCallback::getTopObj (int3 pos) const
+{
+	return vstd::backOrNull(getVisitableObjs(pos));
+}
 
 std::vector < const CGObjectInstance * > CGameInfoCallback::getFlaggableObjects(int3 pos) const
 {

+ 1 - 0
lib/IGameCallback.h

@@ -111,6 +111,7 @@ public:
 	std::vector <const CGObjectInstance * > getBlockingObjs(int3 pos)const;
 	std::vector <const CGObjectInstance * > getVisitableObjs(int3 pos, bool verbose = true)const;
 	std::vector <const CGObjectInstance * > getFlaggableObjects(int3 pos) const;
+	const CGObjectInstance * getTopObj (int3 pos) const;
 	std::vector <std::string > getObjDescriptions(int3 pos)const; //returns descriptions of objects at pos in order from the lowest to the highest
 	PlayerColor getOwner(ObjectInstanceID heroID) const;
 	const CGObjectInstance *getObjByQuestIdentifier(int identifier) const; //nullptr if object has been removed (eg. killed)

+ 5 - 0
lib/mapping/CMap.cpp

@@ -132,6 +132,11 @@ int TerrainTile::topVisitableId() const
 	return visitableObjects.size() ? visitableObjects.back()->ID : -1;
 }
 
+CGObjectInstance * TerrainTile::topVisitableObj() const
+{
+	return visitableObjects.size() ? visitableObjects.back() : nullptr;
+}
+
 bool TerrainTile::isCoastal() const
 {
 	return extTileFlags & 64;

+ 1 - 0
lib/mapping/CMap.h

@@ -288,6 +288,7 @@ struct DLL_LINKAGE TerrainTile
 	bool isClear(const TerrainTile * from = nullptr) const;
 	/// Gets the ID of the top visitable object or -1 if there is none.
 	int topVisitableId() const;
+	CGObjectInstance * topVisitableObj() const;
 	bool isWater() const;
 	bool isCoastal() const;
 	bool hasFavourableWinds() const;