Browse Source

- Compile fixes for MVS
- AI goals will now be handled by smart pointers

DjWarmonger 12 years ago
parent
commit
1657f124e1
6 changed files with 107 additions and 95 deletions
  1. 75 67
      AI/VCAI/Goals.cpp
  2. 1 1
      AI/VCAI/Goals.h
  3. 27 24
      AI/VCAI/VCAI.cpp
  4. 1 1
      lib/BattleAction.h
  5. 1 1
      lib/BattleHex.h
  6. 2 1
      lib/CHeroHandler.cpp

+ 75 - 67
AI/VCAI/Goals.cpp

@@ -67,7 +67,7 @@ std::string Goals::CGoal::name() const //TODO: virtualize
 	}
 }
 
-#define I_AM_ELEMENTAR return (*this).setisElementar(true)
+#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 Goals::GetArtOfType().setaid(vc.objectId);
+		return make_shared<Goals::CGoal> (Goals::GetArtOfType().setaid(vc.objectId));
 	case EVictoryConditionType::BEATHERO:
-		return Goals::GetObj(vc.obj->id.getNum());
+		return make_shared<Goals::CGoal> (Goals::GetObj(vc.obj->id.getNum()));
 	case EVictoryConditionType::BEATMONSTER:
-		return Goals::GetObj(vc.obj->id.getNum());
+		return make_shared<Goals::CGoal> (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 Goals::BuildThis().setbid(BuildingID::GRAIL).settown(t);
+					return make_shared<Goals::CGoal> (Goals::BuildThis().setbid(BuildingID::GRAIL).settown(t));
 				}
 				else
 				{
@@ -113,7 +113,7 @@ TSubgoal Win::whatToDoToAchieve()
 					boost::sort(towns, isCloser);
 					if(towns.size())
 					{
-						return Goals::VisitTile(towns.front()->visitablePos()).sethero(h);
+						return make_shared<Goals::CGoal> (Goals::VisitTile(towns.front()->visitablePos()).sethero(h));
 					}
 				}
 			}
@@ -121,25 +121,25 @@ TSubgoal Win::whatToDoToAchieve()
 			int3 grailPos = cb->getGrailPos(ratio);
 			if(ratio > 0.99)
 			{
-				return Goals::DigAtTile().settile(grailPos);
+				return make_shared<Goals::CGoal> (Goals::DigAtTile().settile(grailPos));
 			} //TODO: use FIND_OBJ
 			else if(const CGObjectInstance * obj = ai->getUnvisitedObj(objWithID<Obj::OBELISK>)) //there are unvisited Obelisks
 			{
-				return Goals::GetObj(obj->id.getNum());
+				return make_shared<Goals::CGoal> (Goals::GetObj(obj->id.getNum()));
 			}
 			else
-				return Goals::Explore();
+				return make_shared<Goals::CGoal> (Goals::Explore());
 		}
 		break;
 	case EVictoryConditionType::CAPTURECITY:
-		return Goals::GetObj(vc.obj->id.getNum());
+		return make_shared<Goals::CGoal> (Goals::GetObj(vc.obj->id.getNum()));
 	case EVictoryConditionType::GATHERRESOURCE:
-        return Goals::CollectRes().setresID(static_cast<Res::ERes>(vc.objectId)).setvalue(vc.count);
+        return make_shared<Goals::CGoal> (Goals::CollectRes().setresID(static_cast<Res::ERes>(vc.objectId)).setvalue(vc.count));
 		//TODO mines? piles? marketplace?
 		//save?
 		break;
 	case EVictoryConditionType::GATHERTROOP:
-		return Goals::GatherTroops().setobjid(vc.objectId).setvalue(vc.count);
+		return make_shared<Goals::CGoal> (Goals::GatherTroops().setobjid(vc.objectId).setvalue(vc.count));
 		break;
 	case EVictoryConditionType::TAKEDWELLINGS:
 		break;
@@ -148,11 +148,11 @@ TSubgoal Win::whatToDoToAchieve()
 	case EVictoryConditionType::TRANSPORTITEM:
 		break;
 	case EVictoryConditionType::WINSTANDARD:
-		return Goals::Conquer();
+		return make_shared<Goals::CGoal> (Goals::Conquer());
 	default:
 		assert(0);
 	}
