Browse Source

Template magic. Implemented method chaining + clone pattern for Goals.

DjWarmonger 12 năm trước cách đây
mục cha
commit
900d7a03f0
4 tập tin đã thay đổi với 240 bổ sung179 xóa
  1. 88 83
      AI/VCAI/Goals.cpp
  2. 118 57
      AI/VCAI/Goals.h
  3. 25 30
      AI/VCAI/VCAI.cpp
  4. 9 9
      AI/VCAI/VCAI.h

+ 88 - 83
AI/VCAI/Goals.cpp

@@ -18,7 +18,7 @@ extern boost::thread_specific_ptr<VCAI> ai;
 using namespace vstd;
 using namespace Goals;
 
-std::string Goals::CGoal::name() const //TODO: virtualize
+std::string Goals::AbstractGoal::name() const //TODO: virtualize
 {
 	switch (goalType)
 	{
@@ -67,7 +67,7 @@ std::string Goals::CGoal::name() const //TODO: virtualize
 	}
 }
 
-#define I_AM_ELEMENTAR return make_shared<Goals::CGoal>(setisElementar(true))
+
 
 TSubgoal Win::whatToDoToAchieve()
 {
@@ -83,11 +83,11 @@ TSubgoal Win::whatToDoToAchieve()
 	switch(cond)
 	{
 	case EVictoryConditionType::ARTIFACT:
-		return make_shared<Goals::CGoal> (Goals::GetArtOfType().setaid(vc.objectId));
+		return sptr (Goals::GetArtOfType(vc.objectId));
 	case EVictoryConditionType::BEATHERO:
-		return make_shared<Goals::CGoal> (Goals::GetObj(vc.obj->id.getNum()));
+		return sptr (Goals::GetObj(vc.obj->id.getNum()));
 	case EVictoryConditionType::BEATMONSTER:
-		return make_shared<Goals::CGoal> (Goals::GetObj(vc.obj->id.getNum()));
+		return sptr (Goals::GetObj(vc.obj->id.getNum()));
 	case EVictoryConditionType::BUILDCITY:
 		//TODO build castle/capitol
 		break;
@@ -99,7 +99,7 @@ TSubgoal Win::whatToDoToAchieve()
 				if(h->visitedTown && !vstd::contains(h->visitedTown->forbiddenBuildings, BuildingID::GRAIL))
 				{
 					const CGTownInstance *t = h->visitedTown;
-					return make_shared<Goals::CGoal> (Goals::BuildThis().setbid(BuildingID::GRAIL).settown(t));
+					return sptr (Goals::BuildThis(BuildingID::GRAIL, t));
 				}
 				else
 				{
@@ -113,7 +113,7 @@ TSubgoal Win::whatToDoToAchieve()
 					boost::sort(towns, isCloser);
 					if(towns.size())
 					{
-						return make_shared<Goals::CGoal> (Goals::VisitTile(towns.front()->visitablePos()).sethero(h));
+						return sptr (Goals::VisitTile(towns.front()->visitablePos()).sethero(h));
 					}
 				}
 			}
@@ -121,25 +121,25 @@ TSubgoal Win::whatToDoToAchieve()
 			int3 grailPos = cb->getGrailPos(ratio);
 			if(ratio > 0.99)
 			{
-				return make_shared<Goals::CGoal> (Goals::DigAtTile().settile(grailPos));
+				return sptr (Goals::DigAtTile(grailPos));
 			} //TODO: use FIND_OBJ
 			else if(const CGObjectInstance * obj = ai->getUnvisitedObj(objWithID<Obj::OBELISK>)) //there are unvisited Obelisks
 			{
-				return make_shared<Goals::CGoal> (Goals::GetObj(obj->id.getNum()));
+				return sptr (Goals::GetObj(obj->id.getNum()));
 			}
 			else
-				return make_shared<Goals::CGoal> (Goals::Explore());
+				return sptr (Goals::Explore());
 		}
 		break;
 	case EVictoryConditionType::CAPTURECITY:
-		return make_shared<Goals::CGoal> (Goals::GetObj(vc.obj->id.getNum()));
+		return sptr (Goals::GetObj(vc.obj->id.getNum()));
 	case EVictoryConditionType::GATHERRESOURCE:
-        return make_shared<Goals::CGoal> (Goals::CollectRes().setresID(static_cast<Res::ERes>(vc.objectId)).setvalue(vc.count));
+        return sptr (Goals::CollectRes(static_cast<Res::ERes>(vc.objectId), vc.count));
 		//TODO mines? piles? marketplace?
 		//save?
 		break;
 	case EVictoryConditionType::GATHERTROOP:
-		return make_shared<Goals::CGoal> (Goals::GatherTroops().setobjid(vc.objectId).setvalue(vc.count));
+		return sptr (Goals::GatherTroops(vc.objectId, vc.count));
 		break;
 	case EVictoryConditionType::TAKEDWELLINGS:
 		break;
@@ -148,11 +148,11 @@ TSubgoal Win::whatToDoToAchieve()
 	case EVictoryConditionType::TRANSPORTITEM:
 		break;
 	case EVictoryConditionType::WINSTANDARD:
-		return make_shared<Goals::CGoal> (Goals::Conquer());
+		return sptr (Goals::Conquer());
 	default:
 		assert(0);
 	}
-	return make_shared<Goals::CGoal> (Goals::INVALID);
+	return sptr (Goals::Invalid());
 }
 
 TSubgoal FindObj::whatToDoToAchieve()
@@ -181,37 +181,40 @@ TSubgoal FindObj::whatToDoToAchieve()
 		}
 	}
 	if (o && isReachable(o))
-		return make_shared<Goals::CGoal> (Goals::GetObj(o->id.getNum()));
+		return sptr (Goals::GetObj(o->id.getNum()));
 	else
-		return make_shared<Goals::CGoal> (Goals::Explore());
+		return sptr (Goals::Explore());
 }
 TSubgoal GetObj::whatToDoToAchieve()
 {
 	const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid));
 	if(!obj)
-		return make_shared<Goals::CGoal> (Goals::Explore());
+		return sptr (Goals::Explore());
 	int3 pos = obj->visitablePos();
-	return make_shared<Goals::CGoal> (Goals::VisitTile(pos));
+	return sptr (Goals::VisitTile(pos));
 }
 
 TSubgoal VisitHero::whatToDoToAchieve()
 {
 	const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid));
 	if(!obj)
