Browse Source

Merge pull request #203 from vcmi/logFormat

boost::format support for CLogger
Alexander Shishkin 9 years ago
parent
commit
5e5ce0bb09

+ 4 - 4
AI/BattleAI/BattleAI.cpp

@@ -179,7 +179,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
 	}
 	catch(std::exception &e)
 	{
-		logAi->errorStream() << "Exception occurred in " << __FUNCTION__ << " " << e.what();
+		logAi->error("Exception occurred in %s %s",__FUNCTION__, e.what());
 	}
 
 	return BattleAction::makeDefend(stack);
@@ -268,7 +268,7 @@ void CBattleAI::battleStacksRemoved(const BattleStacksRemoved & bsr)
 
 void CBattleAI::print(const std::string &text) const
 {
-	logAi->traceStream() << "CBattleAI [" << this <<"]: " << text;
+	logAi->trace("CBattleAI [%p]: %s", this, text);
 }
 
 BattleAction CBattleAI::goTowards(const CStack * stack, BattleHex destination)
@@ -283,7 +283,7 @@ BattleAction CBattleAI::goTowards(const CStack * stack, BattleHex destination)
 	auto destNeighbours = destination.neighbouringTiles();
 	if(vstd::contains_if(destNeighbours, [&](BattleHex n) { return stack->coversPos(destination); }))
 	{
-		logAi->warnStream() << "Warning: already standing on neighbouring tile!";
+		logAi->warn("Warning: already standing on neighbouring tile!");
 		//We shouldn't even be here...
 		return BattleAction::makeDefend(stack);
 	}
@@ -457,7 +457,7 @@ void CBattleAI::attemptCastingSpell()
 		case OFFENSIVE_SPELL:
 			{
 				int damageDealt = 0, damageReceived = 0;
-				
+
 				auto stacksSuffering = ps.spell->getAffectedStacks(cb.get(), ECastingMode::HERO_CASTING, playerID, skillLevel, ps.dest, hero);
 
 				if(stacksSuffering.empty())

+ 1 - 1
AI/BattleAI/CMakeLists.txt

@@ -1,7 +1,7 @@
 project(battleAI)
 cmake_minimum_required(VERSION 2.6)
 
-include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
+include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
 
 set(battleAI_SRCS
 		StdInc.cpp

+ 0 - 1
AI/EmptyAI/CEmptyAI.cpp

@@ -8,7 +8,6 @@ void CEmptyAI::init(std::shared_ptr<CCallback> CB)
 	cb = CB;
 	human=false;
 	playerID = *cb->getMyColor();
-	//logAi->infoStream() << "EmptyAI initialized.";
 }
 void CEmptyAI::yourTurn()
 {

+ 1 - 1
AI/EmptyAI/CMakeLists.txt

@@ -1,7 +1,7 @@
 project(emptyAI)
 cmake_minimum_required(VERSION 2.6)
 
-include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
+include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
 
 set(emptyAI_SRCS
         CEmptyAI.cpp

+ 1 - 1
AI/StupidAI/CMakeLists.txt

@@ -1,7 +1,7 @@
 project(stupidAI)
 cmake_minimum_required(VERSION 2.6)
 
-include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
+include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
 
 set(stupidAI_SRCS
 		StdInc.cpp

+ 2 - 2
AI/StupidAI/StupidAI.cpp

@@ -261,7 +261,7 @@ void CStupidAI::battleStacksRemoved(const BattleStacksRemoved & bsr)
 
 void CStupidAI::print(const std::string &text) const
 {
-	logAi->traceStream() << "CStupidAI [" << this <<"]: " << text;
+	logAi->trace("CStupidAI  [%p]: %s", this, text);
 }
 
 BattleAction CStupidAI::goTowards(const CStack * stack, BattleHex destination)
@@ -276,7 +276,7 @@ BattleAction CStupidAI::goTowards(const CStack * stack, BattleHex destination)
 	auto destNeighbours = destination.neighbouringTiles();
 	if(vstd::contains_if(destNeighbours, [&](BattleHex n) { return stack->coversPos(destination); }))
 	{
-		logAi->warnStream() << "Warning: already standing on neighbouring tile!";
+		logAi->warn("Warning: already standing on neighbouring tile!");
 		//We shouldn't even be here...
 		return BattleAction::makeDefend(stack);
 	}

+ 1 - 1
AI/VCAI/AIUtility.cpp

@@ -339,7 +339,7 @@ bool isSafeToVisit(HeroPtr h, crint3 tile)
 	{
 		if(heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength)
 		{
-			logAi->traceStream() << boost::format("It's safe for %s to visit tile %s") % h->name % tile;
+			logAi->trace("It's safe for %s to visit tile %s", h->name, tile());
 			return true;
 		}
 		else

+ 3 - 3
AI/VCAI/AIUtility.h

@@ -42,7 +42,7 @@ struct HeroPtr
 public:
 	std::string name;
 
-	
+
 	HeroPtr();
 	HeroPtr(const CGHeroInstance *H);
 	~HeroPtr();
@@ -105,7 +105,7 @@ struct TimeCheck
 
 	~TimeCheck()
 	{
-		logAi->traceStream() << boost::format("Time of %s was %d ms.") % txt % time.getDiff();
+		logAi->trace("Time of %s was %d ms.",txt,time.getDiff());
 	}
 };
 
@@ -132,7 +132,7 @@ bool objWithID(const CGObjectInstance *obj)
 	return obj->ID == id;
 }
 
-std::string strFromInt3(int3 pos);
+std::string strFromInt3(int3 pos);//todo: remove
 void foreach_tile_pos(std::function<void(const int3& pos)> foo);
 void foreach_tile_pos(CCallback * cbp, std::function<void(CCallback * cbp, const int3& pos)> foo); // avoid costly retrieval of thread-specific pointer
 void foreach_neighbour(const int3 &pos, std::function<void(const int3& pos)> foo);

+ 2 - 2
AI/VCAI/CMakeLists.txt

@@ -2,9 +2,9 @@ project(VCAI)
 cmake_minimum_required(VERSION 2.6)
 
 if (FL_FOUND)
-    include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib ${FL_INCLUDE_DIRS})
+    include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib ${FL_INCLUDE_DIRS})
 else()
-    include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib ${CMAKE_HOME_DIRECTORY}/AI/FuzzyLite/fuzzylite)
+    include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib ${CMAKE_HOME_DIRECTORY}/AI/FuzzyLite/fuzzylite)
 endif()
 
 set(VCAI_SRCS

+ 10 - 11
AI/VCAI/Fuzzy.cpp

@@ -46,7 +46,7 @@ engineBase::engineBase()
 void engineBase::configure()
 {
 	engine.configure("Minimum", "Maximum", "Minimum", "AlgebraicSum", "Centroid");
-	logAi->infoStream() << engine.toString();
+	logAi->info(engine.toString());
 }
 
 void engineBase::addRule(const std::string &txt)
@@ -149,7 +149,7 @@ void FuzzyHelper::initTacticalAdvantage()
 		{
 			fl::Rectangle* none = new fl::Rectangle("NONE", CGTownInstance::NONE, CGTownInstance::NONE + (CGTownInstance::FORT - CGTownInstance::NONE) * 0.5f);
 			ta.castleWalls->addTerm(none);
-                    
+
 			fl::Trapezoid* medium = new fl::Trapezoid("MEDIUM", (CGTownInstance::FORT - CGTownInstance::NONE) * 0.5f, CGTownInstance::FORT,
 				CGTownInstance::CITADEL, CGTownInstance::CITADEL + (CGTownInstance::CASTLE - CGTownInstance::CITADEL) * 0.5f);
 			ta.castleWalls->addTerm(medium);
@@ -160,7 +160,7 @@ void FuzzyHelper::initTacticalAdvantage()
 			ta.castleWalls->setRange(CGTownInstance::NONE, CGTownInstance::CASTLE);
 		}
 
-		
+
 
 		ta.bankPresent = new fl::InputVariable("Bank");
 		ta.engine.addInputVariable(ta.bankPresent);
@@ -201,7 +201,7 @@ void FuzzyHelper::initTacticalAdvantage()
 	}
 	catch (fl::Exception & pe)
 	{
-		logAi->errorStream() << "initTacticalAdvantage " << ": " << pe.getWhat();
+		logAi->error("initTacticalAdvantage: %s", pe.getWhat());
 	}
 }
 