-	return TSubgoal(Goals::INVALID);
+	return make_shared<Goals::CGoal> (Goals::INVALID);
 }
 
 TSubgoal FindObj::whatToDoToAchieve()
@@ -181,37 +181,37 @@ TSubgoal FindObj::whatToDoToAchieve()
 		}
 	}
 	if (o && isReachable(o))
-		return Goals::GetObj(o->id.getNum());
+		return make_shared<Goals::CGoal> (Goals::GetObj(o->id.getNum()));
 	else
-		return Goals::Explore();
+		return make_shared<Goals::CGoal> (Goals::Explore());
 }
 TSubgoal GetObj::whatToDoToAchieve()
 {
 	const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid));
 	if(!obj)
-		return Goals::Explore();
+		return make_shared<Goals::CGoal> (Goals::Explore());
 	int3 pos = obj->visitablePos();
-	return Goals::VisitTile(pos);
+	return make_shared<Goals::CGoal> (Goals::VisitTile(pos));
 }
 
 TSubgoal VisitHero::whatToDoToAchieve()
 {
 	const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid));
 	if(!obj)
-		return Goals::Explore();
+		return make_shared<Goals::CGoal> (Goals::Explore());
 	int3 pos = obj->visitablePos();
 
 	if (hero && ai->isAccessibleForHero(pos, hero, true) && isSafeToVisit(hero, pos)) //enemy heroes can get reinforcements
-		return (*this).settile(pos).setisElementar(true);
-	return TSubgoal(Goals::INVALID);
+		return make_shared<Goals::CGoal> (settile(pos).setisElementar(true));
+	return make_shared<Goals::CGoal> (Goals::INVALID);
 }
 
 TSubgoal GetArtOfType::whatToDoToAchieve()
 {
 	TSubgoal alternativeWay = CGoal::lookForArtSmart(aid); //TODO: use
-	if(alternativeWay.invalid())
-		return Goals::FindObj(Obj::ARTIFACT, aid);
-	return TSubgoal(Goals::INVALID);
+	if(alternativeWay->invalid())
+		return make_shared<Goals::CGoal> (Goals::FindObj(Obj::ARTIFACT, aid));
+	return make_shared<Goals::CGoal> (Goals::INVALID);
 }
 
 TSubgoal ClearWayTo::whatToDoToAchieve()
@@ -220,12 +220,12 @@ TSubgoal ClearWayTo::whatToDoToAchieve()
 	if(!cb->isVisible(tile))
 	{
         logAi->errorStream() << "Clear way should be used with visible tiles!";
-		return Goals::Explore();
+		return make_shared<Goals::CGoal> (Goals::Explore());
 	}
 
 	HeroPtr h = hero ? hero : ai->primaryHero();
 	if(!h)
-		return Goals::RecruitHero();
+		return make_shared<Goals::CGoal> (Goals::RecruitHero());
 
 	cb->setSelection(*h);
 
@@ -238,7 +238,7 @@ TSubgoal ClearWayTo::whatToDoToAchieve()
 	//if(isSafeToVisit(h, tileToHit))
 	if(isBlockedBorderGate(tileToHit))
 	{	//FIXME: this way we'll not visit gate and activate quest :?
-		return Goals::FindObj (Obj::KEYMASTER, cb->getTile(tileToHit)->visitableObjects.back()->subID);
+		return make_shared<Goals::CGoal> (Goals::FindObj (Obj::KEYMASTER, cb->getTile(tileToHit)->visitableObjects.back()->subID));
 	}
 
 	//FIXME: this code shouldn't be necessary
@@ -256,7 +256,8 @@ TSubgoal ClearWayTo::whatToDoToAchieve()
 		throw cannotFulfillGoalException(problem);
 	}
 
-	return Goals::VisitTile(tileToHit).sethero(h); //FIXME:: attempts to visit completely unreachable tile with hero results in stall
+	return make_shared<Goals::CGoal> (Goals::VisitTile(tileToHit).sethero(h));
+	//FIXME:: attempts to visit completely unreachable tile with hero results in stall
 
 	//TODO czy istnieje lepsza droga?
 
@@ -295,7 +296,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 Goals::VisitTile(pos).sethero(hero);
+					return make_shared<Goals::CGoal> (Goals::VisitTile(pos).sethero(hero));
 			}
 		}
 		else
