Browse Source

Merge remote-tracking branch 'remotes/origin/develop' into issue/2306
s reverts commit fa8a2826963b3089a758beea6e61af0a660bdbf6.

Conflicts:
AI/VCAI/VCAI.cpp

Conflicts:
AI/VCAI/VCAI.cpp
client/windows/CAdvmapInterface.cpp
lib/CPathfinder.cpp
lib/CPathfinder.h

AlexVinS 10 years ago
parent
commit
0fab319c73
100 changed files with 2390 additions and 1176 deletions
  1. 3 0
      .gitignore
  2. 1 1
      AI/EmptyAI/CEmptyAI.cpp
  3. 1 1
      AI/EmptyAI/CEmptyAI.h
  4. 6 0
      AI/StupidAI/StupidAI.cpp
  5. 9 5
      AI/VCAI/AIUtility.cpp
  6. 2 3
      AI/VCAI/AIUtility.h
  7. 5 3
      AI/VCAI/Fuzzy.cpp
  8. 13 8
      AI/VCAI/Goals.cpp
  9. 84 36
      AI/VCAI/VCAI.cpp
  10. 5 20
      AI/VCAI/VCAI.h
  11. 1 11
      CCallback.cpp
  12. 0 1
      CCallback.h
  13. 10 0
      ChangeLog
  14. 12 0
      Global.h
  15. 15 4
      client/CMT.cpp
  16. 38 20
      client/CPlayerInterface.cpp
  17. 2 2
      client/CPlayerInterface.h
  18. 5 4
      client/CPreGame.cpp
  19. 11 27
      client/CVideoHandler.cpp
  20. 27 12
      client/CVideoHandler.h
  21. 1 0
      client/Graphics.cpp
  22. 4 3
      client/NetPacksClient.cpp
  23. 4 4
      client/battle/CBattleAnimations.cpp
  24. 1 0
      client/battle/CBattleInterfaceClasses.cpp
  25. 0 7
      client/gui/Geometries.h
  26. 0 9
      client/gui/SDL_Extensions.h
  27. 6 0
      client/mapHandler.cpp
  28. 56 127
      client/windows/CAdvmapInterface.cpp
  29. 1 1
      client/windows/CQuestLog.cpp
  30. 4 2
      client/windows/GUIClasses.cpp
  31. 1 1
      config/resolutions.json
  32. 69 1
      config/schemas/settings.json
  33. 1 1
      launcher/modManager/cmodlistmodel_moc.h
  34. 2 1
      lib/BattleState.cpp
  35. 0 3
      lib/BattleState.h
  36. 2 2
      lib/CArtHandler.cpp
  37. 2 0
      lib/CBattleCallback.cpp
  38. 1 1
      lib/CConsoleHandler.cpp
  39. 39 4
      lib/CGameInfoCallback.cpp
  40. 1 1
      lib/CGameInfoCallback.h
  41. 3 1
      lib/CGameInterface.h
  42. 163 111
      lib/CGameState.cpp
  43. 26 119
      lib/CGameState.h
  44. 73 0
      lib/CGameStateFwd.h
  45. 1 0
      lib/CGeneralTextHandler.cpp
  46. 1 0
      lib/CGeneralTextHandler.h
  47. 2 0
      lib/CMakeLists.txt
  48. 1 1
      lib/CModHandler.cpp
  49. 835 203
      lib/CPathfinder.cpp
  50. 186 32
      lib/CPathfinder.h
  51. 66 0
      lib/CPlayerState.h
  52. 2 6
      lib/CTownHandler.cpp
  53. 2 0
      lib/Connection.cpp
  54. 2 2
      lib/Connection.h
  55. 23 52
      lib/GameConstants.cpp
  56. 112 28
      lib/GameConstants.h
  57. 23 12
      lib/HeroBonus.cpp
  58. 29 2
      lib/HeroBonus.h
  59. 11 3
      lib/IGameCallback.cpp
  60. 1 1
      lib/IGameCallback.h
  61. 3 2
      lib/NetPacks.h
  62. 13 3
      lib/NetPacksLib.cpp
  63. 8 8
      lib/StartInfo.h
  64. 3 3
      lib/VCMIDirs.cpp
  65. 12 4
      lib/int3.h
  66. 2 1
      lib/mapObjects/CArmedInstance.cpp
  67. 55 53
      lib/mapObjects/CGHeroInstance.cpp
  68. 19 12
      lib/mapObjects/CGHeroInstance.h
  69. 2 1
      lib/mapObjects/CGMarket.cpp
  70. 16 1
      lib/mapObjects/CGPandoraBox.cpp
  71. 3 1
      lib/mapObjects/CGPandoraBox.h
  72. 3 1
      lib/mapObjects/CGTownInstance.cpp
  73. 8 7
      lib/mapObjects/CGTownInstance.h
  74. 3 1
      lib/mapObjects/CObjectHandler.cpp
  75. 4 0
      lib/mapObjects/CObjectHandler.h
  76. 1 0
      lib/mapObjects/CQuest.cpp
  77. 1 0
      lib/mapObjects/CRewardableObject.cpp
  78. 45 32
      lib/mapObjects/MiscObjects.cpp
  79. 25 17
      lib/mapObjects/MiscObjects.h
  80. 2 2
      lib/mapping/CCampaignHandler.cpp
  81. 1 0
      lib/mapping/CDrawRoadsOperation.cpp
  82. 1 1
      lib/mapping/CDrawRoadsOperation.h
  83. 18 4
      lib/mapping/CMap.cpp
  84. 1 99
      lib/mapping/CMap.h
  85. 99 0
      lib/mapping/CMapDefines.h
  86. 1 0
      lib/mapping/CMapEditManager.cpp
  87. 3 1
      lib/mapping/CMapEditManager.h
  88. 4 4
      lib/mapping/CMapService.cpp
  89. 4 4
      lib/mapping/MapFormatH3M.cpp
  90. 1 1
      lib/mapping/MapFormatH3M.h
  91. 1 3
      lib/registerTypes/RegisterTypes.h
  92. 6 1
      lib/rmg/CMapGenOptions.cpp
  93. 2 0
      lib/rmg/CMapGenOptions.h
  94. 1 1
      lib/rmg/CMapGenerator.cpp
  95. 7 6
      lib/rmg/CRmgTemplateZone.cpp
  96. 1 0
      lib/rmg/CZonePlacer.cpp
  97. 0 2
      lib/rmg/CZonePlacer.h
  98. 2 0
      lib/spells/AdventureSpellMechanics.cpp
  99. 1 0
      lib/spells/BattleSpellMechanics.cpp
  100. 1 1
      lib/spells/CSpellHandler.cpp

+ 3 - 0
.gitignore

@@ -32,3 +32,6 @@ build-*
 CMakeLists.txt.user.*
 Doxyfile
 doc/*
+VCMI_VS11.sdf
+*.ipch
+VCMI_VS11.opensdf

+ 1 - 1
AI/EmptyAI/CEmptyAI.cpp

@@ -30,7 +30,7 @@ void CEmptyAI::showBlockingDialog(const std::string &text, const std::vector<Com
 	cb->selectionMade(0, askID);
 }
 
-void CEmptyAI::showTeleportDialog(TeleportChannelID channel, std::vector<ObjectInstanceID> exits, bool impassable, QueryID askID)
+void CEmptyAI::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID)
 {
 	cb->selectionMade(0, askID);
 }

+ 1 - 1
AI/EmptyAI/CEmptyAI.h

@@ -15,7 +15,7 @@ public:
 	void heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID) override;
 	void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override;
 	void showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel) override;
-	void showTeleportDialog(TeleportChannelID channel, std::vector<ObjectInstanceID> exits, bool impassable, QueryID askID) override;
+	void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
 	void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override;
 };
 

+ 6 - 0
AI/StupidAI/StupidAI.cpp

@@ -153,6 +153,12 @@ BattleAction CStupidAI::activeStack( const CStack * stack )
 		}
 	}
 
+	for ( auto & enemy : enemiesReachable )
+		enemy.calcDmg( stack );
+
+	for ( auto & enemy : enemiesShootable )
+		enemy.calcDmg( stack );
+
 	if(enemiesShootable.size())
 	{
 		const EnemyInfo &ei= *std::max_element(enemiesShootable.begin(), enemiesShootable.end(), isMoreProfitable);

+ 9 - 5
AI/VCAI/AIUtility.cpp

@@ -7,6 +7,10 @@
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CHeroHandler.h"
 #include "../../lib/mapObjects/CBank.h"
+#include "../../lib/mapObjects/CGTownInstance.h"
+#include "../../lib/mapObjects/CQuest.h"
+#include "../../lib/CPathfinder.h"
+#include "../../lib/mapping/CMapDefines.h"
 
 /*
  * AIUtility.cpp, part of VCMI engine
@@ -150,7 +154,7 @@ void foreach_neighbour(const int3 &pos, std::function<void(const int3& pos)> foo
 {
 	CCallback * cbp = cb.get(); // avoid costly retrieval of thread-specific pointer
 
-	for(const int3 &dir : dirs)
+	for(const int3 &dir : int3::getDirs())
 	{
 		const int3 n = pos + dir;
 		if(cbp->isInTheMap(n))
@@ -160,7 +164,7 @@ void foreach_neighbour(const int3 &pos, std::function<void(const int3& pos)> foo
 
 void foreach_neighbour(CCallback * cbp, const int3 &pos, std::function<void(CCallback * cbp, const int3& pos)> foo)
 {
-	for(const int3 &dir : dirs)
+	for(const int3 &dir : int3::getDirs())
 	{
 		const int3 n = pos + dir;
 		if(cbp->isInTheMap(n))
@@ -359,7 +363,7 @@ int3 whereToExplore(HeroPtr h)
 	int radius = h->getSightRadious();
 	int3 hpos = h->visitablePos();
 
-	SectorMap &sm = ai->getCachedSectorMap(h);
+	auto sm = ai->getCachedSectorMap(h);
 
 	//look for nearby objs -> visit them if they're close enouh
 	const int DIST_LIMIT = 3;
@@ -372,9 +376,9 @@ int3 whereToExplore(HeroPtr h)
 			{
 				int3 op = obj->visitablePos();
 				CGPath p;
-				ai->myCb->getPathsInfo(h.get())->getPath(op, p);
+				ai->myCb->getPathsInfo(h.get())->getPath(p, op);
 				if (p.nodes.size() && p.endPos() == op && p.nodes.size() <= DIST_LIMIT)
-					if (ai->isGoodForVisit(obj, h, sm))
+					if (ai->isGoodForVisit(obj, h, *sm))
 						nearbyVisitableObjs.push_back(obj);
 			}
 		}

+ 2 - 3
AI/VCAI/AIUtility.h

@@ -6,9 +6,6 @@
 #include "../../lib/CTownHandler.h"
 #include "../../lib/spells/CSpellHandler.h"
 #include "../../lib/Connection.h"
-#include "../../lib/CGameState.h"
-#include "../../lib/mapping/CMap.h"
-#include "../../lib/NetPacks.h"
 #include "../../lib/CStopWatch.h"
 
 /*
@@ -21,6 +18,8 @@
  *
  */
 
+class CCallback;
+
 typedef const int3& crint3;
 typedef const std::string& crstring;
 

+ 5 - 3
AI/VCAI/Fuzzy.cpp

@@ -5,6 +5,8 @@
 #include "../../lib/mapObjects/MapObjects.h"
 #include "../../lib/mapObjects/CommonConstructors.h"
 #include "../../lib/CCreatureHandler.h"
+#include "../../lib/CPathfinder.h"
+#include "../../lib/CGameStateFwd.h"
 #include "../../lib/VCMI_Lib.h"
 #include "../../CCallback.h"
 #include "VCAI.h"
@@ -421,7 +423,7 @@ float FuzzyHelper::evaluate (Goals::VisitTile & g)
 
 	//assert(cb->isInTheMap(g.tile));
 	float turns = 0;
-	float distance = cb->getMovementCost(g.hero.h, g.tile);
+	float distance = CPathfinderHelper::getMovementCost(g.hero.h, g.tile);
 	if (!distance) //we stand on that tile
 		turns = 0;
 	else
@@ -482,7 +484,7 @@ float FuzzyHelper::evaluate (Goals::ClearWayTo & g)
 	if (!g.hero.h)
 		throw cannotFulfillGoalException("ClearWayTo called without hero!");
 
-	int3 t = ai->getCachedSectorMap(g.hero).firstTileToGet(g.hero, g.tile);
+	int3 t = ai->getCachedSectorMap(g.hero)->firstTileToGet(g.hero, g.tile);
 
 	if (t.valid())
 	{
@@ -530,4 +532,4 @@ float FuzzyHelper::evaluate (Goals::AbstractGoal & g)
 void FuzzyHelper::setPriority (Goals::TSubgoal & g)
 {
 	g->setpriority(g->accept(this)); //this enforces returned value is set
-}
+}

+ 13 - 8
AI/VCAI/Goals.cpp

@@ -3,6 +3,7 @@
 #include "VCAI.h"
 #include "Fuzzy.h"
 #include "../../lib/mapping/CMap.h" //for victory conditions
+#include "../../lib/CPathfinder.h"
 
 /*
  * Goals.cpp, part of VCMI engine
@@ -483,9 +484,9 @@ TGoalVec ClearWayTo::getAllPossibleSubgoals()
 
 		//if our hero is trapped, make sure we request clearing the way from OUR perspective
 
-		SectorMap &sm = ai->getCachedSectorMap(h);
+		auto sm = ai->getCachedSectorMap(h);
 
-		int3 tileToHit = sm.firstTileToGet(h, tile);
+		int3 tileToHit = sm->firstTileToGet(h, tile);
 		if (!tileToHit.valid())
 			continue;
 
@@ -633,11 +634,11 @@ TGoalVec Explore::getAllPossibleSubgoals()
 
 	for (auto h : heroes)
 	{
-		SectorMap &sm = ai->getCachedSectorMap(h);
+		auto sm = ai->getCachedSectorMap(h);
 
 		for (auto obj : objs) //double loop, performance risk?
 		{
-			auto t = sm.firstTileToGet(h, obj->visitablePos()); //we assume that no more than one tile on the way is guarded
+			auto t = sm->firstTileToGet(h, obj->visitablePos()); //we assume that no more than one tile on the way is guarded
 			if (ai->isTileNotReserved(h, t))
 				ret.push_back (sptr(Goals::ClearWayTo(obj->visitablePos(), h).setisAbstract(true)));
 		}
@@ -963,7 +964,7 @@ TGoalVec Conquer::getAllPossibleSubgoals()
 
 	for (auto h : cb->getHeroesInfo())
 	{
-		SectorMap &sm = ai->getCachedSectorMap(h);
+		auto sm = ai->getCachedSectorMap(h);
 		std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects
 
 		for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero
@@ -974,7 +975,7 @@ TGoalVec Conquer::getAllPossibleSubgoals()
 		for (auto obj : ourObjs)
 		{
 			int3 dest = obj->visitablePos();
-			auto t = sm.firstTileToGet(h, dest); //we assume that no more than one tile on the way is guarded
+			auto t = sm->firstTileToGet(h, dest); //we assume that no more than one tile on the way is guarded
 			if (t.valid()) //we know any path at all
 			{
 				if (ai->isTileNotReserved(h, t)) //no other hero wants to conquer that tile
@@ -1027,6 +1028,10 @@ TSubgoal GatherArmy::whatToDoToAchieve()
 
 	return fh->chooseSolution (getAllPossibleSubgoals()); //find dwelling. use current hero to prevent him from doing nothing.
 }
+
+static const BuildingID unitsSource[] = { BuildingID::DWELL_LVL_1, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3,
+	BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7};
+
 TGoalVec GatherArmy::getAllPossibleSubgoals()
 {
 	//get all possible towns, heroes and dwellings we may use
@@ -1093,11 +1098,11 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
 	}
 	for(auto h : cb->getHeroesInfo())
 	{
-		SectorMap &sm = ai->getCachedSectorMap(h);
+		auto sm = ai->getCachedSectorMap(h);
 		for (auto obj : objs)
 		{ //find safe dwelling
 			auto pos = obj->visitablePos();
-			if (ai->isGoodForVisit(obj, h, sm))
+			if (ai->isGoodForVisit(obj, h, *sm))
 				ret.push_back (sptr (Goals::VisitTile(pos).sethero(h)));
 		}
 	}

+ 84 - 36
AI/VCAI/VCAI.cpp

@@ -8,6 +8,8 @@
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CHeroHandler.h"
 #include "../../lib/CModHandler.h"
+#include "../../lib/CGameState.h"
+#include "../../lib/NetPacks.h"
 
 
 /*
@@ -96,6 +98,7 @@ VCAI::VCAI(void)
 	LOG_TRACE(logAi);
 	makingTurn = nullptr;
 	destinationTeleport = ObjectInstanceID();
+	destinationTeleportPos = int3(-1);
 }
 
 VCAI::~VCAI(void)
@@ -614,33 +617,46 @@ void VCAI::showBlockingDialog(const std::string &text, const std::vector<Compone
 	});
 }
 
-void VCAI::showTeleportDialog(TeleportChannelID channel, std::vector<ObjectInstanceID> exits, bool impassable, QueryID askID)
+void VCAI::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID)
 {
-	LOG_TRACE_PARAMS(logAi, "askID '%i', exits '%s'", askID % exits);
+//	LOG_TRACE_PARAMS(logAi, "askID '%i', exits '%s'", askID % exits);
 	NET_EVENT_HANDLER;
 	status.addQuery(askID, boost::str(boost::format("Teleport dialog query with %d exits")
 																			% exits.size()));
 
-	ObjectInstanceID choosenExit;
+	int choosenExit = -1;
 	if(impassable)
 		knownTeleportChannels[channel]->passability = TeleportChannel::IMPASSABLE;
-	else
+	else if(destinationTeleport != ObjectInstanceID() && destinationTeleportPos.valid())
 	{
-		if(destinationTeleport != ObjectInstanceID() && vstd::contains(exits, destinationTeleport))
-			choosenExit = destinationTeleport;
+		auto neededExit = std::make_pair(destinationTeleport, destinationTeleportPos);
+		if(destinationTeleport != ObjectInstanceID() && vstd::contains(exits, neededExit))
+			choosenExit = vstd::find_pos(exits, neededExit);
+	}
 
-		if(!status.channelProbing())
+	for(auto exit : exits)
+	{
+		if(status.channelProbing() && exit.first == destinationTeleport)
+		{
+			choosenExit = vstd::find_pos(exits, exit);
+			break;
+		}
+		else
 		{
-			vstd::copy_if(exits, vstd::set_inserter(teleportChannelProbingList), [&](ObjectInstanceID id) -> bool
+			// TODO: Implement checking if visiting that teleport will uncovert any FoW
+			// So far this is the best option to handle decision about probing
+			auto obj = cb->getObj(exit.first, false);
+			if(obj == nullptr && !vstd::contains(teleportChannelProbingList, exit.first) &&
+				exit.first != destinationTeleport)
 			{
-				return !(vstd::contains(visitableObjs, cb->getObj(id)) || id == choosenExit);
-			});
+				teleportChannelProbingList.push_back(exit.first);
+			}
 		}
 	}
 
 	requestActionASAP([=]()
 	{
-		answerQuery(askID, choosenExit.getNum());
+		answerQuery(askID, choosenExit);
 	});
 }
 
@@ -843,9 +859,9 @@ void VCAI::makeTurnInternal()
 bool VCAI::goVisitObj(const CGObjectInstance * obj, HeroPtr h)
 {
 	int3 dst = obj->visitablePos();
-	SectorMap &sm = getCachedSectorMap(h);
+	auto sm = getCachedSectorMap(h);
 	logAi->debugStream() << boost::format("%s will try to visit %s at (%s)") % h->name % obj->getObjectName() % strFromInt3(dst);
-	int3 pos = sm.firstTileToGet(h, dst);
+	int3 pos = sm->firstTileToGet(h, dst);
 	if (!pos.valid()) //rare case when we are already standing on one of potential objects
 		return false;
 	return moveHeroToTile(pos, h);
@@ -1301,6 +1317,20 @@ bool VCAI::tryBuildNextStructure(const CGTownInstance * t, std::vector<BuildingI
 	return false;//Nothing to build
 }
 
+//Set of buildings for different goals. Does not include any prerequisites.
+static const BuildingID essential[] = {BuildingID::TAVERN, BuildingID::TOWN_HALL};
+static const BuildingID goldSource[] = {BuildingID::TOWN_HALL, BuildingID::CITY_HALL, BuildingID::CAPITOL};
+static const BuildingID unitsSource[] = { BuildingID::DWELL_LVL_1, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3,
+	BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7};
+static const BuildingID unitsUpgrade[] = { BuildingID::DWELL_LVL_1_UP, BuildingID::DWELL_LVL_2_UP, BuildingID::DWELL_LVL_3_UP,
+	BuildingID::DWELL_LVL_4_UP, BuildingID::DWELL_LVL_5_UP, BuildingID::DWELL_LVL_6_UP, BuildingID::DWELL_LVL_7_UP};
+static const BuildingID unitGrowth[] = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE, BuildingID::HORDE_1,
+	BuildingID::HORDE_1_UPGR, BuildingID::HORDE_2, BuildingID::HORDE_2_UPGR};
+static const BuildingID spells[] = {BuildingID::MAGES_GUILD_1, BuildingID::MAGES_GUILD_2, BuildingID::MAGES_GUILD_3,
+	BuildingID::MAGES_GUILD_4, BuildingID::MAGES_GUILD_5};
+static const BuildingID extra[] = {BuildingID::RESOURCE_SILO, BuildingID::SPECIAL_1, BuildingID::SPECIAL_2, BuildingID::SPECIAL_3,
+	BuildingID::SPECIAL_4, BuildingID::SHIPYARD}; // all remaining buildings
+
 void VCAI::buildStructure(const CGTownInstance * t)
 {
 	//TODO make *real* town development system
@@ -1381,10 +1411,10 @@ std::vector<const CGObjectInstance *> VCAI::getPossibleDestinations(HeroPtr h)
 {
 	validateVisitableObjs();
 	std::vector<const CGObjectInstance *> possibleDestinations;
-	SectorMap &sm = getCachedSectorMap(h);
+	auto sm = getCachedSectorMap(h);
 	for(const CGObjectInstance *obj : visitableObjs)
 	{
-		if (isGoodForVisit(obj, h, sm))
+		if (isGoodForVisit(obj, h, *sm))
 		{
 			possibleDestinations.push_back(obj);
 		}
@@ -1439,12 +1469,12 @@ void VCAI::wander(HeroPtr h)
 		validateVisitableObjs();
 		std::vector <ObjectIdRef> dests, tmp;
 
-		SectorMap &sm = getCachedSectorMap(h);
+		auto sm = getCachedSectorMap(h);
 
 		range::copy(reservedHeroesMap[h], std::back_inserter(tmp)); //also visit our reserved objects - but they are not prioritized to avoid running back and forth
 		for (auto obj : tmp)
 		{
-			int3 pos = sm.firstTileToGet(h, obj->visitablePos());
+			int3 pos = sm->firstTileToGet(h, obj->visitablePos());
 			if (pos.valid())
 				if (isAccessibleForHero (pos, h)) //even nearby objects could be blocked by other heroes :(
 					dests.push_back(obj); //can't use lambda for member function :(
@@ -1453,7 +1483,7 @@ void VCAI::wander(HeroPtr h)
 		range::copy(getPossibleDestinations(h), std::back_inserter(dests));
 		erase_if(dests, [&](ObjectIdRef obj) -> bool
 		{
-			return !isSafeToVisit(h, sm.firstTileToGet(h, obj->visitablePos()));
+			return !isSafeToVisit(h, sm->firstTileToGet(h, obj->visitablePos()));
 		});
 
 		if(!dests.size())
@@ -1840,7 +1870,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 	else
 	{
 		CGPath path;
-		cb->getPathsInfo(h.get())->getPath(dst, path);
+		cb->getPathsInfo(h.get())->getPath(path, dst);
 		if(path.nodes.empty())
 		{
             logAi->errorStream() << "Hero " << h->name << " cannot reach " << dst;
@@ -1860,25 +1890,29 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 			cb->moveHero(*h, CGHeroInstance::convertPosition(dst, true), transit);
 		};
 
-		auto doTeleportMovement = [&](int3 dst, ObjectInstanceID exitId)
+		auto doTeleportMovement = [&](ObjectInstanceID exitId, int3 exitPos)
 		{
 			destinationTeleport = exitId;
-			cb->moveHero(*h, CGHeroInstance::convertPosition(dst, true));
+			if(exitPos.valid())
+				destinationTeleportPos = CGHeroInstance::convertPosition(exitPos, true);
+			cb->moveHero(*h, h->pos);
 			destinationTeleport = ObjectInstanceID();
+			destinationTeleportPos = int3(-1);
 			afterMovementCheck();
 		};
 
 		auto doChannelProbing = [&]() -> void
 		{
-			auto currentExit = getObj(CGHeroInstance::convertPosition(h->pos,false), false);
-			assert(currentExit);
+			auto currentPos = CGHeroInstance::convertPosition(h->pos,false);
+			auto currentExit = getObj(currentPos, true)->id;
 
 			status.setChannelProbing(true);
 			for(auto exit : teleportChannelProbingList)
-				doTeleportMovement(CGHeroInstance::convertPosition(h->pos,false), exit);
+				doTeleportMovement(exit, int3(-1));
 			teleportChannelProbingList.clear();
-			doTeleportMovement(CGHeroInstance::convertPosition(h->pos,false), currentExit->id);
 			status.setChannelProbing(false);
+
+			doTeleportMovement(currentExit, currentPos);
 		};
 
 		int i=path.nodes.size()-1;
@@ -1891,7 +1925,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 			auto nextObject = getObj(nextCoord, false);
 			if(CGTeleport::isConnected(currentObject, nextObject))
 			{ //we use special login if hero standing on teleporter it's mean we need
-				doTeleportMovement(currentCoord, nextObject->id);
+				doTeleportMovement(nextObject->id, nextCoord);
 				if(teleportChannelProbingList.size())
 					doChannelProbing();
 
@@ -1915,6 +1949,8 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 			{ // Hero should be able to go through object if it's allow transit
 				doMovement(endpos, true);
 			}
+			else if(path.nodes[i-1].layer == EPathfindingLayer::AIR)
+				doMovement(endpos, true);
 			else
 				doMovement(endpos, false);
 
@@ -2034,7 +2070,7 @@ void VCAI::tryRealize(Goals::BuildThis & g)
 void VCAI::tryRealize(Goals::DigAtTile & g)
 {
 	assert(g.hero->visitablePos() == g.tile); //surely we want to crash here?
-	if (g.hero->diggingStatus() == CGHeroInstance::CAN_DIG)
+	if (g.hero->diggingStatus() == EDiggingStatus::CAN_DIG)
 	{
 		cb->dig(g.hero.get());
 		completeGoal(sptr(g)); // finished digging
@@ -2463,7 +2499,7 @@ int3 VCAI::explorationBestNeighbour(int3 hpos, int radius, HeroPtr h)
 {
 	int3 ourPos = h->convertPosition(h->pos, false);
 	std::map<int3, int> dstToRevealedTiles;
-	for(crint3 dir : dirs)
+	for(crint3 dir : int3::getDirs())
 		if(cb->isInTheMap(hpos+dir))
 			if (ourPos != dir) //don't stand in place
 				if (isSafeToVisit(h, hpos + dir) && isAccessibleForHero (hpos + dir, h))
@@ -2519,7 +2555,7 @@ int3 VCAI::explorationNewPoint(HeroPtr h)
 				continue;
 
 			CGPath path;
-			cb->getPathsInfo(hero)->getPath(tile, path);
+			cb->getPathsInfo(hero)->getPath(path, tile);
 			float ourValue = (float)howManyTilesWillBeDiscovered(tile, radius, cbp) / (path.nodes.size() + 1); //+1 prevents erratic jumps
 
 			if (ourValue > bestValue) //avoid costly checks of tiles that don't reveal much
@@ -2537,7 +2573,7 @@ int3 VCAI::explorationNewPoint(HeroPtr h)
 
 int3 VCAI::explorationDesperate(HeroPtr h)
 {
-	SectorMap &sm = getCachedSectorMap(h);
+	auto sm = getCachedSectorMap(h);
 	int radius = h->getSightRadious();
 
 	std::vector<std::vector<int3> > tiles; //tiles[distance_to_fow]
@@ -2566,7 +2602,7 @@ int3 VCAI::explorationDesperate(HeroPtr h)
 			if (!howManyTilesWillBeDiscovered(tile, radius, cbp)) //avoid costly checks of tiles that don't reveal much
 				continue;
 
-			auto t = sm.firstTileToGet(h, tile);
+			auto t = sm->firstTileToGet(h, tile);
 			if (t.valid())
 			{
 				ui64 ourDanger = evaluateDanger(t, h.h);
@@ -2669,13 +2705,24 @@ void VCAI::finish()
 
 void VCAI::requestActionASAP(std::function<void()> whatToDo)
 {
-	boost::thread newThread([this, whatToDo]()
+	boost::mutex mutex;
+	mutex.lock();
+
+	boost::thread newThread([&mutex,this,whatToDo]()
 	{
 		setThreadName("VCAI::requestActionASAP::whatToDo");
 		SET_GLOBAL_STATE(this);
 		boost::shared_lock<boost::shared_mutex> gsLock(cb->getGsMutex());
+		// unlock mutex and allow parent function to exit
+		mutex.unlock();
 		whatToDo();
 	});
+
+	// wait for mutex to unlock and for thread to initialize properly
+	mutex.lock();
+
+	// unlock mutex - boost dislikes destruction of locked mutexes
+	mutex.unlock();
 }
 
 void VCAI::lostHero(HeroPtr h)
@@ -2749,14 +2796,14 @@ TResources VCAI::freeResources() const
 	return myRes;
 }
 
-SectorMap& VCAI::getCachedSectorMap(HeroPtr h)
+std::shared_ptr<SectorMap> VCAI::getCachedSectorMap(HeroPtr h)
 {
 	auto it = cachedSectorMaps.find(h);
 	if (it != cachedSectorMaps.end())
 		return it->second;
 	else
 	{
-		cachedSectorMaps.insert(std::make_pair(h, SectorMap(h)));
+		cachedSectorMaps[h] = std::make_shared<SectorMap>(h);
 		return cachedSectorMaps[h];
 	}
 }
@@ -2905,7 +2952,7 @@ void AIStatus::setMove(bool ongoing)
 void AIStatus::setChannelProbing(bool ongoing)
 {
 	boost::unique_lock<boost::mutex> lock(mx);
-	ongoingHeroMovement = ongoing;
+	ongoingChannelProbing = ongoing;
 	cv.notify_all();
 }
 
@@ -3366,7 +3413,8 @@ int3 SectorMap::findFirstVisitableTile (HeroPtr h, crint3 dst)
 	while(curtile != h->visitablePos())
 	{
 		auto topObj = cb->getTopObj(curtile);
-		if (topObj && topObj->ID == Obj::HERO && h->tempOwner == topObj->tempOwner && topObj != h.h)
+		if(topObj && topObj->ID == Obj::HERO && topObj != h.h &&
+			cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
 		{
 			logAi->warnStream() << ("Another allied hero stands in our way");
 			return ret;

+ 5 - 20
AI/VCAI/VCAI.h

@@ -12,11 +12,9 @@
 #include "../../lib/CBuildingHandler.h"
 #include "../../lib/CCreatureHandler.h"
 #include "../../lib/CTownHandler.h"
+#include "../../lib/mapObjects/MiscObjects.h"
 #include "../../lib/spells/CSpellHandler.h"
 #include "../../lib/Connection.h"
-#include "../../lib/CGameState.h"
-#include "../../lib/mapping/CMap.h"
-#include "../../lib/NetPacks.h"
 #include "../../lib/CondSh.h"
 
 struct QuestInfo;
@@ -115,20 +113,6 @@ struct SectorMap
 	int3 findFirstVisitableTile(HeroPtr h, crint3 dst);
 };
 
-//Set of buildings for different goals. Does not include any prerequisites.
-const BuildingID essential[] = {BuildingID::TAVERN, BuildingID::TOWN_HALL};
-const BuildingID goldSource[] = {BuildingID::TOWN_HALL, BuildingID::CITY_HALL, BuildingID::CAPITOL};
-const BuildingID unitsSource[] = { BuildingID::DWELL_LVL_1, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3,
-	BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7};
-const BuildingID unitsUpgrade[] = { BuildingID::DWELL_LVL_1_UP, BuildingID::DWELL_LVL_2_UP, BuildingID::DWELL_LVL_3_UP,
-	BuildingID::DWELL_LVL_4_UP, BuildingID::DWELL_LVL_5_UP, BuildingID::DWELL_LVL_6_UP, BuildingID::DWELL_LVL_7_UP};
-const BuildingID unitGrowth[] = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE, BuildingID::HORDE_1,
-	BuildingID::HORDE_1_UPGR, BuildingID::HORDE_2, BuildingID::HORDE_2_UPGR};
-const BuildingID spells[] = {BuildingID::MAGES_GUILD_1, BuildingID::MAGES_GUILD_2, BuildingID::MAGES_GUILD_3,
-	BuildingID::MAGES_GUILD_4, BuildingID::MAGES_GUILD_5};
-const BuildingID extra[] = {BuildingID::RESOURCE_SILO, BuildingID::SPECIAL_1, BuildingID::SPECIAL_2, BuildingID::SPECIAL_3,
-	BuildingID::SPECIAL_4, BuildingID::SHIPYARD}; // all remaining buildings
-
 class VCAI : public CAdventureAI
 {
 public:
@@ -148,6 +132,7 @@ public:
 	std::map<TeleportChannelID, shared_ptr<TeleportChannel> > knownTeleportChannels;
 	std::map<const CGObjectInstance *, const CGObjectInstance *> knownSubterraneanGates;
 	ObjectInstanceID destinationTeleport;
+	int3 destinationTeleportPos;
 	std::vector<ObjectInstanceID> teleportChannelProbingList; //list of teleport channel exits that not visible and need to be (re-)explored
 	//std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs
 	std::map<HeroPtr, std::set<const CGTownInstance *> > townVisitsThisWeek;
@@ -161,7 +146,7 @@ public:
 	std::set<const CGObjectInstance *> alreadyVisited;
 	std::set<const CGObjectInstance *> reservedObjs; //to be visited by specific hero
 
-	std::map <HeroPtr, SectorMap> cachedSectorMaps; //TODO: serialize? not necessary
+	std::map <HeroPtr, std::shared_ptr<SectorMap>> cachedSectorMaps; //TODO: serialize? not necessary
 
 	TResources saving;
 
@@ -202,7 +187,7 @@ public:
 	virtual void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override; //TODO
 	virtual void showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
 	virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override; //all stacks operations between these objects become allowed, interface has to call onEnd when done
-	virtual void showTeleportDialog(TeleportChannelID channel, std::vector<ObjectInstanceID> exits, bool impassable, QueryID askID) override;
+	virtual void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
 	virtual void saveGame(COSer & h, const int version) override; //saving
 	virtual void loadGame(CISer & h, const int version) override; //loading
 	virtual void finish() override;
@@ -314,7 +299,7 @@ public:
 	const CGObjectInstance *getUnvisitedObj(const std::function<bool(const CGObjectInstance *)> &predicate);
 	bool isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies = false) const;
 	//optimization - use one SM for every hero call
-	SectorMap& getCachedSectorMap(HeroPtr h);
+	std::shared_ptr<SectorMap> getCachedSectorMap(HeroPtr h);
 
 	const CGTownInstance *findTownWithTavern() const;
 	bool canRecruitAnyHero(const CGTownInstance * t = NULL) const;

+ 1 - 11
CCallback.cpp

@@ -18,12 +18,7 @@
 #include "lib/spells/CSpellHandler.h"
 #include "lib/CArtHandler.h"
 #include "lib/GameConstants.h"
-#ifdef min
-#undef min
-#endif
-#ifdef max
-#undef max
-#endif
+#include "lib/CPlayerState.h"
 #include "lib/UnlockGuard.h"
 
 /*
@@ -290,11 +285,6 @@ bool CCallback::canMoveBetween(const int3 &a, const int3 &b)
 	return gs->checkForVisitableDir(a, b) && gs->checkForVisitableDir(b, a);
 }
 
-int CCallback::getMovementCost(const CGHeroInstance * hero, int3 dest)
-{
-	return gs->getMovementCost(hero, hero->visitablePos(), dest, hero->movement);
-}
-
 const CPathsInfo * CCallback::getPathsInfo(const CGHeroInstance *h)
 {
 	return cl->getPathsInfo(h);

+ 0 - 1
CCallback.h

@@ -104,7 +104,6 @@ public:
 
 	//client-specific functionalities (pathfinding)
 	virtual bool canMoveBetween(const int3 &a, const int3 &b);
-	virtual int getMovementCost(const CGHeroInstance * hero, int3 dest);
 	virtual int3 getGuardingCreaturePosition(int3 tile);
 	virtual const CPathsInfo * getPathsInfo(const CGHeroInstance *h);
 

+ 10 - 0
ChangeLog

@@ -1,9 +1,19 @@
 0.98 -> 0.next
 
+GENERAL:
+* New Bonus NO_TERRAIN_PENALTY
+* Nomads will remove Sand movement penalty from army
+* Flying and water walking is now supported in pathfinder
+* New artifacts supported
+- Angel Wings
+- Boots of Levitation
+* Implemented rumors in tavern window
+
 ADVETURE AI:
 * Fixed AI trying to go through underground rock
 * Fixed several cases causing AI wandering aimlessly
 * AI can again pick best artifacts and exchange artifacts between heroes
+* AI heroes with patrol enabled won't leave patrol area anymore
 
 RANDOM MAP GENERATOR:
 * Changed fractalization algorithm so it can create cycles

+ 12 - 0
Global.h

@@ -95,6 +95,18 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #  define NOMINMAX					// Exclude min/max macros from <Windows.h>. Use std::[min/max] from <algorithm> instead.
 #endif
 
+/* ---------------------------------------------------------------------------- */
+/* A macro to force inlining some of our functions */
+/* ---------------------------------------------------------------------------- */
+// Compiler (at least MSVC) is not so smart here-> without that displaying is MUCH slower
+#ifdef _MSC_VER
+#  define STRONG_INLINE __forceinline
+#elif __GNUC__
+#  define STRONG_INLINE inline __attribute__((always_inline))
+#else
+#  define STRONG_INLINE inline
+#endif
+
 #define _USE_MATH_DEFINES
 
 #include <cstdio>