-		return make_shared<Goals::CGoal> (Goals::Explore());
+		return sptr (Goals::Explore());
 	int3 pos = obj->visitablePos();
 
 	if (hero && ai->isAccessibleForHero(pos, hero, true) && isSafeToVisit(hero, pos)) //enemy heroes can get reinforcements
-		return make_shared<Goals::CGoal> (settile(pos).setisElementar(true));
-	return make_shared<Goals::CGoal> (Goals::INVALID);
+	{
+		settile(pos).setisElementar(true);
+		return sptr (*this);
+	}
+	return sptr (Goals::Invalid());
 }
 
 TSubgoal GetArtOfType::whatToDoToAchieve()
 {
 	TSubgoal alternativeWay = CGoal::lookForArtSmart(aid); //TODO: use
 	if(alternativeWay->invalid())
-		return make_shared<Goals::CGoal> (Goals::FindObj(Obj::ARTIFACT, aid));
-	return make_shared<Goals::CGoal> (Goals::INVALID);
+		return sptr (Goals::FindObj(Obj::ARTIFACT, aid));
+	return sptr (Goals::Invalid());
 }
 
 TSubgoal ClearWayTo::whatToDoToAchieve()
@@ -220,12 +223,12 @@ TSubgoal ClearWayTo::whatToDoToAchieve()
 	if(!cb->isVisible(tile))
 	{
         logAi->errorStream() << "Clear way should be used with visible tiles!";
-		return make_shared<Goals::CGoal> (Goals::Explore());
+		return sptr (Goals::Explore());
 	}
 
 	HeroPtr h = hero ? hero : ai->primaryHero();
 	if(!h)
-		return make_shared<Goals::CGoal> (Goals::RecruitHero());
+		return sptr (Goals::RecruitHero());
 
 	cb->setSelection(*h);
 
@@ -238,7 +241,7 @@ TSubgoal ClearWayTo::whatToDoToAchieve()
 	//if(isSafeToVisit(h, tileToHit))
 	if(isBlockedBorderGate(tileToHit))
 	{	//FIXME: this way we'll not visit gate and activate quest :?
-		return make_shared<Goals::CGoal> (Goals::FindObj (Obj::KEYMASTER, cb->getTile(tileToHit)->visitableObjects.back()->subID));
+		return sptr (Goals::FindObj (Obj::KEYMASTER, cb->getTile(tileToHit)->visitableObjects.back()->subID));
 	}
 
 	//FIXME: this code shouldn't be necessary
@@ -256,7 +259,7 @@ TSubgoal ClearWayTo::whatToDoToAchieve()
 		throw cannotFulfillGoalException(problem);
 	}
 
-	return make_shared<Goals::CGoal> (Goals::VisitTile(tileToHit).sethero(h));
+	return sptr (Goals::VisitTile(tileToHit).sethero(h));
 	//FIXME:: attempts to visit completely unreachable tile with hero results in stall
 
 	//TODO czy istnieje lepsza droga?
@@ -296,7 +299,7 @@ TSubgoal Explore::whatToDoToAchieve()
 				auto pos = obj->visitablePos();
 				//FIXME: this confition fails if everything but guarded subterranen gate was explored. in this case we should gather army for hero
 				if (isSafeToVisit(hero, pos) && ai->isAccessibleForHero(pos, hero))
-					return make_shared<Goals::CGoal> (Goals::VisitTile(pos).sethero(hero));
+					return sptr (Goals::VisitTile(pos).sethero(hero));
 			}
 		}
 		else
@@ -305,7 +308,7 @@ TSubgoal Explore::whatToDoToAchieve()
 			{
 				auto pos = obj->visitablePos();
 				if (ai->isAccessible (pos)) //TODO: check safety?
-					return make_shared<Goals::CGoal> (Goals::VisitTile(pos).sethero(hero));
+					return sptr (Goals::VisitTile(pos).sethero(hero));
 			}
 		}
 	}
@@ -333,12 +336,12 @@ TSubgoal Explore::whatToDoToAchieve()
 			});
 			if (objs.size())
 			{
-				return make_shared<Goals::CGoal> (Goals::VisitTile(objs.front()->visitablePos()).sethero(hero).setisAbstract(true));
+				return sptr (Goals::VisitTile(objs.front()->visitablePos()).sethero(hero).setisAbstract(true));
 			}
 			else
 				throw cannotFulfillGoalException("Cannot explore - no possible ways found!");
 		}
-		return make_shared<Goals::CGoal> (Goals::VisitTile(t).sethero(hero));
+		return sptr (Goals::VisitTile(t).sethero(hero));
 	}
 
 	auto hs = cb->getHeroesInfo();