@@ -304,7 +305,7 @@ TSubgoal Explore::whatToDoToAchieve()
 			{
 				auto pos = obj->visitablePos();
 				if (ai->isAccessible (pos)) //TODO: check safety?
-					return Goals::VisitTile(pos).sethero(hero);
+					return make_shared<Goals::CGoal> (Goals::VisitTile(pos).sethero(hero));
 			}
 		}
 	}
@@ -332,12 +333,12 @@ TSubgoal Explore::whatToDoToAchieve()
 			});
 			if (objs.size())
 			{
-				return Goals::VisitTile(objs.front()->visitablePos()).sethero(hero).setisAbstract(true);
+				return make_shared<Goals::CGoal> (Goals::VisitTile(objs.front()->visitablePos()).sethero(hero).setisAbstract(true));
 			}
 			else
 				throw cannotFulfillGoalException("Cannot explore - no possible ways found!");
 		}
-		return Goals::VisitTile(t).sethero(hero);
+		return make_shared<Goals::CGoal> (Goals::VisitTile(t).sethero(hero));
 	}
 
 	auto hs = cb->getHeroesInfo();
@@ -350,7 +351,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 Goals::RecruitHero();
+			return make_shared<Goals::CGoal> (Goals::RecruitHero());
 		else //find mobile hero with weakest army
 		{
 			hs = cb->getHeroesInfo();
@@ -361,7 +362,7 @@ TSubgoal Explore::whatToDoToAchieve()
 			if (hs.empty())
 			{
 				if (howManyHeroes < GameConstants::MAX_HEROES_PER_PLAYER)
-					return Goals::RecruitHero();
+					return make_shared<Goals::CGoal> (Goals::RecruitHero());
 				else
 					throw cannotFulfillGoalException("No heroes with remaining MPs for exploring!\n");
 			}
@@ -371,7 +372,7 @@ TSubgoal Explore::whatToDoToAchieve()
 
 	const CGHeroInstance *h = hs.front();
 
-	return (*this).sethero(h).setisAbstract(true);
+	return make_shared<Goals::CGoal> (sethero(h).setisAbstract(true));
 
 	I_AM_ELEMENTAR; //FIXME: how can this be called?
 };
@@ -380,10 +381,10 @@ TSubgoal RecruitHero::whatToDoToAchieve()
 {
 	const CGTownInstance *t = ai->findTownWithTavern();
 	if(!t)
-		return Goals::BuildThis().setbid(BuildingID::TAVERN);
+		return make_shared<Goals::CGoal> (Goals::BuildThis().setbid(BuildingID::TAVERN));
 
 	if(cb->getResourceAmount(Res::GOLD) < HERO_GOLD_COST)
-		return Goals::CollectRes().setresID(Res::GOLD).setvalue(HERO_GOLD_COST);
+		return make_shared<Goals::CGoal> (Goals::CollectRes().setresID(Res::GOLD).setvalue(HERO_GOLD_COST));
 
 	I_AM_ELEMENTAR;
 }
@@ -391,7 +392,7 @@ TSubgoal RecruitHero::whatToDoToAchieve()
 TSubgoal VisitTile::whatToDoToAchieve()
 {
 	if(!cb->isVisible(tile))
-		return Goals::Explore();
+		return make_shared<Goals::CGoal> (Goals::Explore());
 
 	if(hero && !ai->isAccessibleForHero(tile, hero))
 		hero = nullptr;
@@ -400,7 +401,7 @@ TSubgoal VisitTile::whatToDoToAchieve()
 	{
 		if(cb->getHeroesInfo().empty())
 		{
-			return Goals::RecruitHero();
+			return make_shared<Goals::CGoal> (Goals::RecruitHero());
 		}
 
 		for(const CGHeroInstance *h : cb->getHeroesInfo())
@@ -416,15 +417,15 @@ TSubgoal VisitTile::whatToDoToAchieve()
 	if(hero)
 	{
 		if(isSafeToVisit(hero, tile))
-			return (*this).setisElementar(true);
+			return make_shared<Goals::CGoal>(setisElementar(true));
 		else
 		{
-			return Goals::GatherArmy().sethero(hero).setvalue(evaluateDanger(tile, *hero) * SAFE_ATTACK_CONSTANT); //TODO: should it be abstract?
+			return make_shared<Goals::CGoal>(Goals::GatherArmy().sethero(hero).setvalue(evaluateDanger(tile, *hero) * SAFE_ATTACK_CONSTANT)); //TODO: should it be abstract?
 		}
 	}
 	else	//inaccessible for all heroes
 	{
-		return Goals::ClearWayTo(tile);
+		return make_shared<Goals::CGoal>(Goals::ClearWayTo(tile));
 	}
 }
 
@@ -434,10 +435,10 @@ 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 (*this).sethero(h).setisElementar(true);
+		return make_shared<Goals::CGoal> (sethero(h).setisElementar(true));
 	}
 