+ 15 - 4
client/CMT.cpp

@@ -529,8 +529,9 @@ void processCommand(const std::string &message)
 	std::string cn; //command name
 	readed >> cn;
 
-	if(LOCPLINT && LOCPLINT->cingconsole)
-		LOCPLINT->cingconsole->print(message);
+// Check mantis issue 2292 for details
+//	if(LOCPLINT && LOCPLINT->cingconsole)
+//		LOCPLINT->cingconsole->print(message);
 
 	if(ermInteractiveMode)
 	{
@@ -789,11 +790,12 @@ void processCommand(const std::string &message)
 		Settings session = settings.write["session"];
 		session["autoSkip"].Bool() = !session["autoSkip"].Bool();
 	}
-	else if(client && client->serv && client->serv->connected && LOCPLINT) //send to server
+	// Check mantis issue 2292 for details
+/* 	else if(client && client->serv && client->serv->connected && LOCPLINT) //send to server
 	{
 		boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
 		LOCPLINT->cb->sendMessage(message);
-	}
+	}*/
 }
 
 //plays intro, ends when intro is over or button has been pressed (handles events)
@@ -1071,6 +1073,15 @@ static void handleEvent(SDL_Event & ev)
 
 		return;
 	}
+	else if(ev.type == SDL_WINDOWEVENT)
+	{
+		switch (ev.window.event) {
+		case SDL_WINDOWEVENT_RESTORED:
+			fullScreenChanged();
+			break;
+		}
+		return;
+	}
 	{
 		boost::unique_lock<boost::mutex> lock(eventsM);
 		events.push(ev);

+ 38 - 20
client/CPlayerInterface.cpp

@@ -37,6 +37,7 @@
 #include "../lib/CStopWatch.h"
 #include "../lib/StartInfo.h"
 #include "../lib/CGameState.h"
+#include "../lib/CPlayerState.h"
 #include "../lib/GameConstants.h"
 #include "gui/CGuiHandler.h"
 #include "windows/InfoWindows.h"
@@ -97,6 +98,7 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player)
 {
 	logGlobal->traceStream() << "\tHuman player interface for player " << Player << " being constructed";
 	destinationTeleport = ObjectInstanceID();
+	destinationTeleportPos = int3(-1);
 	observerInDuelMode = false;
 	howManyPeople++;
 	GH.defActionsDef = 0;
@@ -1147,14 +1149,15 @@ void CPlayerInterface::showBlockingDialog( const std::string &text, const std::v
 
 }
 
-void CPlayerInterface::showTeleportDialog(TeleportChannelID channel, std::vector<ObjectInstanceID> exits, bool impassable, QueryID askID)
+void CPlayerInterface::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	ObjectInstanceID choosenExit;
-	if(destinationTeleport != ObjectInstanceID() && vstd::contains(exits, destinationTeleport))
-		choosenExit = destinationTeleport;
+	int choosenExit = -1;
+	auto neededExit = std::make_pair(destinationTeleport, destinationTeleportPos);
+	if(destinationTeleport != ObjectInstanceID() && vstd::contains(exits, neededExit))
+		choosenExit = vstd::find_pos(exits, neededExit);
 
-	cb->selectionMade(choosenExit.getNum(), askID);
+	cb->selectionMade(choosenExit, askID);
 }
 
 void CPlayerInterface::tileRevealed(const std::unordered_set<int3, ShashInt3> &pos)
@@ -1288,7 +1291,7 @@ template <typename Handler> void CPlayerInterface::serializeTempl( Handler &h, c
 			for(auto &p : pathsMap)
 			{
 				CGPath path;
-				cb->getPathsInfo(p.first)->getPath(p.second, path);
+				cb->getPathsInfo(p.first)->getPath(path, p.second);
 				paths[p.first] = path;
 				logGlobal->traceStream() << boost::format("Restored path for hero %s leading to %s with %d nodes")
 					% p.first->nodeName() % p.second % path.nodes.size();
@@ -1414,6 +1417,7 @@ void CPlayerInterface::requestRealized( PackageApplied *pa )
 	   && stillMoveHero.get() == DURING_MOVE)
 	{ // After teleportation via CGTeleport object is finished
 		destinationTeleport = ObjectInstanceID();
+		destinationTeleportPos = int3(-1);
 		stillMoveHero.setn(CONTINUE_MOVE);
 	}
 }
@@ -2226,7 +2230,7 @@ CGPath * CPlayerInterface::getAndVerifyPath(const CGHeroInstance * h)
 		{
 			assert(h->getPosition(false) == path.startPos());
 			//update the hero path in case of something has changed on map
-			if(LOCPLINT->cb->getPathsInfo(h)->getPath(path.endPos(), path))
+			if(LOCPLINT->cb->getPathsInfo(h)->getPath(path, path.endPos()))
 				return &path;
 			else
 				paths.erase(h);
@@ -2315,23 +2319,22 @@ void CPlayerInterface::tryDiggging(const CGHeroInstance *h)
 {
 	std::string hlp;
 	CGI->mh->getTerrainDescr(h->getPosition(false), hlp, false);
-
-	int msgToShow = -1;
-	CGHeroInstance::ECanDig isDiggingPossible = h->diggingStatus();
+	auto isDiggingPossible = h->diggingStatus();
 	if(hlp.length())
-		isDiggingPossible = CGHeroInstance::TILE_OCCUPIED; //TODO integrate with canDig
+		isDiggingPossible = EDiggingStatus::TILE_OCCUPIED; //TODO integrate with canDig
 
+	int msgToShow = -1;
 	switch(isDiggingPossible)
 	{
-	case CGHeroInstance::CAN_DIG:
+	case EDiggingStatus::CAN_DIG:
 		break;
-	case CGHeroInstance::LACK_OF_MOVEMENT:
+	case EDiggingStatus::LACK_OF_MOVEMENT:
 		msgToShow = 56; //"Digging for artifacts requires a whole day, try again tomorrow."
 		break;
-	case CGHeroInstance::TILE_OCCUPIED:
+	case EDiggingStatus::TILE_OCCUPIED:
 		msgToShow = 97; //Try searching on clear ground.
 		break;
-	case CGHeroInstance::WRONG_TERRAIN:
+	case EDiggingStatus::WRONG_TERRAIN:
 		msgToShow = 60; ////Try looking on land!
 		break;
 	default:
@@ -2642,7 +2645,18 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
 		ETerrainType newTerrain;
 		int sh = -1;
 
-		for(i=path.nodes.size()-1; i>0 && (stillMoveHero.data == CONTINUE_MOVE); i--)
+		auto canStop = [&](CGPathNode * node) -> bool
+		{
+			if(node->layer == EPathfindingLayer::LAND || node->layer == EPathfindingLayer::SAIL)
+				return true;
+
+			if(node->accessible == CGPathNode::ACCESSIBLE)
+				return true;
+
+			return false;
+		};
+
+		for(i=path.nodes.size()-1; i>0 && (stillMoveHero.data == CONTINUE_MOVE || !canStop(&path.nodes[i])); i--)
 		{
 			int3 currentCoord = path.nodes[i].coord;
 			int3 nextCoord = path.nodes[i-1].coord;
@@ -2652,6 +2666,7 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
 			{
 				CCS->soundh->stopSound(sh);
 				destinationTeleport = nextObject->id;
+				destinationTeleportPos = nextCoord;
 				doMovement(h->pos, false);
 				sh = CCS->soundh->playSound(CCS->soundh->horseSounds[currentTerrain], -1);
 				continue;
@@ -2683,18 +2698,21 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
 			int3 endpos(nextCoord.x, nextCoord.y, h->pos.z);
 			logGlobal->traceStream() << "Requesting hero movement to " << endpos;
 
+			bool useTransit = false;
 			if((i-2 >= 0) // Check there is node after next one; otherwise transit is pointless
 				&& (CGTeleport::isConnected(nextObject, getObj(path.nodes[i-2].coord, false))
 					|| CGTeleport::isTeleport(nextObject)))
 			{ // Hero should be able to go through object if it's allow transit
-				doMovement(endpos, true);
+				useTransit = true;
 			}
-			else
-				doMovement(endpos, false);
+			else if(path.nodes[i-1].layer == EPathfindingLayer::AIR)
+				useTransit = true;
+
+			doMovement(endpos, useTransit);
 
 			logGlobal->traceStream() << "Resuming " << __FUNCTION__;
 			bool guarded = cb->isInTheMap(cb->getGuardingCreaturePosition(endpos - int3(1, 0, 0)));
-			if(guarded || showingDialog->get() == true) // Abort movement if a guard was fought or there is a dialog to display (Mantis #1136)
+			if((!useTransit && guarded) || showingDialog->get() == true) // Abort movement if a guard was fought or there is a dialog to display (Mantis #1136)
 				break;
 		}
 

+ 2 - 2
client/CPlayerInterface.h

@@ -5,7 +5,6 @@
 #include "../lib/CGameInterface.h"
 #include "../lib/NetPacksBase.h"
 #include "gui/CIntObject.h"
-//#include "../lib/CGameState.h"
 
 #ifdef __GNUC__
 #define sprintf_s snprintf
@@ -90,6 +89,7 @@ class CPlayerInterface : public CGameInterface, public IUpdateable
 public:
 	bool observerInDuelMode;
 	ObjectInstanceID destinationTeleport; //contain -1 or object id if teleportation
+	int3 destinationTeleportPos;
 
 	//minor interfaces
 	CondSh<bool> *showingDialog; //indicates if dialog box is displayed
@@ -168,7 +168,7 @@ public:
 	void showRecruitmentDialog(const CGDwelling *dwelling, const CArmedInstance *dst, int level) override;
 	void showShipyardDialog(const IShipyard *obj) override; //obj may be town or shipyard;
 	void showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
-	void showTeleportDialog(TeleportChannelID channel, std::vector<ObjectInstanceID> exits, bool impassable, QueryID askID) override;
+	void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
 	void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override;
 	void showPuzzleMap() override;
 	void viewWorldMap() override;

+ 5 - 4
client/CPreGame.cpp

@@ -1875,9 +1875,9 @@ void CRandomMapTab::updateMapInfo()
 
 	// Generate player information
 	mapInfo->mapHeader->players.clear();
-	int playersToGen = (mapGenOptions.getPlayerCount() == CMapGenOptions::RANDOM_SIZE
-		|| mapGenOptions.getCompOnlyPlayerCount() == CMapGenOptions::RANDOM_SIZE)
-			? 8 : mapGenOptions.getPlayerCount() + mapGenOptions.getCompOnlyPlayerCount();
+	int playersToGen = PlayerColor::PLAYER_LIMIT_I;
+	if(mapGenOptions.getPlayerCount() != CMapGenOptions::RANDOM_SIZE)
+		playersToGen = mapGenOptions.getPlayerCount();
 	mapInfo->mapHeader->howManyTeams = playersToGen;
 
 	for(int i = 0; i < playersToGen; ++i)
@@ -1885,7 +1885,8 @@ void CRandomMapTab::updateMapInfo()
 		PlayerInfo player;
 		player.isFactionRandom = true;
 		player.canComputerPlay = true;
-		if(i >= mapGenOptions.getPlayerCount() && mapGenOptions.getPlayerCount() != CMapGenOptions::RANDOM_SIZE)
+		if(mapGenOptions.getCompOnlyPlayerCount() != CMapGenOptions::RANDOM_SIZE &&
+			i >= mapGenOptions.getHumanOnlyPlayerCount())
 		{
 			player.canHumanPlay = false;
 		}

+ 11 - 27
client/CVideoHandler.cpp

@@ -104,11 +104,7 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal
 		return false;
 	}
 	// Retrieve stream information
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 17, 0)
-	if (av_find_stream_info(format) < 0)
-#else
 	if (avformat_find_stream_info(format, nullptr) < 0)
-#endif
 		return false;
 
 	// Find the first video stream
@@ -139,22 +135,16 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal
 	}
 
 	// Open codec
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 6, 0)
-	if ( avcodec_open(codecContext, codec) < 0 )
-#else
 	if ( avcodec_open2(codecContext, codec, nullptr) < 0 )
-#endif
 	{
 		// Could not open codec
 		codec = nullptr;
 		return false;
 	}
-
 	// Allocate video frame
-	frame = avcodec_alloc_frame();
+	frame = av_frame_alloc();
 	
 	//setup scaling
-	
 	if(scale)
 	{
 		pos.w = screen->w;		
@@ -185,21 +175,21 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal
 	if (texture)
 	{ // Convert the image into YUV format that SDL uses
 		sws = sws_getContext(codecContext->width, codecContext->height, codecContext->pix_fmt, 
-							 pos.w, pos.h, PIX_FMT_YUV420P, 
+							 pos.w, pos.h,
+							 AV_PIX_FMT_YUV420P,
 							 SWS_BICUBIC, nullptr, nullptr, nullptr);
 	}
 	else
 	{
-
-		PixelFormat screenFormat = PIX_FMT_NONE;
+		AVPixelFormat screenFormat = AV_PIX_FMT_NONE;
 		if (screen->format->Bshift > screen->format->Rshift)
 		{
 			// this a BGR surface
 			switch (screen->format->BytesPerPixel)
 			{
-				case 2: screenFormat = PIX_FMT_BGR565; break;
-				case 3: screenFormat = PIX_FMT_BGR24; break;
-				case 4: screenFormat = PIX_FMT_BGR32; break;
+				case 2: screenFormat = AV_PIX_FMT_BGR565; break;
+				case 3: screenFormat = AV_PIX_FMT_BGR24; break;
+				case 4: screenFormat = AV_PIX_FMT_BGR32; break;
 				default: return false;
 			}
 		}
@@ -208,9 +198,9 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal
 			// this a RGB surface
 			switch (screen->format->BytesPerPixel)
 			{
-				case 2: screenFormat = PIX_FMT_RGB565; break;
-				case 3: screenFormat = PIX_FMT_RGB24; break;
-				case 4: screenFormat = PIX_FMT_RGB32; break;
+				case 2: screenFormat = AV_PIX_FMT_RGB565; break;
+				case 3: screenFormat = AV_PIX_FMT_RGB24; break;
+				case 4: screenFormat = AV_PIX_FMT_RGB32; break;
 				default: return false;
 			}
 		}
@@ -367,8 +357,7 @@ void CVideoPlayer::close()
 
 	if (frame)
 	{
-		av_free(frame);
-		frame = nullptr;
+		av_frame_free(&frame);//will be set to null		
 	}
 
 	if (codec)
@@ -380,12 +369,7 @@ void CVideoPlayer::close()
 
 	if (format)
 	{
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 17, 0)
-		av_close_input_file(format);
-		format = nullptr;
-#else
 		avformat_close_input(&format);
-#endif
 	}
 
 	if (context)

