浏览代码

- Unified interface for visitable objects
- AI will manage visitable objects smarter

DjWarmonger 13 年之前
父节点
当前提交
a3f36ccc71
共有 4 个文件被更改,包括 105 次插入24 次删除
  1. 49 12
      AI/VCAI/VCAI.cpp
  2. 3 1
      AI/VCAI/VCAI.h
  3. 42 9
      lib/CObjectHandler.cpp
  4. 11 2
      lib/CObjectHandler.h

+ 49 - 12
AI/VCAI/VCAI.cpp

@@ -374,12 +374,6 @@ ui64 evaluateDanger(const CGObjectInstance *obj)
 	case Obj::DERELICT_SHIP: //derelict ship
 	case Obj::PYRAMID:
 		return fh->estimateBankDanger (VLC->objh->bankObjToIndex(obj));
-	case Obj::WHIRLPOOL: //whirlpool
-	case Obj::MONOLITH1:
-	case Obj::MONOLITH2:
-	case Obj::MONOLITH3:
-		//TODO mechanism for handling monoliths
-		return 1000000000;
 	default:
 		return 0;
 	}
@@ -559,9 +553,8 @@ void VCAI::heroVisit(const CGHeroInstance *visitor, const CGObjectInstance *visi
 	LOG_ENTRY;
 	if (start)
 	{
-		visitedObject = const_cast<CGObjectInstance *>(visitedObj); // remember teh object and wait for return
-		if(visitedObj->ID != Obj::MONSTER) //TODO: poll bank if it was cleared
-			alreadyVisited.push_back(visitedObj);
+		visitedObject = const_cast<CGObjectInstance *>(visitedObj); // remember the object and wait for return
+		markObjectVisited (visitedObj);
 	}
 }
 
@@ -1098,7 +1091,7 @@ std::vector<const CGObjectInstance *> VCAI::getPossibleDestinations(const CGHero
 	std::vector<const CGObjectInstance *> possibleDestinations;
 	BOOST_FOREACH(const CGObjectInstance *obj, visitableObjs)
 	{
-		if(cb->getPathInfo(obj->visitablePos())->reachable()  &&
+		if(cb->getPathInfo(obj->visitablePos())->reachable() && !obj->wasVisited(playerID) &&
 			(obj->tempOwner != playerID || isWeeklyRevisitable(obj))) //flag or get weekly resources / creatures
 			possibleDestinations.push_back(obj);
 	}
@@ -1113,6 +1106,9 @@ std::vector<const CGObjectInstance *> VCAI::getPossibleDestinations(const CGHero
 			if(!isSafeToVisit(h, obj->visitablePos()))
 				return true;
 
+			if (!shouldVisit(h, obj))
+				return true;
+
 			return false;
 		}),possibleDestinations.end());
 
@@ -1134,7 +1130,7 @@ void VCAI::wander(const CGHeroInstance * h)
 		if(!goVisitObj(obj, h))
 		{
 			BNLOG("Hero %s apparently used all MPs (%d left)\n", h->name % h->movement);
-			alreadyVisited.push_back(obj); //reserve that object - we predict it will be reached soon
+			markObjectVisited(obj); //reserve that object - we predict it will be reached soon
 			setGoal(h, CGoal(VISIT_TILE).sethero(h).settile(obj->visitablePos()));
 			break;
 		}
@@ -1183,6 +1179,15 @@ void VCAI::waitTillFree()
 	status.waitTillFree();
 }
 