@@ -351,7 +354,7 @@ TSubgoal Explore::whatToDoToAchieve()
 	if(hs.empty()) //all heroes are busy. buy new one
 	{
 		if (howManyHeroes < 3  && ai->findTownWithTavern()) //we may want to recruit second hero. TODO: make it smart finally
-			return make_shared<Goals::CGoal> (Goals::RecruitHero());
+			return sptr (Goals::RecruitHero());
 		else //find mobile hero with weakest army
 		{
 			hs = cb->getHeroesInfo();
@@ -362,7 +365,7 @@ TSubgoal Explore::whatToDoToAchieve()
 			if (hs.empty())
 			{
 				if (howManyHeroes < GameConstants::MAX_HEROES_PER_PLAYER)
-					return make_shared<Goals::CGoal> (Goals::RecruitHero());
+					return sptr (Goals::RecruitHero());
 				else
 					throw cannotFulfillGoalException("No heroes with remaining MPs for exploring!\n");
 			}
@@ -372,27 +375,27 @@ TSubgoal Explore::whatToDoToAchieve()
 
 	const CGHeroInstance *h = hs.front();
 
-	return make_shared<Goals::CGoal> (sethero(h).setisAbstract(true));
+	return sptr (sethero(h).setisAbstract(true));
 
-	I_AM_ELEMENTAR; //FIXME: how can this be called?
+	return iAmElementar(); //FIXME: how can this be called?
 };
 
 TSubgoal RecruitHero::whatToDoToAchieve()
 {
 	const CGTownInstance *t = ai->findTownWithTavern();
 	if(!t)
-		return make_shared<Goals::CGoal> (Goals::BuildThis().setbid(BuildingID::TAVERN));
+		return sptr (Goals::BuildThis(BuildingID::TAVERN));
 
 	if(cb->getResourceAmount(Res::GOLD) < HERO_GOLD_COST)
-		return make_shared<Goals::CGoal> (Goals::CollectRes().setresID(Res::GOLD).setvalue(HERO_GOLD_COST));
+		return sptr (Goals::CollectRes(Res::GOLD, HERO_GOLD_COST));
 
-	I_AM_ELEMENTAR;
+	return iAmElementar();
 }
 
 TSubgoal VisitTile::whatToDoToAchieve()
 {
 	if(!cb->isVisible(tile))
-		return make_shared<Goals::CGoal> (Goals::Explore());
+		return sptr (Goals::Explore());
 
 	if(hero && !ai->isAccessibleForHero(tile, hero))
 		hero = nullptr;
@@ -401,7 +404,7 @@ TSubgoal VisitTile::whatToDoToAchieve()
 	{
 		if(cb->getHeroesInfo().empty())
 		{
-			return make_shared<Goals::CGoal> (Goals::RecruitHero());
+			return sptr (Goals::RecruitHero());
 		}
 
 		for(const CGHeroInstance *h : cb->getHeroesInfo())
@@ -417,15 +420,15 @@ TSubgoal VisitTile::whatToDoToAchieve()
 	if(hero)
 	{
 		if(isSafeToVisit(hero, tile))
-			return make_shared<Goals::CGoal>(setisElementar(true));
+			return sptr (setisElementar(true));
 		else
 		{
-			return make_shared<Goals::CGoal>(Goals::GatherArmy().sethero(hero).setvalue(evaluateDanger(tile, *hero) * SAFE_ATTACK_CONSTANT)); //TODO: should it be abstract?
+			return sptr (Goals::GatherArmy(evaluateDanger(tile, *hero) * SAFE_ATTACK_CONSTANT).sethero(hero));
 		}
 	}
 	else	//inaccessible for all heroes
 	{
-		return make_shared<Goals::CGoal>(Goals::ClearWayTo(tile));
+		return sptr (Goals::ClearWayTo(tile));
 	}
 }
 
@@ -435,10 +438,11 @@ TSubgoal DigAtTile::whatToDoToAchieve()
 	if(firstObj && firstObj->ID == Obj::HERO && firstObj->tempOwner == ai->playerID) //we have hero at dest
 	{
 		const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(firstObj);
-		return make_shared<Goals::CGoal> (sethero(h).setisElementar(true));
+		sethero(h).setisElementar(true);
+		return sptr (*this);
 	}
 
-	return make_shared<Goals::CGoal>(Goals::VisitTile(tile));
+	return sptr (Goals::VisitTile(tile));
 }
 
 TSubgoal BuildThis::whatToDoToAchieve()
@@ -446,7 +450,7 @@ TSubgoal BuildThis::whatToDoToAchieve()
 	//TODO check res
 	//look for town
 	//prerequisites?
-	I_AM_ELEMENTAR;
+	return iAmElementar();
 }
 
 TSubgoal CollectRes::whatToDoToAchieve()
@@ -482,7 +486,7 @@ TSubgoal CollectRes::whatToDoToAchieve()
 		for(const CGTownInstance *t : cb->getTownsInfo())
 		{
 			if(cb->canBuildStructure(t, BuildingID::MARKETPLACE) == EBuildingState::ALLOWED)
-				return make_shared<Goals::CGoal>(Goals::BuildThis().settown(t).setbid(BuildingID::MARKETPLACE));
+				return sptr (Goals::BuildThis(BuildingID::MARKETPLACE, t));
 		}
 	}
 	else
@@ -503,12 +507,17 @@ TSubgoal CollectRes::whatToDoToAchieve()
 		{
 			auto backObj = backOrNull(cb->getVisitableObjs(m->o->visitablePos())); //it'll be a hero if we have one there; otherwise marketplace
 			assert(backObj);
-			if(backObj->tempOwner != ai->playerID)
-				return make_shared<Goals::CGoal>(Goals::GetObj(m->o->id.getNum()));
-			return make_shared<Goals::CGoal>(setobjid(m->o->id.getNum()).setisElementar(true));
+			if (backObj->tempOwner != ai->playerID)
+			{
+				return sptr (Goals::GetObj(m->o->id.getNum()));
+			}
+			else
+			{
+				return sptr (Goals::GetObj(m->o->id.getNum()).setisElementar(true));
+			}
 		}
 	}
-	return make_shared<Goals::CGoal>(Goals::Invalid()); //FIXME: unused?
+	return sptr (Goals::Invalid()); //FIXME: unused?
 }
 
 TSubgoal GatherTroops::whatToDoToAchieve()
@@ -534,7 +543,7 @@ TSubgoal GatherTroops::whatToDoToAchieve()
 			}
 			else
 			{
-				return make_shared<Goals::CGoal>(Goals::BuildThis().settown(t).setbid(bid));
+				return sptr (Goals::BuildThis(bid, t));
 			}
 		}
 	}
@@ -559,10 +568,10 @@ TSubgoal GatherTroops::whatToDoToAchieve()
 	if (dwellings.size())
 	{
 		boost::sort(dwellings, isCloser);
-		return make_shared<Goals::CGoal>(Goals::GetObj(dwellings.front()->id.getNum()));
+		return sptr (Goals::GetObj(dwellings.front()->id.getNum()));
 	}
 	else
-		return make_shared<Goals::CGoal>(Goals::Explore());
+		return sptr (Goals::Explore());
 	//TODO: exchange troops between heroes
 }
 