-	return Goals::VisitTile(tile);
+	return make_shared<Goals::CGoal>(Goals::VisitTile(tile));
 }
 
 TSubgoal BuildThis::whatToDoToAchieve()
@@ -481,7 +482,7 @@ TSubgoal CollectRes::whatToDoToAchieve()
 		for(const CGTownInstance *t : cb->getTownsInfo())
 		{
 			if(cb->canBuildStructure(t, BuildingID::MARKETPLACE) == EBuildingState::ALLOWED)
-				return Goals::BuildThis().settown(t).setbid(BuildingID::MARKETPLACE);
+				return make_shared<Goals::CGoal>(Goals::BuildThis().settown(t).setbid(BuildingID::MARKETPLACE));
 		}
 	}
 	else
@@ -503,11 +504,11 @@ 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 Goals::GetObj(m->o->id.getNum());
-			return setobjid(m->o->id.getNum()).setisElementar(true);
+				return make_shared<Goals::CGoal>(Goals::GetObj(m->o->id.getNum()));
+			return make_shared<Goals::CGoal>(setobjid(m->o->id.getNum()).setisElementar(true));
 		}
 	}
-	return Goals::Invalid(); //FIXME: unused?
+	return make_shared<Goals::CGoal>(Goals::Invalid()); //FIXME: unused?
 }
 
 TSubgoal GatherTroops::whatToDoToAchieve()
@@ -533,7 +534,7 @@ TSubgoal GatherTroops::whatToDoToAchieve()
 			}
 			else
 			{
-				return Goals::BuildThis().settown(t).setbid(bid);
+				return make_shared<Goals::CGoal>(Goals::BuildThis().settown(t).setbid(bid));
 			}
 		}
 	}
@@ -558,10 +559,10 @@ TSubgoal GatherTroops::whatToDoToAchieve()
 	if (dwellings.size())
 	{
 		boost::sort(dwellings, isCloser);
-		return Goals::GetObj(dwellings.front()->id.getNum());
+		return make_shared<Goals::CGoal>(Goals::GetObj(dwellings.front()->id.getNum()));
 	}
 	else
-		return Goals::Explore();
+		return make_shared<Goals::CGoal>(Goals::Explore());
 	//TODO: exchange troops between heroes
 }
 
@@ -577,7 +578,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 Goals::RecruitHero();
+			return make_shared<Goals::CGoal>(Goals::RecruitHero());
 		else //find mobile hero with weakest army
 		{
 			hs = cb->getHeroesInfo();
@@ -588,7 +589,7 @@ TSubgoal Conquer::whatToDoToAchieve()
 			if (hs.empty())
 			{
 				if (howManyHeroes < GameConstants::MAX_HEROES_PER_PLAYER)
-					return Goals::RecruitHero();
+					return make_shared<Goals::CGoal>(Goals::RecruitHero());
 				else
 					throw cannotFulfillGoalException("No heroes with remaining MPs for exploring!\n");
 			}
@@ -617,7 +618,7 @@ TSubgoal Conquer::whatToDoToAchieve()
 	}
 
 	if(objs.empty())
-		return Goals::Explore(); //we need to find an enemy
+		return make_shared<Goals::CGoal>(Goals::Explore()); //we need to find an enemy
 
 	erase_if(objs,  [&](const CGObjectInstance *obj)
 	{
@@ -635,13 +636,15 @@ TSubgoal Conquer::whatToDoToAchieve()
 			ai->reserveObject(h, obj); //no one else will capture same object until we fail
 
 			if (obj->ID == Obj::HERO)
-				return Goals::VisitHero().sethero(h).setobjid(obj->id.getNum()).setisAbstract(true); //track enemy hero
+				return make_shared<Goals::CGoal>(
+					Goals::VisitHero().sethero(h).setobjid(obj->id.getNum()).setisAbstract(true));
+					//track enemy hero
 			else
-				return Goals::VisitTile(obj->visitablePos()).sethero(h);
+				return make_shared<Goals::CGoal>(Goals::VisitTile(obj->visitablePos()).sethero(h));
 		}
 	}
 
