Prechádzať zdrojové kódy

Merge branch 'develop' into SpellsRefactoring8

AlexVinS 9 rokov pred
rodič
commit
d993710f8e

+ 38 - 19
AI/VCAI/VCAI.cpp

@@ -115,15 +115,16 @@ void VCAI::heroMoved(const TryMoveHero & details)
 	NET_EVENT_HANDLER;
 
 	validateObject(details.id); //enemy hero may have left visible area
+	auto hero = cb->getHero(details.id);
 	cachedSectorMaps.clear();
 
+	const int3 from = CGHeroInstance::convertPosition(details.start, false),
+		to = CGHeroInstance::convertPosition(details.end, false);
+	const CGObjectInstance *o1 = vstd::frontOrNull(cb->getVisitableObjs(from)),
+		*o2 = vstd::frontOrNull(cb->getVisitableObjs(to));
+
 	if(details.result == TryMoveHero::TELEPORTATION)
 	{
-		const int3 from = CGHeroInstance::convertPosition(details.start, false),
-			to = CGHeroInstance::convertPosition(details.end, false);
-		const CGObjectInstance *o1 = vstd::frontOrNull(cb->getVisitableObjs(from)),
-			*o2 = vstd::frontOrNull(cb->getVisitableObjs(to));
-
 		auto t1 = dynamic_cast<const CGTeleport *>(o1);
 		auto t2 = dynamic_cast<const CGTeleport *>(o2);
 		if(t1 && t2)
@@ -139,6 +140,17 @@ void VCAI::heroMoved(const TryMoveHero & details)
 			}
 		}
 	}
+	else if(details.result == TryMoveHero::EMBARK && hero)
+	{
+		//make sure AI not attempt to visit used boat
+		validateObject(hero->boat);
+	}
+	else if(details.result == TryMoveHero::DISEMBARK && o1)
+	{
+		auto boat = dynamic_cast<const CGBoat *>(o1);
+		if(boat)
+			addVisitableObj(boat);
+	}
 }
 
 void VCAI::stackChagedCount(const StackLocation &location, const TQuantity &change, bool isAbsolute)