@@ -262,7 +262,7 @@ float FuzzyHelper::getTacticalAdvantage (const CArmedInstance *we, const CArmedI
 	}
 	catch (fl::Exception & fe)
 	{
-		logAi->errorStream() << "getTacticalAdvantage " << ": " << fe.getWhat();
+		logAi->error("getTacticalAdvantage: %s ",fe.getWhat());
 	}
 
 	if (output < 0 || (output != output))
@@ -273,7 +273,7 @@ float FuzzyHelper::getTacticalAdvantage (const CArmedInstance *we, const CArmedI
 
 		for (int i = 0; i < boost::size(tab); i++)
 			log << names[i] << ": " << tab[i]->getInputValue() << " ";
-		logAi->errorStream() << log.str();
+		logAi->error(log.str());
 		assert(false);
 	}
 
@@ -410,7 +410,7 @@ void FuzzyHelper::initVisitTile()
 	}
 	catch (fl::Exception & fe)
 	{
-		logAi->errorStream() << "visitTile " << ": " << fe.getWhat();
+		logAi->error("visitTile: %s",fe.getWhat());
 	}
 }
 
@@ -455,11 +455,10 @@ float FuzzyHelper::evaluate (Goals::VisitTile & g)
 	}
 	catch (fl::Exception & fe)
 	{
-		logAi->errorStream() << "evaluate VisitTile " << ": " << fe.getWhat();
+		logAi->error("evaluate VisitTile: %s",fe.getWhat());
 	}
 	assert (g.priority >= 0);
 	return g.priority;
-
 }
 float FuzzyHelper::evaluate (Goals::VisitHero & g)
 {
@@ -468,7 +467,7 @@ float FuzzyHelper::evaluate (Goals::VisitHero & g)
 		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;	
+	return g.priority;
 }
 float FuzzyHelper::evaluate (Goals::GatherArmy & g)
 {
@@ -525,7 +524,7 @@ float FuzzyHelper::evaluate (Goals::Invalid & g)
 }
 float FuzzyHelper::evaluate (Goals::AbstractGoal & g)
 {
-	logAi->warnStream() << boost::format("Cannot evaluate goal %s") % g.name();
+	logAi->warn("Cannot evaluate goal %s", g.name());
 	return g.priority;
 }
 void FuzzyHelper::setPriority (Goals::TSubgoal & g)

+ 8 - 9
AI/VCAI/Goals.cpp

@@ -182,7 +182,7 @@ namespace Goals
 
 //TSubgoal AbstractGoal::whatToDoToAchieve()
 //{
-//	logAi->debugStream() << boost::format("Decomposing goal of type %s") % name();
+//	logAi->debug("Decomposing goal of type %s",name());
 //	return sptr (Goals::Explore());
 //}
 