+ 27 - 12
client/CVideoHandler.h

@@ -51,19 +51,34 @@ public:
 extern "C" {
 #include <libavformat/avformat.h>
 #include <libswscale/swscale.h>
+}
 
-// compatibility with different versions od libavutil
-#if (LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 42, 0)) || \
-    (LIBAVUTIL_VERSION_INT == AV_VERSION_INT(51, 73, 101))
-
-#define AV_PIX_FMT_NONE         PIX_FMT_NONE
-#define AV_PIX_FMT_NV12         PIX_FMT_NV12
-#define AV_PIX_FMT_YUV420P      PIX_FMT_YUV420P
-#define AV_PIX_FMT_UYVY422      PIX_FMT_UYVY422
-#define AV_PIX_FMT_YUYV422      PIX_FMT_YUYV422
+//compatibility for libav 9.18 in ubuntu 14.04, 52.66.100 is ffmpeg 2.2.3
+#if (LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 66, 100))
+inline AVFrame * av_frame_alloc()
+{
+	return avcodec_alloc_frame();
+}
 
-#endif 
+inline void av_frame_free(AVFrame ** frame)
+{
+	av_free(*frame);
+	*frame = nullptr;
 }
+#endif // VCMI_USE_OLD_AVUTIL
+
+//fix for travis-ci
+#if (LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 0, 0))
+	#define AVPixelFormat PixelFormat
+	#define AV_PIX_FMT_NONE PIX_FMT_NONE
+	#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P
+	#define AV_PIX_FMT_BGR565 PIX_FMT_BGR565
+	#define AV_PIX_FMT_BGR24 PIX_FMT_BGR24
+	#define AV_PIX_FMT_BGR32 PIX_FMT_BGR32
+	#define AV_PIX_FMT_RGB565 PIX_FMT_RGB565
+	#define AV_PIX_FMT_RGB24 PIX_FMT_RGB24
+	#define AV_PIX_FMT_RGB32 PIX_FMT_RGB32
+#endif
 
 class CVideoPlayer : public IMainVideoPlayer
 {
@@ -71,7 +86,7 @@ class CVideoPlayer : public IMainVideoPlayer
 	AVFormatContext *format;
 	AVCodecContext *codecContext; // codec context for stream
 	AVCodec *codec;
-	AVFrame *frame; 
+	AVFrame *frame;
 	struct SwsContext *sws;
 
 	AVIOContext * context;
@@ -102,7 +117,7 @@ public:
 	void show(int x, int y, SDL_Surface *dst, bool update = true) override; //blit current frame
 	void redraw(int x, int y, SDL_Surface *dst, bool update = true) override; //reblits buffer
 	void update(int x, int y, SDL_Surface *dst, bool forceRedraw, bool update = true) override; //moves to next frame if appropriate, and blits it or blits only if redraw parameter is set true
-	
+
 	// Opens video, calls playVideo, closes video; returns playVideo result (if whole video has been played)
 	bool openAndPlayVideo(std::string name, int x, int y, SDL_Surface *dst, bool stopOnKey = false, bool scale = false) override;
 

+ 1 - 0
client/Graphics.cpp

@@ -22,6 +22,7 @@
 #include "../lib/GameConstants.h"
 #include "../lib/CStopWatch.h"
 #include "../lib/mapObjects/CObjectClassesHandler.h"
+#include "../lib/mapObjects/CObjectHandler.h"
 
 using namespace CSDL_Ext;
 #ifdef min

+ 4 - 3
client/NetPacksClient.cpp

@@ -25,6 +25,7 @@
 #include "../lib/CGameState.h"
 #include "../lib/BattleState.h"
 #include "../lib/GameConstants.h"
+#include "../lib/CPlayerState.h"
 #include "gui/CGuiHandler.h"
 #include "widgets/MiscWidgets.h"
 #include "widgets/AdventureMapClasses.h"
@@ -819,10 +820,10 @@ void SaveGame::applyCl(CClient *cl)
 
 void PlayerMessage::applyCl(CClient *cl)
 {
-	std::ostringstream str;
-	str << "Player "<< player <<" sends a message: " << text;
+	logNetwork->debugStream() << "Player "<< player <<" sends a message: " << text;
 
-    logNetwork->debugStream() << str.str();
+	std::ostringstream str;
+	str << cl->getPlayer(player)->nodeName() <<": " << text;
 	if(LOCPLINT)
 		LOCPLINT->cingconsole->print(str.str());
 }

+ 4 - 4
client/battle/CBattleAnimations.cpp

@@ -233,8 +233,9 @@ std::string CDefenceAnimation::getMySound()
 	if(killed)
 		return battle_sound(stack->getCreature(), killed);
 
-	if (stack->valOfBonuses(Bonus::UntilGetsTurn))
+	if (vstd::contains(stack->state, EBattleStackState::DEFENDING_ANIM))
 		return battle_sound(stack->getCreature(), defend);
+
 	return battle_sound(stack->getCreature(), wince);
 }
 
@@ -243,10 +244,9 @@ CCreatureAnim::EAnimType CDefenceAnimation::getMyAnimType()
 	if(killed)
 		return CCreatureAnim::DEATH;
 	
-	auto selector = CSelector(Bonus::UntilGetsTurn).And(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE));
-	
-	if(stack->valOfBonuses(selector))
+	if (vstd::contains(stack->state, EBattleStackState::DEFENDING_ANIM))
 		return CCreatureAnim::DEFENCE;
+
 	return CCreatureAnim::HITTED;
 }
 

+ 1 - 0
client/battle/CBattleInterfaceClasses.cpp

@@ -29,6 +29,7 @@
 #include "../../lib/NetPacks.h"
 #include "../../lib/StartInfo.h"
 #include "../../lib/CondSh.h"
+#include "../../lib/mapObjects/CGTownInstance.h"
 
 /*
  * CBattleInterfaceClasses.cpp, part of VCMI engine

+ 0 - 7
client/gui/Geometries.h

@@ -13,13 +13,6 @@
  *
  */
 
-#ifdef max
-#undef max
-#endif
-#ifdef min
-#undef min
-#endif
-
 struct SDL_MouseMotionEvent;
 
 // A point with x/y coordinate, used mostly for graphic rendering

+ 0 - 9
client/gui/SDL_Extensions.h

@@ -18,15 +18,6 @@
 #include "../../lib/GameConstants.h"
 
 
-//A macro to force inlining some of our functions. Compiler (at least MSVC) is not so smart here-> without that displaying is MUCH slower
-#ifdef _MSC_VER
-	#define STRONG_INLINE __forceinline
-#elif __GNUC__
-	#define STRONG_INLINE inline __attribute__((always_inline))
-#else
-	#define STRONG_INLINE inline
-#endif
-
 extern SDL_Window * mainWindow;
 extern SDL_Renderer * mainRenderer;
 extern SDL_Texture * screenTexture;

+ 6 - 0
client/mapHandler.cpp

@@ -1579,7 +1579,13 @@ void CMapHandler::getTerrainDescr( const int3 &pos, std::string & out, bool terN
 	if(t.hasFavourableWinds())
 		out = CGI->objtypeh->getObjectName(Obj::FAVORABLE_WINDS);
 	else if(terName)
+	{
 		out = CGI->generaltexth->terrainNames[t.terType];
+		if(t.getDiggingStatus(false) == EDiggingStatus::CAN_DIG)
+		{
+			out = boost::str(boost::format("%s %s") % out % CGI->generaltexth->allTexts[330]); /// digging ok
+		}
+	}
 }
 
 void CMapHandler::discardWorldViewCache()

+ 56 - 127
client/windows/CAdvmapInterface.cpp

@@ -114,7 +114,7 @@ void CTerrainRect::clickRight(tribool down, bool previousState)
 		adventureInt->tileRClicked(mp);
 }
 
-void CTerrainRect::mouseMoved (const SDL_MouseMotionEvent & sEvent)
+void CTerrainRect::mouseMoved(const SDL_MouseMotionEvent & sEvent)
 {
 	int3 tHovered = whichTileIsIt(sEvent.x,sEvent.y);
 	int3 pom = adventureInt->verifyPos(tHovered);
@@ -126,11 +126,11 @@ void CTerrainRect::mouseMoved (const SDL_MouseMotionEvent & sEvent)
 	}
 
 	if (pom != curHoveredTile)
-		curHoveredTile=pom;
+		curHoveredTile = pom;
 	else
 		return;
 