@@ -210,7 +222,7 @@ void VCAI::gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryL
 {
 	LOG_TRACE_PARAMS(logAi, "victoryLossCheckResult '%s'", victoryLossCheckResult.messageToSelf);
 	NET_EVENT_HANDLER;
-	logAi->debug("Player %d: I heard that player %d %s.", playerID.getNum(), player.getNum(),(victoryLossCheckResult.victory() ? "won" : "lost"));
+	logAi->debug("Player %d (%s): I heard that player %d (%s) %s.", playerID, playerID.getStr(), player, player.getStr(),(victoryLossCheckResult.victory() ? "won" : "lost"));
 	if(player == playerID)
 	{
 		if(victoryLossCheckResult.victory())
@@ -220,7 +232,7 @@ void VCAI::gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryL
 		}
 		else
 		{
-			logAi->debug("VCAI: Player %d lost. It's me. What a disappointment! :(", player.getNum());
+			logAi->debug("VCAI: Player %d (%s) lost. It's me. What a disappointment! :(", player, player.getStr());
 		}
 
 		finish();
@@ -309,7 +321,7 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q
 	auto firstHero = cb->getHero(hero1);
 	auto secondHero = cb->getHero(hero2);
 
-	status.addQuery(query, boost::str(boost::format("Exchange between heroes %s and %s") % firstHero->name % secondHero->name));
+	status.addQuery(query, boost::str(boost::format("Exchange between heroes %s (%d) and %s (%d)") % firstHero->name % firstHero->tempOwner % secondHero->name % secondHero->tempOwner));
 
 	requestActionASAP([=]()
 	{
@@ -328,9 +340,13 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q
 			this->pickBestArtifacts(h1, h2);
 		};
 
-		if (goalpriority1 > goalpriority2)
+		//Do not attempt army or artifacts exchange if we visited ally player
+		//Visits can still be useful if hero have skills like Scholar
+		if(firstHero->tempOwner != secondHero->tempOwner)
+			logAi->debug("Heroes owned by different players. Do not exchange army or artifacts.");
+		else if(goalpriority1 > goalpriority2)
 			transferFrom2to1 (firstHero, secondHero);
-		else if (goalpriority1 < goalpriority2)
+		else if(goalpriority1 < goalpriority2)
 			transferFrom2to1 (secondHero, firstHero);
 		else //regular criteria
 		{
@@ -405,6 +421,9 @@ void VCAI::objectRemoved(const CGObjectInstance *obj)
 		{
 			vstd::erase_if_present(visitableObjs, hero->boat);
 			vstd::erase_if_present(alreadyVisited, hero->boat);
+
+			for (auto h : cb->getHeroesInfo())
+				unreserveObject(h, hero->boat);
 		}
 	}
 
@@ -524,16 +543,13 @@ void VCAI::objectPropertyChanged(const SetObjectProperty * sop)
 	NET_EVENT_HANDLER;
 	if(sop->what == ObjProperty::OWNER)
 	{
-		//we don't want to visit know object twice (do we really?)
-		if(sop->val == playerID.getNum())
-			vstd::erase_if_present(visitableObjs, myCb->getObj(sop->id));
-		else if(myCb->getPlayerRelations(playerID, (PlayerColor)sop->val) == PlayerRelations::ENEMIES)
+		if(myCb->getPlayerRelations(playerID, (PlayerColor)sop->val) == PlayerRelations::ENEMIES)
 		{
 			//we want to visit objects owned by oppponents
 			auto obj = myCb->getObj(sop->id, false);
 			if (obj)
 			{
-				addVisitableObj(obj);
+				addVisitableObj(obj); // TODO: Remove once save compatability broken. In past owned objects were removed from this set
 				vstd::erase_if_present(alreadyVisited, obj);
 			}
 		}
@@ -728,7 +744,7 @@ void makePossibleUpgrades(const CArmedInstance *obj)
 
 void VCAI::makeTurn()
 {
-	logGlobal->info("Player %d starting turn", playerID.getNum());
+	logGlobal->info("Player %d (%s) starting turn", playerID, playerID.getStr());
 
 	MAKING_TURN;
 	boost::shared_lock<boost::shared_mutex> gsLock(cb->getGsMutex());
@@ -1651,7 +1667,7 @@ void VCAI::battleEnd(const BattleResult *br)
 	assert(status.getBattle() == ONGOING_BATTLE);
 	status.setBattle(ENDING_BATTLE);
 	bool won = br->winner == myCb->battleGetMySide();
-	logAi->debug("Player %d: I %s the %s!", playerID.getNum(), (won  ? "won" : "lost"), battlename);
+	logAi->debug("Player %d (%s): I %s the %s!", playerID, playerID.getStr(), (won  ? "won" : "lost"), battlename);
 	battlename.clear();
 	CAdventureAI::battleEnd(br);
 }
@@ -2233,7 +2249,7 @@ HeroPtr VCAI::primaryHero() const
 
 void VCAI::endTurn()
 {
-	logAi->info("Player %d ends turn", playerID.getNum());
+	logAi->info("Player %d (%s) ends turn", playerID, playerID.getStr());
 	if(!status.haveTurn())
 	{
 		logAi->error("Not having turn at the end of turn???");
@@ -2245,7 +2261,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 %d ended turn", playerID.getNum();
+	logGlobal->info("Player %d (%s) ended turn", playerID, playerID.getStr());
 }
 
 void VCAI::striveToGoal(Goals::TSubgoal ultimateGoal)
@@ -2501,6 +2517,9 @@ void VCAI::performTypicalActions()
 {
 	for(auto h : getUnblockedHeroes())
 	{
+		if(!h) //hero might be lost. getUnblockedHeroes() called once on start of turn
+			continue;
+
 		logAi->debugStream() << boost::format("Looking into %s, MP=%d") % h->name.c_str() % h->movement;
 		makePossibleUpgrades(*h);
 		pickBestArtifacts(*h);

+ 4 - 0
Global.h

@@ -146,6 +146,10 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #define BOOST_THREAD_DONT_PROVIDE_THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE 1
 #define BOOST_BIND_NO_PLACEHOLDERS
 
+#if defined(_MSC_VER) && (_MSC_VER == 1900)
+#define BOOST_NO_CXX11_VARIADIC_TEMPLATES //Variadic templates are buggy in VS2015, so turn this off to avoid compile errors
+#endif
+
 #include <boost/algorithm/string.hpp>
 #include <boost/cstdint.hpp>
 #include <boost/current_function.hpp>

+ 5 - 0
client/CMT.cpp

@@ -240,6 +240,7 @@ int main(int argc, char** argv)
 		("autoSkip", "automatically skip turns in GUI")
 		("disable-video", "disable video player")
 		("nointro,i", "skips intro movies")
+		("donotstartserver,d","do not attempt to start server and just connect to it instead server")
         ("loadserver","specifies we are the multiplayer server for loaded games")
         ("loadnumplayers",po::value<int>(),"specifies the number of players connecting to a multiplayer game")
         ("loadhumanplayerindices",po::value<std::vector<int>>(),"Indexes of human players (0=Red, etc.)")
@@ -277,6 +278,10 @@ int main(int argc, char** argv)
 		gNoGUI = true;
 		vm.insert(std::pair<std::string, po::variable_value>("onlyAI", po::variable_value()));
 	}
+	if(vm.count("donotstartserver"))
+	{
+		CServerHandler::DO_NOT_START_SERVER = true;
+	}
 
 	// Have effect on X11 system only (Linux).
 	// For whatever reason in fullscreen mode SDL takes "raw" mouse input from DGA X11 extension

+ 4 - 0
client/CPreGame.cpp

@@ -719,6 +719,10 @@ CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMulti
 		else if(current)
 		{
 			SelectMap sm(*current);
+			// FIXME: Super dirty hack to avoid crash on multiplayer game start.
+			// There is some issues with TriggeredEvent serialization that cause it.
+			// We'll look into them once refactored serializer fixed and merged
+			sm.mapInfo->mapHeader->triggeredEvents.clear();
 			*serv << &sm;
 
 			UpdateStartOptions uso(sInfo);

+ 8 - 0
client/Client.cpp

@@ -928,10 +928,15 @@ std::string CClient::aiNameForPlayer(const PlayerSettings &ps, bool battleAI)
 	return goodAI;
 }
 
+bool CServerHandler::DO_NOT_START_SERVER = false;
 
 void CServerHandler::startServer()
 {
+	if(DO_NOT_START_SERVER)
+		return;
+
 	th.update();
+
 	serverThread = new boost::thread(&CServerHandler::callServer, this); //runs server executable;
 	if(verbose)
 		logNetwork->infoStream() << "Setting up thread calling server: " << th.getDiff();
@@ -939,6 +944,9 @@ void CServerHandler::startServer()
 
 void CServerHandler::waitForServer()
 {
+	if(DO_NOT_START_SERVER)
+		return;
+
 	if(!serverThread)
 		startServer();
 

+ 2 - 0
client/Client.h

@@ -42,6 +42,8 @@ class CServerHandler
 private:
 	void callServer(); //calls server via system(), should be called as thread
 public:
+	static bool DO_NOT_START_SERVER;
+
 	CStopWatch th;
 	boost::thread *serverThread; //thread that called system to run server
 	SharedMem *shared; //interprocess memory (for waiting for server)

+ 2 - 0
client/windows/CCastleInterface.cpp

@@ -627,6 +627,8 @@ void CCastleBuildings::buildingClicked(BuildingID building)
 		case BuildingID::SHIPYARD:
 				if(town->shipyardStatus() == IBoatGenerator::GOOD)
 					LOCPLINT->showShipyardDialog(town);
+				else if(town->shipyardStatus() == IBoatGenerator::BOAT_ALREADY_BUILT)
+					LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[51]);
 				break;
 
 		case BuildingID::FORT:

+ 18 - 0
lib/CGameInfoCallback.cpp

@@ -520,6 +520,24 @@ EBuildingState::EBuildingState CGameInfoCallback::canBuildStructure( const CGTow
 	if(vstd::contains(t->forbiddenBuildings, ID))
 		return EBuildingState::FORBIDDEN; //forbidden
 
+	auto possiblyNotBuiltTest = [&](BuildingID id) -> bool
+	{
+		return ((id == BuildingID::CAPITOL) ? true : !t->hasBuilt(id));
+	};
+
+	std::function<bool(BuildingID id)> allowedTest = [&](BuildingID id) -> bool
+	{
+		if (vstd::contains(t->forbiddenBuildings, id))
+		{
+			return false;
+		}
+
+		return t->genBuildingRequirements(id, true).satisfiable(allowedTest, possiblyNotBuiltTest);
+	};
+
+	if (!t->genBuildingRequirements(ID, true).satisfiable(allowedTest, possiblyNotBuiltTest))
+		return EBuildingState::FORBIDDEN;
+
 	if(ID == BuildingID::CAPITOL)
 	{
 		const PlayerState *ps = getPlayer(t->tempOwner, false);

+ 8 - 5
lib/CGameState.cpp

@@ -2139,8 +2139,6 @@ void CGameState::updateRumor()
 	int rumorId = -1, rumorExtra = -1;
 	auto & rand = getRandomGenerator();
 	rumor.type = *RandomGeneratorUtil::nextItem(rumorTypes, rand);
-	if(!map->rumors.size() && rumor.type == RumorState::TYPE_MAP)
-		rumor.type = RumorState::TYPE_RAND;
 
 	do
 	{
@@ -2181,9 +2179,14 @@ void CGameState::updateRumor()
 			break;
 		}
 		case RumorState::TYPE_MAP:
-			rumorId = rand.nextInt(map->rumors.size() - 1);
-
-			break;
+			// Makes sure that map rumors only used if there enough rumors too choose from
+			if(map->rumors.size() && (map->rumors.size() > 1 || !rumor.last.count(RumorState::TYPE_MAP)))
+			{
+				rumorId = rand.nextInt(map->rumors.size() - 1);
+				break;
+			}
+			else
+				rumor.type = RumorState::TYPE_RAND;
 
 		case RumorState::TYPE_RAND:
 			do

+ 28 - 0
lib/GameConstants.cpp

@@ -17,6 +17,8 @@
 #include "CArtHandler.h"
 #include "CCreatureHandler.h"
 #include "spells/CSpellHandler.h"
+#include "StringConstants.h"
+#include "CGeneralTextHandler.h"
 
 const SlotID SlotID::COMMANDER_SLOT_PLACEHOLDER = SlotID(-2);
 const SlotID SlotID::SUMMONED_SLOT_PLACEHOLDER = SlotID(-3);
@@ -57,6 +59,32 @@ bool PlayerColor::isValidPlayer() const
 	return num < PLAYER_LIMIT_I;
 }
 
+std::string PlayerColor::getStr(bool L10n) const
+{
+	std::string ret = "unnamed";
+	if(isValidPlayer())
+	{
+		if(L10n)
+			ret = VLC->generaltexth->colors[num];
+		else
+			ret = GameConstants::PLAYER_COLOR_NAMES[num];
+	}
+	else if(L10n)
+	{
+		ret = VLC->generaltexth->allTexts[508];
+		ret[0] = std::tolower(ret[0]);
+	}
+
+	return ret;
+}
+
+std::string PlayerColor::getStrCap(bool L10n) const
+{
+	std::string ret = getStr(L10n);
+	ret[0] = std::toupper(ret[0]);
+	return ret;
+}
+
 std::ostream & operator<<(std::ostream & os, const Battle::ActionType actionType)
 {
 	static const std::map<Battle::ActionType, std::string> actionTypeToString =

+ 3 - 0
lib/GameConstants.h

@@ -262,6 +262,9 @@ class PlayerColor : public BaseForID<PlayerColor, ui8>
 
 	DLL_LINKAGE bool isValidPlayer() const; //valid means < PLAYER_LIMIT (especially non-neutral)
 
+	DLL_LINKAGE std::string getStr(bool L10n = false) const;
+	DLL_LINKAGE std::string getStrCap(bool L10n = false) const;
+
 	friend class CGameInfoCallback;
 	friend class CNonConstInfoCallback;
 };

+ 147 - 0
lib/LogicalExpression.h

@@ -99,6 +99,129 @@ namespace LogicalExpressionDetail
 		}
 	};
 
+	template <typename ContainedClass>
+	class SatisfiabilityVisitor;
+
+	template <typename ContainedClass>
+	class FalsifiabilityVisitor;
+
+	template <typename ContainedClass>
+	class PossibilityVisitor : public boost::static_visitor<bool>
+	{
+		typedef ExpressionBase<ContainedClass> Base;
+
+	protected:
+		std::function<bool(const typename Base::Value &)> satisfiabilityTest;
+		std::function<bool(const typename Base::Value &)> falsifiabilityTest;
+		SatisfiabilityVisitor<ContainedClass> *satisfiabilityVisitor;
+		FalsifiabilityVisitor<ContainedClass> *falsifiabilityVisitor;
+
+		size_t countSatisfiable(const std::vector<typename Base::Variant> & element) const
+		{
+			return boost::range::count_if(element, [&](const typename Base::Variant & expr)
+			{
+				return boost::apply_visitor(*satisfiabilityVisitor, expr);
+			});
+		}
+
+		size_t countFalsifiable(const std::vector<typename Base::Variant> & element) const
+		{
+			return boost::range::count_if(element, [&](const typename Base::Variant & expr)
+			{
+				return boost::apply_visitor(*falsifiabilityVisitor, expr);
+			});
+		}
+
+	public:
+		PossibilityVisitor(std::function<bool (const typename Base::Value &)> satisfiabilityTest,
+		                   std::function<bool (const typename Base::Value &)> falsifiabilityTest):
+			satisfiabilityTest(satisfiabilityTest),
+			falsifiabilityTest(falsifiabilityTest),
+			satisfiabilityVisitor(nullptr),
+			falsifiabilityVisitor(nullptr)
+		{}
+
+		void setSatisfiabilityVisitor(SatisfiabilityVisitor<ContainedClass> *satisfiabilityVisitor)
+		{
+			this->satisfiabilityVisitor = satisfiabilityVisitor;
+		}
+
+		void setFalsifiabilityVisitor(FalsifiabilityVisitor<ContainedClass> *falsifiabilityVisitor)
+		{
+			this->falsifiabilityVisitor = falsifiabilityVisitor;
+		}
+	};
+
+	/// Visitor to test whether expression's value can be true
+	template <typename ContainedClass>
+	class SatisfiabilityVisitor : public PossibilityVisitor<ContainedClass>
+	{
+		typedef ExpressionBase<ContainedClass> Base;
+
+	public:
+		SatisfiabilityVisitor(std::function<bool (const typename Base::Value &)> satisfiabilityTest,
+		                      std::function<bool (const typename Base::Value &)> falsifiabilityTest):
+			PossibilityVisitor<ContainedClass>(satisfiabilityTest, falsifiabilityTest)
+		{
+			this->setSatisfiabilityVisitor(this);
+		}
+
+		bool operator()(const typename Base::OperatorAny & element) const
+		{
+			return this->countSatisfiable(element.expressions) != 0;
+		}
+
+		bool operator()(const typename Base::OperatorAll & element) const
+		{
+			return this->countSatisfiable(element.expressions) == element.expressions.size();
+		}
+
+		bool operator()(const typename Base::OperatorNone & element) const
+		{
+			return this->countFalsifiable(element.expressions) == element.expressions.size();
+		}
+
+		bool operator()(const typename Base::Value & element) const
+		{
+			return this->satisfiabilityTest(element);
+		}
+	};
+
+	/// Visitor to test whether expression's value can be false
+	template <typename ContainedClass>
+	class FalsifiabilityVisitor : public PossibilityVisitor<ContainedClass>
+	{
+		typedef ExpressionBase<ContainedClass> Base;
+
+	public:
+		FalsifiabilityVisitor(std::function<bool (const typename Base::Value &)> satisfiabilityTest,
+		                      std::function<bool (const typename Base::Value &)> falsifiabilityTest):
+			PossibilityVisitor<ContainedClass>(satisfiabilityTest, falsifiabilityTest)
+		{
+			this->setFalsifiabilityVisitor(this);
+		}
+
+		bool operator()(const typename Base::OperatorAny & element) const
+		{
+			return this->countFalsifiable(element.expressions) == element.expressions.size();
+		}
+
+		bool operator()(const typename Base::OperatorAll & element) const
+		{
+			return this->countFalsifiable(element.expressions) != 0;
+		}
+
+		bool operator()(const typename Base::OperatorNone & element) const
+		{
+			return this->countSatisfiable(element.expressions) != 0;
+		}
+
+		bool operator()(const typename Base::Value & element) const
+		{
+			return this->falsifiabilityTest(element);
+		}
+	};
+
 	/// visitor that is trying to generates candidates that must be fulfilled
 	/// to complete this expression
 	template <typename ContainedClass>
@@ -436,6 +559,30 @@ public:
 		return boost::apply_visitor(testVisitor, data);
 	}
 
+	/// calculates if expression can evaluate to "true".
+	bool satisfiable(std::function<bool(const Value &)> satisfiabilityTest, std::function<bool(const Value &)> falsifiabilityTest) const
+	{
+		LogicalExpressionDetail::SatisfiabilityVisitor<Value> satisfiabilityVisitor(satisfiabilityTest, falsifiabilityTest);
+		LogicalExpressionDetail::FalsifiabilityVisitor<Value> falsifiabilityVisitor(satisfiabilityTest, falsifiabilityTest);
+
+		satisfiabilityVisitor.setFalsifiabilityVisitor(&falsifiabilityVisitor);
+		falsifiabilityVisitor.setSatisfiabilityVisitor(&satisfiabilityVisitor);
+
+		return boost::apply_visitor(satisfiabilityVisitor, data);
+	}
+
+	/// calculates if expression can evaluate to "false".
+	bool falsifiable(std::function<bool(const Value &)> satisfiabilityTest, std::function<bool(const Value &)> falsifiabilityTest) const
+	{
+		LogicalExpressionDetail::SatisfiabilityVisitor<Value> satisfiabilityVisitor(satisfiabilityTest);
+		LogicalExpressionDetail::FalsifiabilityVisitor<Value> falsifiabilityVisitor(falsifiabilityTest);
+
+		satisfiabilityVisitor.setFalsifiabilityVisitor(&falsifiabilityVisitor);
+		falsifiabilityVisitor.setFalsifiabilityVisitor(&satisfiabilityVisitor);
+
+		return boost::apply_visitor(falsifiabilityVisitor, data);
+	}
+
 	/// generates list of candidates that can be fulfilled by caller (like AI)
 	std::vector<Value> getFulfillmentCandidates(std::function<bool(const Value &)> toBool) const
 	{

+ 9 - 4
lib/mapObjects/CGTownInstance.cpp

@@ -1124,7 +1124,7 @@ bool CGTownInstance::hasBuilt(BuildingID buildingID) const
 	return vstd::contains(builtBuildings, buildingID);
 }
 
-CBuilding::TRequired CGTownInstance::genBuildingRequirements(BuildingID buildID) const
+CBuilding::TRequired CGTownInstance::genBuildingRequirements(BuildingID buildID, bool deep) const
 {
 	const CBuilding * building = town->buildings.at(buildID);
 
@@ -1132,17 +1132,22 @@ CBuilding::TRequired CGTownInstance::genBuildingRequirements(BuildingID buildID)
 	[&](const BuildingID & id) -> CBuilding::TRequired::Variant
 	{
 		const CBuilding * build = town->buildings.at(id);
+		CBuilding::TRequired::OperatorAll requirements;
 
 		if (!hasBuilt(id))
-			return id;
+		{
+			requirements.expressions.push_back(id);
 
-		CBuilding::TRequired::OperatorAll requirements;
+			if (!deep)
+			{
+				return requirements;
+			}
+		}
 
 		if (build->upgrade != BuildingID::NONE)
 			requirements.expressions.push_back(dependTest(build->upgrade));
 
 		requirements.expressions.push_back(build->requirements.morph(dependTest));
-
 		return requirements;
 	};
 

+ 1 - 1
lib/mapObjects/CGTownInstance.h

@@ -238,7 +238,7 @@ public:
 	bool armedGarrison() const; //true if town has creatures in garrison or garrisoned hero
 	int getTownLevel() const;
 
-	CBuilding::TRequired genBuildingRequirements(BuildingID build) const;
+	CBuilding::TRequired genBuildingRequirements(BuildingID build, bool deep = false) const;
 
 	void mergeGarrisonOnSiege() const; // merge garrison into army of visiting hero
 	void removeCapitols (PlayerColor owner) const;

+ 3 - 1
lib/mapObjects/CQuest.cpp

@@ -498,7 +498,9 @@ IQuestObject::IQuestObject():
 
 IQuestObject::~IQuestObject()
 {
-	delete quest;
+	///Information about quest should remain accessible even if IQuestObject removed from map
+	///All CQuest objects are freed in CMap destructor
+	//delete quest;
 }
 
 bool IQuestObject::checkQuest(const CGHeroInstance* h) const

+ 4 - 4
lib/mapObjects/MiscObjects.cpp

@@ -1607,14 +1607,14 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const
 	{
 		iw.text.addTxt(MetaString::ADVOB_TXT,131);
 	}
-	else if(ID == Obj::SHRINE_OF_MAGIC_THOUGHT  && !h->getSecSkillLevel(SecondarySkill::WISDOM)) //it's third level spell and hero doesn't have wisdom
-	{
-		iw.text.addTxt(MetaString::ADVOB_TXT,130);
-	}
 	else if(vstd::contains(h->spells,spell))//hero already knows the spell
 	{
 		iw.text.addTxt(MetaString::ADVOB_TXT,174);
 	}
+	else if(ID == Obj::SHRINE_OF_MAGIC_THOUGHT  && !h->getSecSkillLevel(SecondarySkill::WISDOM)) //it's third level spell and hero doesn't have wisdom
+	{
+		iw.text.addTxt(MetaString::ADVOB_TXT,130);
+	}
 	else //give spell
 	{
 		std::set<SpellID> spells;

+ 3 - 0
lib/mapping/CMap.cpp

@@ -245,6 +245,9 @@ CMap::~CMap()
 
 	for(auto obj : objects)
 		obj.dellNull();
+
+	for(auto quest : quests)
+		quest.dellNull();
 }
 
 void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total)

+ 3 - 3
server/CGameHandler.cpp

@@ -945,8 +945,8 @@ void CGameHandler::handleConnection(std::set<PlayerColor> players, CConnection &
 
 				packType = typeList.getTypeID(pack); //get the id of type
 
-				logGlobal->trace("Received client message (request %d by player %d) of type with ID=%d (%s).\n",
-					requestID, player.getNum(), packType, typeid(*pack).name());
+				logGlobal->trace("Received client message (request %d by player %d (%s)) of type with ID=%d (%s).\n",
+					requestID, player, player.getStr(), packType, typeid(*pack).name());
 			}
 
 			//prepare struct informing that action was applied
@@ -1963,7 +1963,7 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo
 		return false;
 	}
 
-	logGlobal->trace("Player %d wants to move hero %d from %s to %s", asker.getNum(), hid.getNum(), h->pos(), dst());
+	logGlobal->trace("Player %d (%s) wants to move hero %d from %s to %s", asker, asker.getStr(), hid.getNum(), h->pos(), dst());
 	const int3 hmpos = CGHeroInstance::convertPosition(dst, false);
 
 	if(!gs->map->isInTheMap(hmpos))

+ 2 - 1
server/CVCMIServer.cpp

@@ -579,7 +579,8 @@ int main(int argc, char** argv)
 	logConfig.configureDefault();
 
 	handleCommandOptions(argc, argv);
-	port = cmdLineOptions["port"].as<int>();
+	if(cmdLineOptions.count("port"))
+		port = cmdLineOptions["port"].as<int>();
 	logNetwork->info("Port %d will be used.", port);
 
 	preinitDLL(console);