@@ -578,7 +587,7 @@ TSubgoal Conquer::whatToDoToAchieve()
 	if(hs.empty()) //all heroes are busy. buy new one
 	{
 		if (howManyHeroes < 3  && ai->findTownWithTavern()) //we may want to recruit second hero. TODO: make it smart finally
-			return make_shared<Goals::CGoal>(Goals::RecruitHero());
+			return sptr (Goals::RecruitHero());
 		else //find mobile hero with weakest army
 		{
 			hs = cb->getHeroesInfo();
@@ -589,7 +598,7 @@ TSubgoal Conquer::whatToDoToAchieve()
 			if (hs.empty())
 			{
 				if (howManyHeroes < GameConstants::MAX_HEROES_PER_PLAYER)
-					return make_shared<Goals::CGoal>(Goals::RecruitHero());
+					return sptr (Goals::RecruitHero());
 				else
 					throw cannotFulfillGoalException("No heroes with remaining MPs for exploring!\n");
 			}
@@ -618,7 +627,7 @@ TSubgoal Conquer::whatToDoToAchieve()
 	}
 
 	if(objs.empty())
-		return make_shared<Goals::CGoal>(Goals::Explore()); //we need to find an enemy
+		return sptr (Goals::Explore()); //we need to find an enemy
 
 	erase_if(objs,  [&](const CGObjectInstance *obj)
 	{
@@ -626,7 +635,7 @@ TSubgoal Conquer::whatToDoToAchieve()
 	});
 
 	if(objs.empty())
-		I_AM_ELEMENTAR;
+		return iAmElementar();
 
 	boost::sort(objs, isCloser);
 	for(const CGObjectInstance *obj : objs)
@@ -636,25 +645,24 @@ TSubgoal Conquer::whatToDoToAchieve()
 			ai->reserveObject(h, obj); //no one else will capture same object until we fail
 
 			if (obj->ID == Obj::HERO)
-				return make_shared<Goals::CGoal>(
-					Goals::VisitHero().sethero(h).setobjid(obj->id.getNum()).setisAbstract(true));
+				return sptr (Goals::VisitHero(obj->id.getNum()).sethero(h).setisAbstract(true));
 					//track enemy hero
 			else
-				return make_shared<Goals::CGoal>(Goals::VisitTile(obj->visitablePos()).sethero(h));
+				return sptr (Goals::VisitTile(obj->visitablePos()).sethero(h));
 		}
 	}
 
-	return make_shared<Goals::CGoal>(Goals::Explore()); //enemy is inaccessible
+	return sptr (Goals::Explore()); //enemy is inaccessible
 }
 
 TSubgoal Build::whatToDoToAchieve()
 {
-	I_AM_ELEMENTAR;
+	return iAmElementar();
 }
 
 TSubgoal Invalid::whatToDoToAchieve()
 {
-	I_AM_ELEMENTAR;
+	return iAmElementar();
 }
 
 TSubgoal GatherArmy::whatToDoToAchieve()
@@ -681,8 +689,7 @@ TSubgoal GatherArmy::whatToDoToAchieve()
 	if(townsReachable.size()) //try towns first
 	{
 		boost::sort(townsReachable, compareReinforcements);
-		return make_shared<Goals::CGoal>(
-			Goals::VisitTile(townsReachable.back()->visitablePos()).sethero(hero));
+		return sptr (Goals::VisitTile(townsReachable.back()->visitablePos()).sethero(hero));
 	}
 	else
 	{
@@ -705,13 +712,11 @@ TSubgoal GatherArmy::whatToDoToAchieve()
 				secondaryPath = cb->getPathInfo(hero->visitablePos())->turns;
 
 				if (primaryPath < secondaryPath)
-					return make_shared<Goals::CGoal>(
-						Goals::VisitHero().setisAbstract(true).setobjid(h->id.getNum()).sethero(hero));
+					return sptr (Goals::VisitHero(h->id.getNum()).setisAbstract(true).sethero(hero));
 						//go to the other hero if we are faster
 				else
-					return make_shared<Goals::CGoal>(
-						Goals::VisitHero().setisAbstract(true).setobjid(hero->id.getNum()).sethero(h))
-						; //let the other hero come to us
+					return sptr (Goals::VisitHero(h->id.getNum()).setisAbstract(true).sethero(h));
+						//let the other hero come to us
 			}
 		}
 
@@ -747,7 +752,7 @@ TSubgoal GatherArmy::whatToDoToAchieve()
 			return true;
 		});
 		if(objs.empty()) //no possible objects, we did eveyrthing already
-			return make_shared<Goals::CGoal>(Goals::Explore().sethero(hero));
+			return sptr (Goals::Explore(hero));
 		//TODO: check if we can recruit any creatures there, evaluate army
 		else
 		{
@@ -767,34 +772,34 @@ TSubgoal GatherArmy::whatToDoToAchieve()
 					}
 				}
 				if (h && isSafeToVisit(h, pos) && ai->isAccessibleForHero(pos, h))
-					return make_shared<Goals::CGoal>(Goals::VisitTile(pos).sethero(h));
+					return sptr (Goals::VisitTile(pos).sethero(h));
 			}
 		}
 	}
 
-	return make_shared<Goals::CGoal>(Goals::Explore().sethero(hero)); //find dwelling. use current hero to prevent him from doing nothing.
+	return sptr (Goals::Explore(hero)); //find dwelling. use current hero to prevent him from doing nothing.
 }
 
-TSubgoal CGoal::whatToDoToAchieve()
+TSubgoal AbstractGoal::whatToDoToAchieve()
 {
     logAi->debugStream() << boost::format("Decomposing goal of type %s") % name();
-	return make_shared<Goals::CGoal>(Goals::Explore());
+	return sptr (Goals::Explore());
 }
 
-TSubgoal CGoal::goVisitOrLookFor(const CGObjectInstance *obj)
+TSubgoal AbstractGoal::goVisitOrLookFor(const CGObjectInstance *obj)
 {
 	if(obj)
-		return make_shared<Goals::CGoal>(Goals::GetObj(obj->id.getNum()));
+		return sptr (Goals::GetObj(obj->id.getNum()));
 	else
-		return make_shared<Goals::CGoal>(Goals::Explore());
+		return sptr (Goals::Explore());
 }
 
-TSubgoal CGoal::lookForArtSmart(int aid)
+TSubgoal AbstractGoal::lookForArtSmart(int aid)
 {
-	return make_shared<Goals::CGoal>(Goals::Invalid());
+	return sptr (Goals::Invalid());
 }
 
-bool CGoal::invalid() const
+bool AbstractGoal::invalid() const
 {
 	return goalType == INVALID;
 }

+ 118 - 57
AI/VCAI/Goals.h