+void VCAI::markObjectVisited (const CGObjectInstance *obj)
+{
+	if(dynamic_cast<const CGVisitableOPH *>(obj) || //we may want to wisit it with another hero
+		dynamic_cast<const CGBonusingObject *>(obj) || //or another time
+		(obj->ID == Obj::MONSTER))
+		return;
+	alreadyVisited.push_back(obj);
+}
+
 void VCAI::validateVisitableObjs()
 {
 	std::vector<const CGObjectInstance *> hlp;
@@ -2476,7 +2481,9 @@ void SectorMap::write(crstring fname)
 
 bool isWeeklyRevisitable (const CGObjectInstance * obj)
 { //TODO: allow polling of remaining creatures in dwelling
-	if (dynamic_cast<const CGVisitableOPW *>(obj) || dynamic_cast<const CGDwelling *>(obj)) //ensures future compatibility, unlike IDs
+	if (dynamic_cast<const CGVisitableOPW *>(obj) || //ensures future compatibility, unlike IDs
+		dynamic_cast<const CGDwelling *>(obj) ||
+		dynamic_cast<const CBank *>(obj)) //banks tend to respawn often in mods
 		return true;
 	switch (obj->ID)
 	{
@@ -2486,6 +2493,36 @@ bool isWeeklyRevisitable (const CGObjectInstance * obj)
 	return false;
 }
 
+bool shouldVisit (const CGHeroInstance * h, const CGObjectInstance * obj)
+{
+	if (obj->wasVisited(h))
+		return false;
+	switch (obj->ID)
+	{
+		case Obj::CREATURE_GENERATOR1:
+		{
+			bool canRecruitCreatures = false;
+			const CGDwelling * d = dynamic_cast<const CGDwelling *>(obj);
+			BOOST_FOREACH(auto level, d->creatures)
+			{
+				BOOST_FOREACH(auto c, level.second)
+				{
+					if (h->getSlotFor(c) != -1)
+						canRecruitCreatures = true;
+				}
+			}
+			return canRecruitCreatures;
+		}
+		case Obj::MONOLITH1:
+		case Obj::MONOLITH2:
+		case Obj::MONOLITH3:
+		case Obj::WHIRLPOOL:
+			//TODO: mehcanism for handling monoliths
+			return false;
+	}
+	return true;
+}
+
 int3 SectorMap::firstTileToGet(const CGHeroInstance *h, crint3 dst)
 {
 	int sourceSector = retreiveTile(h->visitablePos()),

+ 3 - 1
AI/VCAI/VCAI.h

@@ -265,6 +265,7 @@ public:
 	void waitTillFree();
 
 	void addVisitableObj(const CGObjectInstance *obj);
+	void markObjectVisited (const CGObjectInstance *obj);
 	//void removeVisitableObj(const CGObjectInstance *obj);
 	void validateVisitableObjs();
 	void retreiveVisitableObjs(std::vector<const CGObjectInstance *> &out, bool includeOwned = false) const;
@@ -292,4 +293,5 @@ bool objWithID(const CGObjectInstance *obj)
 	return obj->ID == id;
 }
 
-bool isWeeklyRevisitable (const CGObjectInstance * obj);
+bool isWeeklyRevisitable (const CGObjectInstance * obj);
+bool shouldVisit (const CGHeroInstance * h, const CGObjectInstance * obj);

+ 42 - 9
lib/CObjectHandler.cpp

@@ -74,6 +74,15 @@ void IObjectInterface::initObj()
 void IObjectInterface::setProperty( ui8 what, ui32 val )
 {}
 
+bool IObjectInterface::wasVisited (ui8 player) const
+{
+	return false;
+}
+bool IObjectInterface::wasVisited (const CGHeroInstance * h) const
+{
+	return false;
+}
+
 void IObjectInterface::postInit()
 {}
 
@@ -86,7 +95,7 @@ void CPlayersVisited::setPropertyDer( ui8 what, ui32 val )
 		players.insert((ui8)val);
 }
 
-bool CPlayersVisited::hasVisited( ui8 player ) const
+bool CPlayersVisited::wasVisited( ui8 player ) const
 {
 	return vstd::contains(players,player);
 }
@@ -2424,6 +2433,11 @@ bool CGTownInstance::hasBuilt(int buildingID) const
 	return vstd::contains(builtBuildings, buildingID);
 }
 
+bool CGVisitableOPH::wasVisited (const CGHeroInstance * h) const
+{
+	return vstd::contains(visitors, h->id);
+}
+
 void CGVisitableOPH::onHeroVisit( const CGHeroInstance * h ) const
 {
 	if(visitors.find(h->id)==visitors.end())
@@ -3479,6 +3493,10 @@ void CGVisitableOPW::newTurn() const
 		cb->setHoverName(id,&ms);
 	}
 }