-	return Goals::Explore(); //enemy is inaccessible
+	return make_shared<Goals::CGoal>(Goals::Explore()); //enemy is inaccessible
 }
 
 TSubgoal Build::whatToDoToAchieve()
@@ -678,7 +681,8 @@ TSubgoal GatherArmy::whatToDoToAchieve()
 	if(townsReachable.size()) //try towns first
 	{
 		boost::sort(townsReachable, compareReinforcements);
-		return Goals::VisitTile(townsReachable.back()->visitablePos()).sethero(hero);
+		return make_shared<Goals::CGoal>(
+			Goals::VisitTile(townsReachable.back()->visitablePos()).sethero(hero));
 	}
 	else
 	{
@@ -701,9 +705,13 @@ TSubgoal GatherArmy::whatToDoToAchieve()
 				secondaryPath = cb->getPathInfo(hero->visitablePos())->turns;
 
 				if (primaryPath < secondaryPath)
-					return Goals::VisitHero().setisAbstract(true).setobjid(h->id.getNum()).sethero(hero); //go to the other hero if we are faster
+					return make_shared<Goals::CGoal>(
+						Goals::VisitHero().setisAbstract(true).setobjid(h->id.getNum()).sethero(hero));
+						//go to the other hero if we are faster
 				else
-					return Goals::VisitHero().setisAbstract(true).setobjid(hero->id.getNum()).sethero(h); //let the other hero come to us
+					return make_shared<Goals::CGoal>(
+						Goals::VisitHero().setisAbstract(true).setobjid(hero->id.getNum()).sethero(h))
+						; //let the other hero come to us
 			}
 		}
 
@@ -739,7 +747,7 @@ TSubgoal GatherArmy::whatToDoToAchieve()
 			return true;
 		});
 		if(objs.empty()) //no possible objects, we did eveyrthing already
-			return Goals::Explore().sethero(hero);
+			return make_shared<Goals::CGoal>(Goals::Explore().sethero(hero));
 		//TODO: check if we can recruit any creatures there, evaluate army
 		else
 		{
@@ -759,31 +767,31 @@ TSubgoal GatherArmy::whatToDoToAchieve()
 					}
 				}
 				if (h && isSafeToVisit(h, pos) && ai->isAccessibleForHero(pos, h))
-					return Goals::VisitTile(pos).sethero(h);
+					return make_shared<Goals::CGoal>(Goals::VisitTile(pos).sethero(h));
 			}
 		}
 	}
 
-	return Goals::Explore().sethero(hero); //find dwelling. use current hero to prevent him from doing nothing.
+	return make_shared<Goals::CGoal>(Goals::Explore().sethero(hero)); //find dwelling. use current hero to prevent him from doing nothing.
 }
 
 TSubgoal CGoal::whatToDoToAchieve()
 {
     logAi->debugStream() << boost::format("Decomposing goal of type %s") % name();
-	return Goals::Explore();
+	return make_shared<Goals::CGoal>(Goals::Explore());
 }
 
 TSubgoal CGoal::goVisitOrLookFor(const CGObjectInstance *obj)
 {
 	if(obj)
-		return Goals::GetObj(obj->id.getNum());
+		return make_shared<Goals::CGoal>(Goals::GetObj(obj->id.getNum()));
 	else
-		return Goals::Explore();
+		return make_shared<Goals::CGoal>(Goals::Explore());
 }
 
 TSubgoal CGoal::lookForArtSmart(int aid)
 {
-	return Goals::Invalid();
+	return make_shared<Goals::CGoal>(Goals::Invalid());
 }
 
 bool CGoal::invalid() const

+ 1 - 1
AI/VCAI/Goals.h