@@ -20,8 +20,8 @@ struct HeroPtr;
 
 namespace Goals
 {
-	struct CGoal;
-	typedef std::shared_ptr<Goals::CGoal> TSubgoal;
+	struct AbstractGoal;
+	typedef std::shared_ptr<Goals::AbstractGoal> TSubgoal;
 
 	enum EGoals
 {
@@ -48,24 +48,32 @@ namespace Goals
 	DIG_AT_TILE //elementar with hero on tile
 };
 
-#define SETTER(type, field) CGoal &set ## field(const type &rhs) { field = rhs; return *this; }
+	//method chaining + clone pattern
+#define VSETTER(type, field) AbstractGoal & set ## field(const type &rhs) { field = rhs; return *this; };
+#define OSETTER(type, field) CGoal<T> & set ## field(const type &rhs) { field = rhs; return *this; };
+
 #if 0
 	#define SETTER
 #endif // _DEBUG
 
 enum {LOW_PR = -1};
 
-struct CGoal
+class AbstractGoal
 {
-	EGoals goalType;
-	bool isElementar; SETTER(bool, isElementar)
-	bool isAbstract; SETTER(bool, isAbstract) //allows to remember abstract goals
-	int priority; SETTER(bool, priority)
-	std::string name() const;
-
-	virtual TSubgoal whatToDoToAchieve();
+public:
+	bool isElementar; VSETTER(bool, isElementar)
+	bool isAbstract; VSETTER(bool, isAbstract)
+	int priority; VSETTER(bool, priority)
+	int value; VSETTER(int, value)
+	int resID; VSETTER(int, resID)
+	int objid; VSETTER(int, objid)
+	int aid; VSETTER(int, aid)
+	int3 tile; VSETTER(int3, tile)
+	HeroPtr hero; VSETTER(HeroPtr, hero)
+	const CGTownInstance *town; VSETTER(CGTownInstance *, town)
+	int bid; VSETTER(int, bid)
 
-	CGoal(EGoals goal = INVALID) : goalType(goal)
+	AbstractGoal (EGoals goal = INVALID) : goalType (goal)
 	{
 		priority = 0;
 		isElementar = false;
@@ -77,22 +85,18 @@ struct CGoal
 		town = nullptr;
 	}
 
+	EGoals goalType;
+	std::string name() const;
+
 	bool invalid() const;
 
 	static TSubgoal goVisitOrLookFor(const CGObjectInstance *obj); //if obj is nullptr, then we'll explore
 	static TSubgoal lookForArtSmart(int aid); //checks non-standard ways of obtaining art (merchants, quests, etc.)
 	static TSubgoal tryRecruitHero();
 
-	int value; SETTER(int, value)
-	int resID; SETTER(int, resID)
-	int objid; SETTER(int, objid)
-	int aid; SETTER(int, aid)
-	int3 tile; SETTER(int3, tile)
-	HeroPtr hero; SETTER(HeroPtr, hero)
-	const CGTownInstance *town; SETTER(CGTownInstance *, town)
-	int bid; SETTER(int, bid)
+	virtual TSubgoal whatToDoToAchieve();
 
-	bool operator== (CGoal &g)
+	bool operator== (AbstractGoal &g) //TODO: virtualize - comparison returns true only for same subclasses
 	{
 		switch (goalType)
 		{
@@ -110,131 +114,188 @@ struct CGoal
 	}
 };
 
-class Invalid : public CGoal
+template <typename T = CGoal> class CGoal : public AbstractGoal
+{
+public:
+	CGoal<T> (EGoals goal = INVALID) : AbstractGoal (goal)
+	{
+		priority = 0;
+		isElementar = false;
+		isAbstract = false;
+		value = 0;
+		aid = -1;
+		resID = -1;
+		tile = int3(-1, -1, -1);
+		town = nullptr;
+	}
+	OSETTER(bool, isElementar)
+	OSETTER(bool, isAbstract) //FIXME: find out why this setter does not compile?
+	OSETTER(bool, priority)
+	OSETTER(int, value)
+	OSETTER(int, resID)
+	OSETTER(int, objid)
+	OSETTER(int, aid)
+	OSETTER(int3, tile)
+	OSETTER(HeroPtr, hero)
+	OSETTER(CGTownInstance *, town)
+	OSETTER(int, bid)
+	shared_ptr<CGoal<T>> iAmElementar()
+	{
+		return make_shared<CGoal<T>> (setisElementar(true));
+	}
+};
+
+//There seems to be some ambiguity on these two, template function keeps form consitent
+template <typename T> shared_ptr<CGoal<T>> sptr(CGoal<T>& tmp)
+{
+	return make_shared<CGoal<T>> (tmp);
+}
+template <typename T> shared_ptr<CGoal<T>> sptr(T& obj)
+{
+	return make_shared<CGoal<T>> (obj);
+}
+
+class Invalid : public CGoal<Invalid>
 {
 	public:
 	Invalid() : CGoal (Goals::INVALID){};
 	TSubgoal whatToDoToAchieve() override;
 };
-class Win : public CGoal
+class Win : public CGoal<Win>
 {
 	public:
 	Win() : CGoal (Goals::WIN){};
 	TSubgoal whatToDoToAchieve() override;
 };
-class NotLose : public CGoal
+class NotLose : public CGoal<NotLose>
 {
 	public:
 	NotLose() : CGoal (Goals::DO_NOT_LOSE){};
 	TSubgoal whatToDoToAchieve() override;
 };
-class Conquer : public CGoal
+class Conquer : public CGoal<Conquer>
 {
 	public:
 	Conquer() : CGoal (Goals::CONQUER){};
 	TSubgoal whatToDoToAchieve() override;
 };
-class Build : public CGoal
+class Build : public CGoal<Build>
 {
 	public:
 	Build() : CGoal (Goals::BUILD){};
 	TSubgoal whatToDoToAchieve() override;
 };
-class Explore : public CGoal
+class Explore : public CGoal<Explore>
 {
 	public:
 	Explore() : CGoal (Goals::EXPLORE){};
+	Explore(HeroPtr h) : CGoal (Goals::EXPLORE){hero = h;};
 	TSubgoal whatToDoToAchieve() override;
 };
-class GatherArmy : public CGoal
+class GatherArmy : public CGoal<GatherArmy>
 {
-	public:
+private:
 	GatherArmy() : CGoal (Goals::GATHER_ARMY){};
+public:
+	GatherArmy(int val) : CGoal (Goals::GATHER_ARMY){value = val;};
 	TSubgoal whatToDoToAchieve() override;
 };
-class BoostHero : public CGoal
+class BoostHero : public CGoal<BoostHero>
 {
 	public:
 	BoostHero() : CGoal (Goals::INVALID){}; //TODO
 	TSubgoal whatToDoToAchieve() override;
 };
-class RecruitHero : public CGoal
+class RecruitHero : public CGoal<RecruitHero>
 {
 	public:
 	RecruitHero() : CGoal (Goals::RECRUIT_HERO){};
 	TSubgoal whatToDoToAchieve() override;
 };
-class BuildThis : public CGoal
+class BuildThis : public CGoal<BuildThis>
 {
-	public:
+private:
 	BuildThis() : CGoal (Goals::BUILD_STRUCTURE){};
+public:
+	BuildThis(BuildingID Bid, const CGTownInstance *tid) : CGoal (Goals::BUILD_STRUCTURE) {bid = Bid; town = tid;};
+	BuildThis(BuildingID Bid) : CGoal (Goals::BUILD_STRUCTURE) {bid = Bid;};
 	TSubgoal whatToDoToAchieve() override;
 };
-class CollectRes : public CGoal
+class CollectRes : public CGoal<CollectRes>
 {
-	public:
+private:	
 	CollectRes() : CGoal (Goals::COLLECT_RES){};
+public:
+	CollectRes(int rid, int val) : CGoal (Goals::COLLECT_RES) {resID = rid; value = val;};
 	TSubgoal whatToDoToAchieve() override;
 };
-class GatherTroops : public CGoal
+class GatherTroops : public CGoal<GatherTroops>
 {
-	public:
+private:
 	GatherTroops() : CGoal (Goals::GATHER_TROOPS){};
+public:
+	GatherTroops(int type, int val) : CGoal (Goals::GATHER_TROOPS){objid = type; value = val;};
 	TSubgoal whatToDoToAchieve() override;
 };
-class GetObj : public CGoal
+class GetObj : public CGoal<GetObj>
 {
 	private:
 		GetObj() {}; // empty constructor not allowed
 	public:
-		GetObj(const int Objid) : CGoal(Goals::GET_OBJ) {setobjid(Objid);};
+		GetObj(int Objid) : CGoal(Goals::GET_OBJ) {objid = Objid;};
 	TSubgoal whatToDoToAchieve() override;
 };
-class FindObj : public CGoal
+class FindObj : public CGoal<FindObj>
 {
 	private:
 		FindObj() {}; // empty constructor not allowed
 	public:
-	FindObj(int ID) : CGoal(Goals::FIND_OBJ) {setobjid(ID);};
-	FindObj(int ID, int subID) : CGoal(Goals::FIND_OBJ) {setobjid(ID).setresID(subID);};
+	FindObj(int ID) : CGoal(Goals::FIND_OBJ) {objid = ID;};
+	FindObj(int ID, int subID) : CGoal(Goals::FIND_OBJ) {objid = ID; resID = subID;};
 	TSubgoal whatToDoToAchieve() override;
 };
-class VisitHero : public CGoal
+class VisitHero : public CGoal<VisitHero>
 {
-	public:
+private:
 	VisitHero() : CGoal (Goals::VISIT_HERO){};
+public:
+	VisitHero(int hid) : CGoal (Goals::VISIT_HERO){objid = hid;};
 	TSubgoal whatToDoToAchieve() override;
 };
-class GetArtOfType : public CGoal
+class GetArtOfType : public CGoal<GetArtOfType>
 {
-	public:
+private:
 	GetArtOfType() : CGoal (Goals::GET_ART_TYPE){};
+public:
+	GetArtOfType(int type) : CGoal (Goals::GET_ART_TYPE){aid = type;};
 	TSubgoal whatToDoToAchieve() override;
 };
-class VisitTile : public CGoal
+class VisitTile : public CGoal<VisitTile>
 	//tile, in conjunction with hero elementar; assumes tile is reachable
 {
-	private:
-		VisitTile() {}; // empty constructor not allowed
-	public:
-		VisitTile(int3 Tile) : CGoal (Goals::VISIT_TILE) {settile(Tile);};
+private:
+	VisitTile() {}; // empty constructor not allowed
+public:
+	VisitTile(int3 Tile) : CGoal (Goals::VISIT_TILE) {tile = Tile;};
 	TSubgoal whatToDoToAchieve() override;
 }; 
-class ClearWayTo : public CGoal
+class ClearWayTo : public CGoal<ClearWayTo>
 {
-	public:
-	ClearWayTo(int3 Tile) : CGoal (Goals::CLEAR_WAY_TO) {settile(Tile);};
+public:
+	ClearWayTo(int3 Tile) : CGoal (Goals::CLEAR_WAY_TO) {tile = Tile;};
 	TSubgoal whatToDoToAchieve() override;
 };
-class DigAtTile : public CGoal
+class DigAtTile : public CGoal<DigAtTile>
 	//elementar with hero on tile
 {
-	public:
+private:
 	DigAtTile() : CGoal (Goals::DIG_AT_TILE){};
+public:
+	DigAtTile(int3 Tile) : CGoal (Goals::DIG_AT_TILE) {tile = Tile;};
 	TSubgoal whatToDoToAchieve() override;
 };
 
-class CIssueCommand : CGoal
+class CIssueCommand : public CGoal<CIssueCommand>
 {
 	std::function<bool()> command;
 

+ 25 - 30
AI/VCAI/VCAI.cpp

@@ -257,7 +257,7 @@ void VCAI::heroVisit(const CGHeroInstance *visitor, const CGObjectInstance *visi
 		markObjectVisited (visitedObj);
 		erase_if_present(reservedObjs, visitedObj); //unreserve objects
 		erase_if_present(reservedHeroesMap[visitor], visitedObj);
-		completeGoal (Goals::CGoal(Goals::GET_OBJ).sethero(visitor)); //we don't need to visit in anymore
+		completeGoal (Goals::GetObj(visitedObj->id.getNum()).sethero(visitor)); //we don't need to visit in anymore
 	}
 
 	status.heroVisit(visitedObj, start);
@@ -311,8 +311,8 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q
 		else if (canGetArmy (secondHero, firstHero))
 			pickBestCreatures (secondHero, firstHero);
 
-		completeGoal(Goals::VisitHero().sethero(firstHero)); //TODO: what if we were visited by other hero in the meantime?
-		completeGoal(Goals::VisitHero().sethero(secondHero));
+		completeGoal(Goals::AbstractGoal(Goals::VISIT_HERO).sethero(firstHero)); //TODO: what if we were visited by other hero in the meantime?
+		completeGoal(Goals::AbstractGoal(Goals::VISIT_HERO).sethero(secondHero));
 		//TODO: exchange artifacts
 
 		answerQuery(query, 0);
@@ -667,7 +667,7 @@ void VCAI::makeTurn()
 				ui64 averageDanger = totalDanger / std::max(dangerousObjects, 1);
 				if (dangerousObjects && averageDanger > h->getHeroStrength())
 				{
-					setGoal (h, Goals::GatherArmy().sethero(h).setvalue(averageDanger * SAFE_ATTACK_CONSTANT).setisAbstract(true));
+					setGoal (h, Goals::GatherArmy(averageDanger * SAFE_ATTACK_CONSTANT).sethero(h).setisAbstract(true));
 				}
 			}
 		}
@@ -723,10 +723,10 @@ void VCAI::makeTurnInternal()
 		//finally, continue our abstract long-term goals
 
 		//heroes tend to die in the process and loose their goals, unsafe to iterate it
-		std::vector<std::pair<HeroPtr, Goals::CGoal> > safeCopy;
+		std::vector<std::pair<HeroPtr, Goals::AbstractGoal> > safeCopy;
 		boost::copy(lockedHeroes, std::back_inserter(safeCopy));
 
-		typedef std::pair<HeroPtr, Goals::CGoal> TItrType;
+		typedef std::pair<HeroPtr, Goals::AbstractGoal> TItrType;
 
 		auto lockedHeroesSorter = [](TItrType h1, TItrType h2) -> bool
 		{
@@ -1213,12 +1213,12 @@ void VCAI::wander(HeroPtr h)
 	}
 }
 