+bool CGVisitableOPW::wasVisited(ui8 player) const
+{
+	return !visited; //TODO: other players should see object as unvisited
+}
 
 void CGVisitableOPW::onHeroVisit( const CGHeroInstance * h ) const
 {
@@ -4642,7 +4660,7 @@ void CGWitchHut::onHeroVisit( const CGHeroInstance * h ) const
 	InfoWindow iw;
 	iw.soundID = soundBase::gazebo;
 	iw.player = h->getOwner();
-	if(!hasVisited(h->tempOwner))
+	if(!wasVisited(h->tempOwner))
 		cb->setObjProperty(id,10,h->tempOwner);
 
 	if(h->getSecSkillLevel(static_cast<CGHeroInstance::SecondarySkill>(ability))) //you alredy know this skill
@@ -4669,7 +4687,7 @@ void CGWitchHut::onHeroVisit( const CGHeroInstance * h ) const
 const std::string & CGWitchHut::getHoverText() const
 {
 	hoverName = VLC->generaltexth->names[ID];
-	if(hasVisited(cb->getCurrentPlayer())) //TODO: use local player, not current
+	if(wasVisited(cb->getCurrentPlayer())) //TODO: use local player, not current
 	{
 		hoverName += "\n" + VLC->generaltexth->allTexts[356]; // + (learn %s)
 		boost::algorithm::replace_first(hoverName,"%s",VLC->generaltexth->skillName[ability]);
@@ -4680,6 +4698,11 @@ const std::string & CGWitchHut::getHoverText() const
 	return hoverName;
 }
 
+bool CGBonusingObject::wasVisited (const CGHeroInstance * h) const
+{
+	return h->hasBonusFrom(Bonus::OBJECT, ID);
+}
+
 void CGBonusingObject::onHeroVisit( const CGHeroInstance * h ) const
 {
 	bool visited = h->hasBonusFrom(Bonus::OBJECT,ID);
@@ -5302,7 +5325,7 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const
 		return;
 	}
 
-	if(!hasVisited(h->tempOwner))
+	if(!wasVisited(h->tempOwner))
 		cb->setObjProperty(id,10,h->tempOwner);
 
 	InfoWindow iw;
@@ -5357,7 +5380,7 @@ void CGShrine::initObj()
 const std::string & CGShrine::getHoverText() const
 {
 	hoverName = VLC->generaltexth->names[ID];
-	if(hasVisited(cb->getCurrentPlayer())) //TODO: use local player, not current
+	if(wasVisited(cb->getCurrentPlayer())) //TODO: use local player, not current
 	{
 		hoverName += "\n" + VLC->generaltexth->allTexts[355]; // + (learn %s)
 		boost::algorithm::replace_first(hoverName,"%s",VLC->spellh->spells[spell]->name);
@@ -5597,7 +5620,7 @@ const std::string & CGOnceVisitable::getHoverText() const
 {
 	hoverName = VLC->generaltexth->names[ID] + " ";
 
-	hoverName += (hasVisited(cb->getCurrentPlayer())
+	hoverName += (wasVisited(cb->getCurrentPlayer())
 		? (VLC->generaltexth->allTexts[352])  //visited
 		: ( VLC->generaltexth->allTexts[353])); //not visited
 
@@ -5872,6 +5895,11 @@ void CBank::newTurn() const
 			cb->setObjProperty (id, 11, 1); //daycounter++
 	}
 }
+bool CBank::wasVisited (ui8 player) const
+{
+	return !bc;
+}
+
 void CBank::onHeroVisit (const CGHeroInstance * h) const
 {
 	if (bc)
@@ -6162,6 +6190,11 @@ const std::string & CGKeymasterTent::getHoverText() const
 	return hoverName;
 }
 
+bool CGKeymasterTent::wasVisited (ui8 player) const
+{
+	return wasMyColorVisited (player);
+}
+
 void CGKeymasterTent::onHeroVisit( const CGHeroInstance * h ) const
 {
 	InfoWindow iw;
@@ -6492,7 +6525,7 @@ void CGShipyard::onHeroVisit( const CGHeroInstance * h ) const
 
 void CCartographer::onHeroVisit( const CGHeroInstance * h ) const 
 {
-	if (!hasVisited (h->getOwner()) ) //if hero has not visited yet this cartographer
+	if (!wasVisited (h->getOwner()) ) //if hero has not visited yet this cartographer
 	{
 		if (cb->getResource(h->tempOwner, 6) >= 1000) //if he can afford a map
 		{
@@ -6567,7 +6600,7 @@ void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const
 	assert(ts);
 	int team = ts->id;
 	
-	if(!hasVisited(team))
+	if(!wasVisited(team))
 	{
 		iw.text.addTxt(MetaString::ADVOB_TXT, 96);
 		cb->sendAndApply(&iw);
@@ -6597,7 +6630,7 @@ void CGObelisk::initObj()
 const std::string & CGObelisk::getHoverText() const
 {
 	hoverName = VLC->generaltexth->names[ID];
-	if(hasVisited(cb->getCurrentPlayer()))
+	if(wasVisited(cb->getCurrentPlayer()))
 		hoverName += " " + VLC->generaltexth->allTexts[352]; //not visited
 	else
 		hoverName += " " + VLC->generaltexth->allTexts[353]; //visited

+ 11 - 2
lib/CObjectHandler.h

@@ -93,6 +93,9 @@ public:
 	virtual void newTurn() const;
 	virtual void initObj(); //synchr
 	virtual void setProperty(ui8 what, ui32 val);//synchr
+//unified interface, AI helpers
+	virtual bool wasVisited (ui8 player) const;
+	virtual bool wasVisited (const CGHeroInstance * h) const;
 
 	static void preInit(); //called before objs receive their initObj
 	static void postInit();//caleed after objs receive their initObj
@@ -177,6 +180,7 @@ public:
 	//CGObjectInstance(const CGObjectInstance & right);
 	//CGObjectInstance& operator=(const CGObjectInstance & right);
 	virtual const std::string & getHoverText() const;
+
 	//////////////////////////////////////////////////////////////////////////
 	void initObj();
 	void onHeroVisit(const CGHeroInstance * h) const;
@@ -210,7 +214,7 @@ class DLL_LINKAGE CPlayersVisited: public CGObjectInstance
 public:
 	std::set<ui8> players; //players that visited this object
 
-	bool hasVisited(ui8 player) const;
+	bool wasVisited(ui8 player) const;
 	virtual void setPropertyDer( ui8 what, ui32 val );
 
 	template <typename Handler> void serialize(Handler &h, const int version)
@@ -464,6 +468,7 @@ public:
 	void onHeroVisit(const CGHeroInstance * h) const;
 	void onNAHeroVisit(int heroID, bool alreadyVisited) const;
 	void initObj();
+	bool wasVisited (const CGHeroInstance * h) const;
 	void treeSelected(int heroID, int resType, int resVal, expType expVal, ui32 result) const; //handle player's anwer to the Tree of Knowledge dialog
 	void schoolSelected(int heroID, ui32 which) const;
 	void arenaSelected(int heroID, int primSkill) const;
@@ -921,7 +926,7 @@ public:
 	void offerLeavingGuards(const CGHeroInstance *h) const;
 	void endBattle(BattleResult *result, ui8 attackingPlayer) const;
 	void fight(ui32 agreed, const CGHeroInstance *h) const;
-
+	
 	void onHeroVisit(const CGHeroInstance * h) const;
 
 	void flagMine(ui8 player) const;
@@ -941,6 +946,7 @@ public:
 	ui8 visited; //true if object has been visited this week
 
 	void setPropertyDer(ui8 what, ui32 val);//synchr
+	bool wasVisited(ui8 player) const;
 	void onHeroVisit(const CGHeroInstance * h) const;
 	void newTurn() const;
 
@@ -970,6 +976,7 @@ public:
 class DLL_LINKAGE CGBonusingObject : public CGObjectInstance //objects giving bonuses to luck/morale/movement
 {
 public:
+	bool wasVisited (const CGHeroInstance * h) const;
 	void onHeroVisit(const CGHeroInstance * h) const;
 	const std::string & getHoverText() const;
 	void initObj();	
@@ -1047,6 +1054,7 @@ public:
 class DLL_LINKAGE CGKeymasterTent : public CGKeys
 {
 public:
+	bool wasVisited (ui8 player) const;
 	void onHeroVisit(const CGHeroInstance * h) const;
 	const std::string & getHoverText() const;
 
@@ -1132,6 +1140,7 @@ class DLL_LINKAGE CBank : public CArmedInstance
 	void initialize() const;
 	void reset(ui16 var1);
 	void newTurn() const;
+	bool wasVisited (ui8 player) const;
 	virtual void onHeroVisit (const CGHeroInstance * h) const;
 	virtual void fightGuards (const CGHeroInstance *h, ui32 accept) const;
 	virtual void endBattle (const CGHeroInstance *h, const BattleResult *result) const;