@@ -21,7 +21,7 @@ struct HeroPtr;
 namespace Goals
 {
 	struct CGoal;
-	typedef CGoal TSubgoal;
+	typedef std::shared_ptr<Goals::CGoal> TSubgoal;
 
 	enum EGoals
 {

+ 27 - 24
AI/VCAI/VCAI.cpp

@@ -18,6 +18,7 @@
 extern FuzzyHelper *fh;
 
 class CGVisitableOPW;
+class TSubgoal;
 
 const double SAFE_ATTACK_CONSTANT = 1.5;
 const int GOLD_RESERVE = 10000; //when buying creatures we want to keep at least this much gold (10000 so at least we'll be able to reach capitol)
@@ -1696,25 +1697,25 @@ void VCAI::striveToGoal(const Goals::CGoal &ultimateGoal)
 	if (ultimateGoal.invalid())
 		return;
 
-	Goals::CGoal abstractGoal; //can't create reference from temporary
+	std::shared_ptr<Goals::CGoal> abstractGoal = make_shared<Goals::CGoal>(Goals::Invalid());
 
 	while(1)
 	{
-		Goals::CGoal &goal = const_cast<Goals::CGoal&>(ultimateGoal);
+		std::shared_ptr<Goals::CGoal> goal = make_shared<Goals::CGoal>(ultimateGoal);
         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)
+		while(!goal->isElementar && !goal->isAbstract && maxGoals)
 		{
-            logAi->debugStream() << boost::format("Considering goal %s") % goal.name();
+            logAi->debugStream() << boost::format("Considering goal %s") % goal->name();
 			try
 			{
 				boost::this_thread::interruption_point();
-				goal = goal.whatToDoToAchieve(); //FIXME: must keep information about goal class
+				goal = goal->whatToDoToAchieve(); //FIXME: why it calls always base class?
 				--maxGoals;
 			}
 			catch(std::exception &e)
 			{
-                logAi->debugStream() << boost::format("Goal %s decomposition failed: %s") % goal.name() % e.what();
+                logAi->debugStream() << boost::format("Goal %s decomposition failed: %s") % goal->name() % e.what();
 				//setGoal (goal.hero, INVALID); //test: if we don't know how to realize goal, we should abandon it for now
 				return;
 			}
@@ -1730,26 +1731,26 @@ void VCAI::striveToGoal(const Goals::CGoal &ultimateGoal)
 				throw (e);
 			}
 
-			if (goal.hero) //lock this hero to fulfill ultimate goal
+			if (goal->hero) //lock this hero to fulfill ultimate goal
 			{
 				if (maxGoals)
 				{
-					setGoal(goal.hero, goal);
+					setGoal(goal->hero, *goal.get());
 				}
 				else
 				{
-					setGoal(goal.hero, Goals::INVALID); // we seemingly don't know what to do with hero
+					setGoal(goal->hero, Goals::INVALID); // we seemingly don't know what to do with hero
 				}
 			}
 
-			if (goal.isAbstract)
+			if (goal->isAbstract)
 			{
 				abstractGoal = goal; //allow only one abstract goal per call
-                logAi->debugStream() << boost::format("Choosing abstract goal %s") % goal.name();
+                logAi->debugStream() << boost::format("Choosing abstract goal %s") % goal->name();
 				break;
 			}
 			else
-				tryRealize(goal);
+				tryRealize(*goal);
 
 			boost::this_thread::interruption_point();
 		}
@@ -1760,36 +1761,38 @@ void VCAI::striveToGoal(const Goals::CGoal &ultimateGoal)
 		}
 		catch(goalFulfilledException &e)
 		{
-			completeGoal (goal);
-			if (fulfillsGoal (goal, ultimateGoal) || maxGoals > 98) //completed goal was main goal //TODO: find better condition
+			completeGoal (*goal);
+			if (fulfillsGoal (*goal, ultimateGoal) || maxGoals > 98) //completed goal was main goal //TODO: find better condition
 				return; 
 		}
 		catch(std::exception &e)
 		{
-            logAi->debugStream() << boost::format("Failed to realize subgoal of type %s (greater goal type was %s), I will stop.") % goal.name() % ultimateGoal.name();
+            logAi->debugStream() << boost::format("Failed to realize subgoal of type %s (greater goal type was %s), I will stop.") % goal->name() % ultimateGoal.name();
             logAi->debugStream() << boost::format("The error message was: %s") % e.what();
 			break;
 		}
 	}
 
 	//TODO: save abstract goals not related to hero
