Parcourir la source

-Fixed crash #1037
-AI can now successfully complete several types of quests.
-Lots of tweaks for AI
TODO: fix bizarre crash when opening borderguard

DjWarmonger il y a 13 ans
Parent
commit
d8cb3a34d3
8 fichiers modifiés avec 99 ajouts et 33 suppressions
  1. 41 13
      AI/VCAI/VCAI.cpp
  2. 1 1
      AI/VCAI/VCAI.h
  3. 1 0
      client/NetPacksClient.cpp
  4. 13 0
      lib/CGameState.cpp
  5. 19 11
      lib/CObjectHandler.cpp
  6. 3 1
      lib/CObjectHandler.h
  7. 1 0
      lib/GameConstants.h
  8. 20 7
      lib/NetPacksLib.cpp

+ 41 - 13
AI/VCAI/VCAI.cpp

@@ -80,6 +80,8 @@ std::string goalName(EGoals goalType)
 			return "INVALID";
 		case WIN:
 			return "WIN";
+		case DO_NOT_LOSE:
+			return "DO NOT LOOSE";
 		case CONQUER:
 			return "CONQUER";
 		case BUILD:
@@ -88,10 +90,26 @@ std::string goalName(EGoals goalType)
 			return "EXPLORE";
 		case GATHER_ARMY:
 			return "GATHER ARMY";
+		case BOOST_HERO:
+			return "BOOST_HERO (unsupported)";
+		case BUILD_STRUCTURE:
+			return "BUILD STRUCTURE";
+		case COLLECT_RES:
+			return "COLLECT RESOURCE";
+		case GET_OBJ:
+			return "GET OBJECT";
+		case FIND_OBJ:
+			return "FIND OBJECT";
+		case GET_ART_TYPE:
+			return "GET ARTIFACT OF TYPE";
+		case ISSUE_COMMAND:
+			return "ISSUE COMMAND (unsupported)";
 		case VISIT_TILE:
 			return "VISIT TILE";
 		case CLEAR_WAY_TO:
 			return "CLEAR WAY TO";
+		case DIG_AT_TILE:
+			return "DIG AT TILE";
 		default:
 			return boost::lexical_cast<std::string>(goalType);
 	}
@@ -1851,7 +1869,7 @@ void VCAI::tryRealize(CGoal g)
 					cb->trade(obj, EMarketMode::RESOURCE_RESOURCE, i, g.resID, toGive);
 					if(cb->getResourceAmount(g.resID) >= g.value)
 						return;
-				}
+				} //TODO: stop when we've sold all the resources
 			}
 			else
 			{
@@ -2064,7 +2082,7 @@ void VCAI::striveToQuest (const QuestInfo &q)
 	{
 		MetaString ms;
 		q.quest->getRolloverText(ms, false);
-		BNLOG ("Trying to realize quest: %s\n", ms.toString());
+		BNLOG ("Trying to realize quest: %s", ms.toString());
 		switch (q.quest->missionType)
 		{
 			case CQuest::MISSION_ART:
@@ -2111,7 +2129,11 @@ void VCAI::striveToQuest (const QuestInfo &q)
 			case CQuest::MISSION_KILL_HERO:
 			case CQuest::MISSION_KILL_CREATURE:
 			{
-				striveToGoal (CGoal(GET_OBJ).setobjid(q.quest->m13489val));
+				auto obj = cb->getObjByQuestIdentifier(q.quest->m13489val);
+				if (obj)
+					striveToGoal (CGoal(GET_OBJ).setobjid(obj->id));
+				else
+					striveToGoal (CGoal(VISIT_TILE).settile(q.tile)); //visit seer hut
 				break;
 			}
 			case CQuest::MISSION_PRIMARY_STAT:
@@ -2153,7 +2175,7 @@ void VCAI::striveToQuest (const QuestInfo &q)
 			}
 			case CQuest::MISSION_KEYMASTER:
 			{
-				striveToGoal (CGoal(FIND_OBJ).setobjid(Obj::KEYMASTER).setresID(q.quest->m13489val));
+				striveToGoal (CGoal(FIND_OBJ).setobjid(Obj::KEYMASTER).setresID(q.obj->subID));
 				break;
 			}
 		}
@@ -2246,7 +2268,7 @@ int3 VCAI::explorationNewPoint(int radius, HeroPtr h, std::vector<std::vector<in
 
 		BOOST_FOREACH(const int3 &tile, tiles[i])
 		{
-			if(cb->getPathInfo(tile)->reachable() && isSafeToVisit(h, tile) && howManyTilesWillBeDiscovered(tile, radius))
+			if(cb->getPathInfo(tile)->reachable() && isSafeToVisit(h, tile) && howManyTilesWillBeDiscovered(tile, radius) && !isBlockedBorderGate(tile))
 			{
 				return tile;
 			}
@@ -2657,12 +2679,14 @@ TSubgoal CGoal::whatToDoToAchieve()
 				}
 			}
 			else
-			BOOST_FOREACH(const CGObjectInstance *obj, ai->visitableObjs)
 			{
-				if(obj->ID == objid)
+				BOOST_FOREACH(const CGObjectInstance *obj, ai->visitableObjs)
 				{
-					o = obj;
-					break; //TODO: consider multiple objects and choose best
+					if(obj->ID == objid)
+					{
+						o = obj;
+						break; //TODO: consider multiple objects and choose best
+					}
 				}
 			}
 			if (o)
@@ -2714,6 +2738,8 @@ TSubgoal CGoal::whatToDoToAchieve()
 				return CGoal(FIND_OBJ).setobjid(Obj::KEYMASTER).setresID(cb->getTile(tileToHit)->visitableObjects.back()->subID);
 			}
 