-	adventureInt->tileHovered(curHoveredTile);
+	adventureInt->tileHovered(pom);
 }
 void CTerrainRect::hover(bool on)
 {
@@ -188,7 +188,7 @@ void CTerrainRect::showPath(const SDL_Rect * extRect, SDL_Surface * to)
 			 * is id1=7, id2=5 (pns[7][5])
 			*/
 			bool pathContinuous = curPos.areNeighbours(nextPos) && curPos.areNeighbours(prevPos);
-			if(pathContinuous && cv[i].land == cv[i+1].land)
+			if(pathContinuous && cv[i].action != CGPathNode::EMBARK && cv[i].action != CGPathNode::DISEMBARK)
 			{
 				int id1=(curPos.x-nextPos.x+1)+3*(curPos.y-nextPos.y+1);   //Direction of entering vector
 				int id2=(cv[i-1].coord.x-curPos.x+1)+3*(cv[i-1].coord.y-curPos.y+1); //Direction of exiting vector
@@ -881,7 +881,7 @@ void CAdvMapInt::showAll(SDL_Surface * to)
 
 	statusbar.show(to);
 
-	LOCPLINT->cingconsole->showAll(to);
+	LOCPLINT->cingconsole->show(to);
 }
 
 bool CAdvMapInt::isHeroSleeping(const CGHeroInstance *hero)
@@ -958,7 +958,7 @@ void CAdvMapInt::show(SDL_Surface * to)
 		for(int i=0;i<4;i++)
 			blitAt(gems[i]->ourImages[LOCPLINT->playerID.getNum()].bitmap,ADVOPT.gemX[i],ADVOPT.gemY[i],to);
 		updateScreen=false;
-		LOCPLINT->cingconsole->showAll(to);
+		LOCPLINT->cingconsole->show(to);
 	}
 	else if (terrain.needsAnimUpdate())
 	{
@@ -1190,7 +1190,7 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
 			CGPath &path = LOCPLINT->paths[h];
 			terrain.currentPath = &path;
 			int3 dst = h->getPosition(false) + dir;
-			if(dst != verifyPos(dst) || !LOCPLINT->cb->getPathsInfo(h)->getPath(dst, path))
+			if(dst != verifyPos(dst) || !LOCPLINT->cb->getPathsInfo(h)->getPath(path, dst))
 			{
 				terrain.currentPath = nullptr;
 				return;
@@ -1445,7 +1445,7 @@ void CAdvMapInt::tileLClicked(const int3 &mapPos)
 			{
 				CGPath &path = LOCPLINT->paths[currentHero];
 				terrain.currentPath = &path;
-				bool gotPath = LOCPLINT->cb->getPathsInfo(currentHero)->getPath(mapPos, path); //try getting path, erase if failed
+				bool gotPath = LOCPLINT->cb->getPathsInfo(currentHero)->getPath(path, mapPos); //try getting path, erase if failed
 				updateMoveHero(currentHero);
 				if (!gotPath)
 					LOCPLINT->eraseCurrentPathOf(currentHero);
@@ -1467,7 +1467,8 @@ void CAdvMapInt::tileLClicked(const int3 &mapPos)
 
 void CAdvMapInt::tileHovered(const int3 &mapPos)
 {
-	if(mode != EAdvMapMode::NORMAL)
+	if(mode != EAdvMapMode::NORMAL //disable in world view
+		|| !selection) //may occur just at the start of game (fake move before full intiialization)
 		return;
 	if(!LOCPLINT->cb->isVisible(mapPos))
 	{
@@ -1475,10 +1476,11 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
 		statusbar.clear();
 		return;
 	}
+	auto objRelations = PlayerRelations::ALLIES;
 	const CGObjectInstance *objAtTile = getActiveObject(mapPos);
-
-	if (objAtTile)
+	if(objAtTile)
 	{
+		objRelations = LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, objAtTile->tempOwner);
 		std::string text = curHero() ? objAtTile->getHoverText(curHero()) : objAtTile->getHoverText(LOCPLINT->playerID);
 		boost::replace_all(text,"\n"," ");
 		statusbar.setText(text);
@@ -1490,9 +1492,6 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
 		statusbar.setText(hlp);
 	}
 
-	if(!selection) //may occur just at the start of game (fake move before full intiialization)
-		return;
-
 	if(spellBeingCasted)
 	{
 		switch(spellBeingCasted->id)
@@ -1505,9 +1504,9 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
 			return;
 		case SpellID::DIMENSION_DOOR:
 			{
-				const TerrainTile *t = LOCPLINT->cb->getTile(mapPos, false);
+				const TerrainTile * t = LOCPLINT->cb->getTile(mapPos, false);
 				int3 hpos = selection->getSightCenter();
-				if((!t  ||  t->isClear(LOCPLINT->cb->getTile(hpos)))   &&   isInScreenRange(hpos, mapPos))
+				if((!t || t->isClear(LOCPLINT->cb->getTile(hpos))) && isInScreenRange(hpos, mapPos))
 					CCS->curh->changeGraphic(ECursor::ADVENTURE, 41);
 				else
 					CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
@@ -1516,15 +1515,13 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
 		}
 	}
 
-	const bool guardingCreature = CGI->mh->map->isInTheMap(LOCPLINT->cb->getGuardingCreaturePosition(mapPos));
-
 	if(selection->ID == Obj::TOWN)
 	{
 		if(objAtTile)
 		{
-			if(objAtTile->ID == Obj::TOWN && LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, objAtTile->tempOwner) != PlayerRelations::ENEMIES)
+			if(objAtTile->ID == Obj::TOWN && objRelations != PlayerRelations::ENEMIES)
 				CCS->curh->changeGraphic(ECursor::ADVENTURE, 3);
-			else if(objAtTile->ID == Obj::HERO && objAtTile->tempOwner == LOCPLINT->playerID)
+			else if(objAtTile->ID == Obj::HERO && objRelations == PlayerRelations::SAME_PLAYER)
 				CCS->curh->changeGraphic(ECursor::ADVENTURE, 2);
 			else
 				CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
@@ -1532,129 +1529,61 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
 		else
 			CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
 	}
-	else if(const CGHeroInstance *h = curHero())
+	else if(const CGHeroInstance * h = curHero())
 	{
 		int3 mapPosCopy = mapPos;
-		const CGPathNode *pnode = LOCPLINT->cb->getPathsInfo(h)->getPathInfo(mapPosCopy);
+		const CGPathNode * pnode = LOCPLINT->cb->getPathsInfo(h)->getPathInfo(mapPosCopy);
 		assert(pnode);
 
 		int turns = pnode->turns;
 		vstd::amin(turns, 3);
-		bool accessible  =  pnode->turns < 255;
-
-		if(objAtTile)
+		switch(pnode->action)
 		{
-			if(objAtTile->ID == Obj::HERO)
-			{
-				if(!LOCPLINT->cb->getPlayerRelations( LOCPLINT->playerID, objAtTile->tempOwner)) //enemy hero
-				{
-					if(accessible)
-						CCS->curh->changeGraphic(ECursor::ADVENTURE, 5 + turns*6);
-					else
-						CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
-				}
-				else //our or ally hero
-				{
-					if(selection == objAtTile)
-						CCS->curh->changeGraphic(ECursor::ADVENTURE, 2);
-					else if(accessible)
-						CCS->curh->changeGraphic(ECursor::ADVENTURE, 8 + turns*6);
-					else
-						CCS->curh->changeGraphic(ECursor::ADVENTURE, 2);
-				}
-			}
-			else if(objAtTile->ID == Obj::TOWN)
-			{
-				if(!LOCPLINT->cb->getPlayerRelations( LOCPLINT->playerID, objAtTile->tempOwner)) //enemy town
-				{
-					if(accessible)
-					{
-						const CGTownInstance* townObj = dynamic_cast<const CGTownInstance*>(objAtTile);
-
-						// Show movement cursor for unguarded enemy towns, otherwise attack cursor.
-						if (townObj && !townObj->armedGarrison())
-							CCS->curh->changeGraphic(ECursor::ADVENTURE, 9 + turns*6);
-						else
-							CCS->curh->changeGraphic(ECursor::ADVENTURE, 5 + turns*6);
+		case CGPathNode::NORMAL:
+			if(pnode->layer == EPathfindingLayer::LAND)
+				CCS->curh->changeGraphic(ECursor::ADVENTURE, 4 + turns*6);
+			else
+				CCS->curh->changeGraphic(ECursor::ADVENTURE, 28 + turns);
+			break;
 
-					}
-					else
-					{
-						CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
-					}
-				}
-				else //our or ally town
-				{
-					if(accessible)
-						CCS->curh->changeGraphic(ECursor::ADVENTURE, 9 + turns*6);
-					else
-						CCS->curh->changeGraphic(ECursor::ADVENTURE, 3);
-				}
-			}
-			else if(objAtTile->ID == Obj::BOAT)
+		case CGPathNode::VISIT:
+		case CGPathNode::BLOCKING_VISIT:
+			if(objAtTile && objAtTile->ID == Obj::HERO)
 			{
-				if(accessible)
-					CCS->curh->changeGraphic(ECursor::ADVENTURE, 6 + turns*6);
+				if(selection == objAtTile)
+					CCS->curh->changeGraphic(ECursor::ADVENTURE, 2);
 				else
-					CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
-			}
-			else if (objAtTile->ID == Obj::GARRISON || objAtTile->ID == Obj::GARRISON2)
-			{
-				if (accessible)
-				{
-					const CGGarrison* garrObj = dynamic_cast<const CGGarrison*>(objAtTile); //TODO evil evil cast!
-
-					// Show battle cursor for guarded enemy garrisons or garrisons have guarding creature behind, otherwise movement cursor.
-					if (garrObj  &&  ((garrObj->stacksCount()
-						&& !LOCPLINT->cb->getPlayerRelations( LOCPLINT->playerID, garrObj->tempOwner))
-						|| guardingCreature))
-						CCS->curh->changeGraphic(ECursor::ADVENTURE, 5 + turns*6);
-					else
-						CCS->curh->changeGraphic(ECursor::ADVENTURE, 9 + turns*6);
-				}
-				else
-					CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
-			}
-			else if (guardingCreature && accessible) //(objAtTile->ID == 54) //monster
-			{
-				CCS->curh->changeGraphic(ECursor::ADVENTURE, 5 + turns*6);
+					CCS->curh->changeGraphic(ECursor::ADVENTURE, 8 + turns*6);
 			}
+			else if(pnode->layer == EPathfindingLayer::LAND)
+				CCS->curh->changeGraphic(ECursor::ADVENTURE, 9 + turns*6);
 			else
+				CCS->curh->changeGraphic(ECursor::ADVENTURE, 28 + turns);
+			break;
+
+		case CGPathNode::BATTLE:
+			CCS->curh->changeGraphic(ECursor::ADVENTURE, 5 + turns*6);
+			break;
+
+		case CGPathNode::EMBARK:
+			CCS->curh->changeGraphic(ECursor::ADVENTURE, 6 + turns*6);
+			break;
+
+		case CGPathNode::DISEMBARK:
+			CCS->curh->changeGraphic(ECursor::ADVENTURE, 7 + turns*6);
+			break;
+
+		default:
+			if(objAtTile && objRelations != PlayerRelations::ENEMIES)
 			{
-				if(accessible)
-				{
-					if(pnode->land)
-						CCS->curh->changeGraphic(ECursor::ADVENTURE, 9 + turns*6);
-					else
-						CCS->curh->changeGraphic(ECursor::ADVENTURE, 28 + turns);
-				}
-				else
-					CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
-			}
-		}
-		else //no objs
-		{
-			if(accessible/* && pnode->accessible != CGPathNode::FLYABLE*/)
-			{
-				if (guardingCreature)
-				{
-					CCS->curh->changeGraphic(ECursor::ADVENTURE, 5 + turns*6);
-				}
-				else
-				{
-					if(pnode->land)
-					{
-						if(LOCPLINT->cb->getTile(h->getPosition(false))->terType != ETerrainType::WATER)
-							CCS->curh->changeGraphic(ECursor::ADVENTURE, 4 + turns*6);
-						else
-							CCS->curh->changeGraphic(ECursor::ADVENTURE, 7 + turns*6); //anchor
-					}
-					else
-						CCS->curh->changeGraphic(ECursor::ADVENTURE, 6 + turns*6);
-				}
+				if(objAtTile->ID == Obj::TOWN)
+					CCS->curh->changeGraphic(ECursor::ADVENTURE, 3);
+				else if(objAtTile->ID == Obj::HERO && objRelations == PlayerRelations::SAME_PLAYER)
+					CCS->curh->changeGraphic(ECursor::ADVENTURE, 2);
 			}
 			else
 				CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
+			break;
 		}
 	}
 

+ 1 - 1
client/windows/CQuestLog.cpp

@@ -19,7 +19,7 @@
 #include "../../lib/CGameState.h"
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/NetPacksBase.h"
-
+#include "../../lib/mapObjects/CQuest.h"
 /*
  * CQuestLog.cpp, part of VCMI engine
  *

+ 4 - 2
client/windows/GUIClasses.cpp

@@ -705,7 +705,9 @@ CTavernWindow::CTavernWindow(const CGObjectInstance *TavernObj):
 
 	new CLabel(200, 35, FONT_BIG, CENTER, Colors::YELLOW, CGI->generaltexth->jktexts[37]);
 	new CLabel(320, 328, FONT_SMALL, CENTER, Colors::WHITE, "2500");
-	new CTextBox(LOCPLINT->cb->getTavernGossip(tavernObj), Rect(32, 190, 330, 68), 0, FONT_SMALL, CENTER, Colors::WHITE);
+
+	auto rumorText = boost::str(boost::format(CGI->generaltexth->allTexts[216]) % LOCPLINT->cb->getTavernRumor(tavernObj));
+	new CTextBox(rumorText, Rect(32, 190, 330, 68), 0, FONT_SMALL, CENTER, Colors::WHITE);
 
 	new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
 	cancel = new CButton(Point(310, 428), "ICANCEL.DEF", CButton::tooltip(CGI->generaltexth->tavernInfo[7]), std::bind(&CTavernWindow::close, this), SDLK_ESCAPE);
@@ -1628,9 +1630,9 @@ CThievesGuildWindow::CThievesGuildWindow(const CGObjectInstance * _owner):
 	int counter = 0;
 	for(auto & iter : tgi.colorToBestHero)
 	{
+		new CPicture(colorToBox[iter.first.getNum()], 253 + 66 * counter, 334);
 		if(iter.second.portrait >= 0)
 		{
-			new CPicture(colorToBox[iter.first.getNum()], 253 + 66 * counter, 334);
 			new CAnimImage("PortraitsSmall", iter.second.portrait, 0, 260 + 66 * counter, 360);
 			//TODO: r-click info:
 			// - r-click on hero

+ 1 - 1
config/resolutions.json

@@ -3,7 +3,7 @@
 	[
 		{
 			"resolution": { "x": 800, "y": 600 },
-			"InGameConsole": { "maxInputPerLine": 60, "maxOutputPerLine": 39 },
+			"InGameConsole": { "maxInputPerLine": 60, "maxOutputPerLine": 60 },
 			"AdvMap": { "x": 7, "y": 7, "width": 594, "height": 546, "smoothMove": 1, "puzzleSepia": 1, "objectFading" : 1, "screenFading" : 1 },
 			"InfoBox": { "x": 605, "y": 389 },
 			"gem0": { "x": 6, "y": 508, "graphic": "agemLL.def" },

+ 69 - 1
config/schemas/settings.json

@@ -3,7 +3,7 @@
 {
 	"type" : "object",
 	"$schema": "http://json-schema.org/draft-04/schema",
-	"required" : [ "general", "video", "adventure", "battle", "server", "logging", "launcher" ],
+	"required" : [ "general", "video", "adventure", "pathfinder", "battle", "server", "logging", "launcher" ],
 	"definitions" : {
 		"logLevelEnum" : { 
 			"type" : "string", 
@@ -108,6 +108,74 @@
 				}
 			}
 		},
+		"pathfinder" : {
+			"type" : "object",
+			"additionalProperties" : false,
+			"default": {},
+			"required" : [ "teleports", "layers", "oneTurnSpecialLayersLimit", "originalMovementRules", "lightweightFlyingMode" ],
+			"properties" : {
+				"layers" : {
+					"type" : "object",
+					"additionalProperties" : false,
+					"default": {},
+					"required" : [ "sailing", "waterWalking", "flying" ],
+					"properties" : {
+						"sailing" : {
+							"type" : "boolean",
+							"default" : true
+						},
+						"waterWalking" : {
+							"type" : "boolean",
+							"default" : true
+						},
+						"flying" : {
+							"type" : "boolean",
+							"default" : true
+						}
+					}
+				},
+				"teleports" : {
+					"type" : "object",
+					"additionalProperties" : false,
+					"default": {},
+					"required" : [ "twoWay", "oneWay", "oneWayRandom", "whirlpool", "castleGate" ],
+					"properties" : {
+						"twoWay" : {
+							"type" : "boolean",
+							"default" : true
+						},
+						"oneWay" : {
+							"type" : "boolean",
+							"default" : true
+						},
+						"oneWayRandom" : {
+							"type" : "boolean",
+							"default" : false
+						},
+						"whirlpool" : {
+							"type" : "boolean",
+							"default" : true
+						},
+						"castleGate" : {
+							"type" : "boolean",
+							"default" : false
+						}
+					}
+				},
+				"oneTurnSpecialLayersLimit" : {
+					"type" : "boolean",
+					"default" : true
+				},
+				"originalMovementRules" : {
+					"type" : "boolean",
+					"default" : false
+				},
+				"lightweightFlyingMode" : {
+					"type" : "boolean",
+					"default" : false
+				}
+			}
+		},
 		"battle" : {
 			"type" : "object",
 			"additionalProperties" : false,

+ 1 - 1
launcher/modManager/cmodlistmodel_moc.h

@@ -52,7 +52,7 @@ public:
 	/// CModListContainer overrides
 	void resetRepositories() override;
 	void addRepository(QVariantMap data) override;
-	void modChanged(QString modID);
+	void modChanged(QString modID) override;
 
 	QVariant data(const QModelIndex &index, int role) const override;
 	QVariant headerData(int section, Qt::Orientation orientation, int role) const override;

+ 2 - 1
lib/BattleState.cpp

@@ -1,4 +1,4 @@
-/*
+/*
  * BattleState.cpp, part of VCMI engine
  *
  * Authors: listed in file AUTHORS in main folder
@@ -22,6 +22,7 @@
 #include "JsonNode.h"
 #include "filesystem/Filesystem.h"
 #include "CRandomGenerator.h"
+#include "mapObjects/CGTownInstance.h"
 
 const CStack * BattleInfo::getNextStack() const
 {

+ 0 - 3
lib/BattleState.h

@@ -11,12 +11,9 @@
 #pragma once
 
 #include "BattleHex.h"
-#include "HeroBonus.h"
-#include "CCreatureSet.h"
 #include "mapObjects/CArmedInstance.h" // for army serialization
 #include "mapObjects/CGHeroInstance.h" // for commander serialization
 #include "CCreatureHandler.h"
-#include "CObstacleInstance.h"
 #include "ConstTransitivePtr.h"
 #include "GameConstants.h"
 #include "CBattleCallback.h"

+ 2 - 2
lib/CArtHandler.cpp

@@ -738,8 +738,8 @@ std::string CArtifactInstance::nodeName() const
 
 CArtifactInstance * CArtifactInstance::createScroll( const CSpell *s)
 {
-	auto ret = new CArtifactInstance(VLC->arth->artifacts[1]);
-	auto b = new Bonus(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, 1, s->id);
+	auto ret = new CArtifactInstance(VLC->arth->artifacts[ArtifactID::SPELL_SCROLL]);
+	auto b = new Bonus(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, ArtifactID::SPELL_SCROLL, s->id);
 	ret->addNewBonus(b);
 	return ret;
 }

+ 2 - 0
lib/CBattleCallback.cpp

@@ -6,6 +6,7 @@
 #include "spells/CSpellHandler.h"
 #include "VCMI_Lib.h"
 #include "CTownHandler.h"
+#include "mapObjects/CGTownInstance.h"
 
 /*
  * CBattleCallback.cpp, part of VCMI engine
@@ -2245,6 +2246,7 @@ BattleAttackInfo::BattleAttackInfo(const CStack *Attacker, const CStack *Defende
 	chargedFields = 0;
 
 	luckyHit = false;
+	unluckyHit = false;
 	deathBlow = false;
 	ballistaDoubleDamage = false;
 }

+ 1 - 1
lib/CConsoleHandler.cpp

@@ -27,7 +27,7 @@ DLL_LINKAGE CConsoleHandler * console = nullptr;
 	#define CONSOLE_GRAY "\x1b[1;30m"
 	#define CONSOLE_TEAL "\x1b[1;36m"
 #else
-	#include <Windows.h>
+	#include <windows.h>
 	#include <dbghelp.h>	
 #ifndef __MINGW32__
 	#pragma comment(lib, "dbghelp.lib")

+ 39 - 4
lib/CGameInfoCallback.cpp

@@ -12,12 +12,15 @@
 #include "CGameInfoCallback.h"
 
 #include "CGameState.h" // PlayerState
+#include "CGeneralTextHandler.h"
 #include "mapObjects/CObjectHandler.h" // for CGObjectInstance
 #include "StartInfo.h" // for StartInfo
 #include "BattleState.h" // for BattleInfo
 #include "NetPacks.h" // for InfoWindow
 #include "CModHandler.h"
 #include "spells/CSpellHandler.h"
+#include "mapping/CMap.h"
+#include "CPlayerState.h"
 
 //TODO make clean
 #define ERROR_VERBOSE_OR_NOT_RET_VAL_IF(cond, verbose, txt, retVal) do {if(cond){if(verbose)logGlobal->errorStream() << BOOST_CURRENT_FUNCTION << ": " << txt; return retVal;}} while(0)
@@ -196,7 +199,14 @@ void CGameInfoCallback::getThievesGuildInfo(SThievesGuildInfo & thi, const CGObj
 
 	if(obj->ID == Obj::TOWN  ||  obj->ID == Obj::TAVERN)
 	{
-		gs->obtainPlayersStats(thi, gs->players[obj->tempOwner].towns.size());
+		int taverns = 0;
+		for(auto town : gs->players[*player].towns)
+		{
+			if(town->hasBuilt(BuildingID::TAVERN))
+				taverns++;
+		}
+
+		gs->obtainPlayersStats(thi, taverns);
 	}
 	else if(obj->ID == Obj::DEN_OF_THIEVES)
 	{
@@ -564,9 +574,34 @@ EPlayerStatus::EStatus CGameInfoCallback::getPlayerStatus(PlayerColor player, bo
 	return ps->status;
 }
 
-std::string CGameInfoCallback::getTavernGossip(const CGObjectInstance * townOrTavern) const
+std::string CGameInfoCallback::getTavernRumor(const CGObjectInstance * townOrTavern) const
 {
-	return "GOSSIP TEST";
+	std::string text = "", extraText = "";
+	if(gs->rumor.type == RumorState::TYPE_NONE) // (version < 755 backward compatability
+		return text;
+
+	auto rumor = gs->rumor.last[gs->rumor.type];
+	switch(gs->rumor.type)
+	{
+	case RumorState::TYPE_SPECIAL:
+		if(rumor.first == RumorState::RUMOR_GRAIL)
+			extraText = VLC->generaltexth->arraytxt[158 + rumor.second];
+		else
+			extraText = VLC->generaltexth->capColors[rumor.second];
+
+		text = boost::str(boost::format(VLC->generaltexth->allTexts[rumor.first]) % extraText);
+
+		break;
+	case RumorState::TYPE_MAP:
+		text = gs->map->rumors[rumor.first].text;
+		break;
+
+	case RumorState::TYPE_RAND:
+		text = VLC->generaltexth->tavernRumors[rumor.first];
+		break;
+	}
+
+	return text;
 }
 
 PlayerRelations::PlayerRelations CGameInfoCallback::getPlayerRelations( PlayerColor color1, PlayerColor color2 ) const
@@ -853,7 +888,7 @@ std::vector<ObjectInstanceID> CGameInfoCallback::getVisibleTeleportObjects(std::
 {
 	vstd::erase_if(ids, [&](ObjectInstanceID id) -> bool
 	{
-		auto obj = getObj(id);
+		auto obj = getObj(id, false);
 		return player != PlayerColor::UNFLAGGABLE && (!obj || !isVisible(obj->pos, player));
 	});
 	return ids;

+ 1 - 1
lib/CGameInfoCallback.h

@@ -99,7 +99,7 @@ public:
 	int howManyTowns(PlayerColor Player) const;
 	const CGTownInstance * getTownInfo(int val, bool mode)const; //mode = 0 -> val = player town serial; mode = 1 -> val = object id (serial)
 	std::vector<const CGHeroInstance *> getAvailableHeroes(const CGObjectInstance * townOrTavern) const; //heroes that can be recruited
-	std::string getTavernGossip(const CGObjectInstance * townOrTavern) const; 
+	std::string getTavernRumor(const CGObjectInstance * townOrTavern) const;
 	EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID);//// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
 	virtual bool getTownInfo(const CGObjectInstance * town, InfoAboutTown & dest, const CGObjectInstance * selectedObject = nullptr) const;
 	const CTown *getNativeTown(PlayerColor color) const;

+ 3 - 1
lib/CGameInterface.h

@@ -7,6 +7,8 @@
 
 #include "spells/ViewSpellInt.h"
 
+#include "mapObjects/CObjectHandler.h"
+
 /*
  * CGameInterface.h, part of VCMI engine
  *
@@ -94,7 +96,7 @@ public:
 
 	// all stacks operations between these objects become allowed, interface has to call onEnd when done
 	virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) = 0;
-	virtual void showTeleportDialog(TeleportChannelID channel, std::vector<ObjectInstanceID> exits, bool impassable, QueryID askID) = 0;
+	virtual void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) = 0;
 	virtual void finish(){}; //if for some reason we want to end
 	
 	virtual void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions){};

+ 163 - 111
lib/CGameState.cpp

@@ -907,7 +907,7 @@ void CGameState::initDuel()
 
 			if(!ss.spells.empty())
 			{
-				h->putArtifact(ArtifactPosition::SPELLBOOK, CArtifactInstance::createNewArtifactInstance(0));
+				h->putArtifact(ArtifactPosition::SPELLBOOK, CArtifactInstance::createNewArtifactInstance(ArtifactID::SPELLBOOK));
 				boost::copy(ss.spells, std::inserter(h->spells, h->spells.begin()));
 			}
 
@@ -1383,7 +1383,8 @@ void CGameState::placeStartingHeroes()
 			}
 
 			int heroTypeId = pickNextHeroType(playerColor);
-			if(playerSettingPair.second.hero == -1) playerSettingPair.second.hero = heroTypeId;
+			if(playerSettingPair.second.hero == -1)
+				playerSettingPair.second.hero = heroTypeId;
 
 			placeStartingHero(playerColor, HeroTypeID(heroTypeId), playerInfo.posOfMainTown);
 		}
@@ -2061,101 +2062,6 @@ PlayerRelations::PlayerRelations CGameState::getPlayerRelations( PlayerColor col
 	return PlayerRelations::ENEMIES;
 }
 
-void CGameState::getNeighbours(const TerrainTile &srct, int3 tile, std::vector<int3> &vec, const boost::logic::tribool &onLand, bool limitCoastSailing)
-{
-	static const int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0),
-					int3(1,1,0),int3(-1,1,0),int3(1,-1,0),int3(-1,-1,0) };
-
-	//vec.reserve(8); //optimization
-	for (auto & dir : dirs)
-	{
-		const int3 hlp = tile + dir;
-		if(!map->isInTheMap(hlp))
-			continue;
-
-		const TerrainTile &hlpt = map->getTile(hlp);
-
-// 		//we cannot visit things from blocked tiles
-// 		if(srct.blocked && !srct.visitable && hlpt.visitable && srct.blockingObjects.front()->ID != HEROI_TYPE)
-// 		{
-// 			continue;
-// 		}
-
-        if(srct.terType == ETerrainType::WATER && limitCoastSailing && hlpt.terType == ETerrainType::WATER && dir.x && dir.y) //diagonal move through water
-		{
-			int3 hlp1 = tile,
-				hlp2 = tile;
-			hlp1.x += dir.x;
-			hlp2.y += dir.y;
-
-            if(map->getTile(hlp1).terType != ETerrainType::WATER || map->getTile(hlp2).terType != ETerrainType::WATER)
-				continue;
-		}
-
-        if((indeterminate(onLand)  ||  onLand == (hlpt.terType!=ETerrainType::WATER) )
-            && hlpt.terType != ETerrainType::ROCK)
-		{
-			vec.push_back(hlp);
-		}
-	}
-}
-
-int CGameState::getMovementCost(const CGHeroInstance *h, const int3 &src, const int3 &dest, int remainingMovePoints, bool checkLast)
-{
-	if(src == dest) //same tile
-		return 0;
-
-	TerrainTile &s = map->getTile(src),
-		&d = map->getTile(dest);
-
-	//get basic cost
-	int ret = h->getTileCost(d,s);
-
-	if(d.blocked && h->canFly())
-	{
-		ret *= (100.0 + h->valOfBonuses(Bonus::FLYING_MOVEMENT)) / 100.0;
-	}
-	else if(d.terType == ETerrainType::WATER)
-	{
-		if(h->boat && s.hasFavourableWinds() && d.hasFavourableWinds()) //Favourable Winds
-			ret *= 0.666;
-		else if(!h->boat && h->canWalkOnSea())
-		{
-			ret *= (100.0 + h->valOfBonuses(Bonus::WATER_WALKING)) / 100.0;
-		}
-	}
-
-	if(src.x != dest.x  &&  src.y != dest.y) //it's diagonal move
-	{
-		int old = ret;
-		ret *= 1.414213;
-		//diagonal move costs too much but normal move is possible - allow diagonal move for remaining move points
-		if(ret > remainingMovePoints  &&  remainingMovePoints >= old)
-		{
-			return remainingMovePoints;
-		}
-	}
-
-
-	int left = remainingMovePoints-ret;
-	if(checkLast  &&  left > 0  &&  remainingMovePoints-ret < 250) //it might be the last tile - if no further move possible we take all move points
-	{
-		std::vector<int3> vec;
-		vec.reserve(8); //optimization
-		getNeighbours(d, dest, vec, s.terType != ETerrainType::WATER, true);
-		for(auto & elem : vec)
-		{
-			int fcost = getMovementCost(h, dest, elem, left, false);
-			if(fcost <= left)
-			{
-				return ret;
-			}
-		}
-		ret = remainingMovePoints;
-	}
-	return ret;
-}
-
 void CGameState::apply(CPack *pack)
 {
 	ui16 typ = typeList.getTypeID(pack);
@@ -2228,6 +2134,76 @@ int3 CGameState::guardingCreaturePosition (int3 pos) const
 	return gs->map->guardingCreaturePositions[pos.x][pos.y][pos.z];
 }
 
+void CGameState::updateRumor()
+{
+	static std::vector<RumorState::ERumorType> rumorTypes = {RumorState::TYPE_MAP, RumorState::TYPE_SPECIAL, RumorState::TYPE_RAND, RumorState::TYPE_RAND};
+	std::vector<RumorState::ERumorTypeSpecial> sRumorTypes = {
+		RumorState::RUMOR_OBELISKS, RumorState::RUMOR_ARTIFACTS, RumorState::RUMOR_ARMY, RumorState::RUMOR_INCOME};
+	if(map->grailPos.valid()) // Grail should always be on map, but I had related crash I didn't manage to reproduce
+		sRumorTypes.push_back(RumorState::RUMOR_GRAIL);
+
+	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
+	{
+		switch(rumor.type)
+		{
+		case RumorState::TYPE_SPECIAL:
+		{
+			SThievesGuildInfo tgi;
+			obtainPlayersStats(tgi, 20);
+			rumorId = *RandomGeneratorUtil::nextItem(sRumorTypes, rand);
+			if(rumorId == RumorState::RUMOR_GRAIL)
+			{
+				rumorExtra = getTile(map->grailPos)->terType;
+				break;
+			}
+
+			std::vector<PlayerColor> players = {};
+			switch(rumorId)
+			{
+			case RumorState::RUMOR_OBELISKS:
+				players = tgi.obelisks[0];
+				break;
+
+			case RumorState::RUMOR_ARTIFACTS:
+				players = tgi.artifacts[0];
+				break;
+
+			case RumorState::RUMOR_ARMY:
+				players = tgi.army[0];
+				break;
+
+			case RumorState::RUMOR_INCOME:
+				players = tgi.income[0];
+				break;
+			}
+			rumorExtra = RandomGeneratorUtil::nextItem(players, rand)->getNum();
+
+			break;
+		}
+		case RumorState::TYPE_MAP:
+			rumorId = rand.nextInt(map->rumors.size() - 1);
+
+			break;
+
+		case RumorState::TYPE_RAND:
+			do
+			{
+				rumorId = rand.nextInt(VLC->generaltexth->tavernRumors.size() - 1);
+			}
+			while(!VLC->generaltexth->tavernRumors[rumorId].length());
+
+			break;
+		}
+	}
+	while(!rumor.update(rumorId, rumorExtra));
+}
+
 bool CGameState::isVisible(int3 pos, PlayerColor player)
 {
 	if(player == PlayerColor::NEUTRAL)
@@ -2549,6 +2525,58 @@ struct statsHLP
 		}
 		return str;
 	}
+
+	// get total gold income
+	static int getIncome(const PlayerState * ps)
+	{
+		int totalIncome = 0;
+		const CGObjectInstance * heroOrTown = nullptr;
+
+		//Heroes can produce gold as well - skill, specialty or arts
+		for(auto & h : ps->heroes)
+		{
+			totalIncome += h->valOfBonuses(Selector::typeSubtype(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::ESTATES));
+			totalIncome += h->valOfBonuses(Selector::typeSubtype(Bonus::GENERATE_RESOURCE, Res::GOLD));
+
+			if(!heroOrTown)
+				heroOrTown = h;
+		}
+
+		//Add town income of all towns
+		for(auto & t : ps->towns)
+		{
+			totalIncome += t->dailyIncome()[Res::GOLD];
+
+			if(!heroOrTown)
+				heroOrTown = t;
+		}
+
+		/// FIXME: Dirty dirty hack
+		/// Stats helper need some access to gamestate.
+		std::vector<const CGObjectInstance *> ownedObjects;
+		for(const CGObjectInstance * obj : heroOrTown->cb->gameState()->map->objects)
+		{
+			if(obj && obj->tempOwner == ps->color)
+				ownedObjects.push_back(obj);
+		}
+		/// This is code from CPlayerSpecificInfoCallback::getMyObjects
+		/// I'm really need to find out about callback interface design...
+
+		for(auto object : ownedObjects)
+		{
+			//Mines
+			if ( object->ID == Obj::MINE )
+			{
+				const CGMine *mine = dynamic_cast<const CGMine*>(object);
+				assert(mine);
+
+				if (mine->producedResource == Res::GOLD)
+					totalIncome += mine->producedQuantity;
+			}
+		}
+
+		return totalIncome;
+	}
 };
 
 void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
@@ -2579,20 +2607,22 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
 			tgi.playerColors.push_back(elem.second.color);
 	}
 
-	if(level >= 1) //num of towns & num of heroes
+	if(level >= 0) //num of towns & num of heroes
 	{
 		//num of towns
 		FILL_FIELD(numOfTowns, g->second.towns.size())
 		//num of heroes
 		FILL_FIELD(numOfHeroes, g->second.heroes.size())
-		//best hero's portrait
+	}
+	if(level >= 1) //best hero's portrait
+	{
 		for(auto g = players.cbegin(); g != players.cend(); ++g)
 		{
 			if(playerInactive(g->second.color))
 				continue;
 			const CGHeroInstance * best = statsHLP::findBestHero(this, g->second.color);
 			InfoAboutHero iah;
-			iah.initFromHero(best, level >= 8);
+			iah.initFromHero(best, level >= 2);
 			iah.army.clear();
 			tgi.colorToBestHero[g->second.color] = iah;
 		}
@@ -2609,27 +2639,27 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
 	{
 		FILL_FIELD(mercSulfCrystGems, g->second.resources[Res::MERCURY] + g->second.resources[Res::SULFUR] + g->second.resources[Res::CRYSTAL] + g->second.resources[Res::GEMS])
 	}
-	if(level >= 4) //obelisks found
+	if(level >= 3) //obelisks found
 	{
 		FILL_FIELD(obelisks, CGObelisk::visited[gs->getPlayerTeam(g->second.color)->id])
 	}
-	if(level >= 5) //artifacts
+	if(level >= 4) //artifacts
 	{
 		FILL_FIELD(artifacts, statsHLP::getNumberOfArts(&g->second))
 	}
-	if(level >= 6) //army strength
+	if(level >= 4) //army strength
 	{
 		FILL_FIELD(army, statsHLP::getArmyStrength(&g->second))
 	}
-	if(level >= 7) //income
+	if(level >= 5) //income
 	{
-		//TODO:obtainPlayersStats - income
+		FILL_FIELD(income, statsHLP::getIncome(&g->second))
 	}
-	if(level >= 8) //best hero's stats
+	if(level >= 2) //best hero's stats
 	{
 		//already set in  lvl 1 handling
 	}
-	if(level >= 9) //personality
+	if(level >= 3) //personality
 	{
 		for(auto g = players.cbegin(); g != players.cend(); ++g)
 		{
@@ -2646,7 +2676,7 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
 
 		}
 	}
-	if(level >= 10) //best creature
+	if(level >= 4) //best creature
 	{
 		//best creatures belonging to player (highest AI value)
 		for(auto g = players.cbegin(); g != players.cend(); ++g)
@@ -2865,7 +2895,7 @@ CGHeroInstance * CGameState::getUsedHero(HeroTypeID hid) const
 {
 	for(auto hero : map->heroesOnMap)  //heroes instances initialization
 	{
-		if(hero->subID == hid.getNum())
+		if(hero->type && hero->type->ID == hid)
 		{
 			return hero;
 		}
@@ -2873,9 +2903,12 @@ CGHeroInstance * CGameState::getUsedHero(HeroTypeID hid) const
 
 	for(auto obj : map->objects) //prisons
 	{
-		if(obj && obj->ID == Obj::PRISON && obj->subID == hid.getNum())
+		if(obj && obj->ID == Obj::PRISON )
 		{
-			return dynamic_cast<CGHeroInstance *>(obj.get());
+			auto hero = dynamic_cast<CGHeroInstance *>(obj.get());
+			assert(hero);
+			if ( hero->type && hero->type->ID == hid )
+				return hero;
 		}
 	}
 
@@ -2894,6 +2927,25 @@ std::string PlayerState::nodeName() const
 	return "Player " + (color.getNum() < VLC->generaltexth->capColors.size() ? VLC->generaltexth->capColors[color.getNum()] : boost::lexical_cast<std::string>(color));
 }
 
+
+bool RumorState::update(int id, int extra)
+{
+	if(vstd::contains(last, type))
+	{
+		if(last[type].first != id)
+		{
+			last[type].first = id;
+			last[type].second = extra;
+		}
+		else
+			return false;
+	}
+	else
+		last[type] = std::make_pair(id, extra);
+
+	return true;
+}
+
 InfoAboutArmy::InfoAboutArmy():
     owner(PlayerColor::NEUTRAL)
 {}

+ 26 - 119
lib/CGameState.h

@@ -1,12 +1,7 @@
 #pragma once
 
-
-
-//#ifndef _MSC_VER
 #include "CCreatureHandler.h"
 #include "VCMI_Lib.h"
-#include "mapping/CMap.h"
-//#endif
 
 #include "HeroBonus.h"
 #include "CCreatureSet.h"
@@ -67,79 +62,6 @@ namespace boost
 	class shared_mutex;
 }
 
-//numbers of creatures are exact numbers if detailed else they are quantity ids (1 - a few, 2 - several and so on; additionally 0 - unknown)
-struct ArmyDescriptor : public std::map<SlotID, CStackBasicDescriptor>
-{
-	bool isDetailed;
-	DLL_LINKAGE ArmyDescriptor(const CArmedInstance *army, bool detailed); //not detailed -> quantity ids as count
-	DLL_LINKAGE ArmyDescriptor();
-
-	DLL_LINKAGE int getStrength() const;
-};
-
-struct DLL_LINKAGE InfoAboutArmy
-{
-	PlayerColor owner;
-	std::string name;
-
-	ArmyDescriptor army;
-
-	InfoAboutArmy();
-	InfoAboutArmy(const CArmedInstance *Army, bool detailed);
-
-	void initFromArmy(const CArmedInstance *Army, bool detailed);
-};
-
-struct DLL_LINKAGE InfoAboutHero : public InfoAboutArmy
-{
-private:
-	void assign(const InfoAboutHero & iah);
-public:
-	struct DLL_LINKAGE Details
-	{
-		std::vector<si32> primskills;
-		si32 mana, luck, morale;
-	} *details;
-
-	const CHeroClass *hclass;
-	int portrait;
-
-	InfoAboutHero();
-	InfoAboutHero(const InfoAboutHero & iah);
-	InfoAboutHero(const CGHeroInstance *h, bool detailed);
-	~InfoAboutHero();
-
-	InfoAboutHero & operator=(const InfoAboutHero & iah);
-
-	void initFromHero(const CGHeroInstance *h, bool detailed);
-};
-
-/// Struct which holds a int information about a town
-struct DLL_LINKAGE InfoAboutTown : public InfoAboutArmy
-{
-	struct DLL_LINKAGE Details
-	{
-		si32 hallLevel, goldIncome;
-		bool customRes;
-		bool garrisonedHero;
-
-	} *details;
-
-	const CTown *tType;
-
-	si32 built;
-	si32 fortLevel; //0 - none
-
-	InfoAboutTown();
-	InfoAboutTown(const CGTownInstance *t, bool detailed);
-	~InfoAboutTown();
-	void initFromTown(const CGTownInstance *t, bool detailed);
-};
-
-// typedef si32 TResourceUnit;
-// typedef std::vector<si32> TResourceVector;
-// typedef std::set<si32> TResourceSet;
-
 struct DLL_LINKAGE SThievesGuildInfo
 {
 	std::vector<PlayerColor> playerColors; //colors of players that are in-game
@@ -159,53 +81,32 @@ struct DLL_LINKAGE SThievesGuildInfo
 
 };
 
-struct DLL_LINKAGE PlayerState : public CBonusSystemNode
+struct DLL_LINKAGE RumorState
 {
-public:
-	PlayerColor color;
-	bool human; //true if human controlled player, false for AI
-	TeamID team;
-	TResources resources;
-	std::set<ObjectInstanceID> visitedObjects; // as a std::set, since most accesses here will be from visited status checks
-	std::vector<ConstTransitivePtr<CGHeroInstance> > heroes;
-	std::vector<ConstTransitivePtr<CGTownInstance> > towns;
-	std::vector<ConstTransitivePtr<CGHeroInstance> > availableHeroes; //heroes available in taverns
-	std::vector<ConstTransitivePtr<CGDwelling> > dwellings; //used for town growth
-	std::vector<QuestInfo> quests; //store info about all received quests
-
-	bool enteredWinningCheatCode, enteredLosingCheatCode; //if true, this player has entered cheat codes for loss / victory
-	EPlayerStatus::EStatus status;
-	boost::optional<ui8> daysWithoutCastle;
-
-	PlayerState();
-	std::string nodeName() const override;
+	enum ERumorType : ui8
+	{
+		TYPE_NONE = 0, TYPE_RAND, TYPE_SPECIAL, TYPE_MAP
+	};
 
-	template <typename Handler> void serialize(Handler &h, const int version)
+	enum ERumorTypeSpecial : ui8
 	{
-		h & color & human & team & resources & status;
-		h & heroes & towns & availableHeroes & dwellings & quests & visitedObjects;
-		h & getBonusList(); //FIXME FIXME FIXME
-		h & status & daysWithoutCastle;
-		h & enteredLosingCheatCode & enteredWinningCheatCode;
-		h & static_cast<CBonusSystemNode&>(*this);
-	}
-};
+		RUMOR_OBELISKS = 208,
+		RUMOR_ARTIFACTS = 209,
+		RUMOR_ARMY = 210,
+		RUMOR_INCOME = 211,
+		RUMOR_GRAIL = 212
+	};
 
-struct DLL_LINKAGE TeamState : public CBonusSystemNode
-{
-public:
-	TeamID id; //position in gameState::teams
-	std::set<PlayerColor> players; // members of this team
-	std::vector<std::vector<std::vector<ui8> > >  fogOfWarMap; //true - visible, false - hidden
+	ERumorType type;
+	std::map<ERumorType, std::pair<int, int>> last;
 
-	TeamState();
+	RumorState(){type = TYPE_NONE; last = {};};
+	bool update(int id, int extra);
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & id & players & fogOfWarMap;
-		h & static_cast<CBonusSystemNode&>(*this);
+		h & type & last;
 	}
-
 };
 
 struct UpgradeInfo
@@ -276,7 +177,6 @@ struct DLL_EXPORT DuelParameters
 	}
 };
 
-
 struct BattleInfo;
 
 DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EVictoryLossCheckResult & victoryLossCheckResult);
@@ -311,6 +211,7 @@ public:
 	std::map<PlayerColor, PlayerState> players;
 	std::map<TeamID, TeamState> teams;
 	CBonusSystemNode globalEffects;
+	RumorState rumor;
 
 	boost::shared_mutex *mx;
 
@@ -324,6 +225,7 @@ public:
 	void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out); //calculates possible paths for hero, by default uses current hero position and movement left; returns pointer to newly allocated CPath or nullptr if path does not exists
 	int3 guardingCreaturePosition (int3 pos) const;
 	std::vector<CGObjectInstance*> guardingCreatures (int3 pos) const;
+	void updateRumor();
 
 	// ----- victory, loss condition checks -----
 
@@ -339,8 +241,6 @@ public:
 	bool isVisible(int3 pos, PlayerColor player);
 	bool isVisible(const CGObjectInstance *obj, boost::optional<PlayerColor> player);
 
-	void getNeighbours(const TerrainTile &srct, int3 tile, std::vector<int3> &vec, const boost::logic::tribool &onLand, bool limitCoastSailing);
-	int getMovementCost(const CGHeroInstance *h, const int3 &src, const int3 &dest, int remainingMovePoints=-1, bool checkLast=true);
 	int getDate(Date::EDateType mode=Date::DAY) const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month
 
 	// ----- getters, setters -----
@@ -349,6 +249,13 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & scenarioOps & initialOpts & currentPlayer & day & map & players & teams & hpool & globalEffects & rand;
+		if(version >= 755)
+		{
+			h & rumor;
+		}
+		else if(!h.saving)
+			rumor = RumorState();
+
 		BONUS_TREE_DESERIALIZATION_FIX
 	}
 

+ 73 - 0
lib/CGameStateFwd.h

@@ -10,8 +10,81 @@
  *
  */
 
+#include "CCreatureSet.h"
+
 class CQuest;
 class CGObjectInstance;
+class CHeroClass;
+class CTown;
+
+//numbers of creatures are exact numbers if detailed else they are quantity ids (1 - a few, 2 - several and so on; additionally 0 - unknown)
+struct ArmyDescriptor : public std::map<SlotID, CStackBasicDescriptor>
+{
+	bool isDetailed;
+	DLL_LINKAGE ArmyDescriptor(const CArmedInstance *army, bool detailed); //not detailed -> quantity ids as count
+	DLL_LINKAGE ArmyDescriptor();
+
+	DLL_LINKAGE int getStrength() const;
+};
+
+struct DLL_LINKAGE InfoAboutArmy
+{
+	PlayerColor owner;
+	std::string name;
+
+	ArmyDescriptor army;
+
+	InfoAboutArmy();
+	InfoAboutArmy(const CArmedInstance *Army, bool detailed);
+
+	void initFromArmy(const CArmedInstance *Army, bool detailed);
+};
+
+struct DLL_LINKAGE InfoAboutHero : public InfoAboutArmy
+{
+private:
+	void assign(const InfoAboutHero & iah);
+public:
+	struct DLL_LINKAGE Details
+	{
+		std::vector<si32> primskills;
+		si32 mana, luck, morale;
+	} *details;
+
+	const CHeroClass *hclass;
+	int portrait;
+
+	InfoAboutHero();
+	InfoAboutHero(const InfoAboutHero & iah);
+	InfoAboutHero(const CGHeroInstance *h, bool detailed);
+	~InfoAboutHero();
+
+	InfoAboutHero & operator=(const InfoAboutHero & iah);
+
+	void initFromHero(const CGHeroInstance *h, bool detailed);
+};
+
+/// Struct which holds a int information about a town
+struct DLL_LINKAGE InfoAboutTown : public InfoAboutArmy
+{
+	struct DLL_LINKAGE Details
+	{
+		si32 hallLevel, goldIncome;
+		bool customRes;
+		bool garrisonedHero;
+
+	} *details;
+
+	const CTown *tType;
+
+	si32 built;
+	si32 fortLevel; //0 - none
+
+	InfoAboutTown();
+	InfoAboutTown(const CGTownInstance *t, bool detailed);
+	~InfoAboutTown();
+	void initFromTown(const CGTownInstance *t, bool detailed);
+};
 
 class DLL_LINKAGE EVictoryLossCheckResult
 {

+ 1 - 0
lib/CGeneralTextHandler.cpp

@@ -327,6 +327,7 @@ CGeneralTextHandler::CGeneralTextHandler()
 	readToVector("DATA/PRISKILL.TXT", primarySkillNames);
 	readToVector("DATA/JKTEXT.TXT",   jktexts);
 	readToVector("DATA/TVRNINFO.TXT", tavernInfo);
+	readToVector("DATA/RANDTVRN.TXT", tavernRumors);
 	readToVector("DATA/TURNDUR.TXT",  turnDurations);
 	readToVector("DATA/HEROSCRN.TXT", heroscrn);
 	readToVector("DATA/TENTCOLR.TXT", tentColors);

+ 1 - 0
lib/CGeneralTextHandler.h

@@ -109,6 +109,7 @@ public:
 	//towns
 	std::vector<std::string> tcommands, hcommands, fcommands; //texts for town screen, town hall screen and fort screen
 	std::vector<std::string> tavernInfo;
+	std::vector<std::string> tavernRumors;
 
 	std::vector<std::pair<std::string,std::string> > zelp;
 	std::vector<std::string> lossCondtions;

+ 2 - 0
lib/CMakeLists.txt

@@ -117,6 +117,7 @@ set(lib_HEADERS
 		filesystem/ISimpleResourceLoader.h
 
 		mapObjects/MapObjects.h
+		mapping/CMapDefines.h
 
 		CSoundBase.h
 		AI_Base.h
@@ -131,6 +132,7 @@ set(lib_HEADERS
 		IGameEventsReceiver.h
 		int3.h
 		CGameStateFwd.h
+		CPlayerState.h
 		Interprocess.h
 		NetPacks.h
 		NetPacksBase.h

+ 1 - 1
lib/CModHandler.cpp

@@ -467,7 +467,7 @@ static JsonNode loadModSettings(std::string path)
 JsonNode addMeta(JsonNode config, std::string meta)
 {
 	config.setMeta(meta);
-	return std::move(config);
+	return config;
 }
 
 CModInfo::CModInfo(std::string identifier,const JsonNode & local, const JsonNode & config):

File diff suppressed because it is too large
+ 835 - 203
lib/CPathfinder.cpp


+ 186 - 32
lib/CPathfinder.h

@@ -1,10 +1,12 @@
 #pragma once
 
 #include "VCMI_Lib.h"
-#include "mapping/CMap.h"
 #include "IGameCallback.h"
+#include "HeroBonus.h"
 #include "int3.h"
 
+#include <boost/heap/priority_queue.hpp>
+
 /*
  * CPathfinder.h, part of VCMI engine
  *
@@ -18,26 +20,47 @@
 class CGHeroInstance;
 class CGObjectInstance;
 struct TerrainTile;
+class CPathfinderHelper;
+class CMap;
+class CGWhirlpool;
 
 struct DLL_LINKAGE CGPathNode
 {
-	enum EAccessibility
+	typedef EPathfindingLayer ELayer;
+
+	enum ENodeAction : ui8
+	{
+		UNKNOWN = 0,
+		EMBARK = 1,
+		DISEMBARK,
+		NORMAL,
+		BATTLE,
+		VISIT,
+		BLOCKING_VISIT
+	};
+
+	enum EAccessibility : ui8
 	{
 		NOT_SET = 0,
 		ACCESSIBLE = 1, //tile can be entered and passed
 		VISITABLE, //tile can be entered as the last tile in path
 		BLOCKVIS,  //visitable from neighbouring tile but not passable
+		FLYABLE, //can only be accessed in air layer
 		BLOCKED //tile can't be entered nor visited
 	};
 
-	EAccessibility accessible;
-	ui8 land;
-	ui8 turns; //how many turns we have to wait before reachng the tile - 0 means current turn
-	ui32 moveRemains; //remaining tiles after hero reaches the tile
 	CGPathNode * theNodeBefore;
 	int3 coord; //coordinates
+	ui32 moveRemains; //remaining tiles after hero reaches the tile
+	ui8 turns; //how many turns we have to wait before reachng the tile - 0 means current turn
+	ELayer layer;
+	EAccessibility accessible;
+	ENodeAction action;
+	bool locked;
 
 	CGPathNode();
+	void reset();
+	void update(const int3 & Coord, const ELayer Layer, const EAccessibility Accessible);
 	bool reachable() const;
 };
 
@@ -52,27 +75,36 @@ struct DLL_LINKAGE CGPath
 
 struct DLL_LINKAGE CPathsInfo
 {
+	typedef EPathfindingLayer ELayer;
+
 	mutable boost::mutex pathMx;
 
-	const CGHeroInstance *hero;
+	const CGHeroInstance * hero;
 	int3 hpos;
 	int3 sizes;
-	CGPathNode ***nodes; //[w][h][level]
+	boost::multi_array<CGPathNode, 4> nodes; //[w][h][level][layer]
 
-	CPathsInfo(const int3 &Sizes);
+	CPathsInfo(const int3 & Sizes);
 	~CPathsInfo();
-	const CGPathNode * getPathInfo(const int3& tile) const;
-	bool getPath(const int3 &dst, CGPath &out) const;
-	int getDistance( int3 tile ) const;
+	const CGPathNode * getPathInfo(const int3 & tile) const;
+	bool getPath(CGPath & out, const int3 & dst) const;
+	int getDistance(const int3 & tile) const;
+	const CGPathNode * getNode(const int3 & coord) const;
+
+	CGPathNode * getNode(const int3 & coord, const ELayer layer);
 };
 
 class CPathfinder : private CGameInfoCallback
 {
 public:
-	CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero);
+	friend class CPathfinderHelper;
+
+	CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstance * _hero);
 	void calculatePaths(); //calculates possible paths for hero, uses current hero position and movement left; returns pointer to newly allocated CPath or nullptr if path does not exists
 
 private:
+	typedef EPathfindingLayer ELayer;
+
 	struct PathfinderOptions
 	{
 		bool useFlying;
@@ -83,42 +115,164 @@ private:
 		bool useTeleportOneWayRandom; // One-way monoliths with more than one known exit
 		bool useTeleportWhirlpool; // Force enabled if hero protected or unaffected (have one stack of one creature)
 
+		/// TODO: Find out with client and server code, merge with normal teleporters.
+		/// Likely proper implementation would require some refactoring of CGTeleport.
+		/// So for now this is unfinished and disabled by default.
+		bool useCastleGate;
+
+		/// If true transition into air layer only possible from initial node.
+		/// This is drastically decrease path calculation complexity (and time).
+		/// Downside is less MP effective paths calculation.
+		///
+		/// TODO: If this option end up useful for slow devices it's can be improved:
+		/// - Allow transition into air layer not only from initial position, but also from teleporters.
+		///   Movement into air can be also allowed when hero disembarked.
+		/// - Other idea is to allow transition into air within certain radius of N tiles around hero.
+		///   Patrol support need similar functionality so it's won't be ton of useless code.
+		///   Such limitation could be useful as it's can be scaled depend on device performance.
+		bool lightweightFlyingMode;
+
+		/// This option enable one turn limitation for flying and water walking.
+		/// So if we're out of MP while cp is blocked or water tile we won't add dest tile to queue.
+		///
+		/// Following imitation is default H3 mechanics, but someone may want to disable it in mods.
+		/// After all this limit should benefit performance on maps with tons of water or blocked tiles.
+		///
+		/// TODO:
+		/// - Behavior when option is disabled not implemented and will lead to crashes.
+		bool oneTurnSpecialLayersLimit;
+
+		/// VCMI have different movement rules to solve flaws original engine has.
+		/// If this option enabled you'll able to do following things in fly:
+		/// - Move from blocked tiles to visitable one
+		/// - Move from guarded tiles to blockvis tiles without being attacked
+		/// - Move from guarded tiles to guarded visitable tiles with being attacked after
+		/// TODO:
+		/// - Option should also allow same tile land <-> air layer transitions.
+		///   Current implementation only allow go into (from) air layer only to neighbour tiles.
+		///   I find it's reasonable limitation, but it's will make some movements more expensive than in H3.
+		bool originalMovementRules;
+
 		PathfinderOptions();
 	} options;
 
-	CPathsInfo &out;
-	const CGHeroInstance *hero;
+	CPathsInfo & out;
+	const CGHeroInstance * hero;
 	const std::vector<std::vector<std::vector<ui8> > > &FoW;
+	unique_ptr<CPathfinderHelper> hlp;
+
+	enum EPatrolState {
+		PATROL_NONE = 0,
+		PATROL_LOCKED = 1,
+		PATROL_RADIUS
+	} patrolState;
+	std::unordered_set<int3, ShashInt3> patrolTiles;
 
-	std::list<CGPathNode*> mq; //BFS queue -> nodes to be checked
+	struct NodeComparer
+	{
+		bool operator()(const CGPathNode * lhs, const CGPathNode * rhs) const
+		{
+			if(rhs->turns > lhs->turns)
+				return false;
+			else if(rhs->turns == lhs->turns && rhs->moveRemains < lhs->moveRemains)
+				return false;
+
+			return true;
+		}
+	};
+	boost::heap::priority_queue<CGPathNode *, boost::heap::compare<NodeComparer> > pq;
 
+	std::vector<int3> neighbourTiles;
 	std::vector<int3> neighbours;
 
-	CGPathNode *cp; //current (source) path node -> we took it from the queue
-	CGPathNode *dp; //destination node -> it's a neighbour of cp that we consider
-	const TerrainTile *ct, *dt; //tile info for both nodes
-	const CGObjectInstance *sTileObj;
-	ui8 useEmbarkCost; //0 - usual movement; 1 - embark; 2 - disembark
+	CGPathNode * cp; //current (source) path node -> we took it from the queue
+	CGPathNode * dp; //destination node -> it's a neighbour of cp that we consider
+	const TerrainTile * ct, * dt; //tile info for both nodes
+	const CGObjectInstance * ctObj, * dtObj;
+	CGPathNode::ENodeAction destAction;
 
-	void addNeighbours(const int3 &coord);
-	void addTeleportExits(bool noTeleportExcludes = false);
+	void addNeighbours();
+	void addTeleportExits();
 
-	bool isMovementPossible(); //checks if current move will be between sea<->land. If so, checks it legality (returns false if movement is not possible) and sets useEmbarkCost
-	bool checkDestinationTile();
+	bool isHeroPatrolLocked() const;
+	bool isPatrolMovementAllowed(const int3 & dst) const;
 
-	int3 getSourceGuardPosition();
-	bool isSourceGuarded();
-	bool isDestinationGuarded();
-	bool isDestinationGuardian();
+	bool isLayerTransitionPossible(const ELayer dstLayer) const;
+	bool isLayerTransitionPossible() const;
+	bool isMovementToDestPossible() const;
+	bool isMovementAfterDestPossible() const;
+	CGPathNode::ENodeAction getDestAction() const;
 
+	bool isSourceInitialPosition() const;
+	bool isSourceVisitableObj() const;
+	bool isSourceGuarded() const;
+	bool isDestVisitableObj() const;
+	bool isDestinationGuarded(const bool ignoreAccessibility = true) const;
+	bool isDestinationGuardian() const;
+
+	void initializePatrol();
 	void initializeGraph();
 
-	CGPathNode *getNode(const int3 &coord);
-	CGPathNode::EAccessibility evaluateAccessibility(const int3 &pos, const TerrainTile *tinfo) const;
-	bool canMoveBetween(const int3 &a, const int3 &b) const; //checks only for visitable objects that may make moving between tiles impossible, not other conditions (like tiles itself accessibility)
+	CGPathNode::EAccessibility evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo, const ELayer layer) const;
+	bool isVisitableObj(const CGObjectInstance * obj, const ELayer layer) const;
+	bool canSeeObj(const CGObjectInstance * obj) const;
+	bool canMoveBetween(const int3 & a, const int3 & b) const; //checks only for visitable objects that may make moving between tiles impossible, not other conditions (like tiles itself accessibility)
 
+	bool isAllowedTeleportEntrance(const CGTeleport * obj) const;
 	bool addTeleportTwoWay(const CGTeleport * obj) const;
 	bool addTeleportOneWay(const CGTeleport * obj) const;
 	bool addTeleportOneWayRandom(const CGTeleport * obj) const;
 	bool addTeleportWhirlpool(const CGWhirlpool * obj) const;
+
+};
+
+struct DLL_LINKAGE TurnInfo
+{
+	/// This is certainly not the best design ever and certainly can be improved
+	/// Unfortunately for pathfinder that do hundreds of thousands calls onus system add too big overhead
+	struct BonusCache {
+		std::vector<bool> noTerrainPenalty;
+		bool freeShipBoarding;
+		bool flyingMovement;
+		int flyingMovementVal;
+		bool waterWalking;
+		int waterWalkingVal;
+
+		BonusCache(TBonusListPtr bonusList);
+	};
+	unique_ptr<BonusCache> bonusCache;
+
+	const CGHeroInstance * hero;
+	TBonusListPtr bonuses;
+	mutable int maxMovePointsLand;
+	mutable int maxMovePointsWater;
+	int nativeTerrain;
+
+	TurnInfo(const CGHeroInstance * Hero, const int Turn = 0);
+	bool isLayerAvailable(const EPathfindingLayer layer) const;
+	bool hasBonusOfType(const Bonus::BonusType type, const int subtype = -1) const;
+	int valOfBonuses(const Bonus::BonusType type, const int subtype = -1) const;
+	int getMaxMovePoints(const EPathfindingLayer layer) const;
+};
+
+class DLL_LINKAGE CPathfinderHelper
+{
+public:
+	CPathfinderHelper(const CGHeroInstance * Hero, const CPathfinder::PathfinderOptions & Options);
+	void updateTurnInfo(const int turn = 0);
+	bool isLayerAvailable(const EPathfindingLayer layer) const;
+	const TurnInfo * getTurnInfo() const;
+	bool hasBonusOfType(const Bonus::BonusType type, const int subtype = -1) const;
+	int getMaxMovePoints(const EPathfindingLayer layer) const;
+
+	static void getNeighbours(const CMap * map, const TerrainTile & srct, const int3 & tile, std::vector<int3> & vec, const boost::logic::tribool & onLand, const bool limitCoastSailing);
+
+	static int getMovementCost(const CGHeroInstance * h, const int3 & src, const int3 & dst, const TerrainTile * ct, const TerrainTile * dt, const int remainingMovePoints =- 1, const TurnInfo * ti = nullptr, const bool checkLast = true);
+	static int getMovementCost(const CGHeroInstance * h, const int3 & dst);
+
+private:
+	int turn;
+	const CGHeroInstance * hero;
+	std::vector<TurnInfo *> turnsInfo;
+	const CPathfinder::PathfinderOptions & options;
 };

+ 66 - 0
lib/CPlayerState.h

@@ -0,0 +1,66 @@
+#pragma once
+
+/*
+ * CPlayerState.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
+ *
+ */
+
+#include "HeroBonus.h"
+
+class CGHeroInstance;
+class CGTownInstance;
+class CGDwelling;
+
+struct DLL_LINKAGE PlayerState : public CBonusSystemNode
+{
+public:
+	PlayerColor color;
+	bool human; //true if human controlled player, false for AI
+	TeamID team;
+	TResources resources;
+	std::set<ObjectInstanceID> visitedObjects; // as a std::set, since most accesses here will be from visited status checks
+	std::vector<ConstTransitivePtr<CGHeroInstance> > heroes;
+	std::vector<ConstTransitivePtr<CGTownInstance> > towns;
+	std::vector<ConstTransitivePtr<CGHeroInstance> > availableHeroes; //heroes available in taverns
+	std::vector<ConstTransitivePtr<CGDwelling> > dwellings; //used for town growth
+	std::vector<QuestInfo> quests; //store info about all received quests
+
+	bool enteredWinningCheatCode, enteredLosingCheatCode; //if true, this player has entered cheat codes for loss / victory
+	EPlayerStatus::EStatus status;
+	boost::optional<ui8> daysWithoutCastle;
+
+	PlayerState();
+	std::string nodeName() const override;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & color & human & team & resources & status;
+		h & heroes & towns & availableHeroes & dwellings & quests & visitedObjects;
+		h & getBonusList(); //FIXME FIXME FIXME
+		h & status & daysWithoutCastle;
+		h & enteredLosingCheatCode & enteredWinningCheatCode;
+		h & static_cast<CBonusSystemNode&>(*this);
+	}
+};
+
+struct DLL_LINKAGE TeamState : public CBonusSystemNode
+{
+public:
+	TeamID id; //position in gameState::teams
+	std::set<PlayerColor> players; // members of this team
+	std::vector<std::vector<std::vector<ui8> > >  fogOfWarMap; //true - visible, false - hidden
+
+	TeamState();
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & id & players & fogOfWarMap;
+		h & static_cast<CBonusSystemNode&>(*this);
+	}
+
+};

+ 2 - 6
lib/CTownHandler.cpp

@@ -774,12 +774,8 @@ std::set<TFaction> CTownHandler::getAllowedFactions(bool withTown /*=true*/) con
 	if (withTown)
 		allowed = getDefaultAllowed();
 	else
-	{
-		for (auto town : factions)
-		{
-			allowed.push_back (true);
-		}
-	}
+		allowed.resize( factions.size(), true);
+
 	for (size_t i=0; i<allowed.size(); i++)
 		if (allowed[i])
 			allowedFactions.insert(i);

+ 2 - 0
lib/Connection.cpp

@@ -2,6 +2,8 @@
 #include "Connection.h"
 
 #include "registerTypes/RegisterTypes.h"
+#include "mapping/CMap.h"
+#include "CGameState.h"
 
 #include <boost/asio.hpp>
 

+ 2 - 2
lib/Connection.h

@@ -11,7 +11,7 @@
 
 #pragma once
 
-#include <typeinfo> //XXX this is in namespace std if you want w/o use typeinfo.h?
+#include <typeinfo>
 #include <type_traits>
 
 #include <boost/mpl/eval_if.hpp>
@@ -27,7 +27,7 @@
 #include "mapping/CCampaignHandler.h" //for CCampaignState
 #include "rmg/CMapGenerator.h" // for CMapGenOptions
 
-const ui32 version = 754;
+const ui32 version = 755;
 const ui32 minSupportedVersion = 753;
 
 class CISer;

+ 23 - 52
lib/GameConstants.cpp

@@ -25,56 +25,6 @@ const PlayerColor PlayerColor::NEUTRAL = PlayerColor(255);
 const PlayerColor PlayerColor::PLAYER_LIMIT = PlayerColor(PLAYER_LIMIT_I);
 const TeamID TeamID::NO_TEAM = TeamID(255);
 
-#define ID_LIKE_OPERATORS_INTERNAL(A, B, AN, BN)	\
-bool operator==(const A & a, const B & b)			\
-{													\
-	return AN == BN ;								\
-}													\
-bool operator!=(const A & a, const B & b)			\
-{													\
-	return AN != BN ;								\
-}													\
-bool operator<(const A & a, const B & b)			\
-{													\
-	return AN < BN ;								\
-}													\
-bool operator<=(const A & a, const B & b)			\
-{													\
-	return AN <= BN ;								\
-}													\
-bool operator>(const A & a, const B & b)			\
-{													\
-	return AN > BN ;								\
-}													\
-bool operator>=(const A & a, const B & b)			\
-{													\
-	return AN >= BN ;								\
-}
-
-#define ID_LIKE_OPERATORS(CLASS_NAME, ENUM_NAME)	\
-	ID_LIKE_OPERATORS_INTERNAL(CLASS_NAME, CLASS_NAME, a.num, b.num)	\
-	ID_LIKE_OPERATORS_INTERNAL(CLASS_NAME, ENUM_NAME, a.num, b)	\
-	ID_LIKE_OPERATORS_INTERNAL(ENUM_NAME, CLASS_NAME, a, b.num)
-
-
-ID_LIKE_OPERATORS(SecondarySkill, SecondarySkill::ESecondarySkill)
-
-ID_LIKE_OPERATORS(Obj, Obj::EObj)
-
-ID_LIKE_OPERATORS(ETerrainType, ETerrainType::EETerrainType)
-
-ID_LIKE_OPERATORS(ArtifactID, ArtifactID::EArtifactID)
-
-ID_LIKE_OPERATORS(ArtifactPosition, ArtifactPosition::EArtifactPosition)
-
-ID_LIKE_OPERATORS(CreatureID, CreatureID::ECreatureID)
-
-ID_LIKE_OPERATORS(SpellID, SpellID::ESpellID)
-
-ID_LIKE_OPERATORS(BuildingID, BuildingID::EBuildingID)
-
-ID_LIKE_OPERATORS(BFieldType, BFieldType::EBFieldType)
-
 CArtifact * ArtifactID::toArtifact() const
 {
 	return VLC->arth->artifacts[*this];
@@ -130,7 +80,7 @@ std::ostream & operator<<(std::ostream & os, const Battle::ActionType actionType
 	else return os << it->second;
 }
 
-std::ostream & operator<<(std::ostream & os, const ETerrainType actionType)
+std::ostream & operator<<(std::ostream & os, const ETerrainType terrainType)
 {
 	static const std::map<ETerrainType::EETerrainType, std::string> terrainTypeToString =
 	{
@@ -147,9 +97,10 @@ std::ostream & operator<<(std::ostream & os, const ETerrainType actionType)
 		DEFINE_ELEMENT(LAVA),
 		DEFINE_ELEMENT(WATER),
 		DEFINE_ELEMENT(ROCK)
+	#undef DEFINE_ELEMENT
 	};
 
-	auto it = terrainTypeToString.find(actionType.num);
+	auto it = terrainTypeToString.find(terrainType.num);
 	if (it == terrainTypeToString.end()) return os << "<Unknown type>";
 	else return os << it->second;
 }
@@ -160,3 +111,23 @@ std::string ETerrainType::toString() const
 	ss << *this;
 	return ss.str();
 }
+
+std::ostream & operator<<(std::ostream & os, const EPathfindingLayer pathfindingLayer)
+{
+	static const std::map<EPathfindingLayer::EEPathfindingLayer, std::string> pathfinderLayerToString
+	{
+	#define DEFINE_ELEMENT(element) {EPathfindingLayer::element, #element}
+		DEFINE_ELEMENT(WRONG),
+		DEFINE_ELEMENT(AUTO),
+		DEFINE_ELEMENT(LAND),
+		DEFINE_ELEMENT(SAIL),
+		DEFINE_ELEMENT(WATER),
+		DEFINE_ELEMENT(AIR),
+		DEFINE_ELEMENT(NUM_LAYERS)
+	#undef DEFINE_ELEMENT
+	};
+
+	auto it = pathfinderLayerToString.find(pathfindingLayer.num);
+	if (it == pathfinderLayerToString.end()) return os << "<Unknown type>";
+	else return os << it->second;
+}

+ 112 - 28
lib/GameConstants.h

@@ -14,7 +14,7 @@
 
 namespace GameConstants
 {
-	const std::string VCMI_VERSION = "VCMI 0.98e";
+	const std::string VCMI_VERSION = "VCMI 0.98f";
 
 	const int BFIELD_WIDTH = 17;
 	const int BFIELD_HEIGHT = 11;
@@ -93,18 +93,37 @@ CLASS_NAME & advance(int i)							\
 }
 
 
-#define ID_LIKE_OPERATORS_INTERNAL_DECLS(A, B)			\
-bool DLL_LINKAGE operator==(const A & a, const B & b);	\
-bool DLL_LINKAGE operator!=(const A & a, const B & b);	\
-bool DLL_LINKAGE operator<(const A & a, const B & b);	\
-bool DLL_LINKAGE operator<=(const A & a, const B & b);	\
-bool DLL_LINKAGE operator>(const A & a, const B & b);	\
-bool DLL_LINKAGE operator>=(const A & a, const B & b);
+// Operators are performance-critical and to be inlined they must be in header
+#define ID_LIKE_OPERATORS_INTERNAL(A, B, AN, BN)	\
+STRONG_INLINE bool operator==(const A & a, const B & b)			\
+{													\
+	return AN == BN ;								\
+}													\
+STRONG_INLINE bool operator!=(const A & a, const B & b)			\
+{													\
+	return AN != BN ;								\
+}													\
+STRONG_INLINE bool operator<(const A & a, const B & b)			\
+{													\
+	return AN < BN ;								\
+}													\
+STRONG_INLINE bool operator<=(const A & a, const B & b)			\
+{													\
+	return AN <= BN ;								\
+}													\
+STRONG_INLINE bool operator>(const A & a, const B & b)			\
+{													\
+	return AN > BN ;								\
+}													\
+STRONG_INLINE bool operator>=(const A & a, const B & b)			\
+{													\
+	return AN >= BN ;								\
+}
 
-#define ID_LIKE_OPERATORS_DECLS(CLASS_NAME, ENUM_NAME)			\
-	ID_LIKE_OPERATORS_INTERNAL_DECLS(CLASS_NAME, CLASS_NAME)	\
-	ID_LIKE_OPERATORS_INTERNAL_DECLS(CLASS_NAME, ENUM_NAME)		\
-	ID_LIKE_OPERATORS_INTERNAL_DECLS(ENUM_NAME, CLASS_NAME)
+#define ID_LIKE_OPERATORS(CLASS_NAME, ENUM_NAME)	\
+	ID_LIKE_OPERATORS_INTERNAL(CLASS_NAME, CLASS_NAME, a.num, b.num)	\
+	ID_LIKE_OPERATORS_INTERNAL(CLASS_NAME, ENUM_NAME, a.num, b)	\
+	ID_LIKE_OPERATORS_INTERNAL(ENUM_NAME, CLASS_NAME, a, b.num)
 
 
 #define OP_DECL_INT(CLASS_NAME, OP)					\
@@ -296,7 +315,7 @@ public:
 	ESecondarySkill num;
 };
 
-ID_LIKE_OPERATORS_DECLS(SecondarySkill, SecondarySkill::ESecondarySkill)
+ID_LIKE_OPERATORS(SecondarySkill, SecondarySkill::ESecondarySkill)
 
 namespace EAlignment
 {
@@ -384,7 +403,19 @@ public:
 	EBuildingID num;
 };
 
-ID_LIKE_OPERATORS_DECLS(BuildingID, BuildingID::EBuildingID)
+ID_LIKE_OPERATORS(BuildingID, BuildingID::EBuildingID)
+
+namespace EAiTactic
+{
+enum EAiTactic
+{
+	NONE = -1,
+	RANDOM,
+	WARRIOR,
+	BUILDER,
+	EXPLORER
+};
+}
 
 namespace EBuildingState
 {
@@ -432,8 +463,21 @@ namespace EMarketMode
 
 namespace EBattleStackState
 {
-	enum EBattleStackState{ALIVE = 180, SUMMONED, CLONED, DEAD_CLONE, HAD_MORALE, WAITING, MOVED, DEFENDING, FEAR,
-		DRAINED_MANA /*remember to drain mana only once per turn*/};
+	enum EBattleStackState
+	{
+		ALIVE = 180,
+		SUMMONED, CLONED,
+		DEAD_CLONE,
+		HAD_MORALE,
+		WAITING,
+		MOVED,
+		DEFENDING,
+		FEAR,
+		//remember to drain mana only once per turn
+		DRAINED_MANA,
+		//only for defending animation
+		DEFENDING_ANIM
+	};
 }
 
 namespace ECommander
@@ -651,7 +695,7 @@ public:
 	EObj num;
 };
 
-ID_LIKE_OPERATORS_DECLS(Obj, Obj::EObj)
+ID_LIKE_OPERATORS(Obj, Obj::EObj)
 
 namespace SecSkillLevel
 {
@@ -741,10 +785,51 @@ public:
 	std::string toString() const;
 };
 
-DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const ETerrainType actionType);
+DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const ETerrainType terrainType);
+
+ID_LIKE_OPERATORS(ETerrainType, ETerrainType::EETerrainType)
+
+class DLL_LINKAGE EDiggingStatus
+{
+public:
+	enum EEDiggingStatus
+	{
+		UNKNOWN = -1,
+		CAN_DIG = 0,
+		LACK_OF_MOVEMENT,
+		WRONG_TERRAIN,
+		TILE_OCCUPIED
+	};
+
+	EDiggingStatus(EEDiggingStatus _num = UNKNOWN) : num(_num)
+	{}
+
+	ID_LIKE_CLASS_COMMON(EDiggingStatus, EEDiggingStatus)
+
+	EEDiggingStatus num;
+};
+
+ID_LIKE_OPERATORS(EDiggingStatus, EDiggingStatus::EEDiggingStatus)
 
-ID_LIKE_OPERATORS_DECLS(ETerrainType, ETerrainType::EETerrainType)
+class DLL_LINKAGE EPathfindingLayer
+{
+public:
+	enum EEPathfindingLayer : ui8
+	{
+		LAND = 0, SAIL = 1, WATER, AIR, NUM_LAYERS, WRONG, AUTO
+	};
+
+	EPathfindingLayer(EEPathfindingLayer _num = WRONG) : num(_num)
+	{}
 
+	ID_LIKE_CLASS_COMMON(EPathfindingLayer, EEPathfindingLayer)
+
+	EEPathfindingLayer num;
+};
+
+DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EPathfindingLayer pathfindingLayer);
+
+ID_LIKE_OPERATORS(EPathfindingLayer, EPathfindingLayer::EEPathfindingLayer)
 
 class BFieldType
 {
@@ -767,7 +852,7 @@ public:
 	EBFieldType num;
 };
 