-	if (!abstractGoal.invalid()) //try to realize our one goal
+	//TODO: refactor, duplicated code
+	if (!abstractGoal->invalid()) //try to realize our one goal
 	{
 		while (1)
 		{
-			Goals::CGoal goal = Goals::CGoal(abstractGoal).setisAbstract(false);
+			std::shared_ptr<Goals::CGoal> goal(abstractGoal);
+			goal->setisAbstract(false);
 			int maxGoals = 50;
-			while (!goal.isElementar && maxGoals) //find elementar goal and fulfill it
+			while (!goal->isElementar && maxGoals) //find elementar goal and fulfill it
 			{
 				try
 				{
 					boost::this_thread::interruption_point();
-					goal = goal.whatToDoToAchieve();
+					goal = goal->whatToDoToAchieve();
 					--maxGoals;
 				}
 				catch(std::exception &e)
 				{
-                    logAi->debugStream() << boost::format("Goal %s decomposition failed: %s") % goal.name() % e.what();
+                    logAi->debugStream() << boost::format("Goal %s decomposition failed: %s") % goal->name() % e.what();
 					//setGoal (goal.hero, INVALID);
 					return;
 				}
@@ -1802,7 +1805,7 @@ void VCAI::striveToGoal(const Goals::CGoal &ultimateGoal)
 					std::runtime_error e("Too many subgoals, don't know what to do");
 					throw (e);
 				}
-				tryRealize(goal);
+				tryRealize(*goal);
 				boost::this_thread::interruption_point();
 			}
 			catch(boost::thread_interrupted &e)
@@ -1812,13 +1815,13 @@ void VCAI::striveToGoal(const Goals::CGoal &ultimateGoal)
 			}
 			catch(goalFulfilledException &e)
 			{
-				completeGoal (goal); //FIXME: deduce that we have realized GET_OBJ goal
-				if (fulfillsGoal (goal, abstractGoal) || maxGoals > 98) //completed goal was main goal
+				completeGoal (*goal); //FIXME: deduce that we have realized GET_OBJ goal
+				if (fulfillsGoal (*goal, *abstractGoal) || maxGoals > 98) //completed goal was main goal
 					return;
 			}
 			catch(std::exception &e)
 			{
-                logAi->debugStream() << boost::format("Failed to realize subgoal of type %s (greater goal type was %s), I will stop.") % goal.name() % ultimateGoal.name();
+                logAi->debugStream() << boost::format("Failed to realize subgoal of type %s (greater goal type was %s), I will stop.") % goal->name() % ultimateGoal.name();
                 logAi->debugStream() << boost::format("The error message was: %s") % e.what();
 				break;
 			}

+ 1 - 1
lib/BattleAction.h

@@ -41,4 +41,4 @@ struct DLL_LINKAGE BattleAction
 	static BattleAction makeEndOFTacticPhase(ui8 side);
 };
 
-std::ostream & operator<<(std::ostream & os, const BattleAction & ba);
+DLL_EXPORT std::ostream & operator<<(std::ostream & os, const BattleAction & ba);

+ 1 - 1
lib/BattleHex.h

@@ -114,4 +114,4 @@ struct DLL_LINKAGE BattleHex
 	static BattleHex getClosestTile(bool attackerOwned, BattleHex initialPos, std::set<BattleHex> & possibilities); //TODO: vector or set? copying one to another is bad
 };
 
-std::ostream & operator<<(std::ostream & os, const BattleHex & hex);
+DLL_EXPORT std::ostream & operator<<(std::ostream & os, const BattleHex & hex);

+ 2 - 1
lib/CHeroHandler.cpp

@@ -10,6 +10,7 @@
 #include "CModHandler.h"
 #include "CTownHandler.h"
 #include "CObjectHandler.h" //for hero specialty
+#include <math.h>
 
 /*
  * CHeroHandler.cpp, part of VCMI engine
@@ -203,7 +204,7 @@ void CHeroClassHandler::afterLoadFinalization()
 				continue;
 
 			float chance = heroClass->defaultTavernChance * faction->town->defaultTavernChance;
-			heroClass->selectionProbability[faction->index] = round(sqrt(chance));
+			heroClass->selectionProbability[faction->index] = static_cast<int>(sqrt(chance) + 0.5); //FIXME: replace with std::round once MVS supports it
 		}
 	}
 }