-void VCAI::setGoal(HeroPtr h, const Goals::CGoal goal)
+void VCAI::setGoal(HeroPtr h, const Goals::AbstractGoal &goal)
 { //TODO: check for presence?
-	if (goal.goalType == Goals::INVALID)
+	if (goal.invalid())
 		erase_if_present(lockedHeroes, h);
 	else
-		lockedHeroes[h] = Goals::CGoal(goal).setisElementar(false); //always evaluate goals before realizing
+		lockedHeroes[h] = Goals::AbstractGoal(goal).setisElementar(false); //always evaluate goals before realizing
 }
 
 void VCAI::setGoal(HeroPtr h, Goals::EGoals goalType)
@@ -1226,10 +1226,10 @@ void VCAI::setGoal(HeroPtr h, Goals::EGoals goalType)
 	if (goalType == Goals::INVALID)
 		erase_if_present(lockedHeroes, h);
 	else
-		lockedHeroes[h] = Goals::CGoal(goalType).setisElementar(false); //always evaluate goals before realizing;
+		lockedHeroes[h] = Goals::AbstractGoal(goalType).setisElementar(false); //always evaluate goals before realizing;
 }
 
-void VCAI::completeGoal (const Goals::CGoal goal)
+void VCAI::completeGoal (const Goals::AbstractGoal &goal)
 {
 	if (const CGHeroInstance * h = goal.hero.get(true))
 	{
@@ -1425,7 +1425,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 		{
             logAi->errorStream() << "Hero " << h->name << " cannot reach " << dst;
 			//setGoal(h, INVALID);
-			completeGoal (Goals::CGoal(Goals::VISIT_TILE).sethero(h));
+			completeGoal (Goals::AbstractGoal(Goals::VISIT_TILE).sethero(h));
 			cb->recalculatePaths();
 			throw std::runtime_error("Wrong move order!");
 		}
@@ -1617,7 +1617,7 @@ void VCAI::tryRealize(Goals::Invalid g)
 	throw cannotFulfillGoalException("I don't know how to fulfill this!");
 }
 
-void VCAI::tryRealize(Goals::CGoal g)
+void VCAI::tryRealize(Goals::AbstractGoal g)
 {
     logAi->debugStream() << boost::format("Attempting realizing goal with code %s") % g.name();
 	throw cannotFulfillGoalException("Unknown type of goal !");
@@ -1673,7 +1673,7 @@ void VCAI::endTurn()
     logAi->debugStream() << "Player " << static_cast<int>(playerID.getNum()) << " ended turn";
 }
 
-bool VCAI::fulfillsGoal (Goals::CGoal &goal, Goals::CGoal &mainGoal)
+bool VCAI::fulfillsGoal (Goals::AbstractGoal &goal, Goals::AbstractGoal &mainGoal)
 {
 	if (mainGoal.goalType == Goals::GET_OBJ && goal.goalType == Goals::VISIT_TILE) //deduce that GET_OBJ was completed by visiting object's tile
 	{ //TODO: more universal mechanism
@@ -1682,26 +1682,21 @@ bool VCAI::fulfillsGoal (Goals::CGoal &goal, Goals::CGoal &mainGoal)
 	}
 	return false;
 }
-bool VCAI::fulfillsGoal (Goals::CGoal &goal, const Goals::CGoal &mainGoal)
+bool VCAI::fulfillsGoal (Goals::AbstractGoal &goal, const Goals::AbstractGoal &mainGoal)
 {
-	if (mainGoal.goalType == Goals::GET_OBJ && goal.goalType == Goals::VISIT_TILE) //deduce that GET_OBJ was completed by visiting object's tile
-	{ //TODO: more universal mechanism
-		if (cb->getObj(ObjectInstanceID(mainGoal.objid))->visitablePos() == goal.tile)
-			return true;
-	}
-	return false;
+	return fulfillsGoal (goal, const_cast<Goals::AbstractGoal&>(mainGoal));
 }
 
-void VCAI::striveToGoal(const Goals::CGoal &ultimateGoal)
+void VCAI::striveToGoal(const Goals::AbstractGoal &ultimateGoal)
 {
 	if (ultimateGoal.invalid())
 		return;
 
-	std::shared_ptr<Goals::CGoal> abstractGoal = make_shared<Goals::CGoal>(Goals::Invalid());
+	std::shared_ptr<Goals::AbstractGoal> abstractGoal = make_shared<Goals::AbstractGoal>(Goals::Invalid());
 
 	while(1)
 	{
-		std::shared_ptr<Goals::CGoal> goal = make_shared<Goals::CGoal>(ultimateGoal);
+		std::shared_ptr<Goals::AbstractGoal> goal = make_shared<Goals::AbstractGoal>(ultimateGoal); //FIXME: preserve subclass of goal
         logAi->debugStream() << boost::format("Striving to goal of type %s") % ultimateGoal.name();
 		int maxGoals = 100; //preventing deadlock for mutually dependent goals, FIXME: do not try to realize goal when loop didn't suceed
 		while(!goal->isElementar && !goal->isAbstract && maxGoals)
@@ -1779,7 +1774,7 @@ void VCAI::striveToGoal(const Goals::CGoal &ultimateGoal)
 	{
 		while (1)
 		{
-			std::shared_ptr<Goals::CGoal> goal(abstractGoal);
+			std::shared_ptr<Goals::AbstractGoal> goal(abstractGoal);
 			goal->setisAbstract(false);
 			int maxGoals = 50;
 			while (!goal->isElementar && maxGoals) //find elementar goal and fulfill it
@@ -1852,7 +1847,7 @@ void VCAI::striveToQuest (const QuestInfo &q)
 				}
 				for (auto art : q.quest->m5arts)
 				{
-					striveToGoal (Goals::GetArtOfType().setaid(art)); //TODO: transport?
+					striveToGoal (Goals::GetArtOfType(art)); //TODO: transport?
 				}
 				break;
 			}
@@ -1883,7 +1878,7 @@ void VCAI::striveToQuest (const QuestInfo &q)
 				}
 				for (auto creature : q.quest->m6creatures)
 				{
-					striveToGoal (Goals::GatherTroops().setobjid(creature.type->idNumber).setvalue(creature.count));
+					striveToGoal (Goals::GatherTroops(creature.type->idNumber, creature.count));
 				}
 				//TODO: exchange armies... oh my
 				//BNLOG ("Don't know how to recruit %d of %s\n", (int)(creature.count) % creature.type->namePl);
@@ -1902,7 +1897,7 @@ void VCAI::striveToQuest (const QuestInfo &q)
 						for (int i = 0; i < q.quest->m7resources.size(); ++i)
 						{
 							if (q.quest->m7resources[i])
-								striveToGoal (Goals::CollectRes().setresID(i).setvalue(q.quest->m7resources[i]));
+								striveToGoal (Goals::CollectRes(i, q.quest->m7resources[i]));
 						}
 					}
 				}