+
+			//FIXME: this code shouldn't be necessary
 			if(tileToHit == tile)
 			{
 				tlog1 << boost::format("Very strange, tile to hit is %s and tile is also %s, while hero %s is at %s\n")
@@ -3052,7 +3078,7 @@ bool CGoal::invalid() const
 	return goalType == INVALID;
 }
 
-bool CGoal::isBlockedBorderGate(int3 tileToHit)
+bool isBlockedBorderGate(int3 tileToHit)
 {
 	return cb->getTile(tileToHit)->topVisitableID() == Obj::BORDER_GATE
 		&& cb->getPathInfo(tileToHit)->accessible != CGPathNode::ACCESSIBLE;
@@ -3199,20 +3225,22 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
 {
 	switch (obj->ID)
 	{
+		case Obj::BORDERGUARD:
+		case Obj::BORDER_GATE:
 		case Obj::SEER_HUT:
+		case Obj::QUEST_GUARD:
 		{
 			BOOST_FOREACH (auto q, ai->myCb->getMyQuests())
 			{
-				if (q.obj = obj)
+				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
-
 			}
+			return true; //we don't have this quest yet
 		}
 		case Obj::CREATURE_GENERATOR1:
 		{

+ 1 - 1
AI/VCAI/VCAI.h

@@ -110,7 +110,6 @@ struct CGoal
 
 	virtual TSubgoal whatToDoToAchieve();
 
-	bool isBlockedBorderGate(int3 tileToHit);
 	CGoal(EGoals goal = INVALID) : goalType(goal)
 	{
 		priority = 0;
@@ -374,6 +373,7 @@ bool objWithID(const CGObjectInstance *obj)
 {
 	return obj->ID == id;
 }
+bool isBlockedBorderGate(int3 tileToHit);
 
 bool isWeeklyRevisitable (const CGObjectInstance * obj);
 bool shouldVisit (HeroPtr h, const CGObjectInstance * obj);

+ 1 - 0
client/NetPacksClient.cpp

@@ -318,6 +318,7 @@ void UpdateCampaignState::applyCl( CClient *cl )
 void RemoveObject::applyFirstCl( CClient *cl )
 {
 	const CGObjectInstance *o = cl->getObj(id);
+
 	CGI->mh->hideObject(o);
 
 	int3 pos = o->visitablePos();

+ 13 - 0
lib/CGameState.cpp

@@ -1468,6 +1468,19 @@ void CGameState::init(StartInfo * si)
 		if(obj->ID == 62) //prison also needs to initialize hero
 			static_cast<CGHeroInstance*>(obj)->initHero();
 	}
+	BOOST_FOREACH(CGObjectInstance *obj, map->objects)
+	{
+		switch (obj->ID)
+		{
+			case Obj::QUEST_GUARD:
+			case Obj::SEER_HUT:
+			{
+				auto q = static_cast<CGSeerHut*>(obj);
+				assert (q);
+				q->setObjToKill();
+			}
+		}
+	}
 	CGTeleport::postInit(); //pairing subterranean gates
 
 	buildBonusSystemTree();

+ 19 - 11
lib/CObjectHandler.cpp

@@ -4399,6 +4399,20 @@ void CQuest::getCompletionText (MetaString &iwText, std::vector<Component> &comp
 			break;
 	}
 }
+void CGSeerHut::setObjToKill()
+{
+	if (missionType == MISSION_KILL_CREATURE)
+	{
+		stackToKill = getCreatureToKill(false)->getStack(0); //FIXME: stacks tend to dissapear (desync?) on server :?
+		assert (stackToKill.count > 0); //seemingly creatures are uninitialized
+		stackDirection = checkDirection();
+	}
+	else if (missionType == MISSION_KILL_HERO)
+	{
+		heroName = getHeroToKill(false)->name;
+		heroPortrait = getHeroToKill(false)->portrait;
+	}
+}
 
 void CGSeerHut::initObj()
 {
@@ -4413,17 +4427,6 @@ void CGSeerHut::initObj()
 			nextVisitText = VLC->generaltexth->quests[missionType-1][1][textOption];	
 		if (!isCustomComplete)
 			completedText = VLC->generaltexth->quests[missionType-1][2][textOption];
-
-		if(missionType == MISSION_KILL_CREATURE)
-		{
-			stackToKill = getCreatureToKill(false)->getStack(0);
-			stackDirection = checkDirection();
-		}
-		else if(missionType == MISSION_KILL_HERO)
-		{
-			heroName = getHeroToKill(false)->name;
-			heroPortrait = getHeroToKill(false)->portrait;
-		}
 	}
 	else
 		firstVisitText = VLC->generaltexth->seerEmpty[textOption];
@@ -6326,6 +6329,11 @@ void CGBorderGuard::getRolloverText (MetaString &text, bool onHover) const
 		text << VLC->generaltexth->tentColors[subID] << " " << VLC->generaltexth->names[Obj::KEYMASTER];
 }
 
+bool CGBorderGuard::checkQuest (const CGHeroInstance * h) const
+{
+	return wasMyColorVisited (h->tempOwner);
+}
+
 void CGBorderGuard::onHeroVisit( const CGHeroInstance * h ) const 
 {
 	if (wasMyColorVisited (h->getOwner()) )

+ 3 - 1
lib/CObjectHandler.h

@@ -82,7 +82,7 @@ public:
 	std::string firstVisitText, nextVisitText, completedText;
 	bool isCustomFirst, isCustomNext, isCustomComplete;
 
-	bool checkQuest (const CGHeroInstance * h) const; //determines whether the quest is complete or not
+	virtual bool checkQuest (const CGHeroInstance * h) const; //determines whether the quest is complete or not
 	virtual void getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h = NULL) const;
 	virtual void getCompletionText (MetaString &text, std::vector<Component> &components, bool isCustom, const CGHeroInstance * h = NULL) const;
 	virtual void getRolloverText (MetaString &text, bool onHover) const; //hover or quest log entry
@@ -782,6 +782,7 @@ public:
 	void finishQuest (const CGHeroInstance * h, ui32 accept) const; //common for both objects
 	void completeQuest (const CGHeroInstance * h) const;
 
+	void setObjToKill(); //remember creatures / heroes to kill after they are initialized
 	const CGHeroInstance *getHeroToKill(bool allowNull = false) const;
 	const CGCreature *getCreatureToKill(bool allowNull = false) const;
 
@@ -1077,6 +1078,7 @@ public:
 	const std::string & getHoverText() const;
 	void getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h = NULL) const;
 	void getRolloverText (MetaString &text, bool onHover) const;
+	bool checkQuest (const CGHeroInstance * h) const;
 	void onHeroVisit(const CGHeroInstance * h) const;
 	void openGate(const CGHeroInstance *h, ui32 accept) const;
 

+ 1 - 0
lib/GameConstants.h

@@ -216,6 +216,7 @@ namespace Obj
 		SCHOOL_OF_WAR = 107,
 		WHIRLPOOL = 111,
 		BORDER_GATE = 212,
+		QUEST_GUARD = 215,
 		GARRISON2 = 219,
 		CLOVER_FIELD = 222,
 		CURSED_GROUND2 = 223,

+ 20 - 7
lib/NetPacksLib.cpp

@@ -308,11 +308,24 @@ DLL_LINKAGE void RemoveObject::applyGs( CGameState *gs )
 
 		return;
 	}
-// 	else if (obj->ID==CREI_TYPE  &&  gs->map->version > CMapHeader::RoE) //only fixed monsters can be a part of quest
-// 	{
-// 		CGCreature *cre = static_cast<CGCreature*>(obj);
-// 		gs->map->monsters[cre->identifier]->pos = int3 (-1,-1,-1);	//use nonexistent monster for quest :>
-// 	}
+	//FIXME: for some reason this code causes crash in Bonus System ?!
+
+	auto quest = dynamic_cast<const CQuest *>(obj);
+	if (quest)
+	{
+		BOOST_FOREACH (auto player, gs->players)
+		{
+			BOOST_FOREACH (auto q, player.second.quests)
+			{
+				if (q.obj == obj)
+				{
+					q.quest = NULL; //remove entries related to quest guards?
+					q.obj = NULL;
+				}
+			}
+		}
+	}
+
 	gs->map->objects[id].dellNull();
 }
 
@@ -1495,7 +1508,7 @@ DLL_LINKAGE void SetSelection::applyGs( CGameState *gs )
 }
 
 DLL_LINKAGE Component::Component(const CStackBasicDescriptor &stack)
-	:id(CREATURE), subtype(stack.type->idNumber), val(stack.count), when(0)
+	: id(CREATURE), subtype(stack.type->idNumber), val(stack.count), when(0)
 {
-
+	type = 2002;
 }