@@ -422,7 +422,7 @@ TSubgoal VisitHero::whatToDoToAchieve()
 	if (hero && ai->isAccessibleForHero(pos, hero, true) && isSafeToVisit(hero, pos)) //enemy heroes can get reinforcements
 	{
 		if (hero->pos == pos)
-			logAi->errorStream() << "Hero " << hero.name << " tries to visit himself.";
+			logAi->error("Hero %s tries to visit himself.", hero.name);
 		else
 		{
 			//can't use VISIT_TILE here as tile appears blocked by target hero
@@ -442,8 +442,7 @@ bool VisitHero::fulfillsMe (TSubgoal goal)
 	auto obj = cb->getObj(ObjectInstanceID(objid));
 	if (!obj)
 	{
-		logAi->errorStream() << boost::format("Hero %s: VisitHero::fulfillsMe at %s: object %d not found")
-			% hero.name % goal->tile % objid;
+		logAi->error("Hero %s: VisitHero::fulfillsMe at %s: object %d not found", hero.name, goal->tile, objid);
 		return false;
 	}
 	return obj->visitablePos() == goal->tile;
@@ -462,7 +461,7 @@ TSubgoal ClearWayTo::whatToDoToAchieve()
 	assert(cb->isInTheMap(tile)); //set tile
 	if(!cb->isVisible(tile))
 	{
-		logAi->errorStream() << "Clear way should be used with visible tiles!";
+		logAi->error("Clear way should be used with visible tiles!");
 		return sptr (Goals::Explore());
 	}
 
@@ -513,7 +512,7 @@ TGoalVec ClearWayTo::getAllPossibleSubgoals()
 
 			if (topObj->ID == Obj::HERO && cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
 				if (topObj != hero.get(true)) //the hero we want to free
-					logAi->errorStream() << boost::format("%s stands in the way of %s") % topObj->getObjectName()  % h->getObjectName();
+					logAi->error("%s stands in the way of %s", topObj->getObjectName(), h->getObjectName());
 			if (topObj->ID == Obj::QUEST_GUARD || topObj->ID == Obj::BORDERGUARD)
 			{
 				if (shouldVisit(h, topObj))
@@ -525,7 +524,7 @@ TGoalVec ClearWayTo::getAllPossibleSubgoals()
 				else
 				{
 					//TODO: we should be able to return apriopriate quest here (VCAI::striveToQuest)
-					logAi->debugStream() << "Quest guard blocks the way to " + tile();
+					logAi->debug("Quest guard blocks the way to %s", tile());
 					continue; //do not access quets guard if we can't complete the quest
 				}
 			}
@@ -545,7 +544,7 @@ TGoalVec ClearWayTo::getAllPossibleSubgoals()
 
 	if (ret.empty())
 	{
-		logAi->warnStream() << "There is no known way to clear the way to tile " + tile();
+		logAi->warn("There is no known way to clear the way to tile %s",tile());
 		throw goalFulfilledException (sptr(Goals::ClearWayTo(tile))); //make sure asigned hero gets unlocked
 	}
 
@@ -1129,7 +1128,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
 
 //TSubgoal AbstractGoal::whatToDoToAchieve()
 //{
-//	logAi->debugStream() << boost::format("Decomposing goal of type %s") % name();
+//	logAi->debug("Decomposing goal of type %s",name());
 //	return sptr (Goals::Explore());
 //}
 

+ 37 - 38
AI/VCAI/VCAI.cpp

@@ -134,7 +134,7 @@ void VCAI::heroMoved(const TryMoveHero & details)
 				{
 					knownSubterraneanGates[o1] = o2;
 					knownSubterraneanGates[o2] = o1;
-					logAi->debugStream() << boost::format("Found a pair of subterranean gates between %s and %s!") % from % to;
+					logAi->debug("Found a pair of subterranean gates between %s and %s!", from(), to());
 				}
 			}
 		}
@@ -210,17 +210,17 @@ void VCAI::gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryL
 {
 	LOG_TRACE_PARAMS(logAi, "victoryLossCheckResult '%s'", victoryLossCheckResult.messageToSelf);
 	NET_EVENT_HANDLER;
-	logAi->debugStream() << boost::format("Player %d: I heard that player %d %s.") % playerID % player.getNum() % (victoryLossCheckResult.victory() ? "won" : "lost");
+	logAi->debug("Player %d: I heard that player %d %s.", playerID.getNum(), player.getNum(),(victoryLossCheckResult.victory() ? "won" : "lost"));
 	if(player == playerID)
 	{
 		if(victoryLossCheckResult.victory())
 		{
-			logAi->debugStream() << "VCAI: I won! Incredible!";
-			logAi->debugStream() << "Turn nr " << myCb->getDate();
+			logAi->debug("VCAI: I won! Incredible!");
+			logAi->debug("Turn nr %d", myCb->getDate());
 		}
 		else
 		{
-			logAi->debugStream() << "VCAI: Player " << player << " lost. It's me. What a disappointment! :(";
+			logAi->debug("VCAI: Player %d lost. It's me. What a disappointment! :(", player.getNum());
 		}
 
 		finish();
@@ -718,7 +718,7 @@ void makePossibleUpgrades(const CArmedInstance *obj)
 
 void VCAI::makeTurn()
 {
-	logGlobal->infoStream() << boost::format("Player %d starting turn") % static_cast<int>(playerID.getNum());
+	logGlobal->info("Player %d starting turn", playerID.getNum());
 
 	MAKING_TURN;
 	boost::shared_lock<boost::shared_mutex> gsLock(cb->getGsMutex());
@@ -768,7 +768,7 @@ void VCAI::makeTurnInternal()
 				continue; //hero might have been removed while we were in this loop
 			if(!hero.first.validAndSet())
 			{
-				logAi->errorStream() << "Hero " << hero.first.name << " present on reserved map. Shouldn't be. ";
+				logAi->error("Hero %s present on reserved map. Shouldn't be.", hero.first.name);
 				continue;
 			}
 
@@ -778,7 +778,7 @@ void VCAI::makeTurnInternal()
 			{
 				if(!obj || !cb->getObj(obj->id))
 				{
-					logAi->errorStream() << "Error: there is wrong object on list for hero " << hero.first->name;
+					logAi->error("Error: there is wrong object on list for hero %s", hero.first->name);
 					continue;
 				}
 				striveToGoal (sptr(Goals::VisitTile(obj->visitablePos()).sethero(hero.first)));
@@ -807,7 +807,7 @@ void VCAI::makeTurnInternal()
 			}
 			if (newMovement == oldMovement) //means our heroes didn't move or didn't re-assign their goals
 			{
-				logAi->warnStream() << "Our heroes don't move anymore, exhaustive decomposition failed";
+				logAi->warn("Our heroes don't move anymore, exhaustive decomposition failed");
 				break;
 			}
 			if (safeCopy.empty())
@@ -838,17 +838,17 @@ void VCAI::makeTurnInternal()
 		for (auto h : cb->getHeroesInfo())
 		{
 			if (h->movement)
-				logAi->warnStream() << boost::format("hero %s has %d MP left") % h->name % h->movement;
+				logAi->warn("Hero %s has %d MP left", h->name, h->movement);
 		}
 	}
 	catch(boost::thread_interrupted &e)
 	{
-		logAi->debugStream() << "Making turn thread has been interrupted. We'll end without calling endTurn.";
+		logAi->debug("Making turn thread has been interrupted. We'll end without calling endTurn.");
 		return;
 	}
 	catch(std::exception &e)
 	{
-		logAi->debugStream() << "Making turn thread has caught an exception: " << e.what();
+		logAi->debug("Making turn thread has caught an exception: %s", e.what());
 	}
 
 	endTurn();
@@ -858,7 +858,7 @@ bool VCAI::goVisitObj(const CGObjectInstance * obj, HeroPtr h)
 {
 	int3 dst = obj->visitablePos();
 	auto sm = getCachedSectorMap(h);
-	logAi->debugStream() << boost::format("%s will try to visit %s at (%s)") % h->name % obj->getObjectName() % strFromInt3(dst);
+	logAi->debug("%s will try to visit %s at (%s)",h->name, obj->getObjectName(), dst());
 	int3 pos = sm->firstTileToGet(h, dst);
 	if (!pos.valid()) //rare case when we are already standing on one of potential objects
 		return false;
@@ -902,7 +902,7 @@ bool VCAI::canGetArmy (const CGHeroInstance * army, const CGHeroInstance * sourc
 
 	if(army->tempOwner != source->tempOwner)
 	{
-		logAi->errorStream() << "Why are we even considering exchange between heroes from different players?";
+		logAi->error("Why are we even considering exchange between heroes from different players?");
 		return false;
 	}
 
@@ -1129,7 +1129,7 @@ bool VCAI::tryBuildStructure(const CGTownInstance * t, BuildingID building, unsi
 {
 	if (maxDays == 0)
 	{
-		logAi->warnStream() << "Request to build building " << building <<  " in 0 days!";
+		logAi->warn("Request to build building %d in 0 days!", building.toEnum());
 		return false;
 	}
 
@@ -1171,7 +1171,7 @@ bool VCAI::tryBuildStructure(const CGTownInstance * t, BuildingID building, unsi
 		{
 			if(!containsSavedRes(b->resources))
 			{
-				logAi->debugStream() << boost::format("Player %d will build %s in town of %s at %s") % playerID % b->Name() % t->name % t->pos;
+				logAi->debug("Player %d will build %s in town of %s at %s", playerID, b->Name(), t->name, t->pos());
 				cb->buildBuilding(t, buildID);
 				return true;
 			}
@@ -1209,7 +1209,7 @@ bool VCAI::tryBuildStructure(const CGTownInstance * t, BuildingID building, unsi
 //{
 //		if (maxDays == 0)
 //	{
-//		logAi->warnStream() << "Request to build building " << building <<  " in 0 days!";
+//		logAi->warn("Request to build building %d in 0 days!", building.toEnum());
 //		return false;
 //	}
 //
@@ -1252,7 +1252,7 @@ bool VCAI::tryBuildStructure(const CGTownInstance * t, BuildingID building, unsi
 //		{
 //			if(!containsSavedRes(b->resources))
 //			{
-//				logAi->debugStream() << boost::format("Player %d will build %s in town of %s at %s") % playerID % b->Name() % t->name % t->pos;
+//				logAi->debug("Player %d will build %s in town of %s at %s", playerID, b->Name(), t->name, t->pos());
 //				return true;
 //			}
 //			continue;
@@ -1522,7 +1522,7 @@ void VCAI::wander(HeroPtr h)
 				boost::sort(townsNotReachable, compareReinforcements);
 				//TODO pick the truly best
 				const CGTownInstance *t = townsNotReachable.back();
-				logAi->debugStream() << boost::format("%s can't reach any town, we'll try to make our way to %s at %s") % h->name % t->name % t->visitablePos();
+				logAi->debug("%s can't reach any town, we'll try to make our way to %s at %s",h->name,t->name, t->visitablePos()());
 				int3 pos1 = h->pos;
 				striveToGoal(sptr(Goals::ClearWayTo(t->visitablePos()).sethero(h)));
 				//if out hero is stuck, we may need to request another hero to clear the way we see
@@ -1551,7 +1551,7 @@ void VCAI::wander(HeroPtr h)
 			}
 			else
 			{
-				logAi->debugStream() << "Nowhere more to go...";
+				logAi->debug("Nowhere more to go...");
 				break;
 			}
 		}
@@ -1563,16 +1563,16 @@ void VCAI::wander(HeroPtr h)
 
 			//wander should not cause heroes to be reserved - they are always considered free
 			const ObjectIdRef&dest = dests.front();
-			logAi->debugStream() << boost::format("Of all %d destinations, object oid=%d seems nice") % dests.size() % dest.id.getNum();
+			logAi->debug("Of all %d destinations, object oid=%d seems nice",dests.size(), dest.id.getNum());
 			if(!goVisitObj(dest, h))
 			{
 				if(!dest)
 				{
-					logAi->debugStream() << boost::format("Visit attempt made the object (id=%d) gone...") % dest.id.getNum();
+					logAi->debug("Visit attempt made the object (id=%d) gone...", dest.id.getNum());
 				}
 				else
 				{
-					logAi->debugStream() << boost::format("Hero %s apparently used all MPs (%d left)") % h->name % h->movement;
+					logAi->debug("Hero %s apparently used all MPs (%d left)", h->name, h->movement);
 					return;
 				}
 			}
@@ -1599,14 +1599,14 @@ void VCAI::setGoal(HeroPtr h, Goals::TSubgoal goal)
 
 void VCAI::completeGoal (Goals::TSubgoal goal)
 {
-	logAi->traceStream() << boost::format("Completing goal: %s") % goal->name();
+	logAi->trace("Completing goal: %s", goal->name());
 	if (const CGHeroInstance * h = goal->hero.get(true))
 	{
 		auto it = lockedHeroes.find(h);
 		if (it != lockedHeroes.end())
 			if (it->second == goal)
 			{
-				logAi->debugStream() << boost::format("%s") % goal->completeMessage();
+				logAi->debug(goal->completeMessage());
 				lockedHeroes.erase(it); //goal fulfilled, free hero
 			}
 	}
@@ -1616,7 +1616,7 @@ void VCAI::completeGoal (Goals::TSubgoal goal)
 		{
 			if (*(p.second) == *goal || p.second->fulfillsMe(goal)) //we could have fulfilled goals of other heroes by chance
 			{
-				logAi->debugStream() << boost::format("%s") % p.second->completeMessage();
+				logAi->debug(p.second->completeMessage());
 				return true;
 			}
 			return false;
@@ -1641,7 +1641,7 @@ void VCAI::battleEnd(const BattleResult *br)
 	assert(status.getBattle() == ONGOING_BATTLE);
 	status.setBattle(ENDING_BATTLE);
 	bool won = br->winner == myCb->battleGetMySide();
-	logAi->debugStream() << boost::format("Player %d: I %s the %s!") % playerID % (won  ? "won" : "lost") % battlename;
+	logAi->debug("Player %d: I %s the %s!", playerID.getNum(), (won  ? "won" : "lost"), battlename);
 	battlename.clear();
 	CAdventureAI::battleEnd(br);
 }
@@ -1665,7 +1665,7 @@ void VCAI::reserveObject(HeroPtr h, const CGObjectInstance *obj)
 {
 	reservedObjs.insert(obj);
 	reservedHeroesMap[h].insert(obj);
-	logAi->debugStream() << "reserved object id=" << obj->id << "; address=" << (intptr_t)obj << "; name=" << obj->getObjectName();
+	logAi->debug("reserved object id=%d; address=%p; name=%s", obj->id ,obj, obj->getObjectName());
 }
 
 void VCAI::unreserveObject(HeroPtr h, const CGObjectInstance *obj)
@@ -1847,7 +1847,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 		}
 	};
 
-	logAi->debugStream() << boost::format("Moving hero %s to tile %s") % h->name % dst;
+	logAi->debug("Moving hero %s to tile %s", h->name, dst());
 	int3 startHpos = h->visitablePos();
 	bool ret = false;
 	if(startHpos == dst)
@@ -1865,7 +1865,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 		cb->getPathsInfo(h.get())->getPath(path, dst);
 		if(path.nodes.empty())
 		{
-			logAi->errorStream() << "Hero " << h->name << " cannot reach " << dst;
+			logAi->error("Hero %s cannot reach %s.", h->name, dst());
 			throw goalFulfilledException (sptr(Goals::VisitTile(dst).sethero(h)));
 		}
 		int i = path.nodes.size()-1;
@@ -2006,7 +2006,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 			vstd::erase_if_present(lockedHeroes, h); //hero seemingly is confused
 			throw cannotFulfillGoalException("Invalid path found!"); //FIXME: should never happen
 		}
-		logAi->debugStream() << boost::format("Hero %s moved from %s to %s. Returning %d.") % h->name % startHpos % h->visitablePos() % ret;
+		logAi->debug("Hero %s moved from %s to %s. Returning %d.", h->name, startHpos(), h->visitablePos()(), ret);
 	}
 	return ret;
 }
@@ -2031,8 +2031,7 @@ void VCAI::tryRealize(Goals::VisitTile & g)
 		throw cannotFulfillGoalException("Cannot visit tile: hero is out of MPs!");
 	if(g.tile == g.hero->visitablePos()  &&  cb->getVisitableObjs(g.hero->visitablePos()).size() < 2)
 	{
-		logAi->warnStream() << boost::format("Why do I want to move hero %s to tile %s? Already standing on that tile! ")
-												% g.hero->name % g.tile;
+		logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", g.hero->name, g.tile());
 		throw goalFulfilledException (sptr(g));
 	}
 	if (ai->moveHeroToTile(g.tile, g.hero.get()))
@@ -2141,7 +2140,7 @@ void VCAI::tryRealize(Goals::Build & g)
 {
 	for(const CGTownInstance *t : cb->getTownsInfo())
 	{
-		logAi->debugStream() << boost::format("Looking into %s") % t->name;
+		logAi->debug("Looking into %s", t->name);
 		buildStructure(t);
 		buildArmyIn(t);
 
@@ -2162,7 +2161,7 @@ void VCAI::tryRealize(Goals::Invalid & g)
 
 void VCAI::tryRealize(Goals::AbstractGoal & g)
 {
-	logAi->debugStream() << boost::format("Attempting realizing goal with code %s") % g.name();
+	logAi->debug("Attempting realizing goal with code %s",g.name());
 	throw cannotFulfillGoalException("Unknown type of goal !");
 }
 
@@ -2224,10 +2223,10 @@ HeroPtr VCAI::primaryHero() const
 
 void VCAI::endTurn()
 {
-	logAi->infoStream() << "Player " << static_cast<int>(playerID.getNum()) << " ends turn";
+	logAi->info("Player %d ends turn", playerID.getNum());
 	if(!status.haveTurn())
 	{
-		logAi->errorStream() << "Not having turn at the end of turn???";
+		logAi->error("Not having turn at the end of turn???");
 	}
 	logAi->debugStream() << "Resources at the end of turn: " << cb->getResourceAmount();
 
@@ -2236,7 +2235,7 @@ void VCAI::endTurn()
 		cb->endTurn();
 	} while(status.haveTurn()); //for some reasons, our request may fail -> stop requesting end of turn only after we've received a confirmation that it's over
 
-	logGlobal->infoStream() << "Player " << static_cast<int>(playerID.getNum()) << " ended turn";
+	logGlobal->infoStream() << "Player %d ended turn", playerID.getNum();
 }
 
 void VCAI::striveToGoal(Goals::TSubgoal ultimateGoal)

+ 5 - 4
Global.h

@@ -242,7 +242,8 @@ template<typename T, size_t N> char (&_ArrayCountObj(const T (&)[N]))[N];
 /* ---------------------------------------------------------------------------- */
 /* VCMI standard library */
 /* ---------------------------------------------------------------------------- */
-#include "lib/logging/CLogger.h"
+#include <vstd/CLoggerBase.h>
+#include "lib/logging/CLogger.h" //todo: remove
 
 void inline handleException()
 {
@@ -252,15 +253,15 @@ void inline handleException()
 	}
 	catch(const std::exception & ex)
 	{
-		logGlobal->errorStream() << ex.what();
+		logGlobal->error(ex.what());
 	}
 	catch(const std::string & ex)
 	{
-		logGlobal->errorStream() << ex;
+		logGlobal->error(ex);
 	}
 	catch(...)
 	{
-		logGlobal->errorStream() << "Sorry, caught unknown exception type. No more info available.";
+		logGlobal->error("Sorry, caught unknown exception type. No more info available.");
 	}
 }
 

+ 1 - 1
VCMI_global.props

@@ -7,7 +7,7 @@
   <PropertyGroup>
     <_PropertySheetDisplayName>VCMI_global</_PropertySheetDisplayName>
     <LibraryPath>$(SolutionDir)..\libs\$(PlatformShortName);$(VCMI_Out);$(LibraryPath)</LibraryPath>
-    <IncludePath>$(SolutionDir)..\include;$(IncludePath)</IncludePath>
+    <IncludePath>$(SolutionDir)..\include;$(SolutionDir)include;$(IncludePath)</IncludePath>
     <OutDir>$(VCMI_Out)\</OutDir>
   </PropertyGroup>
   <ItemDefinitionGroup>

+ 1 - 1
client/CMakeLists.txt

@@ -1,7 +1,7 @@
 project(vcmiclient)
 cmake_minimum_required(VERSION 2.6)
 
-include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
+include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
 include_directories(${SDL_INCLUDE_DIR} ${SDLIMAGE_INCLUDE_DIR} ${SDLMIXER_INCLUDE_DIR} ${SDLTTF_INCLUDE_DIR})
 include_directories(${Boost_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIR} ${FFMPEG_INCLUDE_DIRS})
 

+ 1 - 0
client/VCMI_client.cbp

@@ -78,6 +78,7 @@
 			<Add directory="../client" />
 			<Add directory="$(#ffmpeg.include)" />
 			<Add directory="$(#sdl2.include)" />
+			<Add directory="../include" />
 		</Compiler>
 		<Linker>
 			<Add option="-lole32" />

+ 1 - 1
editor/CMakeLists.txt

@@ -1,7 +1,7 @@
 project(vcmieditor)
 cmake_minimum_required(VERSION 2.8.7)
 
-include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIR})
+include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIR})
 include_directories(${Qt5Widgets_INCLUDE_DIRS})
 
 

+ 114 - 0
include/vstd/CLoggerBase.h

@@ -0,0 +1,114 @@
+/*
+ * CLoggerBase.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+namespace ELogLevel
+{
+	enum ELogLevel
+	{
+		NOT_SET = 0,
+		TRACE,
+		DEBUG,
+		INFO,
+		WARN,
+		ERROR
+	};
+}
+
+namespace vstd
+{
+	class DLL_LINKAGE CLoggerBase
+	{
+	public:
+		virtual ~CLoggerBase(){};
+
+		virtual void log(ELogLevel::ELogLevel level, const std::string & message) const = 0;
+
+		template<typename T, typename ... Args>
+		void log(ELogLevel::ELogLevel level, const std::string & format, T t, Args ... args) const
+		{
+			boost::format fmt(format);
+			makeFormat(fmt, t, args...);
+			log(level, fmt.str());
+		}
+
+		/// Log methods for various log levels
+		inline void error(const std::string & message) const
+		{
+			log(ELogLevel::ERROR, message);
+		};
+
+		template<typename T, typename ... Args>
+		void error(const std::string & format, T t, Args ... args) const
+		{
+			log(ELogLevel::ERROR, format, t, args...);
+		}
+
+		inline void warn(const std::string & message) const
+		{
+			log(ELogLevel::WARN, message);
+		};
+
+		template<typename T, typename ... Args>
+		void warn(const std::string & format, T t, Args ... args) const
+		{
+			log(ELogLevel::WARN, format, t, args...);
+		}
+
+		inline void info(const std::string & message) const
+		{
+			log(ELogLevel::INFO, message);
+		};
+
+		template<typename T, typename ... Args>
+		void info(const std::string & format, T t, Args ... args) const
+		{
+			log(ELogLevel::INFO, format, t, args...);
+		}
+
+		inline void debug(const std::string & message) const
+		{
+			log(ELogLevel::DEBUG, message);
+		};
+
+
+		template<typename T, typename ... Args>
+		void debug(const std::string & format, T t, Args ... args) const
+		{
+			log(ELogLevel::DEBUG, format, t, args...);
+		}
+
+		inline void trace(const std::string & message) const
+		{
+			log(ELogLevel::TRACE, message);
+		};
+
+		template<typename T, typename ... Args>
+		void trace(const std::string & format, T t, Args ... args) const
+		{
+			log(ELogLevel::TRACE, format, t, args...);
+		}
+
+	private:
+		template <typename T>
+		void makeFormat(boost::format & fmt, T t) const
+		{
+			fmt % t;
+		}
+
+		template <typename T, typename ... Args>
+		void makeFormat(boost::format & fmt, T t, Args ... args) const
+		{
+			fmt % t;
+			makeFormat(fmt, args...);
+		}
+	};
+}

+ 1 - 1
launcher/CMakeLists.txt

@@ -1,7 +1,7 @@
 project(vcmilauncher)
 cmake_minimum_required(VERSION 2.8.7)
 
-include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIR})
+include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIR})
 include_directories(${ZLIB_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS})
 
 set(launcher_modmanager_SRCS

+ 4 - 4
lib/CGameInfoCallback.cpp

@@ -78,14 +78,14 @@ const PlayerState * CGameInfoCallback::getPlayer(PlayerColor color, bool verbose
 		else
 		{
 			if (verbose)
-				logGlobal->errorStream() << boost::format("Cannot access player %d info!") % color;
+				logGlobal->error("Cannot access player %d info!", color);
 			return nullptr;
 		}
 	}
 	else
 	{
 		if (verbose)
-			logGlobal->errorStream() << boost::format("Cannot find player %d info!") % color;
+			logGlobal->error("Cannot find player %d info!", color);
 		return nullptr;
 	}
 }
@@ -835,14 +835,14 @@ const TeamState * CGameInfoCallback::getTeam( TeamID teamID ) const
 				return ret;
 			else
 			{
-				logGlobal->errorStream() << boost::format("Illegal attempt to access team data!");
+				logGlobal->error("Illegal attempt to access team data!");
 				return nullptr;
 			}
 		}
 	}
 	else
 	{
-		logGlobal->errorStream() << boost::format("Cannot find info for team %d") % teamID;
+		logGlobal->error("Cannot find info for team %d", teamID);
 		return nullptr;
 	}
 }

+ 1 - 1
lib/CMakeLists.txt

@@ -1,7 +1,7 @@
 project(libvcmi)
 cmake_minimum_required(VERSION 2.6)
 
-include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/lib)
+include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/lib)
 include_directories(${Boost_INCLUDE_DIRS} ${SDL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
 
 set(lib_SRCS

+ 1 - 1
lib/StdInc.h

@@ -4,4 +4,4 @@
 
 // This header should be treated as a pre compiled header file(PCH) in the compiler building settings.
 
-// Here you can add specific libraries and macros which are specific to this project.
+// Here you can add specific libraries and macros which are specific to this project.

+ 3 - 0
lib/VCMI_lib.cbp

@@ -113,12 +113,15 @@
 			<Add directory="." />
 			<Add directory="$(#sdl2.include)" />
 			<Add directory="$(#zlib.include)" />
+			<Add directory="../include" />
 		</Compiler>
 		<Linker>
 			<Add directory="../" />
 			<Add directory="$(#sdl2.lib)" />
 			<Add directory="$(#zlib.lib)" />
 		</Linker>
+		<Unit filename="../Global.h" />
+		<Unit filename="../include/vstd/CLoggerBase.h" />
 		<Unit filename="AI_Base.h" />
 		<Unit filename="BattleAction.cpp" />
 		<Unit filename="BattleAction.h" />

+ 2 - 8
lib/logging/CLogger.cpp

@@ -101,12 +101,6 @@ CLogger::CLogger(const CLoggerDomain & domain) : domain(domain)
 	}
 }
 
-void CLogger::trace(const std::string & message) const { log(ELogLevel::TRACE, message); }
-void CLogger::debug(const std::string & message) const { log(ELogLevel::DEBUG, message); }
-void CLogger::info(const std::string & message) const { log(ELogLevel::INFO, message); }
-void CLogger::warn(const std::string & message) const { log(ELogLevel::WARN, message); }
-void CLogger::error(const std::string & message) const { log(ELogLevel::ERROR, message); }
-
 CLoggerStream CLogger::traceStream() const { return CLoggerStream(*this, ELogLevel::TRACE); }
 CLoggerStream CLogger::debugStream() const { return CLoggerStream(*this, ELogLevel::DEBUG); }
 CLoggerStream CLogger::infoStream() const { return CLoggerStream(*this, ELogLevel::INFO); }
@@ -233,7 +227,7 @@ std::string CLogFormatter::format(const LogRecord & record) const
 	//Format date
 	boost::algorithm::replace_first(message, "%d", boost::posix_time::to_simple_string (record.timeStamp));
 
-	//Format log level 
+	//Format log level
 	std::string level;
 	switch(record.level)
 	{
@@ -331,7 +325,7 @@ void CLogConsoleTarget::write(const LogRecord & record)
 	{
 		const EConsoleTextColor::EConsoleTextColor textColor =
 			coloredOutputEnabled ? colorMapping.getColorFor(record.domain, record.level) : EConsoleTextColor::DEFAULT;
-		
+
 		console->print(message, true, textColor, printToStdErr);
 	}
 	else

+ 3 - 19
lib/logging/CLogger.h

@@ -18,18 +18,9 @@ class CLogger;
 struct LogRecord;
 class ILogTarget;
 
+
 namespace ELogLevel
 {
-	enum ELogLevel
-	{
-		NOT_SET = 0,
-		TRACE,
-		DEBUG,
-		INFO,
-		WARN,
-		ERROR
-	};
-
 	#ifdef VCMI_ANDROID
 		int toAndroid(ELogLevel logLevel);
 	#endif
@@ -79,7 +70,7 @@ private:
 
 /// The logger is used to log messages to certain targets of a specific domain/name.
 /// It is thread-safe and can be used concurrently by several threads.
-class DLL_LINKAGE CLogger
+class DLL_LINKAGE CLogger: public vstd::CLoggerBase
 {
 public:
 	inline ELogLevel::ELogLevel getLevel() const;
@@ -90,13 +81,6 @@ public:
 	static CLogger * getLogger(const CLoggerDomain & domain);
 	static CLogger * getGlobalLogger();
 
-	/// Log methods for various log levels
-	void trace(const std::string & message) const;
-	void debug(const std::string & message) const;
-	void info(const std::string & message) const;
-	void warn(const std::string & message) const;
-	void error(const std::string & message) const;
-
 	/// Log streams for various log levels
 	CLoggerStream traceStream() const;
 	CLoggerStream debugStream() const;
@@ -104,7 +88,7 @@ public:
 	CLoggerStream warnStream() const;
 	CLoggerStream errorStream() const;
 
-	inline void log(ELogLevel::ELogLevel level, const std::string & message) const;
+	void log(ELogLevel::ELogLevel level, const std::string & message) const override;
 
 	void addTarget(std::unique_ptr<ILogTarget> && target);
 	void clearTargets();

+ 1 - 1
lib/spells/CSpellHandler.cpp

@@ -128,7 +128,7 @@ const CSpell::LevelInfo & CSpell::getLevelInfo(const int level) const
 {
 	if(level < 0 || level >= GameConstants::SPELL_SCHOOL_LEVELS)
 	{
-		logGlobal->errorStream() << __FUNCTION__ << " invalid school level " << level;
+		logGlobal->error("CSpell::getLevelInfo invalid school level %d", level);
 		throw new std::runtime_error("Invalid school level");
 	}
 

+ 1 - 1
scripting/erm/CMakeLists.txt

@@ -1,7 +1,7 @@
 project(vcmiERM)
 cmake_minimum_required(VERSION 2.6)
 
-include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIRECTORY})
+include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIRECTORY})
 
 set(lib_SRCS
 		StdInc.cpp

+ 1 - 0
scripting/erm/ERM.cbp

@@ -62,6 +62,7 @@
 			<Add option="-Wno-unused-parameter" />
 			<Add option="-Wno-overloaded-virtual" />
 			<Add directory="$(#boost.include)" />
+			<Add directory="../../include" />
 		</Compiler>
 		<Linker>
 			<Add option="-lboost_program_options$(#boost.libsuffix)" />

+ 1 - 1
server/CMakeLists.txt

@@ -1,7 +1,7 @@
 project(vcmiserver)
 cmake_minimum_required(VERSION 2.6)
 
-include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
+include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib)
 include_directories(${Boost_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIR})
 
 set(server_SRCS

+ 1 - 0
server/VCMI_server.cbp

@@ -74,6 +74,7 @@
 			<Add option="-D_WIN32" />
 			<Add directory="$(#sdl2.include)" />
 			<Add directory="$(#zlib.include)" />
+			<Add directory="../include" />
 		</Compiler>
 		<Linker>
 			<Add option="-lole32" />

+ 1 - 1
test/CMakeLists.txt

@@ -1,7 +1,7 @@
 cmake_minimum_required(VERSION 2.6)
 
 enable_testing()
-include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/test)
+include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/test)
 include_directories(${Boost_INCLUDE_DIRS})
 
 set(test_SRCS

+ 1 - 0
test/Test.cbp

@@ -49,6 +49,7 @@
 			<Add option="-Wno-unused-local-typedefs" />
 			<Add directory="$(#zlib.include)" />
 			<Add directory="$(#boost.include)" />
+			<Add directory="../include" />
 		</Compiler>
 		<Linker>
 			<Add option="-lVCMI_lib" />