@@ -2123,7 +2118,7 @@ void VCAI::checkHeroArmy (HeroPtr h)
 	if (it != lockedHeroes.end())
 	{
 		if (it->second.goalType == Goals::GATHER_ARMY && it->second.value <= h->getArmyStrength())
-			completeGoal(Goals::GatherArmy().sethero(h));
+			completeGoal(Goals::GatherArmy(it->second.value).sethero(h));
 	}
 }
 
@@ -2630,7 +2625,7 @@ int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst)
 		if(!preds[dst])
 		{
 			write("test.txt");
-			ai->completeGoal (Goals::Explore().sethero(h)); //if we can't find the way, seemingly all tiles were explored
+			ai->completeGoal (Goals::Explore(h)); //if we can't find the way, seemingly all tiles were explored
 			//TODO: more organized way?
             throw cannotFulfillGoalException(boost::str(boost::format("Cannot find connection between sectors %d and %d") % src->id % dst->id));
 		}

+ 9 - 9
AI/VCAI/VCAI.h

@@ -128,7 +128,7 @@ public:
 	//std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs
 	std::map<HeroPtr, std::vector<const CGTownInstance *> > townVisitsThisWeek;
 
-	std::map<HeroPtr, Goals::CGoal> lockedHeroes; //TODO: allow non-elementar objectives
+	std::map<HeroPtr, Goals::AbstractGoal> lockedHeroes; //TODO: allow non-elementar objectives
 	std::map<HeroPtr, std::vector<const CGObjectInstance *> > reservedHeroesMap; //objects reserved by specific heroes
 
 	std::vector<const CGObjectInstance *> visitableObjs;