-ID_LIKE_OPERATORS_DECLS(BFieldType, BFieldType::EBFieldType)
+ID_LIKE_OPERATORS(BFieldType, BFieldType::EBFieldType)
 
 namespace EPlayerStatus
 {
@@ -807,7 +892,7 @@ public:
 	EArtifactPosition num;
 };
 
-ID_LIKE_OPERATORS_DECLS(ArtifactPosition, ArtifactPosition::EArtifactPosition)
+ID_LIKE_OPERATORS(ArtifactPosition, ArtifactPosition::EArtifactPosition)
 
 class ArtifactID
 {
@@ -824,6 +909,7 @@ public:
 		FIRST_AID_TENT = 6,
 		//CENTAUR_AXE = 7,
 		//BLACKSHARD_OF_THE_DEAD_KNIGHT = 8,
+		ARMAGEDDONS_BLADE = 128,
 		TITANS_THUNDER = 135,
 		//CORNUCOPIA = 140,
 		//FIXME: the following is only true if WoG is enabled. Otherwise other mod artifacts will take these slots.
@@ -851,7 +937,7 @@ public:
 	EArtifactID num;
 };
 
-ID_LIKE_OPERATORS_DECLS(ArtifactID, ArtifactID::EArtifactID)
+ID_LIKE_OPERATORS(ArtifactID, ArtifactID::EArtifactID)
 
 class CreatureID
 {
@@ -895,7 +981,7 @@ public:
 	ECreatureID num;
 };
 