@@ -147,7 +147,7 @@ public:
 	VCAI(void);
 	~VCAI(void);
 
-	void tryRealize(Goals::CGoal g);
+	void tryRealize(Goals::AbstractGoal g);
 	void tryRealize(Goals::Explore g);
 	void tryRealize(Goals::RecruitHero g);
 	void tryRealize(Goals::VisitTile g);
@@ -231,15 +231,15 @@ public:
 	void performTypicalActions();
 
 	void buildArmyIn(const CGTownInstance * t);
-	void striveToGoal(const Goals::CGoal & ultimateGoal);
+	void striveToGoal(const Goals::AbstractGoal & ultimateGoal);
 	void endTurn();
 	void wander(HeroPtr h);
-	void setGoal(HeroPtr h, const Goals::CGoal goal);
+	void setGoal(HeroPtr h, const Goals::AbstractGoal &goal);
 	void setGoal(HeroPtr h, Goals::EGoals goalType = Goals::INVALID);
-	void completeGoal (const Goals::CGoal goal); //safely removes goal from reserved hero
+	void completeGoal (const Goals::AbstractGoal &goal); //safely removes goal from reserved hero
 	void striveToQuest (const QuestInfo &q);
-	bool fulfillsGoal (Goals::CGoal &goal, Goals::CGoal &mainGoal);
-	bool fulfillsGoal (Goals::CGoal &goal, const Goals::CGoal &mainGoal); //TODO: something smarter
+	bool fulfillsGoal (Goals::AbstractGoal &goal, Goals::AbstractGoal &mainGoal);
+	bool fulfillsGoal (Goals::AbstractGoal &goal, const Goals::AbstractGoal &mainGoal); //TODO: something smarter
 
 	void recruitHero(const CGTownInstance * t, bool throwing = false);
 	std::vector<const CGObjectInstance *> getPossibleDestinations(HeroPtr h);
@@ -320,9 +320,9 @@ public:
 class goalFulfilledException : public std::exception
 {
 public:
-	Goals::CGoal goal;
+	Goals::AbstractGoal goal;
 
-	explicit goalFulfilledException(Goals::CGoal Goal) : goal(Goal)
+	explicit goalFulfilledException(Goals::AbstractGoal Goal) : goal(Goal)
 	{
 	}