-ID_LIKE_OPERATORS_DECLS(CreatureID, CreatureID::ECreatureID)
+ID_LIKE_OPERATORS(CreatureID, CreatureID::ECreatureID)
 
 class SpellID
 {
@@ -939,7 +1025,7 @@ public:
 	ESpellID num;
 };
 
-ID_LIKE_OPERATORS_DECLS(SpellID, SpellID::ESpellID)
+ID_LIKE_OPERATORS(SpellID, SpellID::ESpellID)
 
 enum class ESpellSchool: ui8
 {
@@ -949,8 +1035,6 @@ enum class ESpellSchool: ui8
 	EARTH 	= 3
 };
 
-ID_LIKE_OPERATORS_DECLS(SpellID, SpellID::ESpellID)
-
 // Typedef declarations
 typedef ui8 TFaction;
 typedef si64 TExpType;
@@ -961,8 +1045,8 @@ typedef si32 TQuantity;
 typedef int TRmgTemplateZoneId;
 
 #undef ID_LIKE_CLASS_COMMON
-#undef ID_LIKE_OPERATORS_DECLS
-#undef ID_LIKE_OPERATORS_INTERNAL_DECLS
+#undef ID_LIKE_OPERATORS
+#undef ID_LIKE_OPERATORS_INTERNAL
 #undef INSTID_LIKE_CLASS_COMMON
 #undef OP_DECL_INT
 

+ 23 - 12
lib/HeroBonus.cpp

@@ -763,8 +763,29 @@ void CBonusSystemNode::popBonuses(const CSelector &s)
 		child->popBonuses(s);
 }
 
+void CBonusSystemNode::updateBonuses(const CSelector &s)
+{
+	BonusList bl;
+	exportedBonuses.getBonuses(bl, s);
+	for(Bonus *b : bl)
+	{
+		b->turnsRemain--;
+		if(b->turnsRemain <= 0)
+			removeBonus(b);
+	}
+
+	for(CBonusSystemNode *child : children)
+		child->updateBonuses(s);
+}
+
 void CBonusSystemNode::addNewBonus(Bonus *b)
 {
+	//turnsRemain shouldn't be zero for following durations
+	if(Bonus::NTurns(b) || Bonus::NDays(b) || Bonus::OneWeek(b))
+	{
+		assert(b->turnsRemain);
+	}
+
 	assert(!vstd::contains(exportedBonuses,b));
 	exportedBonuses.push_back(b);
 	exportBonus(b);
@@ -950,18 +971,7 @@ void CBonusSystemNode::getRedDescendants(TNodes &out)
 
 void CBonusSystemNode::battleTurnPassed()
 {
-	BonusList bonusesCpy = exportedBonuses; //copy, because removing bonuses invalidates iters
-	for (auto & elem : bonusesCpy)
-	{
-		Bonus *b = elem;
-
-		if(b->duration & Bonus::N_TURNS)
-		{
-			b->turnsRemain--;
-			if(b->turnsRemain <= 0)
-				removeBonus(b);
-		}
-	}
+	updateBonuses(Bonus::NTurns);
 }
 
 void CBonusSystemNode::exportBonus(Bonus * b)
@@ -1183,6 +1193,7 @@ namespace Selector
 	DLL_LINKAGE CSelectFieldEqual<Bonus::BonusSource> sourceType(&Bonus::source);
 	DLL_LINKAGE CSelectFieldEqual<Bonus::LimitEffect> effectRange(&Bonus::effectRange);
 	DLL_LINKAGE CWillLastTurns turns;
+	DLL_LINKAGE CWillLastDays days;
 	DLL_LINKAGE CSelectFieldAny<Bonus::LimitEffect> anyRange(&Bonus::effectRange);
 
 	CSelector DLL_LINKAGE typeSubtype(Bonus::BonusType Type, TBonusSubtype Subtype)

+ 29 - 2
lib/HeroBonus.h

@@ -294,7 +294,7 @@ struct DLL_LINKAGE Bonus
 	};
 
 	ui16 duration; //uses BonusDuration values
-	si16 turnsRemain; //used if duration is N_TURNS or N_DAYS
+	si16 turnsRemain; //used if duration is N_TURNS, N_DAYS or ONE_WEEK
 
 	BonusType type; //uses BonusType values - says to what is this bonus - 1 byte
 	TBonusSubtype subtype; //-1 if not applicable - 4 bytes
@@ -686,6 +686,7 @@ public:
 	//bool isLimitedOnUs(Bonus *b) const; //if bonus should be removed from list acquired from this node
 
 	void popBonuses(const CSelector &s);
+	void updateBonuses(const CSelector &s);
 	virtual std::string bonusToString(const Bonus *bonus, bool description) const {return "";}; //description or bonus name
 	virtual std::string nodeName() const;
 
@@ -810,7 +811,7 @@ public:
 	bool operator()(const Bonus *bonus) const
 	{
 		return turnsRequested <= 0					//every present effect will last zero (or "less") turns
-			|| !(bonus->duration & Bonus::N_TURNS)	//so do every not expriing after N-turns effect
+			|| !Bonus::NTurns(bonus) //so do every not expriing after N-turns effect
 			|| bonus->turnsRemain > turnsRequested;
 	}
 	CWillLastTurns& operator()(const int &setVal)
@@ -820,6 +821,31 @@ public:
 	}
 };
 
+class DLL_LINKAGE CWillLastDays
+{
+public:
+	int daysRequested;
+
+	bool operator()(const Bonus *bonus) const
+	{
+		if(daysRequested <= 0 || Bonus::Permanent(bonus) || Bonus::OneBattle(bonus))
+			return true;
+		else if(Bonus::OneDay(bonus))
+			return false;
+		else if(Bonus::NDays(bonus) || Bonus::OneWeek(bonus))
+		{
+			return bonus->turnsRemain > daysRequested;
+		}
+
+		return false; // TODO: ONE_WEEK need support for turnsRemain, but for now we'll exclude all unhandled durations
+	}
+	CWillLastDays& operator()(const int &setVal)
+	{
+		daysRequested = setVal;
+		return *this;
+	}
+};
+
 //Stores multiple limiters. If any of them fails -> bonus is dropped.
 class DLL_LINKAGE LimiterList : public ILimiter
 {
@@ -958,6 +984,7 @@ namespace Selector
 	extern DLL_LINKAGE CSelectFieldEqual<Bonus::BonusSource> sourceType;
 	extern DLL_LINKAGE CSelectFieldEqual<Bonus::LimitEffect> effectRange;
 	extern DLL_LINKAGE CWillLastTurns turns;
+	extern DLL_LINKAGE CWillLastDays days;
 	extern DLL_LINKAGE CSelectFieldAny<Bonus::LimitEffect> anyRange;
 
 	CSelector DLL_LINKAGE typeSubtype(Bonus::BonusType Type, TBonusSubtype Subtype);

+ 11 - 3
lib/IGameCallback.cpp

@@ -21,6 +21,8 @@
 #include "mapObjects/CObjectClassesHandler.h"
 #include "StartInfo.h"
 #include "CGameState.h"
+#include "mapping/CMap.h"
+#include "CPlayerState.h"
 
 void CPrivilagedInfoCallback::getFreeTiles (std::vector<int3> &tiles) const
 {
@@ -38,14 +40,14 @@ void CPrivilagedInfoCallback::getFreeTiles (std::vector<int3> &tiles) const
 			for (int yd = 0; yd < gs->map->height; yd++)
 			{
 				tinfo = getTile(int3 (xd,yd,zd));
-				if (tinfo->terType != ETerrainType::WATER && !tinfo->blocked) //land and free
+				if (tinfo->terType != ETerrainType::WATER && tinfo->terType != ETerrainType::ROCK && !tinfo->blocked) //land and free
 					tiles.push_back (int3 (xd,yd,zd));
 			}
 		}
 	}
 }
 
-void CPrivilagedInfoCallback::getTilesInRange( std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, boost::optional<PlayerColor> player/*=uninit*/, int mode/*=0*/ ) const
+void CPrivilagedInfoCallback::getTilesInRange( std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, boost::optional<PlayerColor> player/*=uninit*/, int mode/*=0*/, bool patrolDistance/*=false*/) const
 {
 	if(!!player && *player >= PlayerColor::PLAYER_LIMIT)
 	{
@@ -61,7 +63,13 @@ void CPrivilagedInfoCallback::getTilesInRange( std::unordered_set<int3, ShashInt
 		{
 			for (int yd = std::max<int>(pos.y - radious, 0); yd <= std::min<int>(pos.y + radious, gs->map->height - 1); yd++)
 			{
-				double distance = pos.dist2d(int3(xd,yd,pos.z)) - 0.5;
+				int3 tilePos(xd,yd,pos.z);
+				double distance;
+				if(patrolDistance)
+					distance = pos.mandist2d(tilePos);
+				else
+					distance = pos.dist2d(tilePos) - 0.5;
+
 				if(distance <= radious)
 				{
 					if(!player

+ 1 - 1
lib/IGameCallback.h

@@ -30,7 +30,7 @@ class DLL_LINKAGE CPrivilagedInfoCallback : public CGameInfoCallback
 public:
 	CGameState * gameState();
 	void getFreeTiles (std::vector<int3> &tiles) const; //used for random spawns
-	void getTilesInRange(std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, boost::optional<PlayerColor> player = boost::optional<PlayerColor>(), int mode=0) const;  //mode 1 - only unrevealed tiles; mode 0 - all, mode -1 -  only unrevealed
+	void getTilesInRange(std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, boost::optional<PlayerColor> player = boost::optional<PlayerColor>(), int mode = 0, bool patrolDistance = false) const;  //mode 1 - only unrevealed tiles; mode 0 - all, mode -1 -  only unrevealed
 	void getAllTiles (std::unordered_set<int3, ShashInt3> &tiles, boost::optional<PlayerColor> player = boost::optional<PlayerColor>(), int level=-1, int surface=0) const; //returns all tiles on given level (-1 - both levels, otherwise number of level); surface: 0 - land and water, 1 - only land, 2 - only water
 	void pickAllowedArtsSet(std::vector<const CArtifact*> &out); //gives 3 treasures, 3 minors, 1 major -> used by Black Market and Artifact Merchant
 	void getAllowedSpells(std::vector<SpellID> &out, ui16 level);

+ 3 - 2
lib/NetPacks.h

@@ -13,7 +13,7 @@
 #include "ResourceSet.h"
 //#include "CObstacleInstance.h"
 #include "CGameStateFwd.h"
-#include "mapping/CMap.h"
+#include "mapping/CMapDefines.h"
 #include "CObstacleInstance.h"
 
 #include "spells/ViewSpellInt.h"
@@ -363,6 +363,7 @@ struct GiveBonus :  public CPackForClient //115
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & bonus & id & bdescr & who;
+		assert( id != -1);
 	}
 };
 
@@ -1218,7 +1219,7 @@ struct TeleportDialog : public Query//2006
 
 	const CGHeroInstance *hero;
 	TeleportChannelID channel;
-	std::vector<ObjectInstanceID> exits;
+	TTeleportExitsList exits;
 	bool impassable;
 
 	template <typename Handler> void serialize(Handler &h, const int version)

+ 13 - 3
lib/NetPacksLib.cpp

@@ -16,6 +16,7 @@
 #include "CTownHandler.h"
 #include "mapping/CMapInfo.h"
 #include "StartInfo.h"
+#include "CPlayerState.h"
 
 /*
  * NetPacksLib.cpp, part of VCMI engine
@@ -263,6 +264,9 @@ DLL_LINKAGE void GiveBonus::applyGs( CGameState *gs )
 
 	assert(cbsn);
 
+	if(Bonus::OneWeek(&bonus))
+		bonus.turnsRemain = 8 - gs->getDate(Date::DAY_OF_WEEK); // set correct number of days before adding bonus
+
 	auto b = new Bonus(bonus);
 	cbsn->addNewBonus(b);
 
@@ -1023,13 +1027,15 @@ DLL_LINKAGE void NewTurn::applyGs( CGameState *gs )
 		creatureSet.second.applyGs(gs);
 
 	gs->globalEffects.popBonuses(Bonus::OneDay); //works for children -> all game objs
-	if(gs->getDate(Date::DAY_OF_WEEK) == 1) //new week
-		gs->globalEffects.popBonuses(Bonus::OneWeek); //works for children -> all game objs
-
+	gs->globalEffects.updateBonuses(Bonus::NDays);
+	gs->globalEffects.updateBonuses(Bonus::OneWeek);
 	//TODO not really a single root hierarchy, what about bonuses placed elsewhere? [not an issue with H3 mechanics but in the future...]
 
 	for(CGTownInstance* t : gs->map->towns)
 		t->builded = 0;
+
+	if(gs->getDate(Date::DAY_OF_WEEK) == 1)
+		gs->updateRumor();
 }
 
 DLL_LINKAGE void SetObjectProperty::applyGs( CGameState *gs )
@@ -1329,14 +1335,18 @@ DLL_LINKAGE void StartAction::applyGs( CGameState *gs )
 	switch(ba.actionType)
 	{
 	case Battle::DEFEND:
+		st->state -= EBattleStackState::DEFENDING_ANIM;
 		st->state.insert(EBattleStackState::DEFENDING);
+		st->state.insert(EBattleStackState::DEFENDING_ANIM);
 		break;
 	case Battle::WAIT:
+		st->state -= EBattleStackState::DEFENDING_ANIM;
 		st->state.insert(EBattleStackState::WAITING);
 		return;
 	case Battle::HERO_SPELL: //no change in current stack state
 		return;
 	default: //any active stack action - attack, catapult, heal, spell...
+		st->state -= EBattleStackState::DEFENDING_ANIM;
 		st->state.insert(EBattleStackState::MOVED);
 		break;
 	}

+ 8 - 8
lib/StartInfo.h

@@ -31,7 +31,7 @@ struct PlayerSettings
 	Ebonus bonus;
 	si16 castle;
 	si32 hero,
-	     heroPortrait; //-1 if default, else ID
+		 heroPortrait; //-1 if default, else ID
 
 	std::string heroName;
 	PlayerColor color; //from 0 - 
@@ -70,13 +70,13 @@ struct StartInfo
 {
 	enum EMode {NEW_GAME, LOAD_GAME, CAMPAIGN, DUEL, INVALID = 255};
 
-	EMode mode;
-	ui8 difficulty; //0=easy; 4=impossible
-
-	typedef std::map<PlayerColor, PlayerSettings> TPlayerInfos;
-	TPlayerInfos playerInfos; //color indexed
-
-	ui32 seedToBeUsed; //0 if not sure (client requests server to decide, will be send in reply pack)
+	EMode mode;
+	ui8 difficulty; //0=easy; 4=impossible
+
+	typedef std::map<PlayerColor, PlayerSettings> TPlayerInfos;
+	TPlayerInfos playerInfos; //color indexed
+
+	ui32 seedToBeUsed; //0 if not sure (client requests server to decide, will be send in reply pack)
 	ui32 seedPostInit; //so we know that game is correctly synced at the start; 0 if not known yet
 	ui32 mapfileChecksum; //0 if not relevant
 	ui8 turnTime; //in minutes, 0=unlimited

+ 3 - 3
lib/VCMIDirs.cpp

@@ -34,9 +34,9 @@ void IVCMIDirs::init()
 	#endif    
 #endif // __MINGW32__
 
-#include <Windows.h>
-#include <Shlobj.h>
-#include <Shellapi.h>
+#include <windows.h>
+#include <shlobj.h>
+#include <shellapi.h>
 
 // Generates script file named _temp.bat in 'to' directory and runs it
 // Script will:

+ 12 - 4
lib/int3.h

@@ -105,6 +105,11 @@ public:
 	{
 		return std::sqrt((double)dist2dSQ(o));
 	}
+	//manhattan distance used for patrol radius (z coord is not used)
+	double mandist2d(const int3 & o) const
+	{
+		return abs(o.x - x) + abs(o.y - y);
+	}
 
 	bool areNeighbours(const int3 & o) const
 	{
@@ -131,6 +136,12 @@ public:
 	{
 		h & x & y & z;
 	}
+
+	static std::array<int3, 8> getDirs()
+	{
+		return { { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0),
+			int3(1,1,0),int3(-1,1,0),int3(1,-1,0),int3(-1,-1,0) } };
+	}
 };
 
 inline std::ostream & operator<<(std::ostream & str, const int3 & sth)
@@ -154,9 +165,6 @@ struct ShashInt3
 	}
 };
 
-static const int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0),
-	int3(1,1,0),int3(-1,1,0),int3(1,-1,0),int3(-1,-1,0) };
-
 template<typename Container>
 int3 findClosestTile (Container & container, int3 dest)
 {
@@ -175,4 +183,4 @@ int3 findClosestTile (Container & container, int3 dest)
 		}
 	}
 	return result;
-}
+}

+ 2 - 1
lib/mapObjects/CArmedInstance.cpp

@@ -1,4 +1,4 @@
-/*
+/*
  * CArmedInstance.cpp, part of VCMI engine
  *
  * Authors: listed in file AUTHORS in main folder
@@ -15,6 +15,7 @@
 #include "../CCreatureHandler.h"
 #include "../CGeneralTextHandler.h"
 #include "../CGameState.h"
+#include "../CPlayerState.h"
 
 void CArmedInstance::randomizeArmy(int type)
 {

+ 55 - 53
lib/mapObjects/CGHeroInstance.cpp

@@ -1,4 +1,4 @@
-/*
+/*
  * CGHeroInstance.cpp, part of VCMI engine
  *
  * Authors: listed in file AUTHORS in main folder
@@ -22,6 +22,8 @@
 #include "../CGameState.h"
 #include "../CCreatureHandler.h"
 #include "../BattleState.h"
+#include "../CTownHandler.h"
+#include "CGTownInstance.h"
 
 ///helpers
 static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const ui16 soundID)
@@ -56,7 +58,7 @@ static int lowestSpeed(const CGHeroInstance * chi)
 	return ret;
 }
 
-ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &from) const
+ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &from, const TurnInfo * ti) const
 {
 	unsigned ret = GameConstants::BASE_MOVEMENT_COST;
 
@@ -80,28 +82,38 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &fro
 			break;
 		}
 	}
-	else if(!hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType))
+	else if(ti->nativeTerrain != from.terType && !ti->hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType))
 	{
-		// NOTE: in H3 neutral stacks will ignore terrain penalty only if placed as topmost stack(s) in hero army.
-		// This is clearly bug in H3 however intended behaviour is not clear.
-		// Current VCMI behaviour will ignore neutrals in calculations so army in VCMI
-		// will always have best penalty without any influence from player-defined stacks order
+		ret = VLC->heroh->terrCosts[from.terType];
+		ret -= getSecSkillLevel(SecondarySkill::PATHFINDING) * 25;
+		if(ret < GameConstants::BASE_MOVEMENT_COST)
+			ret = GameConstants::BASE_MOVEMENT_COST;
+	}
+	return ret;
+}
 
-		for(auto stack : stacks)
-		{
-			int nativeTerrain = VLC->townh->factions[stack.second->type->faction]->nativeTerrain;
-			if(nativeTerrain != -1 && nativeTerrain != from.terType)
-			{
-				ret = VLC->heroh->terrCosts[from.terType];
-				ret -= getSecSkillLevel(SecondarySkill::PATHFINDING) * 25;
-				if(ret < GameConstants::BASE_MOVEMENT_COST)
-					ret = GameConstants::BASE_MOVEMENT_COST;
+int CGHeroInstance::getNativeTerrain() const
+{
+	// NOTE: in H3 neutral stacks will ignore terrain penalty only if placed as topmost stack(s) in hero army.
+	// This is clearly bug in H3 however intended behaviour is not clear.
+	// Current VCMI behaviour will ignore neutrals in calculations so army in VCMI
+	// will always have best penalty without any influence from player-defined stacks order
 
-				break;
-			}
-		}
+	// TODO: What should we do if all hero stacks are neutral creatures?
+	int nativeTerrain = -1;
+	for(auto stack : stacks)
+	{
+		int stackNativeTerrain = VLC->townh->factions[stack.second->type->faction]->nativeTerrain;
+		if(stackNativeTerrain == -1)
+			continue;
+
+		if(nativeTerrain == -1)
+			nativeTerrain = stackNativeTerrain;
+		else if(nativeTerrain != stackNativeTerrain)
+			return -1;
 	}
-	return ret;
+
+	return nativeTerrain;
 }
 
 int3 CGHeroInstance::convertPosition(int3 src, bool toh3m) //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
@@ -129,16 +141,6 @@ int3 CGHeroInstance::getPosition(bool h3m) const //h3m=true - returns position o
 	}
 }
 
-bool CGHeroInstance::canFly() const
-{
-	return hasBonusOfType(Bonus::FLYING_MOVEMENT);
-}
-
-bool CGHeroInstance::canWalkOnSea() const
-{
-	return hasBonusOfType(Bonus::WATER_WALKING);
-}
-
 ui8 CGHeroInstance::getSecSkillLevel(SecondarySkill skill) const
 {
 	for(auto & elem : secSkills)
@@ -181,8 +183,11 @@ bool CGHeroInstance::canLearnSkill() const
 	return secSkills.size() < GameConstants::SKILL_PER_HERO;
 }
 
-int CGHeroInstance::maxMovePoints(bool onLand) const
+int CGHeroInstance::maxMovePoints(bool onLand, const TurnInfo * ti) const
 {
+	if(!ti)
+		ti = new TurnInfo(this);
+
 	int base;
 
 	if(onLand)
@@ -201,10 +206,10 @@ int CGHeroInstance::maxMovePoints(bool onLand) const
 	}
 
 	const Bonus::BonusType bt = onLand ? Bonus::LAND_MOVEMENT : Bonus::SEA_MOVEMENT;
-	const int bonus = valOfBonuses(Bonus::MOVEMENT) + valOfBonuses(bt);
+	const int bonus = ti->valOfBonuses(Bonus::MOVEMENT) + ti->valOfBonuses(bt);
 
 	const int subtype = onLand ? SecondarySkill::LOGISTICS : SecondarySkill::NAVIGATION;
-	const double modifier = valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, subtype) / 100.0;
+	const double modifier = ti->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, subtype) / 100.0;
 
 	return int(base* (1+modifier)) + bonus;
 }
@@ -261,10 +266,10 @@ void CGHeroInstance::initHero()
 		spells -= SpellID::PRESET;
 
 	if(!getArt(ArtifactPosition::MACH4) && !getArt(ArtifactPosition::SPELLBOOK) && type->haveSpellBook) //no catapult means we haven't read pre-existent set -> use default rules for spellbook
-		putArtifact(ArtifactPosition::SPELLBOOK, CArtifactInstance::createNewArtifactInstance(0));
+		putArtifact(ArtifactPosition::SPELLBOOK, CArtifactInstance::createNewArtifactInstance(ArtifactID::SPELLBOOK));
 
 	if(!getArt(ArtifactPosition::MACH4))
-		putArtifact(ArtifactPosition::MACH4, CArtifactInstance::createNewArtifactInstance(3)); //everyone has a catapult
+		putArtifact(ArtifactPosition::MACH4, CArtifactInstance::createNewArtifactInstance(ArtifactID::CATAPULT)); //everyone has a catapult
 
 	if(portrait < 0 || portrait == 255)
 		portrait = type->imageIndex;
@@ -1099,9 +1104,11 @@ int CGHeroInstance::getBoatType() const
 
 void CGHeroInstance::getOutOffsets(std::vector<int3> &offsets) const
 {
+	// FIXME: Offsets need to be fixed once we get rid of convertPosition
+	// Check issue 515 for details
 	offsets = 
 	{ 
-		int3(0,1,0), int3(0,-1,0), int3(-1,0,0), int3(+1,0,0), int3(1,1,0), int3(-1,1,0), int3(1,-1,0), int3(-1,-1,0) 
+		int3(-1,1,0), int3(-1,-1,0), int3(-2,0,0), int3(0,0,0), int3(0,1,0), int3(-2,1,0), int3(0,-1,0), int3(-2,-1,0)
 	};
 }
 
@@ -1171,30 +1178,25 @@ CBonusSystemNode * CGHeroInstance::whereShouldBeAttached(CGameState *gs)
 		return CArmedInstance::whereShouldBeAttached(gs);
 }
 
-int CGHeroInstance::movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark /*= false*/) const
+int CGHeroInstance::movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark /*= false*/, const TurnInfo * ti) const
 {
-	if(hasBonusOfType(Bonus::FREE_SHIP_BOARDING))
-		return (MPsBefore - basicCost) * static_cast<double>(maxMovePoints(disembark)) / maxMovePoints(!disembark);
+	if(!ti)
+		ti = new TurnInfo(this);
+
+	int mp1 = ti->getMaxMovePoints(disembark ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL);
+	int mp2 = ti->getMaxMovePoints(disembark ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND);
+	if(ti->hasBonusOfType(Bonus::FREE_SHIP_BOARDING))
+		return (MPsBefore - basicCost) * static_cast<double>(mp1) / mp2;
 
 	return 0; //take all MPs otherwise
 }
 
-CGHeroInstance::ECanDig CGHeroInstance::diggingStatus() const
+EDiggingStatus CGHeroInstance::diggingStatus() const
 {
 	if(movement < maxMovePoints(true))
-		return LACK_OF_MOVEMENT;
-    else if(cb->getTile(getPosition(false))->terType == ETerrainType::WATER)
-		return WRONG_TERRAIN;
-	else
-	{
-		const TerrainTile *t = cb->getTile(getPosition());
-		//TODO look for hole
-		//CGI->mh->getTerrainDescr(h->getPosition(false), hlp, false);
-		if(/*hlp.length() || */t->blockingObjects.size() > 1)
-			return TILE_OCCUPIED;
-		else
-			return CAN_DIG;
-	}
+		return EDiggingStatus::LACK_OF_MOVEMENT;
+
+	return cb->getTile(getPosition(false))->getDiggingStatus();
 }
 
 ArtBearer::ArtBearer CGHeroInstance::bearerType() const

+ 19 - 12
lib/mapObjects/CGHeroInstance.h

@@ -21,6 +21,7 @@ class CHero;
 class CGBoat;
 class CGTownInstance;
 struct TerrainTile;
+struct TurnInfo;
 
 class CGHeroPlaceholder : public CGObjectInstance
 {
@@ -39,10 +40,6 @@ public:
 class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet, public ISpellCaster
 {
 public:
-	enum ECanDig
-	{
-		CAN_DIG, LACK_OF_MOVEMENT, WRONG_TERRAIN, TILE_OCCUPIED
-	};
 	//////////////////////////////////////////////////////////////////////////
 
 	ui8 moveDir; //format:	123
@@ -75,12 +72,23 @@ public:
 
 	struct DLL_LINKAGE Patrol
 	{
-		Patrol(){patrolling=false;patrolRadious=-1;};
+		Patrol(){patrolling=false;initialPos=int3();patrolRadious=-1;};
 		bool patrolling;
+		int3 initialPos;
 		ui32 patrolRadious;
 		template <typename Handler> void serialize(Handler &h, const int version)
 		{
-			h & patrolling & patrolRadious;
+			h & patrolling;
+			if(version >= 755)
+			{
+				h & initialPos;
+			}
+			else if(!h.saving)
+			{
+				patrolling = false;
+				initialPos = int3();
+			}
+			h & patrolRadious;
 		}
 	} patrol;
 
@@ -129,12 +137,11 @@ public:
 	EAlignment::EAlignment getAlignment() const;
 	const std::string &getBiography() const;
 	bool needsLastStack()const override;
-	ui32 getTileCost(const TerrainTile &dest, const TerrainTile &from) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling
+	ui32 getTileCost(const TerrainTile &dest, const TerrainTile &from, const TurnInfo * ti) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling
+	int getNativeTerrain() const;
 	ui32 getLowestCreatureSpeed() const;
 	int3 getPosition(bool h3m = false) const; //h3m=true - returns position of hero object; h3m=false - returns position of hero 'manifestation'
 	si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day
-	bool canFly() const;
-	bool canWalkOnSea() const;
 	int getCurrentLuck(int stack=-1, bool town=false) const;
 	int getSpellCost(const CSpell *sp) const; //do not use during battles -> bonuses from army would be ignored
 
@@ -161,8 +168,8 @@ public:
 	void setSecSkillLevel(SecondarySkill which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value
 	void levelUp(std::vector<SecondarySkill> skills);
 
-	int maxMovePoints(bool onLand) const;
-	int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false) const;
+	int maxMovePoints(bool onLand, const TurnInfo * ti = nullptr) const;
+	int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false, const TurnInfo * ti = nullptr) const;
 
 	static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
 	double getFightingStrength() const; // takes attack / defense skill into account
@@ -174,7 +181,7 @@ public:
 	bool canCastThisSpell(const CSpell * spell) const; //determines if this hero can cast given spell; takes into account existing spell in spellbook, existing spellbook and artifact bonuses
 	CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const;
 	void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const;
-	ECanDig diggingStatus() const; //0 - can dig; 1 - lack of movement; 2 -
+	EDiggingStatus diggingStatus() const;
 
 	//////////////////////////////////////////////////////////////////////////
 

+ 2 - 1
lib/mapObjects/CGMarket.cpp

@@ -1,4 +1,4 @@
-/*
+/*
  *
  * CGMarket.cpp, part of VCMI engine
  *
@@ -17,6 +17,7 @@
 #include "../IGameCallback.h"
 #include "../CCreatureHandler.h"
 #include "../CGameState.h"
+#include "CGTownInstance.h"
 
 ///helpers
 static void openWindow(const OpenWindow::EWindow type, const int id1, const int id2 = -1)

+ 16 - 1
lib/mapObjects/CGPandoraBox.cpp

@@ -51,7 +51,7 @@ void CGPandoraBox::onHeroVisit(const CGHeroInstance * h) const
 
 void CGPandoraBox::giveContentsUpToExp(const CGHeroInstance *h) const
 {
-	cb->removeAfterVisit(this);
+	afterSuccessfulVisit();
 
 	InfoWindow iw;
 	iw.player = h->getOwner();
@@ -331,6 +331,11 @@ void CGPandoraBox::heroLevelUpDone(const CGHeroInstance *hero) const
 	giveContentsAfterExp(hero);
 }
 
+void CGPandoraBox::afterSuccessfulVisit() const
+{
+	cb->removeAfterVisit(this);
+}
+
 void CGEvent::onHeroVisit( const CGHeroInstance * h ) const
 {
 	if(!(availableFor & (1 << h->tempOwner.getNum())))
@@ -362,3 +367,13 @@ void CGEvent::activated( const CGHeroInstance * h ) const
 		giveContentsUpToExp(h);
 	}
 }
+
+void CGEvent::afterSuccessfulVisit() const
+{
+	if(removeAfterVisit)
+	{
+		cb->removeAfterVisit(this);
+	}
+	else if(hasGuardians)
+		hasGuardians = false;
+}

+ 3 - 1
lib/mapObjects/CGPandoraBox.h

@@ -20,7 +20,7 @@ class DLL_LINKAGE CGPandoraBox : public CArmedInstance
 {
 public:
 	std::string message;
-	bool hasGuardians; //helper - after battle even though we have no stacks, allows us to know that there was battle
+	mutable bool hasGuardians; //helper - after battle even though we have no stacks, allows us to know that there was battle
 
 	//gained things:
 	ui32 gainedExp;
@@ -54,6 +54,7 @@ protected:
 private:
 	void getText( InfoWindow &iw, bool &afterBattle, int val, int negative, int positive, const CGHeroInstance * h ) const;
 	void getText( InfoWindow &iw, bool &afterBattle, int text, const CGHeroInstance * h ) const;
+	virtual void afterSuccessfulVisit() const;
 };
 
 class DLL_LINKAGE CGEvent : public CGPandoraBox  //event objects
@@ -74,4 +75,5 @@ public:
 	void onHeroVisit(const CGHeroInstance * h) const override;
 private:
 	void activated(const CGHeroInstance * h) const;
+	void afterSuccessfulVisit() const override;
 };

+ 3 - 1
lib/mapObjects/CGTownInstance.cpp

@@ -1,4 +1,4 @@
-/*
+/*
  * CGTownInstance.cpp, part of VCMI engine
  *
  * Authors: listed in file AUTHORS in main folder
@@ -17,6 +17,8 @@
 #include "../CModHandler.h"
 #include "../IGameCallback.h"
 #include "../CGameState.h"
+#include "../mapping/CMapDefines.h"
+#include "../CPlayerState.h"
 
 std::vector<const CArtifact *> CGTownInstance::merchantArtifacts;
 std::vector<int> CGTownInstance::universitySkills;

+ 8 - 7
lib/mapObjects/CGTownInstance.h

@@ -1,4 +1,4 @@
-#pragma once
+#pragma once
 
 #include "CObjectHandler.h"
 #include "CGMarket.h" // For IMarket interface
@@ -52,11 +52,7 @@ public:
 	CSpecObjInfo * info; //h3m info about dewlling
 	TCreaturesSet creatures; //creatures[level] -> <vector of alternative ids (base creature and upgrades, creatures amount>
 
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CArmedInstance&>(*this) & creatures;
-	}
-
+private:
 	void initObj() override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void newTurn() const override;
@@ -64,9 +60,14 @@ public:
 	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
 	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
 
-private:
 	void updateGuards() const;
 	void heroAcceptsCreatures(const CGHeroInstance *h) const;
+
+public:
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CArmedInstance&>(*this) & creatures;
+	}
 };
 
 class DLL_LINKAGE CGTownBuilding : public IObjectInterface

+ 3 - 1
lib/mapObjects/CObjectHandler.cpp

@@ -1,4 +1,4 @@
-/*
+/*
  * CObjectHandler.cpp, part of VCMI engine
  *
  * Authors: listed in file AUTHORS in main folder
@@ -18,8 +18,10 @@
 #include "../filesystem/ResourceID.h"
 #include "../IGameCallback.h"
 #include "../CGameState.h"
+#include "../mapping/CMap.h"
 
 #include "CObjectClassesHandler.h"
+#include "CGTownInstance.h"
 
 IGameCallback * IObjectInterface::cb = nullptr;
 

+ 4 - 0
lib/mapObjects/CObjectHandler.h

@@ -22,6 +22,10 @@ class CGObjectInstance;
 struct MetaString;
 struct BattleResult;
 
+// This one teleport-specific, but has to be available everywhere in callbacks and netpacks
+// For now it's will be there till teleports code refactored and moved into own file
+typedef std::vector<std::pair<ObjectInstanceID, int3>> TTeleportExitsList;
+
 class DLL_LINKAGE IObjectInterface
 {
 public:

+ 1 - 0
lib/mapObjects/CQuest.cpp

@@ -17,6 +17,7 @@
 #include "../CGeneralTextHandler.h"
 #include "../CHeroHandler.h"
 #include "CObjectClassesHandler.h"
+#include "MiscObjects.h"
 #include "../IGameCallback.h"
 #include "../CGameState.h"
 

+ 1 - 0
lib/mapObjects/CRewardableObject.cpp

@@ -17,6 +17,7 @@
 #include "../NetPacks.h"
 #include "../IGameCallback.h"
 #include "../CGameState.h"
+#include "../CPlayerState.h"
 
 #include "CObjectClassesHandler.h"
 

+ 45 - 32
lib/mapObjects/MiscObjects.cpp

@@ -20,6 +20,8 @@
 #include "../spells/CSpellHandler.h"
 #include "../IGameCallback.h"
 #include "../CGameState.h"
+#include "../mapping/CMap.h"
+#include "../CPlayerState.h"
 
 std::map <si32, std::vector<ObjectInstanceID> > CGMagi::eyelist;
 ui8 CGObelisk::obeliskCount; //how many obelisks are on map
@@ -164,7 +166,7 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
 	case FIGHT:
 		fight(h);
 		break;
-	case FLEE: //flee
+	case FLEE:
 		{
 			flee(h);
 			break;
@@ -322,13 +324,13 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
 
 	int charisma = powerFactor + h->getSecSkillLevel(SecondarySkill::DIPLOMACY) + sympathy;
 
-	if(charisma < character) //creatures will fight
-		return -2;
+	if(charisma < character)
+		return FIGHT;
 
 	if (allowJoin)
 	{
 		if(h->getSecSkillLevel(SecondarySkill::DIPLOMACY) + sympathy + 1 >= character)
-			return 0; //join for free
+			return JOIN_FOR_FREE;
 
 		else if(h->getSecSkillLevel(SecondarySkill::DIPLOMACY) * 2  +  sympathy  +  1 >= character)
 			return VLC->creh->creatures[subID]->cost[6] * getStackCount(SlotID(0)); //join for gold
@@ -336,10 +338,10 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
 
 	//we are still here - creatures have not joined hero, flee or fight
 
-	if (charisma > character)
-		return -1; //flee
+	if (charisma > character && !neverFlees)
+		return FLEE;
 	else
-		return -2; //fight
+		return FIGHT;
 }
 
 void CGCreature::fleeDecision(const CGHeroInstance *h, ui32 pursue) const
@@ -361,7 +363,7 @@ void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) co
 {
 	if(!accept)
 	{
-		if(takenAction(h,false) == -1) //they flee
+		if(takenAction(h,false) == FLEE)
 		{
 			cb->setObjProperty(id, ObjProperty::MONSTER_REFUSED_JOIN, true);
 			flee(h);
@@ -834,7 +836,7 @@ bool CGTeleport::isTeleport(const CGObjectInstance * obj)
 
 bool CGTeleport::isConnected(const CGTeleport * src, const CGTeleport * dst)
 {
-	return src && dst && src != dst && src->isChannelExit(dst->id);
+	return src && dst && src->isChannelExit(dst->id);
 }
 
 bool CGTeleport::isConnected(const CGObjectInstance * src, const CGObjectInstance * dst)
@@ -913,7 +915,13 @@ void CGMonolith::onHeroVisit( const CGHeroInstance * h ) const
 	if(isEntrance())
 	{
 		if(cb->isTeleportChannelBidirectional(channel) && 1 < cb->getTeleportChannelExits(channel).size())
-			td.exits = cb->getTeleportChannelExits(channel);
+		{
+			auto exits = cb->getTeleportChannelExits(channel);
+			for(auto exit : exits)
+			{
+				td.exits.push_back(std::make_pair(exit, CGHeroInstance::convertPosition(cb->getObj(exit)->visitablePos(), true)));
+			}
+		}
 
 		if(cb->isTeleportChannelImpassable(channel))
 		{
@@ -929,9 +937,9 @@ void CGMonolith::onHeroVisit( const CGHeroInstance * h ) const
 	cb->showTeleportDialog(&td);
 }
 
-void CGMonolith::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector<ObjectInstanceID> exits) const
+void CGMonolith::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const
 {
-	ObjectInstanceID objId(answer);
+	int3 dPos;
 	auto realExits = getAllExits(true);
 	if(!isEntrance() // Do nothing if hero visited exit only object
 		|| (!exits.size() && !realExits.size()) // Do nothing if there no exits on this channel
@@ -939,14 +947,12 @@ void CGMonolith::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer,
 	{
 		return;
 	}
-	else if(objId == ObjectInstanceID())
-		objId = getRandomExit(hero);
+	else if(vstd::isValidIndex(exits, answer))
+		dPos = exits[answer].second;
 	else
-		assert(vstd::contains(exits, objId)); // Likely cheating attempt: not random teleporter choosen, but it's not from provided list
+		dPos = CGHeroInstance::convertPosition(cb->getObj(getRandomExit(hero))->visitablePos(), true);
 
-	auto obj = cb->getObj(objId);
-	if(obj)
-		cb->moveHero(hero->id,CGHeroInstance::convertPosition(obj->pos,true) - getVisitableOffset(), true);
+	cb->moveHero(hero->id, dPos, true);
 }
 
 void CGMonolith::initObj()
@@ -986,7 +992,10 @@ void CGSubterraneanGate::onHeroVisit( const CGHeroInstance * h ) const
 		td.impassable = true;
 	}
 	else
-		td.exits.push_back(getRandomExit(h));
+	{
+		auto exit = getRandomExit(h);
+		td.exits.push_back(std::make_pair(exit, CGHeroInstance::convertPosition(cb->getObj(exit)->visitablePos(), true)));
+	}
 
 	cb->showTeleportDialog(&td);
 }
@@ -1085,31 +1094,35 @@ void CGWhirlpool::onHeroVisit( const CGHeroInstance * h ) const
 		cb->changeStackCount(StackLocation(h, targetstack), -countToTake);
 	}
 	else
-		 td.exits = getAllExits(true);
+	{
+		auto exits = getAllExits();
+		for(auto exit : exits)
+		{
+			auto blockedPosList = cb->getObj(exit)->getBlockedPos();
+			for(auto bPos : blockedPosList)
+				td.exits.push_back(std::make_pair(exit, CGHeroInstance::convertPosition(bPos, true)));
+		}
+	}
 
 	cb->showTeleportDialog(&td);
 }
 
-void CGWhirlpool::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector<ObjectInstanceID> exits) const
+void CGWhirlpool::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const
 {
-	ObjectInstanceID objId(answer);
+	int3 dPos;
 	auto realExits = getAllExits();
 	if(!exits.size() && !realExits.size())
 		return;
-	else if(objId == ObjectInstanceID())
-		objId = getRandomExit(hero);
+	else if(vstd::isValidIndex(exits, answer))
+		dPos = exits[answer].second;
 	else
-		assert(vstd::contains(exits, objId)); // Likely cheating attempt: not random teleporter choosen, but it's not from provided list
-
-	auto obj = cb->getObj(objId);
-	if(obj)
 	{
+		auto obj = cb->getObj(getRandomExit(hero));
 		std::set<int3> tiles = obj->getBlockedPos();
-		auto & tile = *RandomGeneratorUtil::nextItem(tiles, cb->gameState()->getRandomGenerator());
-		cb->moveHero(hero->id, tile + int3(1,0,0), true);
-
-		cb->moveHero(hero->id,CGHeroInstance::convertPosition(obj->pos,true) - getVisitableOffset(), true);
+		dPos = CGHeroInstance::convertPosition(*RandomGeneratorUtil::nextItem(tiles, cb->gameState()->getRandomGenerator()), true);
 	}
+
+	cb->moveHero(hero->id, dPos, true);
 }
 
 bool CGWhirlpool::isProtected( const CGHeroInstance * h )

+ 25 - 17
lib/mapObjects/MiscObjects.h

@@ -227,7 +227,8 @@ class DLL_LINKAGE CGMine : public CArmedInstance
 public:
 	Res::ERes producedResource;
 	ui32 producedQuantity;
-	
+
+private:
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
 	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
@@ -239,6 +240,7 @@ public:
 	std::string getObjectName() const override;
 	std::string getHoverText(PlayerColor player) const override;
 
+public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CArmedInstance&>(*this);
@@ -265,29 +267,33 @@ struct DLL_LINKAGE TeleportChannel
 
 class DLL_LINKAGE CGTeleport : public CGObjectInstance
 {
-public:
-	enum EType {UNKNOWN, ENTRANCE, EXIT, BOTH};
+	bool isChannelEntrance(ObjectInstanceID id) const;
+	bool isChannelExit(ObjectInstanceID id) const;
+
+	std::vector<ObjectInstanceID> getAllEntrances(bool excludeCurrent = false) const;
 
+protected:
+	enum EType {UNKNOWN, ENTRANCE, EXIT, BOTH};
 	EType type;
-	TeleportChannelID channel;
 
 	CGTeleport();
+	ObjectInstanceID getRandomExit(const CGHeroInstance * h) const;
+	std::vector<ObjectInstanceID> getAllExits(bool excludeCurrent = false) const;
+
+public:
+	TeleportChannelID channel;
+
 	bool isEntrance() const;
 	bool isExit() const;
-	bool isChannelEntrance(ObjectInstanceID id) const;
-	bool isChannelExit(ObjectInstanceID id) const;
-	std::vector<ObjectInstanceID> getAllEntrances(bool excludeCurrent = false) const;
-	std::vector<ObjectInstanceID> getAllExits(bool excludeCurrent = false) const;
-	ObjectInstanceID getRandomExit(const CGHeroInstance * h) const;
 
-	virtual void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector<ObjectInstanceID> exits) const = 0;
+	virtual void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const = 0;
 
 	static bool isTeleport(const CGObjectInstance * dst);
 	static bool isConnected(const CGTeleport * src, const CGTeleport * dst);
 	static bool isConnected(const CGObjectInstance * src, const CGObjectInstance * dst);
-	static bool isExitPassable(CGameState * gs, const CGHeroInstance * h, const CGObjectInstance * obj);
-	static std::vector<ObjectInstanceID> getPassableExits(CGameState * gs, const CGHeroInstance * h, std::vector<ObjectInstanceID> exits);
 	static void addToChannel(std::map<TeleportChannelID, shared_ptr<TeleportChannel> > &channelsList, const CGTeleport * obj);
+	static std::vector<ObjectInstanceID> getPassableExits(CGameState * gs, const CGHeroInstance * h, std::vector<ObjectInstanceID> exits);
+	static bool isExitPassable(CGameState * gs, const CGHeroInstance * h, const CGObjectInstance * obj);
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -299,11 +305,12 @@ class DLL_LINKAGE CGMonolith : public CGTeleport
 {
 	TeleportChannelID findMeChannel(std::vector<Obj> IDs, int SubID) const;
 
-public:
+protected:
 	void onHeroVisit(const CGHeroInstance * h) const override;
-	void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector<ObjectInstanceID> exits) const override;
+	void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const override;
 	void initObj() override;
 
+public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CGTeleport&>(*this);
@@ -312,9 +319,10 @@ public:
 
 class DLL_LINKAGE CGSubterraneanGate : public CGMonolith
 {
-public:
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void initObj() override;
+
+public:
 	static void postInit();
 
 	template <typename Handler> void serialize(Handler &h, const int version)
@@ -325,11 +333,11 @@ public:
 
 class DLL_LINKAGE CGWhirlpool : public CGMonolith
 {
-public:
 	void onHeroVisit(const CGHeroInstance * h) const override;
-	void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector<ObjectInstanceID> exits) const override;
+	void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const override;
 	static bool isProtected( const CGHeroInstance * h );
 
+public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CGMonolith&>(*this);

+ 2 - 2
lib/mapping/CCampaignHandler.cpp

@@ -292,7 +292,7 @@ CScenarioTravel CCampaignHandler::readScenarioTravelFromMemory(CBinaryReader & r
 
 std::vector< std::vector<ui8> > CCampaignHandler::getFile(const std::string & name, bool headerOnly)
 {
-	CCompressedStream stream(std::move(CResourceHandler::get()->load(ResourceID(name, EResType::CAMPAIGN))), true);
+	CCompressedStream stream(CResourceHandler::get()->load(ResourceID(name, EResType::CAMPAIGN)), true);
 
 	std::vector< std::vector<ui8> > ret;
 	do
@@ -363,7 +363,7 @@ std::vector<CGHeroInstance *> CCampaignScenario::getLostCrossoverHeroes() const
 			}
 		}
 	}
-	return std::move(lostCrossoverHeroes);
+	return lostCrossoverHeroes;
 }
 
 bool CScenarioTravel::STravelBonus::isBonusForHero() const

+ 1 - 0
lib/mapping/CDrawRoadsOperation.cpp

@@ -10,6 +10,7 @@
 
 #include "StdInc.h"
 #include "CDrawRoadsOperation.h"
+#include "CMap.h"
 
 const std::vector<CDrawRoadsOperation::RoadPattern> CDrawRoadsOperation::patterns = 
 {

+ 1 - 1
lib/mapping/CDrawRoadsOperation.h

@@ -11,9 +11,9 @@
 #pragma once
  
 #include "../CRandomGenerator.h"
-#include "CMap.h"
 #include "CMapEditManager.h"
 
+struct TerrainTile;
 
 class CDrawRoadsOperation : public CMapOperation
 {

+ 18 - 4
lib/mapping/CMap.cpp

@@ -132,11 +132,13 @@ Obj TerrainTile::topVisitableId(bool excludeTop) const
 
 CGObjectInstance * TerrainTile::topVisitableObj(bool excludeTop) const
 {
-	auto visitableObj = visitableObjects;
-	if(excludeTop && visitableObj.size())
-		visitableObj.pop_back();
+	if(visitableObjects.empty() || (excludeTop && visitableObjects.size() == 1))
+		return nullptr;
 
-	return visitableObj.size() ? visitableObj.back() : nullptr;
+	if(excludeTop)
+		return visitableObjects[visitableObjects.size()-2];
+
+	return visitableObjects.back();
 }
 
 bool TerrainTile::isCoastal() const
@@ -144,6 +146,18 @@ bool TerrainTile::isCoastal() const
 	return extTileFlags & 64;
 }
 
+EDiggingStatus TerrainTile::getDiggingStatus(const bool excludeTop) const
+{
+	if(terType == ETerrainType::WATER || terType == ETerrainType::ROCK)
+		return EDiggingStatus::WRONG_TERRAIN;
+
+	int allowedBlocked = excludeTop ? 1 : 0;
+	if(blockingObjects.size() > allowedBlocked || topVisitableObj(excludeTop))
+		return EDiggingStatus::TILE_OCCUPIED;
+	else
+		return EDiggingStatus::CAN_DIG;
+}
+
 bool TerrainTile::hasFavourableWinds() const
 {
 	return extTileFlags & 128;

+ 1 - 99
lib/mapping/CMap.h

@@ -19,6 +19,7 @@
 #include "../int3.h"
 #include "../GameConstants.h"
 #include "../LogicalExpression.h"
+#include "CMapDefines.h"
 
 class CArtifactInstance;
 class CGObjectInstance;
@@ -47,18 +48,6 @@ struct DLL_LINKAGE SHeroName
 	}
 };
 
-namespace EAiTactic
-{
-enum EAiTactic
-{
-	NONE = -1,
-	RANDOM,
-	WARRIOR,
-	BUILDER,
-	EXPLORER
-};
-}
-
 /// The player info constains data about which factions are allowed, AI tactical settings,
 /// the main hero name, where to generate the hero, whether the faction should be selected randomly,...
 struct DLL_LINKAGE PlayerInfo
@@ -216,93 +205,6 @@ struct DLL_LINKAGE DisposedHero
 	}
 };
 
-/// The map event is an event which e.g. gives or takes resources of a specific
-/// amount to/from players and can appear regularly or once a time.
-class DLL_LINKAGE CMapEvent
-{
-public:
-	CMapEvent();
-
-	bool earlierThan(const CMapEvent & other) const;
-	bool earlierThanOrEqual(const CMapEvent & other) const;
-
-	std::string name;
-	std::string message;
-	TResources resources;
-	ui8 players; // affected players, bit field?
-	ui8 humanAffected;
-	ui8 computerAffected;
-	ui32 firstOccurence;
-	ui32 nextOccurence; /// specifies after how many days the event will occur the next time; 0 if event occurs only one time
-
-	template <typename Handler>
-	void serialize(Handler & h, const int version)
-	{
-		h & name & message & resources
-				& players & humanAffected & computerAffected & firstOccurence & nextOccurence;
-	}
-};
-
-/// The castle event builds/adds buildings/creatures for a specific town.
-class DLL_LINKAGE CCastleEvent: public CMapEvent
-{
-public:
-	CCastleEvent();
-
-	std::set<BuildingID> buildings;
-	std::vector<si32> creatures;
-	CGTownInstance * town;
-
-	template <typename Handler>
-	void serialize(Handler & h, const int version)
-	{
-		h & static_cast<CMapEvent &>(*this);
-		h & buildings & creatures;
-	}
-};
-
-/// The terrain tile describes the terrain type and the visual representation of the terrain.
-/// Furthermore the struct defines whether the tile is visitable or/and blocked and which objects reside in it.
-struct DLL_LINKAGE TerrainTile
-{
-	TerrainTile();
-
-	/// Gets true if the terrain is not a rock. If from is water/land, same type is also required.
-	bool entrableTerrain(const TerrainTile * from = nullptr) const;
-	bool entrableTerrain(bool allowLand, bool allowSea) const;
-	/// Checks for blocking objects and terraint type (water / land).
-	bool isClear(const TerrainTile * from = nullptr) const;
-	/// Gets the ID of the top visitable object or -1 if there is none.
-	Obj topVisitableId(bool excludeTop = false) const;
-	CGObjectInstance * topVisitableObj(bool excludeTop = false) const;
-	bool isWater() const;
-	bool isCoastal() const;
-	bool hasFavourableWinds() const;
-
-	ETerrainType terType;
-	ui8 terView;
-	ERiverType::ERiverType riverType;
-	ui8 riverDir;
-	ERoadType::ERoadType roadType;
-	ui8 roadDir;
-	/// first two bits - how to rotate terrain graphic (next two - river graphic, next two - road);
-	///	7th bit - whether tile is coastal (allows disembarking if land or block movement if water); 8th bit - Favourable Winds effect
-	ui8 extTileFlags;
-	bool visitable;
-	bool blocked;
-
-	std::vector<CGObjectInstance *> visitableObjects;
-	std::vector<CGObjectInstance *> blockingObjects;
-
-	template <typename Handler>
-	void serialize(Handler & h, const int version)
-	{
-		h & terType & terView & riverType & riverDir & roadType &roadDir & extTileFlags;
-		h & visitable & blocked;
-		h & visitableObjects & blockingObjects;
-	}
-};
-
 namespace EMapFormat
 {
 enum EMapFormat

+ 99 - 0
lib/mapping/CMapDefines.h

@@ -0,0 +1,99 @@
+/*
+ * CMapDefines.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
+
+/// The map event is an event which e.g. gives or takes resources of a specific
+/// amount to/from players and can appear regularly or once a time.
+class DLL_LINKAGE CMapEvent
+{
+public:
+	CMapEvent();
+
+	bool earlierThan(const CMapEvent & other) const;
+	bool earlierThanOrEqual(const CMapEvent & other) const;
+
+	std::string name;
+	std::string message;
+	TResources resources;
+	ui8 players; // affected players, bit field?
+	ui8 humanAffected;
+	ui8 computerAffected;
+	ui32 firstOccurence;
+	ui32 nextOccurence; /// specifies after how many days the event will occur the next time; 0 if event occurs only one time
+
+	template <typename Handler>
+	void serialize(Handler & h, const int version)
+	{
+		h & name & message & resources
+				& players & humanAffected & computerAffected & firstOccurence & nextOccurence;
+	}
+};
+
+/// The castle event builds/adds buildings/creatures for a specific town.
+class DLL_LINKAGE CCastleEvent: public CMapEvent
+{
+public:
+	CCastleEvent();
+
+	std::set<BuildingID> buildings;
+	std::vector<si32> creatures;
+	CGTownInstance * town;
+
+	template <typename Handler>
+	void serialize(Handler & h, const int version)
+	{
+		h & static_cast<CMapEvent &>(*this);
+		h & buildings & creatures;
+	}
+};
+
+/// The terrain tile describes the terrain type and the visual representation of the terrain.
+/// Furthermore the struct defines whether the tile is visitable or/and blocked and which objects reside in it.
+struct DLL_LINKAGE TerrainTile
+{
+	TerrainTile();
+
+	/// Gets true if the terrain is not a rock. If from is water/land, same type is also required.
+	bool entrableTerrain(const TerrainTile * from = nullptr) const;
+	bool entrableTerrain(bool allowLand, bool allowSea) const;
+	/// Checks for blocking objects and terraint type (water / land).
+	bool isClear(const TerrainTile * from = nullptr) const;
+	/// Gets the ID of the top visitable object or -1 if there is none.
+	Obj topVisitableId(bool excludeTop = false) const;
+	CGObjectInstance * topVisitableObj(bool excludeTop = false) const;
+	bool isWater() const;
+	bool isCoastal() const;
+	EDiggingStatus getDiggingStatus(const bool excludeTop = true) const;
+	bool hasFavourableWinds() const;
+
+	ETerrainType terType;
+	ui8 terView;
+	ERiverType::ERiverType riverType;
+	ui8 riverDir;
+	ERoadType::ERoadType roadType;
+	ui8 roadDir;
+	/// first two bits - how to rotate terrain graphic (next two - river graphic, next two - road);
+	///	7th bit - whether tile is coastal (allows disembarking if land or block movement if water); 8th bit - Favourable Winds effect
+	ui8 extTileFlags;
+	bool visitable;
+	bool blocked;
+
+	std::vector<CGObjectInstance *> visitableObjects;
+	std::vector<CGObjectInstance *> blockingObjects;
+
+	template <typename Handler>
+	void serialize(Handler & h, const int version)
+	{
+		h & terType & terView & riverType & riverDir & roadType &roadDir & extTileFlags;
+		h & visitable & blocked;
+		h & visitableObjects & blockingObjects;
+	}
+};

+ 1 - 0
lib/mapping/CMapEditManager.cpp

@@ -7,6 +7,7 @@
 #include "../mapObjects/CGHeroInstance.h"
 #include "../VCMI_Lib.h"
 #include "CDrawRoadsOperation.h"
+#include "../mapping/CMap.h"
 
 MapRect::MapRect() : x(0), y(0), z(0), width(0), height(0)
 {

+ 3 - 1
lib/mapping/CMapEditManager.h

@@ -12,11 +12,13 @@
 #pragma once
 
 #include "../CRandomGenerator.h"
-#include "CMap.h"
+#include "../int3.h"
+#include "../GameConstants.h"
 
 class CGObjectInstance;
 class CTerrainViewPatternConfig;
 struct TerrainViewPattern;
+class CMap;
 
 /// Represents a map rectangle.
 struct DLL_LINKAGE MapRect

+ 4 - 4
lib/mapping/CMapService.cpp

@@ -20,7 +20,7 @@ std::unique_ptr<CMap> CMapService::loadMap(const std::string & name)
 	getMapPatcher(name)->patchMapHeader(header);
 	header.release();
 
-	return std::move(map);
+	return map;
 }
 
 std::unique_ptr<CMapHeader> CMapService::loadMapHeader(const std::string & name)
@@ -28,7 +28,7 @@ std::unique_ptr<CMapHeader> CMapService::loadMapHeader(const std::string & name)
 	auto stream = getStreamFromFS(name);
 	std::unique_ptr<CMapHeader> header = getMapLoader(stream)->loadMapHeader();
 	getMapPatcher(name)->patchMapHeader(header);
-	return std::move(header);
+	return header;
 }
 
 std::unique_ptr<CMap> CMapService::loadMap(const ui8 * buffer, int size, const std::string & name)
@@ -40,7 +40,7 @@ std::unique_ptr<CMap> CMapService::loadMap(const ui8 * buffer, int size, const s
 	getMapPatcher(name)->patchMapHeader(header);
 	header.release();
 
-	return std::move(map);
+	return map;
 }
 
 std::unique_ptr<CMapHeader> CMapService::loadMapHeader(const ui8 * buffer, int size, const std::string & name)
@@ -48,7 +48,7 @@ std::unique_ptr<CMapHeader> CMapService::loadMapHeader(const ui8 * buffer, int s
 	auto stream = getStreamFromMem(buffer, size);
 	std::unique_ptr<CMapHeader> header = getMapLoader(stream)->loadMapHeader();
 	getMapPatcher(name)->patchMapHeader(header);
-	return std::move(header);
+	return header;
 }
 
 std::unique_ptr<CInputStream> CMapService::getStreamFromFS(const std::string & name)

+ 4 - 4
lib/mapping/MapFormatH3M.cpp

@@ -668,8 +668,7 @@ void CMapLoaderH3M::readAllowedArtifacts()
 		}
 		if (map->version == EMapFormat::ROE)
 		{
-			// Armageddon's Blade
-			map->allowedArtifact[128] = false;
+			map->allowedArtifact[ArtifactID::ARMAGEDDONS_BLADE] = false;
 		}
 	}
 
@@ -1061,7 +1060,7 @@ void CMapLoaderH3M::readObjects()
 		case Obj::RANDOM_HERO:
 		case Obj::PRISON:
 			{
-				nobj = readHero(idToBeGiven);
+				nobj = readHero(idToBeGiven, objPos);
 				break;
 			}
 		case Obj::MONSTER:  //Monster
@@ -1550,7 +1549,7 @@ void CMapLoaderH3M::readCreatureSet(CCreatureSet * out, int number)
 	out->validTypes(true);
 }
 
-CGObjectInstance * CMapLoaderH3M::readHero(ObjectInstanceID idToBeGiven)
+CGObjectInstance * CMapLoaderH3M::readHero(ObjectInstanceID idToBeGiven, const int3 & initialPos)
 {
 	auto nhi = new CGHeroInstance();
 
@@ -1659,6 +1658,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(ObjectInstanceID idToBeGiven)
 	else
 	{
 		nhi->patrol.patrolling = true;
+		nhi->patrol.initialPos = CGHeroInstance::convertPosition(initialPos, false);
 	}
 
 	if(map->version > EMapFormat::ROE)

+ 1 - 1
lib/mapping/MapFormatH3M.h

@@ -172,7 +172,7 @@ private:
 	 * @param idToBeGiven the object id which should be set for the hero
 	 * @return a object instance
 	 */
-	CGObjectInstance * readHero(ObjectInstanceID idToBeGiven);
+	CGObjectInstance * readHero(ObjectInstanceID idToBeGiven, const int3 & initialPos);
 
 	/**
 	 * Reads a seer hut.

+ 1 - 3
lib/registerTypes/RegisterTypes.h

@@ -4,7 +4,7 @@
 #include "../NetPacks.h"
 #include "../VCMI_Lib.h"
 #include "../CArtHandler.h"
-#include "../CGameState.h"
+#include "../CPlayerState.h"
 #include "../CHeroHandler.h"
 #include "../CTownHandler.h"
 #include "../CModHandler.h" //needed?
@@ -24,8 +24,6 @@
  *
  */
 
-
-
 template<typename Serializer>
 void registerTypesMapObjects1(Serializer &s)
 {

+ 6 - 1
lib/rmg/CMapGenOptions.cpp

@@ -78,6 +78,11 @@ void CMapGenOptions::setPlayerCount(si8 value)
 	resetPlayersMap();
 }
 
+si8 CMapGenOptions::getHumanOnlyPlayerCount() const
+{
+	return humanPlayersCount;
+}
+
 si8 CMapGenOptions::getTeamCount() const
 {
 	return teamCount;
@@ -96,7 +101,7 @@ si8 CMapGenOptions::getCompOnlyPlayerCount() const
 
 void CMapGenOptions::setCompOnlyPlayerCount(si8 value)
 {
-	assert(value == RANDOM_SIZE || (value >= 0 && value <= getPlayerCount()));
+	assert(value == RANDOM_SIZE || (getPlayerCount() == RANDOM_SIZE || (value >= 0 && value <= getPlayerCount())));
 	compOnlyPlayerCount = value;
 
 	if (getPlayerCount() != RANDOM_SIZE && getCompOnlyPlayerCount() != RANDOM_SIZE)

+ 2 - 0
lib/rmg/CMapGenOptions.h

@@ -108,6 +108,8 @@ public:
 	si8 getPlayerCount() const;
 	void setPlayerCount(si8 value);
 
+	si8 getHumanOnlyPlayerCount() const;
+
 	/// The count of the teams ranging from 0 to <players count - 1> or RANDOM_SIZE for random.
 	si8 getTeamCount() const;
 	void setTeamCount(si8 value);

+ 1 - 1
lib/rmg/CMapGenerator.cpp

@@ -17,7 +17,7 @@ static const int3 dirs4[] = {int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0)}
 
 void CMapGenerator::foreach_neighbour(const int3 &pos, std::function<void(int3& pos)> foo)
 {
-	for(const int3 &dir : dirs)
+	for(const int3 &dir : int3::getDirs())
 	{
 		int3 n = pos + dir;
 		if(map->isInTheMap(n))

+ 7 - 6
lib/rmg/CRmgTemplateZone.cpp

@@ -735,7 +735,7 @@ bool CRmgTemplateZone::createRoad(CMapGenerator* gen, const int3& src, const int
 	std::map<int3, int3> cameFrom;  // The map of navigated nodes.
 	std::map<int3, float> distances;
 
-	int3 currentNode = src;
+	//int3 currentNode = src;
 	gen->setRoad (src, ERoadType::NO_ROAD); //just in case zone guard already has road under it. Road under nodes will be added at very end
 
 	cameFrom[src] = int3(-1, -1, -1); //first node points to finish condition
@@ -824,7 +824,7 @@ bool CRmgTemplateZone::connectPath(CMapGenerator* gen, const int3& src, bool onl
 	std::map<int3, int3> cameFrom;  // The map of navigated nodes.
 	std::map<int3, float> distances;
 
-	int3 currentNode = src;
+	//int3 currentNode = src;
 
 	cameFrom[src] = int3(-1, -1, -1); //first node points to finish condition
 	distances[src] = 0;
@@ -866,7 +866,7 @@ bool CRmgTemplateZone::connectPath(CMapGenerator* gen, const int3& src, bool onl
 					return;
 				if (distance < bestDistanceSoFar || !vstd::contains(closed, pos))
 				{
-					auto obj = gen->map->getTile(pos).topVisitableObj();
+					//auto obj = gen->map->getTile(pos).topVisitableObj();
 					if (vstd::contains(this->tileinfo, pos))
 					{
 						cameFrom[pos] = currentNode;
@@ -902,7 +902,7 @@ bool CRmgTemplateZone::connectWithCenter(CMapGenerator* gen, const int3& src, bo
 	std::map<int3, int3> cameFrom;  // The map of navigated nodes.
 	std::map<int3, float> distances;
 
-	int3 currentNode = src;
+	//int3 currentNode = src;
 
 	cameFrom[src] = int3(-1, -1, -1); //first node points to finish condition
 	distances[src] = 0;
@@ -950,7 +950,7 @@ bool CRmgTemplateZone::connectWithCenter(CMapGenerator* gen, const int3& src, bo
 
 				if (distance < bestDistanceSoFar || !vstd::contains(closed, pos))
 				{
-					auto obj = gen->map->getTile(pos).topVisitableObj();
+					//auto obj = gen->map->getTile(pos).topVisitableObj();
 					if (vstd::contains(this->tileinfo, pos))
 					{
 						cameFrom[pos] = currentNode;
@@ -2336,7 +2336,8 @@ ObjectInfo CRmgTemplateZone::getRandomObject(CMapGenerator* gen, CTreasurePileIn
 		}
 		assert (0); //we should never be here
 	}
-	//FIXME: control reaches end of non-void function. Missing return?
+
+	return ObjectInfo(); // unreachable
 }
 
 void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)

+ 1 - 0
lib/rmg/CZonePlacer.cpp

@@ -13,6 +13,7 @@
 #include "../CRandomGenerator.h"
 #include "CZonePlacer.h"
 #include "CRmgTemplateZone.h"
+#include "../mapping/CMap.h"
 
 #include "CZoneGraphGenerator.h"
 

+ 0 - 2
lib/rmg/CZonePlacer.h

@@ -12,8 +12,6 @@
 #pragma once
 
 #include "CMapGenerator.h"
-#include "../mapping/CMap.h"
-
 #include "float3.h"
 #include "../int3.h"
 

+ 2 - 0
lib/spells/AdventureSpellMechanics.cpp

@@ -18,6 +18,8 @@
 #include "../BattleState.h"
 #include "../CGameState.h"
 #include "../CGameInfoCallback.h"
+#include "../mapping/CMap.h"
+#include "../CPlayerState.h"
 
 ///SummonBoatMechanics
 ESpellCastResult SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const

+ 1 - 0
lib/spells/BattleSpellMechanics.cpp

@@ -14,6 +14,7 @@
 #include "../NetPacks.h"
 #include "../BattleState.h"
 #include "../mapObjects/CGHeroInstance.h"
+#include "../mapObjects/CGTownInstance.h"
 
 ///HealingSpellMechanics
 void HealingSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const

+ 1 - 1
lib/spells/CSpellHandler.cpp

@@ -640,7 +640,7 @@ std::string CSpell::AnimationInfo::selectProjectile(const double angle) const
 		}
 	}
 
-	return std::move(res);
+	return res;
 }
 
 ///CSpell::TargetInfo

Some files were not shown because too many files changed in this diff