浏览代码

Merge remote-tracking branch 'upstream/develop' into features/cpp-map-editor

# Conflicts:
#	.github/workflows/github.yml
#	launcher/modManager/cmodlist.cpp
#	lib/CModHandler.cpp
#	lib/CModHandler.h
nordsoft 3 年之前
父节点
当前提交
89d0de53da
共有 84 个文件被更改,包括 964 次插入1180 次删除
  1. 56 6
      .github/workflows/github.yml
  2. 6 0
      AI/BattleAI/StackWithBonuses.cpp
  3. 4 0
      AI/BattleAI/StackWithBonuses.h
  4. 20 16
      AI/Nullkiller/AIUtility.h
  5. 19 18
      AI/Nullkiller/Pathfinding/AINodeStorage.cpp
  6. 4 8
      AI/Nullkiller/Pathfinding/AINodeStorage.h
  7. 0 49
      AI/VCAI/AIUtility.cpp
  8. 59 4
      AI/VCAI/AIUtility.h
  9. 0 4
      AI/VCAI/CMakeLists.txt
  10. 1 0
      AI/VCAI/Goals/AbstractGoal.h
  11. 18 13
      AI/VCAI/Goals/Explore.cpp
  12. 9 11
      AI/VCAI/Pathfinding/AINodeStorage.cpp
  13. 7 1
      AI/VCAI/Pathfinding/AINodeStorage.h
  14. 0 431
      AI/VCAI/SectorMap.cpp
  15. 0 70
      AI/VCAI/SectorMap.h
  16. 3 1
      CCallback.cpp
  17. 2 0
      CCallback.h
  18. 12 3
      CMakeLists.txt
  19. 1 5
      CMakePresets.json
  20. 2 0
      client/CGameInfo.cpp
  21. 2 0
      client/CGameInfo.h
  22. 3 1
      client/CMT.cpp
  23. 8 1
      client/CMakeLists.txt
  24. 84 78
      client/CPlayerInterface.cpp
  25. 1 1
      client/CServerHandler.cpp
  26. 10 0
      client/Client.cpp
  27. 7 0
      client/Client.h
  28. 52 90
      client/mapHandler.cpp
  29. 5 4
      client/mapHandler.h
  30. 3 3
      client/windows/CAdvmapInterface.cpp
  31. 1 1
      client/windows/GUIClasses.cpp
  32. 109 0
      config/schemas/terrain.json
  33. 4 0
      include/vcmi/Services.h
  34. 2 0
      include/vcmi/scripting/Service.h
  35. 1 1
      launcher/mainwindow_moc.cpp
  36. 70 27
      launcher/modManager/cmodlist.cpp
  37. 9 9
      launcher/modManager/cmodlistview_moc.ui
  38. 13 12
      lib/CGameInfoCallback.cpp
  39. 2 2
      lib/CGameInfoCallback.h
  40. 2 0
      lib/CGameInterface.cpp
  41. 6 0
      lib/CGameInterface.h
  42. 17 21
      lib/CGameState.cpp
  43. 18 6
      lib/CModHandler.cpp
  44. 21 40
      lib/CModHandler.h
  45. 7 7
      lib/CPathfinder.cpp
  46. 2 2
      lib/CPathfinder.h
  47. 1 1
      lib/CPlayerState.h
  48. 2 0
      lib/CScriptingModule.cpp
  49. 2 0
      lib/CScriptingModule.h
  50. 4 5
      lib/IGameCallback.cpp
  51. 5 2
      lib/IGameCallback.h
  52. 5 3
      lib/NetPacksLib.cpp
  53. 3 3
      lib/PathfinderUtil.h
  54. 2 0
      lib/ScriptHandler.cpp
  55. 2 0
      lib/ScriptHandler.h
  56. 31 43
      lib/VCMIDirs.cpp
  57. 4 1
      lib/VCMIDirs.h
  58. 10 0
      lib/VCMI_Lib.cpp
  59. 11 0
      lib/VCMI_Lib.h
  60. 11 4
      lib/battle/BattleInfo.cpp
  61. 2 0
      lib/battle/BattleInfo.h
  62. 0 7
      lib/battle/CBattleInfoCallback.h
  63. 4 0
      lib/battle/IBattleInfoCallback.h
  64. 3 3
      lib/mapObjects/ObjectTemplate.cpp
  65. 7 3
      lib/mapping/CDrawRoadsOperation.cpp
  66. 41 35
      lib/mapping/CMap.cpp
  67. 22 19
      lib/mapping/CMap.h
  68. 1 1
      lib/mapping/CMapDefines.h
  69. 7 5
      lib/mapping/MapFormatH3M.cpp
  70. 2 2
      lib/rmg/CMapGenerator.cpp
  71. 11 12
      lib/rmg/CZonePlacer.cpp
  72. 0 42
      lib/rmg/Functions.cpp
  73. 9 8
      lib/rmg/ObjectManager.h
  74. 5 12
      lib/rmg/ObstaclePlacer.cpp
  75. 3 3
      lib/rmg/RmgMap.cpp
  76. 4 4
      lib/rmg/Zone.cpp
  77. 14 0
      lib/serializer/BinaryDeserializer.h
  78. 12 0
      lib/serializer/BinarySerializer.h
  79. 2 2
      lib/spells/AdventureSpellMechanics.cpp
  80. 2 0
      lib/spells/ISpellMechanics.cpp
  81. 6 0
      lib/spells/ISpellMechanics.h
  82. 23 13
      server/CGameHandler.cpp
  83. 8 0
      server/CGameHandler.h
  84. 1 1
      server/CVCMIServer.cpp

+ 56 - 6
.github/workflows/github.yml

@@ -1,18 +1,68 @@
 name: VCMI
 name: VCMI
 
 
 on:
 on:
-    push:
-      branches:
-        - features/*
-        - develop
-        - cpp-map-editor
-    pull_request:
+  push:
+    branches:
+      - features/*
+  pull_request:
+  schedule:
+    - cron: '0 2 * * *'
+  workflow_dispatch:
+
 env:
 env:
   # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
   # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
   BUILD_TYPE: Release
   BUILD_TYPE: Release
 
 
 jobs:
 jobs:
+  check_last_build:
+    if: github.event.schedule != ''
+    runs-on: ubuntu-latest
+    outputs:
+      skip_build: ${{ steps.check_if_built.outputs.skip_build }}
+    defaults:
+      run:
+        shell: bash
+    steps:
+      - name: Get repo name
+        id: get_repo_name
+        run: echo "::set-output name=value::${GITHUB_REPOSITORY#*/}"
+      - name: Get last successful build for ${{ github.sha }}
+        uses: octokit/[email protected]
+        id: get_last_scheduled_run
+        with:
+          route: GET /repos/{owner}/{repo}/actions/runs
+          owner: ${{ github.repository_owner }}
+          repo: ${{ steps.get_repo_name.outputs.value }}
+          status: success
+          per_page: 1
+          head_sha: ${{ github.sha }}
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      - name: Check if successful build of the current commit exists
+        id: check_if_built
+        run: |
+          if [ ${{ fromJson(steps.get_last_scheduled_run.outputs.data).total_count }} -gt 0 ]; then
+            echo '::set-output name=skip_build::1'
+          else
+            echo '::set-output name=skip_build::0'
+          fi
+      - name: Cancel current run
+        if: steps.check_if_built.outputs.skip_build == 1
+        uses: octokit/[email protected]
+        with:
+          route: POST /repos/{owner}/{repo}/actions/runs/{run_id}/cancel
+          owner: ${{ github.repository_owner }}
+          repo: ${{ steps.get_repo_name.outputs.value }}
+          run_id: ${{ github.run_id }}
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      - name: Wait for the run to be cancelled
+        if: steps.check_if_built.outputs.skip_build == 1
+        run: sleep 60
+
   build:
   build:
+    needs: check_last_build
+    if: always() && needs.check_last_build.skip_build != 1
     strategy:
     strategy:
       matrix:
       matrix:
         include:
         include:

+ 6 - 0
AI/BattleAI/StackWithBonuses.cpp

@@ -16,7 +16,9 @@
 #include "../../lib/CStack.h"
 #include "../../lib/CStack.h"
 #include "../../lib/ScriptHandler.h"
 #include "../../lib/ScriptHandler.h"
 
 
+#if SCRIPTING_ENABLED
 using scripting::Pool;
 using scripting::Pool;
+#endif
 
 
 void actualizeEffect(TBonusListPtr target, const Bonus & ef)
 void actualizeEffect(TBonusListPtr target, const Bonus & ef)
 {
 {
@@ -217,7 +219,9 @@ HypotheticBattle::HypotheticBattle(const Environment * ENV, Subject realBattle)
 	localEnvironment.reset(new HypotheticEnvironment(this, env));
 	localEnvironment.reset(new HypotheticEnvironment(this, env));
 	serverCallback.reset(new HypotheticServerCallback(this));
 	serverCallback.reset(new HypotheticServerCallback(this));
 
 
+#if SCRIPTING_ENABLED
 	pool.reset(new scripting::PoolImpl(localEnvironment.get(), serverCallback.get()));
 	pool.reset(new scripting::PoolImpl(localEnvironment.get(), serverCallback.get()));
+#endif
 }
 }
 
 
 bool HypotheticBattle::unitHasAmmoCart(const battle::Unit * unit) const
 bool HypotheticBattle::unitHasAmmoCart(const battle::Unit * unit) const
@@ -420,10 +424,12 @@ int64_t HypotheticBattle::getTreeVersion() const
 	return getBattleNode()->getTreeVersion() + bonusTreeVersion;
 	return getBattleNode()->getTreeVersion() + bonusTreeVersion;
 }
 }
 
 
+#if SCRIPTING_ENABLED
 Pool * HypotheticBattle::getContextPool() const
 Pool * HypotheticBattle::getContextPool() const
 {
 {
 	return pool.get();
 	return pool.get();
 }
 }
+#endif
 
 
 ServerCallback * HypotheticBattle::getServerCallback()
 ServerCallback * HypotheticBattle::getServerCallback()
 {
 {

+ 4 - 0
AI/BattleAI/StackWithBonuses.h

@@ -136,7 +136,9 @@ public:
 
 
 	int64_t getTreeVersion() const;
 	int64_t getTreeVersion() const;
 
 
+#if SCRIPTING_ENABLED
 	scripting::Pool * getContextPool() const override;
 	scripting::Pool * getContextPool() const override;
+#endif
 
 
 	ServerCallback * getServerCallback();
 	ServerCallback * getServerCallback();
 
 
@@ -189,6 +191,8 @@ private:
 	std::unique_ptr<HypotheticServerCallback> serverCallback;
 	std::unique_ptr<HypotheticServerCallback> serverCallback;
 	std::unique_ptr<HypotheticEnvironment> localEnvironment;
 	std::unique_ptr<HypotheticEnvironment> localEnvironment;
 
 
+#if SCRIPTING_ENABLED
 	mutable std::shared_ptr<scripting::Pool> pool;
 	mutable std::shared_ptr<scripting::Pool> pool;
+#endif
 	mutable std::shared_ptr<events::EventBus> eventBus;
 	mutable std::shared_ptr<events::EventBus> eventBus;
 };
 };

+ 20 - 16
AI/Nullkiller/AIUtility.h

@@ -205,12 +205,14 @@ void foreach_tile_pos(const Func & foo)
 	// some micro-optimizations since this function gets called a LOT
 	// some micro-optimizations since this function gets called a LOT
 	// callback pointer is thread-specific and slow to retrieve -> read map size only once
 	// callback pointer is thread-specific and slow to retrieve -> read map size only once
 	int3 mapSize = cb->getMapSize();
 	int3 mapSize = cb->getMapSize();
-	for(int i = 0; i < mapSize.x; i++)
+	for(int z = 0; z < mapSize.z; z++)
 	{
 	{
-		for(int j = 0; j < mapSize.y; j++)
+		for(int x = 0; x < mapSize.x; x++)
 		{
 		{
-			for(int k = 0; k < mapSize.z; k++)
-				foo(int3(i, j, k));
+			for(int y = 0; y < mapSize.y; y++)
+			{
+				foo(int3(x, y, z));
+			}
 		}
 		}
 	}
 	}
 }
 }
@@ -219,12 +221,14 @@ template<class Func>
 void foreach_tile_pos(CCallback * cbp, const Func & foo) // avoid costly retrieval of thread-specific pointer
 void foreach_tile_pos(CCallback * cbp, const Func & foo) // avoid costly retrieval of thread-specific pointer
 {
 {
 	int3 mapSize = cbp->getMapSize();
 	int3 mapSize = cbp->getMapSize();
-	for(int i = 0; i < mapSize.x; i++)
+	for(int z = 0; z < mapSize.z; z++)
 	{
 	{
-		for(int j = 0; j < mapSize.y; j++)
+		for(int x = 0; x < mapSize.x; x++)
 		{
 		{
-			for(int k = 0; k < mapSize.z; k++)
-				foo(cbp, int3(i, j, k));
+			for(int y = 0; y < mapSize.y; y++)
+			{
+				foo(cbp, int3(x, y, z));
+			}
 		}
 		}
 	}
 	}
 }
 }
@@ -276,21 +280,21 @@ bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObject
 template<typename TFunc>
 template<typename TFunc>
 void pforeachTilePos(crint3 mapSize, TFunc fn)
 void pforeachTilePos(crint3 mapSize, TFunc fn)
 {
 {
-	parallel_for(blocked_range<size_t>(0, mapSize.x), [&](const blocked_range<size_t>& r)
+	for(int z = 0; z < mapSize.z; ++z)
 	{
 	{
-		int3 pos;
-
-		for(pos.x = r.begin(); pos.x != r.end(); ++pos.x)
+		parallel_for(blocked_range<size_t>(0, mapSize.x), [&](const blocked_range<size_t>& r)
 		{
 		{
-			for(pos.y = 0; pos.y < mapSize.y; ++pos.y)
+			int3 pos(0, 0, z);
+
+			for(pos.x = r.begin(); pos.x != r.end(); ++pos.x)
 			{
 			{
-				for(pos.z = 0; pos.z < mapSize.z; ++pos.z)
+				for(pos.y = 0; pos.y < mapSize.y; ++pos.y)
 				{
 				{
 					fn(pos);
 					fn(pos);
 				}
 				}
 			}
 			}
-		}
-	});
+		});
+	}
 }
 }
 
 
 class CDistanceSorter
 class CDistanceSorter

+ 19 - 18
AI/Nullkiller/Pathfinding/AINodeStorage.cpp

@@ -39,7 +39,7 @@ AISharedStorage::AISharedStorage(int3 sizes)
 {
 {
 	if(!shared){
 	if(!shared){
 		shared.reset(new boost::multi_array<AIPathNode, 5>(
 		shared.reset(new boost::multi_array<AIPathNode, 5>(
-			boost::extents[sizes.x][sizes.y][sizes.z][EPathfindingLayer::NUM_LAYERS][NUM_CHAINS]));
+			boost::extents[EPathfindingLayer::NUM_LAYERS][sizes.z][sizes.x][sizes.y][NUM_CHAINS]));
 	}
 	}
 
 
 	nodes = shared;
 	nodes = shared;
@@ -69,40 +69,41 @@ void AINodeStorage::initialize(const PathfinderOptions & options, const CGameSta
 
 
 	//TODO: fix this code duplication with NodeStorage::initialize, problem is to keep `resetTile` inline
 	//TODO: fix this code duplication with NodeStorage::initialize, problem is to keep `resetTile` inline
 	const PlayerColor fowPlayer = ai->playerID;
 	const PlayerColor fowPlayer = ai->playerID;
-	const auto & fow = static_cast<const CGameInfoCallback *>(gs)->getPlayerTeam(fowPlayer)->fogOfWarMap;
+	const auto fow = static_cast<const CGameInfoCallback *>(gs)->getPlayerTeam(fowPlayer)->fogOfWarMap;
 	const int3 sizes = gs->getMapSize();
 	const int3 sizes = gs->getMapSize();
 
 
+	//Each thread gets different x, but an array of y located next to each other in memory
+
 	parallel_for(blocked_range<size_t>(0, sizes.x), [&](const blocked_range<size_t>& r)
 	parallel_for(blocked_range<size_t>(0, sizes.x), [&](const blocked_range<size_t>& r)
 	{
 	{
-		//make 200% sure that these are loop invariants (also a bit shorter code), let compiler do the rest(loop unswitching)
-		const bool useFlying = options.useFlying;
-		const bool useWaterWalking = options.useWaterWalking;
-		const PlayerColor player = playerID;
-
 		int3 pos;
 		int3 pos;
 
 
-		for(pos.x = r.begin(); pos.x != r.end(); ++pos.x)
+		for(pos.z = 0; pos.z < sizes.z; ++pos.z)
 		{
 		{
-			for(pos.y = 0; pos.y < sizes.y; ++pos.y)
+			const bool useFlying = options.useFlying;
+			const bool useWaterWalking = options.useWaterWalking;
+			const PlayerColor player = playerID;
+
+			for(pos.x = r.begin(); pos.x != r.end(); ++pos.x)
 			{
 			{
-				for(pos.z = 0; pos.z < sizes.z; ++pos.z)
+				for(pos.y = 0; pos.y < sizes.y; ++pos.y)
 				{
 				{
-					const TerrainTile * tile = &gs->map->getTile(pos);
-					if(!tile->terType.isPassable())
+					const TerrainTile* tile = &gs->map->getTile(pos);
+					if (!tile->terType.isPassable())
 						continue;
 						continue;
-					
-					if(tile->terType.isWater())
+
+					if (tile->terType.isWater())
 					{
 					{
 						resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, tile, fow, player, gs));
 						resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, tile, fow, player, gs));
-						if(useFlying)
+						if (useFlying)
 							resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
 							resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
-						if(useWaterWalking)
+						if (useWaterWalking)
 							resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility<ELayer::WATER>(pos, tile, fow, player, gs));
 							resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility<ELayer::WATER>(pos, tile, fow, player, gs));
 					}
 					}
 					else
 					else
 					{
 					{
 						resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility<ELayer::LAND>(pos, tile, fow, player, gs));
 						resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility<ELayer::LAND>(pos, tile, fow, player, gs));
-						if(useFlying)
+						if (useFlying)
 							resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
 							resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
 					}
 					}
 				}
 				}
@@ -140,7 +141,7 @@ boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(
 {
 {
 	int bucketIndex = ((uintptr_t)actor) % BUCKET_COUNT;
 	int bucketIndex = ((uintptr_t)actor) % BUCKET_COUNT;
 	int bucketOffset = bucketIndex * BUCKET_SIZE;
 	int bucketOffset = bucketIndex * BUCKET_SIZE;
-	auto chains = nodes.get(pos, layer);
+	auto chains = nodes.get(pos, layer); //FIXME: chain was the innermost layer
 
 
 	if(chains[0].blocked())
 	if(chains[0].blocked())
 	{
 	{

+ 4 - 8
AI/Nullkiller/Pathfinding/AINodeStorage.h

@@ -120,23 +120,19 @@ enum EHeroChainPass
 
 
 class AISharedStorage
 class AISharedStorage
 {
 {
-	/// 1-3 - position on map, 4 - layer (air, water, land), 5 - chain (normal, battle, spellcast and combinations)
+	// 1 - layer (air, water, land)
+	// 2-4 - position on map[z][x][y]
+	// 5 - chain (normal, battle, spellcast and combinations)
 	static std::shared_ptr<boost::multi_array<AIPathNode, 5>> shared;
 	static std::shared_ptr<boost::multi_array<AIPathNode, 5>> shared;
 	std::shared_ptr<boost::multi_array<AIPathNode, 5>> nodes;
 	std::shared_ptr<boost::multi_array<AIPathNode, 5>> nodes;
 public:
 public:
 	AISharedStorage(int3 mapSize);
 	AISharedStorage(int3 mapSize);
 	~AISharedStorage();
 	~AISharedStorage();
 
 
-	/*STRONG_INLINE
-	boost::detail::multi_array::sub_array<AIPathNode, 1> get(int3 tile, EPathfindingLayer layer)
-	{
-		return (*nodes)[tile.x][tile.y][tile.z][layer];
-	}*/
-
 	STRONG_INLINE
 	STRONG_INLINE
 	boost::detail::multi_array::sub_array<AIPathNode, 1> get(int3 tile, EPathfindingLayer layer) const
 	boost::detail::multi_array::sub_array<AIPathNode, 1> get(int3 tile, EPathfindingLayer layer) const
 	{
 	{
-		return (*nodes)[tile.x][tile.y][tile.z][layer];
+		return (*nodes)[layer][tile.z][tile.x][tile.y];
 	}
 	}
 };
 };
 
 

+ 0 - 49
AI/VCAI/AIUtility.cpp

@@ -137,55 +137,6 @@ bool HeroPtr::operator==(const HeroPtr & rhs) const
 	return h == rhs.get(true);
 	return h == rhs.get(true);
 }
 }
 
 
-void foreach_tile_pos(std::function<void(const int3 & pos)> foo)
-{
-	// some micro-optimizations since this function gets called a LOT
-	// callback pointer is thread-specific and slow to retrieve -> read map size only once
-	int3 mapSize = cb->getMapSize();
-	for(int i = 0; i < mapSize.x; i++)
-	{
-		for(int j = 0; j < mapSize.y; j++)
-		{
-			for(int k = 0; k < mapSize.z; k++)
-				foo(int3(i, j, k));
-		}
-	}
-}
-
-void foreach_tile_pos(CCallback * cbp, std::function<void(CCallback * cbp, const int3 & pos)> foo)
-{
-	int3 mapSize = cbp->getMapSize();
-	for(int i = 0; i < mapSize.x; i++)
-	{
-		for(int j = 0; j < mapSize.y; j++)
-		{
-			for(int k = 0; k < mapSize.z; k++)
-				foo(cbp, int3(i, j, k));
-		}
-	}
-}
-
-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 : int3::getDirs())
-	{
-		const int3 n = pos + dir;
-		if(cbp->isInTheMap(n))
-			foo(pos + dir);
-	}
-}
-
-void foreach_neighbour(CCallback * cbp, const int3 & pos, std::function<void(CCallback * cbp, const int3 & pos)> foo)
-{
-	for(const int3 & dir : int3::getDirs())
-	{
-		const int3 n = pos + dir;
-		if(cbp->isInTheMap(n))
-			foo(cbp, pos + dir);
-	}
-}
-
 bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs) const
 bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs) const
 {
 {
 	const CGPathNode * ln = ai->myCb->getPathsInfo(hero)->getPathInfo(lhs->visitablePos());
 	const CGPathNode * ln = ai->myCb->getPathsInfo(hero)->getPathInfo(lhs->visitablePos());

+ 59 - 4
AI/VCAI/AIUtility.h

@@ -18,6 +18,7 @@
 #include "../../lib/mapObjects/CObjectHandler.h"
 #include "../../lib/mapObjects/CObjectHandler.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/CPathfinder.h"
 #include "../../lib/CPathfinder.h"
+#include "../../CCallback.h"
 
 
 class CCallback;
 class CCallback;
 struct creInfo;
 struct creInfo;
@@ -34,6 +35,8 @@ const int ALLOWED_ROAMING_HEROES = 8;
 extern const double SAFE_ATTACK_CONSTANT;
 extern const double SAFE_ATTACK_CONSTANT;
 extern const int GOLD_RESERVE;
 extern const int GOLD_RESERVE;
 
 
+extern boost::thread_specific_ptr<CCallback> cb;
+
 //provisional class for AI to store a reference to an owned hero object
 //provisional class for AI to store a reference to an owned hero object
 //checks if it's valid on access, should be used in place of const CGHeroInstance*
 //checks if it's valid on access, should be used in place of const CGHeroInstance*
 
 
@@ -154,10 +157,62 @@ struct creInfo
 };
 };
 creInfo infoFromDC(const dwellingContent & dc);
 creInfo infoFromDC(const dwellingContent & dc);
 
 
-void foreach_tile_pos(std::function<void(const int3 & pos)> foo);
-void foreach_tile_pos(CCallback * cbp, std::function<void(CCallback * cbp, const int3 & pos)> foo); // avoid costly retrieval of thread-specific pointer
-void foreach_neighbour(const int3 & pos, std::function<void(const int3 & pos)> foo);
-void foreach_neighbour(CCallback * cbp, const int3 & pos, std::function<void(CCallback * cbp, const int3 & pos)> foo); // avoid costly retrieval of thread-specific pointer
+template<class Func>
+void foreach_tile_pos(const Func & foo)
+{
+	// some micro-optimizations since this function gets called a LOT
+	// callback pointer is thread-specific and slow to retrieve -> read map size only once
+	int3 mapSize = cb->getMapSize();
+	for(int z = 0; z < mapSize.z; z++)
+	{
+		for(int x = 0; x < mapSize.x; x++)
+		{
+			for(int y = 0; y < mapSize.y; y++)
+			{
+				foo(int3(x, y, z));
+			}
+		}
+	}
+}
+
+template<class Func>
+void foreach_tile_pos(CCallback * cbp, const Func & foo) // avoid costly retrieval of thread-specific pointer
+{
+	int3 mapSize = cbp->getMapSize();
+	for(int z = 0; z < mapSize.z; z++)
+	{
+		for(int x = 0; x < mapSize.x; x++)
+		{
+			for(int y = 0; y < mapSize.y; y++)
+			{
+				foo(cbp, int3(x, y, z));
+			}
+		}
+	}
+}
+
+template<class Func>
+void foreach_neighbour(const int3 & pos, const Func & foo)
+{
+	CCallback * cbp = cb.get(); // avoid costly retrieval of thread-specific pointer
+	for(const int3 & dir : int3::getDirs())
+	{
+		const int3 n = pos + dir;
+		if(cbp->isInTheMap(n))
+			foo(pos + dir);
+	}
+}
+
+template<class Func>
+void foreach_neighbour(CCallback * cbp, const int3 & pos, const Func & foo) // avoid costly retrieval of thread-specific pointer
+{
+	for(const int3 & dir : int3::getDirs())
+	{
+		const int3 n = pos + dir;
+		if(cbp->isInTheMap(n))
+			foo(cbp, pos + dir);
+	}
+}
 
 
 bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater);
 bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater);
 bool isBlockedBorderGate(int3 tileToHit);
 bool isBlockedBorderGate(int3 tileToHit);

+ 0 - 4
AI/VCAI/CMakeLists.txt

@@ -17,8 +17,6 @@ set(VCAI_SRCS
 		ArmyManager.cpp
 		ArmyManager.cpp
 		ResourceManager.cpp
 		ResourceManager.cpp
 		BuildingManager.cpp
 		BuildingManager.cpp
-		SectorMap.cpp
-		BuildingManager.cpp
 		MapObjectsEvaluator.cpp
 		MapObjectsEvaluator.cpp
 		FuzzyEngines.cpp
 		FuzzyEngines.cpp
 		FuzzyHelper.cpp
 		FuzzyHelper.cpp
@@ -68,8 +66,6 @@ set(VCAI_HEADERS
 		ArmyManager.h
 		ArmyManager.h
 		ResourceManager.h
 		ResourceManager.h
 		BuildingManager.h
 		BuildingManager.h
-		SectorMap.h
-		BuildingManager.h
 		MapObjectsEvaluator.h
 		MapObjectsEvaluator.h
 		FuzzyEngines.h
 		FuzzyEngines.h
 		FuzzyHelper.h
 		FuzzyHelper.h

+ 1 - 0
AI/VCAI/Goals/AbstractGoal.h

@@ -18,6 +18,7 @@
 struct HeroPtr;
 struct HeroPtr;
 class VCAI;
 class VCAI;
 class FuzzyHelper;
 class FuzzyHelper;
+class CCallback;
 
 
 namespace Goals
 namespace Goals
 {
 {

+ 18 - 13
AI/VCAI/Goals/Explore.cpp

@@ -57,13 +57,16 @@ namespace Goals
 
 
 		void scanSector(int scanRadius)
 		void scanSector(int scanRadius)
 		{
 		{
-			for(int x = ourPos.x - scanRadius; x <= ourPos.x + scanRadius; x++)
+			int3 tile = int3(0, 0, ourPos.z);
+
+			const auto & slice = (*(ts->fogOfWarMap))[ourPos.z];
+
+			for(tile.x = ourPos.x - scanRadius; tile.x <= ourPos.x + scanRadius; tile.x++)
 			{
 			{
-				for(int y = ourPos.y - scanRadius; y <= ourPos.y + scanRadius; y++)
+				for(tile.y = ourPos.y - scanRadius; tile.y <= ourPos.y + scanRadius; tile.y++)
 				{
 				{
-					int3 tile = int3(x, y, ourPos.z);
 
 
-					if(cbp->isInTheMap(tile) && ts->fogOfWarMap[tile.x][tile.y][tile.z])
+					if(cbp->isInTheMap(tile) && slice[tile.x][tile.y])
 					{
 					{
 						scanTile(tile);
 						scanTile(tile);
 					}
 					}
@@ -84,13 +87,13 @@ namespace Goals
 
 
 			foreach_tile_pos([&](const int3 & pos)
 			foreach_tile_pos([&](const int3 & pos)
 			{
 			{
-				if(ts->fogOfWarMap[pos.x][pos.y][pos.z])
+				if((*(ts->fogOfWarMap))[pos.z][pos.x][pos.y])
 				{
 				{
 					bool hasInvisibleNeighbor = false;
 					bool hasInvisibleNeighbor = false;
 
 
 					foreach_neighbour(cbp, pos, [&](CCallback * cbp, int3 neighbour)
 					foreach_neighbour(cbp, pos, [&](CCallback * cbp, int3 neighbour)
 					{
 					{
-						if(!ts->fogOfWarMap[neighbour.x][neighbour.y][neighbour.z])
+						if(!(*(ts->fogOfWarMap))[neighbour.z][neighbour.x][neighbour.y])
 						{
 						{
 							hasInvisibleNeighbor = true;
 							hasInvisibleNeighbor = true;
 						}
 						}
@@ -174,7 +177,7 @@ namespace Goals
 			{
 			{
 				foreach_neighbour(cbp, tile, [&](CCallback * cbp, int3 neighbour)
 				foreach_neighbour(cbp, tile, [&](CCallback * cbp, int3 neighbour)
 				{
 				{
-					if(ts->fogOfWarMap[neighbour.x][neighbour.y][neighbour.z])
+					if((*(ts->fogOfWarMap))[neighbour.z][neighbour.x][neighbour.y])
 					{
 					{
 						out.push_back(neighbour);
 						out.push_back(neighbour);
 					}
 					}
@@ -182,18 +185,20 @@ namespace Goals
 			}
 			}
 		}
 		}
 
 
-		int howManyTilesWillBeDiscovered(
-			const int3 & pos) const
+		int howManyTilesWillBeDiscovered(const int3 & pos) const
 		{
 		{
 			int ret = 0;
 			int ret = 0;
-			for(int x = pos.x - sightRadius; x <= pos.x + sightRadius; x++)
+			int3 npos = int3(0, 0, pos.z);
+
+			const auto & slice = (*(ts->fogOfWarMap))[pos.z];
+
+			for(npos.x = pos.x - sightRadius; npos.x <= pos.x + sightRadius; npos.x++)
 			{
 			{
-				for(int y = pos.y - sightRadius; y <= pos.y + sightRadius; y++)
+				for(npos.y = pos.y - sightRadius; npos.y <= pos.y + sightRadius; npos.y++)
 				{
 				{
-					int3 npos = int3(x, y, pos.z);
 					if(cbp->isInTheMap(npos)
 					if(cbp->isInTheMap(npos)
 						&& pos.dist2d(npos) - 0.5 < sightRadius
 						&& pos.dist2d(npos) - 0.5 < sightRadius
-						&& !ts->fogOfWarMap[npos.x][npos.y][npos.z])
+						&& !slice[npos.x][npos.y])
 					{
 					{
 						if(allowDeadEndCancellation
 						if(allowDeadEndCancellation
 							&& !hasReachableNeighbor(npos))
 							&& !hasReachableNeighbor(npos))

+ 9 - 11
AI/VCAI/Pathfinding/AINodeStorage.cpp

@@ -20,7 +20,7 @@
 AINodeStorage::AINodeStorage(const int3 & Sizes)
 AINodeStorage::AINodeStorage(const int3 & Sizes)
 	: sizes(Sizes)
 	: sizes(Sizes)
 {
 {
-	nodes.resize(boost::extents[sizes.x][sizes.y][sizes.z][EPathfindingLayer::NUM_LAYERS][NUM_CHAINS]);
+	nodes.resize(boost::extents[EPathfindingLayer::NUM_LAYERS][sizes.z][sizes.x][sizes.y][NUM_CHAINS]);
 	dangerEvaluator.reset(new FuzzyHelper());
 	dangerEvaluator.reset(new FuzzyHelper());
 }
 }
 
 
@@ -28,8 +28,6 @@ AINodeStorage::~AINodeStorage() = default;
 
 
 void AINodeStorage::initialize(const PathfinderOptions & options, const CGameState * gs)
 void AINodeStorage::initialize(const PathfinderOptions & options, const CGameState * gs)
 {
 {
-	//TODO: fix this code duplication with NodeStorage::initialize, problem is to keep `resetTile` inline
-
 	int3 pos;
 	int3 pos;
 	const int3 sizes = gs->getMapSize();
 	const int3 sizes = gs->getMapSize();
 	const auto & fow = static_cast<const CGameInfoCallback *>(gs)->getPlayerTeam(hero->tempOwner)->fogOfWarMap;
 	const auto & fow = static_cast<const CGameInfoCallback *>(gs)->getPlayerTeam(hero->tempOwner)->fogOfWarMap;
@@ -39,11 +37,11 @@ void AINodeStorage::initialize(const PathfinderOptions & options, const CGameSta
 	const bool useFlying = options.useFlying;
 	const bool useFlying = options.useFlying;
 	const bool useWaterWalking = options.useWaterWalking;
 	const bool useWaterWalking = options.useWaterWalking;
 
 
-	for(pos.x=0; pos.x < sizes.x; ++pos.x)
+	for(pos.z=0; pos.z < sizes.z; ++pos.z)
 	{
 	{
-		for(pos.y=0; pos.y < sizes.y; ++pos.y)
+		for(pos.x=0; pos.x < sizes.x; ++pos.x)
 		{
 		{
-			for(pos.z=0; pos.z < sizes.z; ++pos.z)
+			for(pos.y=0; pos.y < sizes.y; ++pos.y)
 			{
 			{
 				const TerrainTile * tile = &gs->map->getTile(pos);
 				const TerrainTile * tile = &gs->map->getTile(pos);
 				if(!tile->terType.isPassable())
 				if(!tile->terType.isPassable())
@@ -87,7 +85,7 @@ bool AINodeStorage::isBattleNode(const CGPathNode * node) const
 
 
 boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(const int3 & pos, const EPathfindingLayer layer, int chainNumber)
 boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(const int3 & pos, const EPathfindingLayer layer, int chainNumber)
 {
 {
-	auto chains = nodes[pos.x][pos.y][pos.z][layer];
+	auto chains = nodes[layer][pos.z][pos.x][pos.y];
 
 
 	for(AIPathNode & node : chains)
 	for(AIPathNode & node : chains)
 	{
 	{
@@ -126,7 +124,7 @@ void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, CGPat
 {
 {
 	for(int i = 0; i < NUM_CHAINS; i++)
 	for(int i = 0; i < NUM_CHAINS; i++)
 	{
 	{
-		AIPathNode & heroNode = nodes[coord.x][coord.y][coord.z][layer][i];
+		AIPathNode & heroNode = nodes[layer][coord.z][coord.x][coord.y][i];
 
 
 		heroNode.chainMask = 0;
 		heroNode.chainMask = 0;
 		heroNode.danger = 0;
 		heroNode.danger = 0;
@@ -290,7 +288,7 @@ void AINodeStorage::calculateTownPortalTeleportations(
 bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const
 bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const
 {
 {
 	auto pos = destination.coord;
 	auto pos = destination.coord;
-	auto chains = nodes[pos.x][pos.y][pos.z][EPathfindingLayer::LAND];
+	auto chains = nodes[EPathfindingLayer::LAND][pos.z][pos.x][pos.y];
 	auto destinationNode = getAINode(destination.node);
 	auto destinationNode = getAINode(destination.node);
 
 
 	for(const AIPathNode & node : chains)
 	for(const AIPathNode & node : chains)
@@ -323,7 +321,7 @@ bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNode
 
 
 bool AINodeStorage::isTileAccessible(const int3 & pos, const EPathfindingLayer layer) const
 bool AINodeStorage::isTileAccessible(const int3 & pos, const EPathfindingLayer layer) const
 {
 {
-	const AIPathNode & node = nodes[pos.x][pos.y][pos.z][layer][0];
+	const AIPathNode & node = nodes[layer][pos.z][pos.x][pos.y][0];
 
 
 	return node.action != CGPathNode::ENodeAction::UNKNOWN;
 	return node.action != CGPathNode::ENodeAction::UNKNOWN;
 }
 }
@@ -331,7 +329,7 @@ bool AINodeStorage::isTileAccessible(const int3 & pos, const EPathfindingLayer l
 std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand) const
 std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand) const
 {
 {
 	std::vector<AIPath> paths;
 	std::vector<AIPath> paths;
-	auto chains = nodes[pos.x][pos.y][pos.z][isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL];
+	auto chains = nodes[isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL][pos.z][pos.x][pos.y];
 	auto initialPos = hero->visitablePos();
 	auto initialPos = hero->visitablePos();
 
 
 	for(const AIPathNode & node : chains)
 	for(const AIPathNode & node : chains)

+ 7 - 1
AI/VCAI/Pathfinding/AINodeStorage.h

@@ -17,6 +17,10 @@
 #include "../Goals/AbstractGoal.h"
 #include "../Goals/AbstractGoal.h"
 #include "Actions/ISpecialAction.h"
 #include "Actions/ISpecialAction.h"
 
 
+class CCallback;
+
+extern boost::thread_specific_ptr<CCallback> cb; //for templates
+
 struct AIPathNode : public CGPathNode
 struct AIPathNode : public CGPathNode
 {
 {
 	uint32_t chainMask;
 	uint32_t chainMask;
@@ -57,7 +61,9 @@ class AINodeStorage : public INodeStorage
 private:
 private:
 	int3 sizes;
 	int3 sizes;
 
 
-	/// 1-3 - position on map, 4 - layer (air, water, land), 5 - chain (normal, battle, spellcast and combinations)
+	// 1 - layer (air, water, land)
+	// 2-4 - position on map[z][x][y]
+	// 5 - chain (normal, battle, spellcast and combinations)
 	boost::multi_array<AIPathNode, 5> nodes;
 	boost::multi_array<AIPathNode, 5> nodes;
 	const CPlayerSpecificInfoCallback * cb;
 	const CPlayerSpecificInfoCallback * cb;
 	const VCAI * ai;
 	const VCAI * ai;

+ 0 - 431
AI/VCAI/SectorMap.cpp

@@ -1,431 +0,0 @@
-/*
-* SectorMap.cpp, 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 "StdInc.h"
-#include "SectorMap.h"
-#include "VCAI.h"
-
-#include "../../CCallback.h"
-#include "../../lib/mapping/CMap.h"
-#include "../../lib/mapObjects/MapObjects.h"
-#include "../../lib/CPathfinder.h"
-#include "../../lib/CGameState.h"
-
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-
-SectorMap::SectorMap()
-{
-	update();
-}
-
-SectorMap::SectorMap(HeroPtr h)
-{
-	update();
-	makeParentBFS(h->visitablePos());
-}
-
-bool SectorMap::markIfBlocked(TSectorID & sec, crint3 pos, const TerrainTile * t)
-{
-	if (t->blocked && !t->visitable)
-	{
-		sec = NOT_AVAILABLE;
-		return true;
-	}
-
-	return false;
-}
-
-bool SectorMap::markIfBlocked(TSectorID & sec, crint3 pos)
-{
-	return markIfBlocked(sec, pos, getTile(pos));
-}
-
-void SectorMap::update()
-{
-	visibleTiles = cb->getAllVisibleTiles();
-	auto shape = visibleTiles->shape();
-	sector.resize(boost::extents[shape[0]][shape[1]][shape[2]]);
-
-	clear();
-	int curSector = 3; //0 is invisible, 1 is not explored
-
-	CCallback * cbp = cb.get(); //optimization
-	foreach_tile_pos([&](crint3 pos)
-	{
-		if (retrieveTile(pos) == NOT_CHECKED)
-		{
-			if (!markIfBlocked(retrieveTile(pos), pos))
-				exploreNewSector(pos, curSector++, cbp);
-		}
-	});
-	valid = true;
-}
-
-SectorMap::TSectorID & SectorMap::retrieveTileN(SectorMap::TSectorArray & a, const int3 & pos)
-{
-	return a[pos.x][pos.y][pos.z];
-}
-
-const SectorMap::TSectorID & SectorMap::retrieveTileN(const SectorMap::TSectorArray & a, const int3 & pos)
-{
-	return a[pos.x][pos.y][pos.z];
-}
-
-void SectorMap::clear()
-{
-	//TODO: rotate to [z][x][y]
-	auto fow = cb->getVisibilityMap();
-	//TODO: any magic to automate this? will need array->array conversion
-	//std::transform(fow.begin(), fow.end(), sector.begin(), [](const ui8 &f) -> unsigned short
-	//{
-	//	return f; //type conversion
-	//});
-	auto width = fow.size();
-	auto height = fow.front().size();
-	auto depth = fow.front().front().size();
-	for (size_t x = 0; x < width; x++)
-	{
-		for (size_t y = 0; y < height; y++)
-		{
-			for (size_t z = 0; z < depth; z++)
-				sector[x][y][z] = fow[x][y][z];
-		}
-	}
-	valid = false;
-}
-
-void SectorMap::exploreNewSector(crint3 pos, int num, CCallback * cbp)
-{
-	Sector & s = infoOnSectors[num];
-	s.id = num;
-	s.water = getTile(pos)->isWater();
-
-	std::queue<int3> toVisit;
-	toVisit.push(pos);
-	while (!toVisit.empty())
-	{
-		int3 curPos = toVisit.front();
-		toVisit.pop();
-		TSectorID & sec = retrieveTile(curPos);
-		if (sec == NOT_CHECKED)
-		{
-			const TerrainTile * t = getTile(curPos);
-			if (!markIfBlocked(sec, curPos, t))
-			{
-				if (t->isWater() == s.water) //sector is only-water or only-land
-				{
-					sec = num;
-					s.tiles.push_back(curPos);
-					foreach_neighbour(cbp, curPos, [&](CCallback * cbp, crint3 neighPos)
-					{
-						if (retrieveTile(neighPos) == NOT_CHECKED)
-						{
-							toVisit.push(neighPos);
-							//parent[neighPos] = curPos;
-						}
-						const TerrainTile * nt = getTile(neighPos);
-						if (nt && nt->isWater() != s.water && canBeEmbarkmentPoint(nt, s.water))
-						{
-							s.embarkmentPoints.push_back(neighPos);
-						}
-					});
-
-					if (t->visitable)
-					{
-						auto obj = t->visitableObjects.front();
-						if (cb->getObj(obj->id, false)) // FIXME: we have to filter invisible objcts like events, but probably TerrainTile shouldn't be used in SectorMap at all
-							s.visitableObjs.push_back(obj);
-					}
-				}
-			}
-		}
-	}
-
-	vstd::removeDuplicates(s.embarkmentPoints);
-}
-
-void SectorMap::write(crstring fname)
-{
-	std::ofstream out(fname);
-	for (int k = 0; k < cb->getMapSize().z; k++)
-	{
-		for (int j = 0; j < cb->getMapSize().y; j++)
-		{
-			for (int i = 0; i < cb->getMapSize().x; i++)
-			{
-				out << (int)sector[i][j][k] << '\t';
-			}
-			out << std::endl;
-		}
-		out << std::endl;
-	}
-}
-
-/*
-this functions returns one target tile or invalid tile. We will use it to poll possible destinations
-For ship construction etc, another function (goal?) is needed
-*/
-int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst)
-{
-	int3 ret(-1, -1, -1);
-
-	int sourceSector = retrieveTile(h->visitablePos());
-	int destinationSector = retrieveTile(dst);
-
-	const Sector * src = &infoOnSectors[sourceSector];
-	const Sector * dest = &infoOnSectors[destinationSector];
-
-	if (sourceSector != destinationSector) //use ships, shipyards etc..
-	{
-		if (ai->isAccessibleForHero(dst, h)) //pathfinder can find a way using ships and gates if tile is not blocked by objects
-			return dst;
-
-		std::map<const Sector *, const Sector *> preds;
-		std::queue<const Sector *> sectorQueue;
-		sectorQueue.push(src);
-		while (!sectorQueue.empty())
-		{
-			const Sector * s = sectorQueue.front();
-			sectorQueue.pop();
-
-			for (int3 ep : s->embarkmentPoints)
-			{
-				Sector * neigh = &infoOnSectors[retrieveTile(ep)];
-				//preds[s].push_back(neigh);
-				if (!preds[neigh])
-				{
-					preds[neigh] = s;
-					sectorQueue.push(neigh);
-				}
-			}
-		}
-
-		if (!preds[dest])
-		{
-			//write("test.txt");
-
-			return ret;
-			//throw cannotFulfillGoalException(boost::str(boost::format("Cannot find connection between sectors %d and %d") % src->id % dst->id));
-		}
-
-		std::vector<const Sector *> toTraverse;
-		toTraverse.push_back(dest);
-		while (toTraverse.back() != src)
-		{
-			toTraverse.push_back(preds[toTraverse.back()]);
-		}
-
-		if (preds[dest])
-		{
-			//TODO: would be nice to find sectors in loop
-			const Sector * sectorToReach = toTraverse.at(toTraverse.size() - 2);
-
-			if (!src->water && sectorToReach->water) //embark
-			{
-				//embark on ship -> look for an EP with a boat
-				auto firstEP = boost::find_if(src->embarkmentPoints, [=](crint3 pos) -> bool
-				{
-					const TerrainTile * t = getTile(pos);
-					if (t && t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT)
-					{
-						if (retrieveTile(pos) == sectorToReach->id)
-							return true;
-					}
-					return false;
-				});
-
-				if (firstEP != src->embarkmentPoints.end())
-				{
-					return *firstEP;
-				}
-				else
-				{
-					//we need to find a shipyard with an access to the desired sector's EP
-					//TODO what about Summon Boat spell?
-					std::vector<const IShipyard *> shipyards;
-					for (const CGTownInstance * t : cb->getTownsInfo())
-					{
-						if (t->hasBuilt(BuildingID::SHIPYARD))
-							shipyards.push_back(t);
-					}
-
-					for (const CGObjectInstance * obj : ai->getFlaggedObjects())
-					{
-						if (obj->ID != Obj::TOWN) //towns were handled in the previous loop
-						{
-							if (const IShipyard * shipyard = IShipyard::castFrom(obj))
-								shipyards.push_back(shipyard);
-						}
-					}
-
-					shipyards.erase(boost::remove_if(shipyards, [=](const IShipyard * shipyard) -> bool
-					{
-						return shipyard->shipyardStatus() != 0 || retrieveTile(shipyard->bestLocation()) != sectorToReach->id;
-					}), shipyards.end());
-
-					if (!shipyards.size())
-					{
-						//TODO consider possibility of building shipyard in a town
-						return ret;
-
-						//throw cannotFulfillGoalException("There is no known shipyard!");
-					}
-
-					//we have only shipyards that possibly can build ships onto the appropriate EP
-					auto ownedGoodShipyard = boost::find_if(shipyards, [](const IShipyard * s) -> bool
-					{
-						return s->o->tempOwner == ai->playerID;
-					});
-
-					if (ownedGoodShipyard != shipyards.end())
-					{
-						const IShipyard * s = *ownedGoodShipyard;
-						TResources shipCost;
-						s->getBoatCost(shipCost);
-						if (cb->getResourceAmount().canAfford(shipCost))
-						{
-							int3 ret = s->bestLocation();
-							cb->buildBoat(s); //TODO: move actions elsewhere
-							return ret;
-						}
-						else
-						{
-							//TODO gather res
-							return ret;
-
-							//throw cannotFulfillGoalException("Not enough resources to build a boat");
-						}
-					}
-					else
-					{
-						//TODO pick best shipyard to take over
-						return shipyards.front()->o->visitablePos();
-					}
-				}
-			}
-			else if (src->water && !sectorToReach->water)
-			{
-				//TODO
-				//disembark
-				return ret;
-			}
-			else //use subterranean gates - not needed since gates are now handled via Pathfinder
-			{
-				return ret;
-				//throw cannotFulfillGoalException("Land-land and water-water inter-sector transitions are not implemented!");
-			}
-		}
-		else
-		{
-			return ret;
-			//throw cannotFulfillGoalException("Inter-sector route detection failed: not connected sectors?");
-		}
-	}
-	else //tiles are in same sector
-	{
-		return findFirstVisitableTile(h, dst);
-	}
-}
-
-int3 SectorMap::findFirstVisitableTile(HeroPtr h, crint3 dst)
-{
-	int3 ret(-1, -1, -1);
-	int3 curtile = dst;
-
-	while (curtile != h->visitablePos())
-	{
-		auto topObj = cb->getTopObj(curtile);
-		if (topObj && topObj->ID == Obj::HERO && topObj != h.h)
-		{
-			if (cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
-			{
-				logAi->warn("Another allied hero stands in our way");
-				return ret;
-			}
-		}
-		if (ai->myCb->getPathsInfo(h.get())->getPathInfo(curtile)->reachable())
-		{
-			return curtile;
-		}
-		else
-		{
-			auto i = parent.find(curtile);
-			if (i != parent.end())
-			{
-				assert(curtile != i->second);
-				curtile = i->second;
-			}
-			else
-			{
-				return ret;
-				//throw cannotFulfillGoalException("Unreachable tile in sector? Should not happen!");
-			}
-		}
-	}
-	return ret;
-}
-
-void SectorMap::makeParentBFS(crint3 source)
-{
-	parent.clear();
-
-	int mySector = retrieveTile(source);
-	std::queue<int3> toVisit;
-	toVisit.push(source);
-	while (!toVisit.empty())
-	{
-		int3 curPos = toVisit.front();
-		toVisit.pop();
-		TSectorID & sec = retrieveTile(curPos);
-		assert(sec == mySector); //consider only tiles from the same sector
-		UNUSED(sec);
-
-		foreach_neighbour(curPos, [&](crint3 neighPos)
-		{
-			if (retrieveTile(neighPos) == mySector && !vstd::contains(parent, neighPos))
-			{
-				if (cb->canMoveBetween(curPos, neighPos))
-				{
-					toVisit.push(neighPos);
-					parent[neighPos] = curPos;
-				}
-			}
-		});
-	}
-}
-
-SectorMap::TSectorID & SectorMap::retrieveTile(crint3 pos)
-{
-	return retrieveTileN(sector, pos);
-}
-
-TerrainTile * SectorMap::getTile(crint3 pos) const
-{
-	//out of bounds access should be handled by boost::multi_array
-	//still we cached this array to avoid any checks
-	return visibleTiles->operator[](pos.x)[pos.y][pos.z];
-}
-
-std::vector<const CGObjectInstance *> SectorMap::getNearbyObjs(HeroPtr h, bool sectorsAround)
-{
-	const Sector * heroSector = &infoOnSectors[retrieveTile(h->visitablePos())];
-	if (sectorsAround)
-	{
-		std::vector<const CGObjectInstance *> ret;
-		for (auto embarkPoint : heroSector->embarkmentPoints)
-		{
-			const Sector * embarkSector = &infoOnSectors[retrieveTile(embarkPoint)];
-			range::copy(embarkSector->visitableObjs, std::back_inserter(ret));
-		}
-		return ret;
-	}
-	return heroSector->visitableObjs;
-}

+ 0 - 70
AI/VCAI/SectorMap.h

@@ -1,70 +0,0 @@
-/*
-* SectorMap.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
-
-#include "AIUtility.h"
-
-enum
-{
-	NOT_VISIBLE = 0,
-	NOT_CHECKED = 1,
-	NOT_AVAILABLE
-};
-
-struct SectorMap
-{
-	//a sector is set of tiles that would be mutually reachable if all visitable objs would be passable (incl monsters)
-	struct Sector
-	{
-		int id;
-		std::vector<int3> tiles;
-		std::vector<int3> embarkmentPoints; //tiles of other sectors onto which we can (dis)embark
-		std::vector<const CGObjectInstance *> visitableObjs;
-		bool water; //all tiles of sector are land or water
-		Sector()
-		{
-			id = -1;
-			water = false;
-		}
-	};
-
-	typedef unsigned short TSectorID; //smaller than int to allow -1 value. Max number of sectors 65K should be enough for any proper map.
-	typedef boost::multi_array<TSectorID, 3> TSectorArray;
-
-	bool valid; //some kind of lazy eval
-	std::map<int3, int3> parent;
-	TSectorArray sector;
-	//std::vector<std::vector<std::vector<unsigned char>>> pathfinderSector;
-
-	std::map<int, Sector> infoOnSectors;
-	std::shared_ptr<boost::multi_array<TerrainTile *, 3>> visibleTiles;
-
-	SectorMap();
-	SectorMap(HeroPtr h);
-	void update();
-	void clear();
-	void exploreNewSector(crint3 pos, int num, CCallback * cbp);
-	void write(crstring fname);
-
-	bool markIfBlocked(TSectorID & sec, crint3 pos, const TerrainTile * t);
-	bool markIfBlocked(TSectorID & sec, crint3 pos);
-	TSectorID & retrieveTile(crint3 pos);
-	TSectorID & retrieveTileN(TSectorArray & vectors, const int3 & pos);
-	const TSectorID & retrieveTileN(const TSectorArray & vectors, const int3 & pos);
-	TerrainTile * getTile(crint3 pos) const;
-	std::vector<const CGObjectInstance *> getNearbyObjs(HeroPtr h, bool sectorsAround);
-
-	void makeParentBFS(crint3 source);
-
-	int3 firstTileToGet(HeroPtr h, crint3 dst); //if h wants to reach tile dst, which tile he should visit to clear the way?
-	int3 findFirstVisitableTile(HeroPtr h, crint3 dst);
-};

+ 3 - 1
CCallback.cpp

@@ -324,7 +324,7 @@ int3 CCallback::getGuardingCreaturePosition(int3 tile)
 	if (!gs->map->isInTheMap(tile))
 	if (!gs->map->isInTheMap(tile))
 		return int3(-1,-1,-1);
 		return int3(-1,-1,-1);
 
 
-	return gs->map->guardingCreaturePositions[tile.x][tile.y][tile.z];
+	return gs->map->guardingCreaturePositions[tile.z][tile.x][tile.y];
 }
 }
 
 
 void CCallback::calculatePaths( const CGHeroInstance *hero, CPathsInfo &out)
 void CCallback::calculatePaths( const CGHeroInstance *hero, CPathsInfo &out)
@@ -366,10 +366,12 @@ void CCallback::unregisterBattleInterface(std::shared_ptr<IBattleEventsReceiver>
 	cl->additionalBattleInts[*player] -= battleEvents;
 	cl->additionalBattleInts[*player] -= battleEvents;
 }
 }
 
 
+#if SCRIPTING_ENABLED
 scripting::Pool * CBattleCallback::getContextPool() const
 scripting::Pool * CBattleCallback::getContextPool() const
 {
 {
 	return cl->getGlobalContextPool();
 	return cl->getGlobalContextPool();
 }
 }
+#endif
 
 
 CBattleCallback::CBattleCallback(boost::optional<PlayerColor> Player, CClient *C )
 CBattleCallback::CBattleCallback(boost::optional<PlayerColor> Player, CClient *C )
 {
 {

+ 2 - 0
CCallback.h

@@ -99,7 +99,9 @@ public:
 	int battleMakeAction(const BattleAction * action) override;//for casting spells by hero - DO NOT use it for moving active stack
 	int battleMakeAction(const BattleAction * action) override;//for casting spells by hero - DO NOT use it for moving active stack
 	bool battleMakeTacticAction(BattleAction * action) override; // performs tactic phase actions
 	bool battleMakeTacticAction(BattleAction * action) override; // performs tactic phase actions
 
 
+#if SCRIPTING_ENABLED
 	scripting::Pool * getContextPool() const override;
 	scripting::Pool * getContextPool() const override;
+#endif
 
 
 	friend class CCallback;
 	friend class CCallback;
 	friend class CClient;
 	friend class CClient;

+ 12 - 3
CMakeLists.txt

@@ -41,8 +41,8 @@ set(VCMI_VERSION_MAJOR 1)
 set(VCMI_VERSION_MINOR 0)
 set(VCMI_VERSION_MINOR 0)
 set(VCMI_VERSION_PATCH 0)
 set(VCMI_VERSION_PATCH 0)
 
 
-option(ENABLE_ERM "Enable compilation of ERM scripting module" ON)
-option(ENABLE_LUA "Enable compilation of LUA scripting module" ON)
+option(ENABLE_ERM "Enable compilation of ERM scripting module" OFF)
+option(ENABLE_LUA "Enable compilation of LUA scripting module" OFF)
 option(ENABLE_LAUNCHER "Enable compilation of launcher" ON)
 option(ENABLE_LAUNCHER "Enable compilation of launcher" ON)
 option(ENABLE_EDITOR "Enable compilation of map editor" ON)
 option(ENABLE_EDITOR "Enable compilation of map editor" ON)
 option(ENABLE_TEST "Enable compilation of unit tests" ON)
 option(ENABLE_TEST "Enable compilation of unit tests" ON)
@@ -60,6 +60,11 @@ option(ENABLE_MONOLITHIC_INSTALL "Install everything in single directory on Linu
 set(PACKAGE_NAME_SUFFIX "" CACHE STRING "Suffix for CPack package name")
 set(PACKAGE_NAME_SUFFIX "" CACHE STRING "Suffix for CPack package name")
 set(PACKAGE_FILE_NAME "" CACHE STRING "Override for CPack package filename")
 set(PACKAGE_FILE_NAME "" CACHE STRING "Override for CPack package filename")
 
 
+# ERM depends on LUA implicitly
+if(ENABLE_ERM AND NOT ENABLE_LUA)
+	set(ENABLE_LUA ON)
+endif()
+
 ############################################
 ############################################
 #        Miscellaneous options             #
 #        Miscellaneous options             #
 ############################################
 ############################################
@@ -215,6 +220,10 @@ if(NOT WIN32)
 	endif()
 	endif()
 endif()
 endif()
 
 
+if(ENABLE_LUA)
+	add_compile_definitions(SCRIPTING_ENABLED=1)
+endif()
+
 ############################################
 ############################################
 #        Finding packages                  #
 #        Finding packages                  #
 ############################################
 ############################################
@@ -227,7 +236,7 @@ if(TARGET zlib::zlib)
 	add_library(ZLIB::ZLIB ALIAS zlib::zlib)
 	add_library(ZLIB::ZLIB ALIAS zlib::zlib)
 endif()
 endif()
 
 
-find_package(ffmpeg REQUIRED COMPONENTS avutil swscale avformat avcodec)
+find_package(ffmpeg COMPONENTS avutil swscale avformat avcodec)
 option(FORCE_BUNDLED_MINIZIP "Force bundled Minizip library" OFF)
 option(FORCE_BUNDLED_MINIZIP "Force bundled Minizip library" OFF)
 if(NOT FORCE_BUNDLED_MINIZIP)
 if(NOT FORCE_BUNDLED_MINIZIP)
 	find_package(minizip)
 	find_package(minizip)

+ 1 - 5
CMakePresets.json

@@ -79,11 +79,7 @@
             "name": "macos-arm-conan-ninja-release",
             "name": "macos-arm-conan-ninja-release",
             "displayName": "Ninja+Conan arm64 release",
             "displayName": "Ninja+Conan arm64 release",
             "description": "VCMI MacOS-arm64 Ninja using Conan",
             "description": "VCMI MacOS-arm64 Ninja using Conan",
-            "inherits": "macos-conan-ninja-release",
-            "cacheVariables": {
-                "ENABLE_ERM": "OFF",
-                "ENABLE_LUA": "OFF"
-            }
+            "inherits": "macos-conan-ninja-release"
         },
         },
         {
         {
             "name": "macos-xcode-release",
             "name": "macos-xcode-release",

+ 2 - 0
client/CGameInfo.cpp

@@ -71,10 +71,12 @@ const HeroTypeService * CGameInfo::heroTypes() const
 	return globalServices->heroTypes();
 	return globalServices->heroTypes();
 }
 }
 
 
+#if SCRIPTING_ENABLED
 const scripting::Service * CGameInfo::scripts()  const
 const scripting::Service * CGameInfo::scripts()  const
 {
 {
 	return globalServices->scripts();
 	return globalServices->scripts();
 }
 }
+#endif
 
 
 const spells::Service * CGameInfo::spells()  const
 const spells::Service * CGameInfo::spells()  const
 {
 {

+ 2 - 0
client/CGameInfo.h

@@ -59,7 +59,9 @@ public:
 	const FactionService * factions() const override;
 	const FactionService * factions() const override;
 	const HeroClassService * heroClasses() const override;
 	const HeroClassService * heroClasses() const override;
 	const HeroTypeService * heroTypes() const override;
 	const HeroTypeService * heroTypes() const override;
+#if SCRIPTING_ENABLED
 	const scripting::Service * scripts() const override;
 	const scripting::Service * scripts() const override;
+#endif
 	const spells::Service * spells() const override;
 	const spells::Service * spells() const override;
 	const SkillService * skills() const override;
 	const SkillService * skills() const override;
 	const BattleFieldService * battlefields() const override;
 	const BattleFieldService * battlefields() const override;

+ 3 - 1
client/CMT.cpp

@@ -232,7 +232,7 @@ int main(int argc, char * argv[])
 	*console->cb = processCommand;
 	*console->cb = processCommand;
 	console->start();
 	console->start();
 
 
-	const bfs::path logPath = VCMIDirs::get().userCachePath() / "VCMI_Client_log.txt";
+	const bfs::path logPath = VCMIDirs::get().userLogsPath() / "VCMI_Client_log.txt";
 	logConfig = new CBasicLogConfigurator(logPath, console);
 	logConfig = new CBasicLogConfigurator(logPath, console);
 	logConfig->configureDefault();
 	logConfig->configureDefault();
 	logGlobal->info(NAME);
 	logGlobal->info(NAME);
@@ -686,6 +686,7 @@ void processCommand(const std::string &message)
 		std::cout << "\rExtracting done :)\n";
 		std::cout << "\rExtracting done :)\n";
 		std::cout << " Extracted files can be found in " << outPath << " directory\n";
 		std::cout << " Extracted files can be found in " << outPath << " directory\n";
 	}
 	}
+#if SCRIPTING_ENABLED
 	else if(message=="get scripts")
 	else if(message=="get scripts")
 	{
 	{
 		std::cout << "Command accepted.\t";
 		std::cout << "Command accepted.\t";
@@ -708,6 +709,7 @@ void processCommand(const std::string &message)
 		std::cout << "\rExtracting done :)\n";
 		std::cout << "\rExtracting done :)\n";
 		std::cout << " Extracted files can be found in " << outPath << " directory\n";
 		std::cout << " Extracted files can be found in " << outPath << " directory\n";
 	}
 	}
+#endif
 	else if(message=="get txt")
 	else if(message=="get txt")
 	{
 	{
 		std::cout << "Command accepted.\t";
 		std::cout << "Command accepted.\t";

+ 8 - 1
client/CMakeLists.txt

@@ -177,9 +177,16 @@ endif()
 
 
 target_link_libraries(vcmiclient PRIVATE
 target_link_libraries(vcmiclient PRIVATE
 		vcmi SDL2::SDL2 SDL2::Image SDL2::Mixer SDL2::TTF
 		vcmi SDL2::SDL2 SDL2::Image SDL2::Mixer SDL2::TTF
-		ffmpeg::swscale ffmpeg::avutil ffmpeg::avcodec ffmpeg::avformat
 )
 )
 
 
+if(ffmpeg_LIBRARIES)
+	target_link_libraries(vcmiclient PRIVATE
+		ffmpeg::swscale ffmpeg::avutil ffmpeg::avcodec ffmpeg::avformat
+	)
+else()
+	target_compile_definitions(vcmiclient PRIVATE DISABLE_VIDEO)
+endif()
+
 target_include_directories(vcmiclient
 target_include_directories(vcmiclient
 	PUBLIC	${CMAKE_CURRENT_SOURCE_DIR})
 	PUBLIC	${CMAKE_CURRENT_SOURCE_DIR})
 
 

+ 84 - 78
client/CPlayerInterface.cpp

@@ -218,7 +218,7 @@ void CPlayerInterface::yourTurn()
 
 
 STRONG_INLINE void subRect(const int & x, const int & y, const int & z, const SDL_Rect & r, const ObjectInstanceID & hid)
 STRONG_INLINE void subRect(const int & x, const int & y, const int & z, const SDL_Rect & r, const ObjectInstanceID & hid)
 {
 {
-	TerrainTile2 & hlp = CGI->mh->ttiles[x][y][z];
+	TerrainTile2 & hlp = CGI->mh->ttiles[z][x][y];
 	for (auto & elem : hlp.objects)
 	for (auto & elem : hlp.objects)
 		if (elem.obj && elem.obj->id == hid)
 		if (elem.obj && elem.obj->id == hid)
 		{
 		{
@@ -229,7 +229,7 @@ STRONG_INLINE void subRect(const int & x, const int & y, const int & z, const SD
 
 
 STRONG_INLINE void delObjRect(const int & x, const int & y, const int & z, const ObjectInstanceID & hid)
 STRONG_INLINE void delObjRect(const int & x, const int & y, const int & z, const ObjectInstanceID & hid)
 {
 {
-	TerrainTile2 & hlp = CGI->mh->ttiles[x][y][z];
+	TerrainTile2 & hlp = CGI->mh->ttiles[z][x][y];
 	for (int h=0; h<hlp.objects.size(); ++h)
 	for (int h=0; h<hlp.objects.size(); ++h)
 		if (hlp.objects[h].obj && hlp.objects[h].obj->id == hid)
 		if (hlp.objects[h].obj && hlp.objects[h].obj->id == hid)
 		{
 		{
@@ -244,6 +244,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
 	if(LOCPLINT != this)
 	if(LOCPLINT != this)
 		return;
 		return;
 
 
+	//FIXME: read once and store
 	if(settings["session"]["spectate"].Bool() && settings["session"]["spectate-ignore-hero"].Bool())
 	if(settings["session"]["spectate"].Bool() && settings["session"]["spectate-ignore-hero"].Bool())
 		return;
 		return;
 
 
@@ -255,7 +256,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
 		//AI hero left the visible area (we can't obtain info)
 		//AI hero left the visible area (we can't obtain info)
 		//TODO very evil workaround -> retrieve pointer to hero so we could animate it
 		//TODO very evil workaround -> retrieve pointer to hero so we could animate it
 		// TODO -> we should not need full CGHeroInstance structure to display animation or it should not be handled by playerint (but by the client itself)
 		// TODO -> we should not need full CGHeroInstance structure to display animation or it should not be handled by playerint (but by the client itself)
-		const TerrainTile2 & tile = CGI->mh->ttiles[hp.x - 1][hp.y][hp.z];
+		const TerrainTile2 & tile = CGI->mh->ttiles[hp.z][hp.x - 1][hp.y];
 		for(auto & elem : tile.objects)
 		for(auto & elem : tile.objects)
 			if(elem.obj && elem.obj->id == details.id)
 			if(elem.obj && elem.obj->id == details.id)
 				hero = dynamic_cast<const CGHeroInstance *>(elem.obj);
 				hero = dynamic_cast<const CGHeroInstance *>(elem.obj);
@@ -1738,41 +1739,43 @@ int CPlayerInterface::getLastIndex( std::string namePrefix)
 
 
 void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroInstance * ho, const int3 &hp )
 void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroInstance * ho, const int3 &hp )
 {
 {
+	auto subArr = (CGI->mh->ttiles)[hp.z];
+
 	if (details.end.x+1 == details.start.x && details.end.y+1 == details.start.y) //tl
 	if (details.end.x+1 == details.start.x && details.end.y+1 == details.start.y) //tl
 	{
 	{
 		//ho->moveDir = 1;
 		//ho->moveDir = 1;
 		ho->isStanding = false;
 		ho->isStanding = false;
-		CGI->mh->ttiles[hp.x-3][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, -31)));
-		CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 1, -31)));
-		CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 33, -31)));
-		CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 65, -31)));
+		subArr[hp.x-3][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, -31)));
+		subArr[hp.x-2][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 1, -31)));
+		subArr[hp.x-1][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 33, -31)));
+		subArr[hp.x][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 65, -31)));
 
 
-		CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 1)));
+		subArr[hp.x-3][hp.y-1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 1)));
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, 1, 1), ho->id);
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, 1, 1), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 33, 1), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 33, 1), ho->id);
 		subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 65, 1), ho->id);
 		subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 65, 1), ho->id);
 
 
-		CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 33)));
+		subArr[hp.x-3][hp.y].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 33)));
 		subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, 1, 33), ho->id);
 		subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, 1, 33), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 33, 33), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 33, 33), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 65, 33), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 65, 33), ho->id);
 
 
-		std::stable_sort(CGI->mh->ttiles[hp.x-3][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-3][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter);
-		std::stable_sort(CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter);
-		std::stable_sort(CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter);
-		std::stable_sort(CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-3][hp.y-2].objects.begin(), subArr[hp.x-3][hp.y-2].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-2][hp.y-2].objects.begin(), subArr[hp.x-2][hp.y-2].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-1][hp.y-2].objects.begin(), subArr[hp.x-1][hp.y-2].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x][hp.y-2].objects.begin(), subArr[hp.x][hp.y-2].objects.end(), objectBlitOrderSorter);
 
 
-		std::stable_sort(CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-3][hp.y-1].objects.begin(), subArr[hp.x-3][hp.y-1].objects.end(), objectBlitOrderSorter);
 
 
-		std::stable_sort(CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-3][hp.y].objects.begin(), subArr[hp.x-3][hp.y].objects.end(), objectBlitOrderSorter);
 	}
 	}
 	else if (details.end.x == details.start.x && details.end.y+1 == details.start.y) //t
 	else if (details.end.x == details.start.x && details.end.y+1 == details.start.y) //t
 	{
 	{
 		//ho->moveDir = 2;
 		//ho->moveDir = 2;
 		ho->isStanding = false;
 		ho->isStanding = false;
-		CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 0, -31)));
-		CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 32, -31)));
-		CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 64, -31)));
+		subArr[hp.x-2][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 0, -31)));
+		subArr[hp.x-1][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 32, -31)));
+		subArr[hp.x][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 64, -31)));
 
 
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, 0, 1), ho->id);
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, 0, 1), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 32, 1), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 32, 1), ho->id);
@@ -1782,37 +1785,37 @@ void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroIns
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 32, 33), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 32, 33), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 64, 33), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 64, 33), ho->id);
 
 
-		std::stable_sort(CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter);
-		std::stable_sort(CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter);
-		std::stable_sort(CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-2][hp.y-2].objects.begin(), subArr[hp.x-2][hp.y-2].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-1][hp.y-2].objects.begin(), subArr[hp.x-1][hp.y-2].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x][hp.y-2].objects.begin(), subArr[hp.x][hp.y-2].objects.end(), objectBlitOrderSorter);
 	}
 	}
 	else if (details.end.x-1 == details.start.x && details.end.y+1 == details.start.y) //tr
 	else if (details.end.x-1 == details.start.x && details.end.y+1 == details.start.y) //tr
 	{
 	{
 		//ho->moveDir = 3;
 		//ho->moveDir = 3;
 		ho->isStanding = false;
 		ho->isStanding = false;
-		CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -1, -31)));
-		CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 31, -31)));
-		CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 63, -31)));
-		CGI->mh->ttiles[hp.x+1][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, -31)));
+		subArr[hp.x-2][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -1, -31)));
+		subArr[hp.x-1][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 31, -31)));
+		subArr[hp.x][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 63, -31)));
+		subArr[hp.x+1][hp.y-2].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, -31)));
 
 
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, -1, 1), ho->id);
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, -1, 1), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 31, 1), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 31, 1), ho->id);
 		subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 63, 1), ho->id);
 		subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 63, 1), ho->id);
-		CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 1)));
+		subArr[hp.x+1][hp.y-1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 1)));
 
 
 		subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, -1, 33), ho->id);
 		subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, -1, 33), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 31, 33), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 31, 33), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 63, 33), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 63, 33), ho->id);
-		CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 33)));
+		subArr[hp.x+1][hp.y].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 33)));
 
 
-		std::stable_sort(CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter);
-		std::stable_sort(CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter);
-		std::stable_sort(CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter);
-		std::stable_sort(CGI->mh->ttiles[hp.x+1][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x+1][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-2][hp.y-2].objects.begin(), subArr[hp.x-2][hp.y-2].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-1][hp.y-2].objects.begin(), subArr[hp.x-1][hp.y-2].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x][hp.y-2].objects.begin(), subArr[hp.x][hp.y-2].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x+1][hp.y-2].objects.begin(), subArr[hp.x+1][hp.y-2].objects.end(), objectBlitOrderSorter);
 
 
-		std::stable_sort(CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x+1][hp.y-1].objects.begin(), subArr[hp.x+1][hp.y-1].objects.end(), objectBlitOrderSorter);
 
 
-		std::stable_sort(CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.begin(), CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x+1][hp.y].objects.begin(), subArr[hp.x+1][hp.y].objects.end(), objectBlitOrderSorter);
 	}
 	}
 	else if (details.end.x-1 == details.start.x && details.end.y == details.start.y) //r
 	else if (details.end.x-1 == details.start.x && details.end.y == details.start.y) //r
 	{
 	{
@@ -1821,16 +1824,16 @@ void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroIns
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, -1, 0), ho->id);
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, -1, 0), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 31, 0), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 31, 0), ho->id);
 		subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 63, 0), ho->id);
 		subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 63, 0), ho->id);
-		CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 0)));
+		subArr[hp.x+1][hp.y-1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 0)));
 
 
 		subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, -1, 32), ho->id);
 		subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, -1, 32), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 31, 32), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 31, 32), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 63, 32), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 63, 32), ho->id);
-		CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 32)));
+		subArr[hp.x+1][hp.y].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 32)));
 
 
-		std::stable_sort(CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x+1][hp.y-1].objects.begin(), subArr[hp.x+1][hp.y-1].objects.end(), objectBlitOrderSorter);
 
 
-		std::stable_sort(CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.begin(), CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x+1][hp.y].objects.begin(), subArr[hp.x+1][hp.y].objects.end(), objectBlitOrderSorter);
 	}
 	}
 	else if (details.end.x-1 == details.start.x && details.end.y-1 == details.start.y) //br
 	else if (details.end.x-1 == details.start.x && details.end.y-1 == details.start.y) //br
 	{
 	{
@@ -1839,26 +1842,26 @@ void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroIns
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, -1, -1), ho->id);
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, -1, -1), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 31, -1), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 31, -1), ho->id);
 		subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 63, -1), ho->id);
 		subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 63, -1), ho->id);
-		CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, -1)));
+		subArr[hp.x+1][hp.y-1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, -1)));
 
 
 		subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, -1, 31), ho->id);
 		subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, -1, 31), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 31, 31), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 31, 31), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 63, 31), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 63, 31), ho->id);
-		CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 31)));
+		subArr[hp.x+1][hp.y].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 31)));
 
 
-		CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -1, 63)));
-		CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 31, 63)));
-		CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 63, 63)));
-		CGI->mh->ttiles[hp.x+1][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 63)));
+		subArr[hp.x-2][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -1, 63)));
+		subArr[hp.x-1][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 31, 63)));
+		subArr[hp.x][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 63, 63)));
+		subArr[hp.x+1][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 63)));
 
 
-		std::stable_sort(CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x+1][hp.y-1].objects.begin(), subArr[hp.x+1][hp.y-1].objects.end(), objectBlitOrderSorter);
 
 
-		std::stable_sort(CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.begin(), CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x+1][hp.y].objects.begin(), subArr[hp.x+1][hp.y].objects.end(), objectBlitOrderSorter);
 
 
-		std::stable_sort(CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter);
-		std::stable_sort(CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter);
-		std::stable_sort(CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter);
-		std::stable_sort(CGI->mh->ttiles[hp.x+1][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x+1][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-2][hp.y+1].objects.begin(), subArr[hp.x-2][hp.y+1].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-1][hp.y+1].objects.begin(), subArr[hp.x-1][hp.y+1].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x][hp.y+1].objects.begin(), subArr[hp.x][hp.y+1].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x+1][hp.y+1].objects.begin(), subArr[hp.x+1][hp.y+1].objects.end(), objectBlitOrderSorter);
 	}
 	}
 	else if (details.end.x == details.start.x && details.end.y-1 == details.start.y) //b
 	else if (details.end.x == details.start.x && details.end.y-1 == details.start.y) //b
 	{
 	{
@@ -1872,59 +1875,59 @@ void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroIns
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 32, 31), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 32, 31), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 64, 31), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 64, 31), ho->id);
 
 
-		CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 0, 63)));
-		CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 32, 63)));
-		CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 64, 63)));
+		subArr[hp.x-2][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 0, 63)));
+		subArr[hp.x-1][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 32, 63)));
+		subArr[hp.x][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 64, 63)));
 
 
-		std::stable_sort(CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter);
-		std::stable_sort(CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter);
-		std::stable_sort(CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-2][hp.y+1].objects.begin(), subArr[hp.x-2][hp.y+1].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-1][hp.y+1].objects.begin(), subArr[hp.x-1][hp.y+1].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x][hp.y+1].objects.begin(), subArr[hp.x][hp.y+1].objects.end(), objectBlitOrderSorter);
 	}
 	}
 	else if (details.end.x+1 == details.start.x && details.end.y-1 == details.start.y) //bl
 	else if (details.end.x+1 == details.start.x && details.end.y-1 == details.start.y) //bl
 	{
 	{
 		//ho->moveDir = 7;
 		//ho->moveDir = 7;
 		ho->isStanding = false;
 		ho->isStanding = false;
-		CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, -1)));
+		subArr[hp.x-3][hp.y-1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, -1)));
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, 1, -1), ho->id);
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, 1, -1), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 33, -1), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 33, -1), ho->id);
 		subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 65, -1), ho->id);
 		subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 65, -1), ho->id);
 
 
-		CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 31)));
+		subArr[hp.x-3][hp.y].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 31)));
 		subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, 1, 31), ho->id);
 		subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, 1, 31), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 33, 31), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 33, 31), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 65, 31), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 65, 31), ho->id);
 
 
-		CGI->mh->ttiles[hp.x-3][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 63)));
-		CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 1, 63)));
-		CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 33, 63)));
-		CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 65, 63)));
+		subArr[hp.x-3][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 63)));
+		subArr[hp.x-2][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 1, 63)));
+		subArr[hp.x-1][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 33, 63)));
+		subArr[hp.x][hp.y+1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 65, 63)));
 
 
-		std::stable_sort(CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-3][hp.y-1].objects.begin(), subArr[hp.x-3][hp.y-1].objects.end(), objectBlitOrderSorter);
 
 
-		std::stable_sort(CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-3][hp.y].objects.begin(), subArr[hp.x-3][hp.y].objects.end(), objectBlitOrderSorter);
 
 
-		std::stable_sort(CGI->mh->ttiles[hp.x-3][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-3][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter);
-		std::stable_sort(CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter);
-		std::stable_sort(CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter);
-		std::stable_sort(CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-3][hp.y+1].objects.begin(), subArr[hp.x-3][hp.y+1].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-2][hp.y+1].objects.begin(), subArr[hp.x-2][hp.y+1].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-1][hp.y+1].objects.begin(), subArr[hp.x-1][hp.y+1].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x][hp.y+1].objects.begin(), subArr[hp.x][hp.y+1].objects.end(), objectBlitOrderSorter);
 	}
 	}
 	else if (details.end.x+1 == details.start.x && details.end.y == details.start.y) //l
 	else if (details.end.x+1 == details.start.x && details.end.y == details.start.y) //l
 	{
 	{
 		//ho->moveDir = 8;
 		//ho->moveDir = 8;
 		ho->isStanding = false;
 		ho->isStanding = false;
-		CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 0)));
+		subArr[hp.x-3][hp.y-1].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 0)));
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, 1, 0), ho->id);
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, 1, 0), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 33, 0), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 33, 0), ho->id);
 		subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 65, 0), ho->id);
 		subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 65, 0), ho->id);
 
 
-		CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 32)));
+		subArr[hp.x-3][hp.y].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 32)));
 		subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, 1, 32), ho->id);
 		subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, 1, 32), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 33, 32), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 33, 32), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 65, 32), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 65, 32), ho->id);
 
 
-		std::stable_sort(CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-3][hp.y-1].objects.begin(), subArr[hp.x-3][hp.y-1].objects.end(), objectBlitOrderSorter);
 
 
-		std::stable_sort(CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.end(), objectBlitOrderSorter);
+		std::stable_sort(subArr[hp.x-3][hp.y].objects.begin(), subArr[hp.x-3][hp.y].objects.end(), objectBlitOrderSorter);
 	}
 	}
 }
 }
 
 
@@ -2154,13 +2157,16 @@ void CPlayerInterface::finishMovement( const TryMoveHero &details, const int3 &h
 	subRect(details.end.x, details.end.y, details.end.z, genRect(32, 32, 64, 32), ho->id);
 	subRect(details.end.x, details.end.y, details.end.z, genRect(32, 32, 64, 32), ho->id);
 
 
 	//restoring good order of objects
 	//restoring good order of objects
-	std::stable_sort(CGI->mh->ttiles[details.end.x-2][details.end.y-1][details.end.z].objects.begin(), CGI->mh->ttiles[details.end.x-2][details.end.y-1][details.end.z].objects.end(), objectBlitOrderSorter);
-	std::stable_sort(CGI->mh->ttiles[details.end.x-1][details.end.y-1][details.end.z].objects.begin(), CGI->mh->ttiles[details.end.x-1][details.end.y-1][details.end.z].objects.end(), objectBlitOrderSorter);
-	std::stable_sort(CGI->mh->ttiles[details.end.x][details.end.y-1][details.end.z].objects.begin(), CGI->mh->ttiles[details.end.x][details.end.y-1][details.end.z].objects.end(), objectBlitOrderSorter);
 
 
-	std::stable_sort(CGI->mh->ttiles[details.end.x-2][details.end.y][details.end.z].objects.begin(), CGI->mh->ttiles[details.end.x-2][details.end.y][details.end.z].objects.end(), objectBlitOrderSorter);
-	std::stable_sort(CGI->mh->ttiles[details.end.x-1][details.end.y][details.end.z].objects.begin(), CGI->mh->ttiles[details.end.x-1][details.end.y][details.end.z].objects.end(), objectBlitOrderSorter);
-	std::stable_sort(CGI->mh->ttiles[details.end.x][details.end.y][details.end.z].objects.begin(), CGI->mh->ttiles[details.end.x][details.end.y][details.end.z].objects.end(), objectBlitOrderSorter);
+	boost::detail::multi_array::sub_array<TerrainTile2, 2> subArr = (CGI->mh->ttiles)[details.end.z];
+
+	std::stable_sort(subArr[details.end.x-2][details.end.y-1].objects.begin(), subArr[details.end.x-2][details.end.y-1].objects.end(), objectBlitOrderSorter);
+	std::stable_sort(subArr[details.end.x-1][details.end.y-1].objects.begin(), subArr[details.end.x-1][details.end.y-1].objects.end(), objectBlitOrderSorter);
+	std::stable_sort(subArr[details.end.x][details.end.y-1].objects.begin(), subArr[details.end.x][details.end.y-1].objects.end(), objectBlitOrderSorter);
+
+	std::stable_sort(subArr[details.end.x-2][details.end.y].objects.begin(), subArr[details.end.x-2][details.end.y].objects.end(), objectBlitOrderSorter);
+	std::stable_sort(subArr[details.end.x-1][details.end.y].objects.begin(), subArr[details.end.x-1][details.end.y].objects.end(), objectBlitOrderSorter);
+	std::stable_sort(subArr[details.end.x][details.end.y].objects.begin(), subArr[details.end.x][details.end.y].objects.end(), objectBlitOrderSorter);
 }
 }
 
 
 void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult )
 void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult )
@@ -2882,7 +2888,7 @@ void CPlayerInterface::updateAmbientSounds(bool resetAll)
 	{
 	{
 		int dist = pos.dist(tile, int3::DIST_CHEBYSHEV);
 		int dist = pos.dist(tile, int3::DIST_CHEBYSHEV);
 		// We want sound for every special terrain on tile and not just one on top
 		// We want sound for every special terrain on tile and not just one on top
-		for(auto & ttObj : CGI->mh->ttiles[tile.x][tile.y][tile.z].objects)
+		for(auto & ttObj : CGI->mh->ttiles[tile.z][tile.x][tile.y].objects)
 		{
 		{
 			if(ttObj.ambientSound)
 			if(ttObj.ambientSound)
 				updateSounds(ttObj.ambientSound.get(), dist);
 				updateSounds(ttObj.ambientSound.get(), dist);

+ 1 - 1
client/CServerHandler.cpp

@@ -685,7 +685,7 @@ void CServerHandler::threadRunServer()
 {
 {
 #ifndef VCMI_ANDROID
 #ifndef VCMI_ANDROID
 	setThreadName("CServerHandler::threadRunServer");
 	setThreadName("CServerHandler::threadRunServer");
-	const std::string logName = (VCMIDirs::get().userCachePath() / "server_log.txt").string();
+	const std::string logName = (VCMIDirs::get().userLogsPath() / "server_log.txt").string();
 	std::string comm = VCMIDirs::get().serverPath().string()
 	std::string comm = VCMIDirs::get().serverPath().string()
 		+ " --port=" + getDefaultPortStr()
 		+ " --port=" + getDefaultPortStr()
 		+ " --run-by-client"
 		+ " --run-by-client"

+ 10 - 0
client/Client.cpp

@@ -263,12 +263,14 @@ void CClient::serialize(BinarySerializer & h, const int version)
 		i->second->saveGame(h, version);
 		i->second->saveGame(h, version);
 	}
 	}
 
 
+#if SCRIPTING_ENABLED
 	if(version >= 800)
 	if(version >= 800)
 	{
 	{
 		JsonNode scriptsState;
 		JsonNode scriptsState;
 		clientScripts->serializeState(h.saving, scriptsState);
 		clientScripts->serializeState(h.saving, scriptsState);
 		h & scriptsState;
 		h & scriptsState;
 	}
 	}
+#endif
 }
 }
 
 
 void CClient::serialize(BinaryDeserializer & h, const int version)
 void CClient::serialize(BinaryDeserializer & h, const int version)
@@ -329,11 +331,13 @@ void CClient::serialize(BinaryDeserializer & h, const int version)
 		LOCPLINT = prevInt;
 		LOCPLINT = prevInt;
 	}
 	}
 
 
+#if SCRIPTING_ENABLED
 	{
 	{
 		JsonNode scriptsState;
 		JsonNode scriptsState;
 		h & scriptsState;
 		h & scriptsState;
 		clientScripts->serializeState(h.saving, scriptsState);
 		clientScripts->serializeState(h.saving, scriptsState);
 	}
 	}
+#endif
 
 
 	logNetwork->trace("Loaded client part of save %d ms", CSH->th->getDiff());
 	logNetwork->trace("Loaded client part of save %d ms", CSH->th->getDiff());
 }
 }
@@ -352,7 +356,9 @@ void CClient::save(const std::string & fname)
 
 
 void CClient::endGame()
 void CClient::endGame()
 {
 {
+#if SCRIPTING_ENABLED
 	clientScripts.reset();
 	clientScripts.reset();
+#endif
 
 
 	//suggest interfaces to finish their stuff (AI should interrupt any bg working threads)
 	//suggest interfaces to finish their stuff (AI should interrupt any bg working threads)
 	for(auto & i : playerint)
 	for(auto & i : playerint)
@@ -732,6 +738,7 @@ PlayerColor CClient::getLocalPlayer() const
 	return getCurrentPlayer();
 	return getCurrentPlayer();
 }
 }
 
 
+#if SCRIPTING_ENABLED
 scripting::Pool * CClient::getGlobalContextPool() const
 scripting::Pool * CClient::getGlobalContextPool() const
 {
 {
 	return clientScripts.get();
 	return clientScripts.get();
@@ -741,11 +748,14 @@ scripting::Pool * CClient::getContextPool() const
 {
 {
 	return clientScripts.get();
 	return clientScripts.get();
 }
 }
+#endif
 
 
 void CClient::reinitScripting()
 void CClient::reinitScripting()
 {
 {
 	clientEventBus = make_unique<events::EventBus>();
 	clientEventBus = make_unique<events::EventBus>();
+#if SCRIPTING_ENABLED
 	clientScripts.reset(new scripting::PoolImpl(this));
 	clientScripts.reset(new scripting::PoolImpl(this));
+#endif
 }
 }
 
 
 
 

+ 7 - 0
client/Client.h

@@ -39,10 +39,12 @@ namespace boost { class thread; }
 template<typename T> class CApplier;
 template<typename T> class CApplier;
 class CBaseForCLApply;
 class CBaseForCLApply;
 
 
+#if SCRIPTING_ENABLED
 namespace scripting
 namespace scripting
 {
 {
 	class PoolImpl;
 	class PoolImpl;
 }
 }
+#endif
 
 
 namespace events
 namespace events
 {
 {
@@ -233,13 +235,18 @@ public:
 	void showInfoDialog(InfoWindow * iw) override {};
 	void showInfoDialog(InfoWindow * iw) override {};
 	void showInfoDialog(const std::string & msg, PlayerColor player) override {};
 	void showInfoDialog(const std::string & msg, PlayerColor player) override {};
 
 
+#if SCRIPTING_ENABLED
 	scripting::Pool * getGlobalContextPool() const override;
 	scripting::Pool * getGlobalContextPool() const override;
 	scripting::Pool * getContextPool() const override;
 	scripting::Pool * getContextPool() const override;
+#endif
+
 private:
 private:
 	std::map<PlayerColor, std::shared_ptr<CBattleCallback>> battleCallbacks; //callbacks given to player interfaces
 	std::map<PlayerColor, std::shared_ptr<CBattleCallback>> battleCallbacks; //callbacks given to player interfaces
 	std::map<PlayerColor, std::shared_ptr<CPlayerEnvironment>> playerEnvironments;
 	std::map<PlayerColor, std::shared_ptr<CPlayerEnvironment>> playerEnvironments;
 
 
+#if SCRIPTING_ENABLED
 	std::shared_ptr<scripting::PoolImpl> clientScripts;
 	std::shared_ptr<scripting::PoolImpl> clientScripts;
+#endif
 	std::unique_ptr<events::EventBus> clientEventBus;
 	std::unique_ptr<events::EventBus> clientEventBus;
 
 
 	std::shared_ptr<CApplier<CBaseForCLApply>> applier;
 	std::shared_ptr<CApplier<CBaseForCLApply>> applier;

+ 52 - 90
client/mapHandler.cpp

@@ -52,20 +52,22 @@ struct NeighborTilesInfo
 		 d1,
 		 d1,
 		 d2,
 		 d2,
 		 d3;
 		 d3;
-	NeighborTilesInfo(const int3 & pos, const int3 & sizes, const std::vector< std::vector< std::vector<ui8> > > & visibilityMap)
+	NeighborTilesInfo(const int3 & pos, const int3 & sizes, std::shared_ptr<const boost::multi_array<ui8, 3>> visibilityMap)
 	{
 	{
 		auto getTile = [&](int dx, int dy)->bool
 		auto getTile = [&](int dx, int dy)->bool
 		{
 		{
 			if ( dx + pos.x < 0 || dx + pos.x >= sizes.x
 			if ( dx + pos.x < 0 || dx + pos.x >= sizes.x
 			  || dy + pos.y < 0 || dy + pos.y >= sizes.y)
 			  || dy + pos.y < 0 || dy + pos.y >= sizes.y)
 				return false;
 				return false;
-			return settings["session"]["spectate"].Bool() ? true : visibilityMap[dx+pos.x][dy+pos.y][pos.z];
+
+			//FIXME: please do not read settings for every tile...
+			return settings["session"]["spectate"].Bool() ? true : (*visibilityMap)[pos.z][dx+pos.x][dy+pos.y];
 		};
 		};
 		d7 = getTile(-1, -1); //789
 		d7 = getTile(-1, -1); //789
 		d8 = getTile( 0, -1); //456
 		d8 = getTile( 0, -1); //456
 		d9 = getTile(+1, -1); //123
 		d9 = getTile(+1, -1); //123
 		d4 = getTile(-1, 0);
 		d4 = getTile(-1, 0);
-		d5 = visibilityMap[pos.x][pos.y][pos.z];
+		d5 = (*visibilityMap)[pos.z][pos.x][pos.y];
 		d6 = getTile(+1, 0);
 		d6 = getTile(+1, 0);
 		d1 = getTile(-1, +1);
 		d1 = getTile(-1, +1);
 		d2 = getTile( 0, +1);
 		d2 = getTile( 0, +1);
@@ -113,22 +115,9 @@ void CMapHandler::prepareFOWDefs()
 		FoWfullHide[frame] = graphics->fogOfWarFullHide->getImage(frame);
 		FoWfullHide[frame] = graphics->fogOfWarFullHide->getImage(frame);
 
 
 	//initialization of type of full-hide image
 	//initialization of type of full-hide image
-	hideBitmap.resize(sizes.x);
-	for (auto & elem : hideBitmap)
-	{
-		elem.resize(sizes.y);
-	}
-	for (auto & elem : hideBitmap)
-	{
-		for (int j = 0; j < sizes.y; ++j)
-		{
-			elem[j].resize(sizes.z);
-			for(int k = 0; k < sizes.z; ++k)
-			{
-				elem[j][k] = CRandomGenerator::getDefault().nextInt((int)size - 1);
-			}
-		}
-	}
+	hideBitmap.resize(boost::extents[sizes.z][sizes.x][sizes.y]);
+	for (int i = 0; i < hideBitmap.num_elements(); i++)
+		hideBitmap.data()[i] = CRandomGenerator::getDefault().nextInt(size - 1);
 
 
 	size = graphics->fogOfWarPartialHide->size(0);
 	size = graphics->fogOfWarPartialHide->size(0);
 	FoWpartialHide.resize(size);
 	FoWpartialHide.resize(size);
@@ -211,16 +200,12 @@ void CMapHandler::initTerrainGraphics()
 
 
 	// Create enough room for the whole map and its frame
 	// Create enough room for the whole map and its frame
 
 
-	ttiles.resize(sizes.x, frameW, frameW);
-	for (int i=0-frameW;i<ttiles.size()-frameW;i++)
-	{
-		ttiles[i].resize(sizes.y, frameH, frameH);
-	}
-	for (int i=0-frameW;i<ttiles.size()-frameW;i++)
-	{
-		for (int j=0-frameH;j<(int)sizes.y+frameH;j++)
-			ttiles[i][j].resize(sizes.z, 0, 0);
-	}
+	//ttiles.resize(sizes.x, frameW, frameW);
+
+	//FIXME: why do we even handle array with z (surface and undeground) at the same time?
+
+	ttiles.resize(boost::extents[sizes.z][sizes.x + 2 * frameW][sizes.y + 2 * frameH]); 
+	ttiles.reindex(std::list<int>{ 0, -frameW, -frameH }); //need to move starting coordinates so that used index is always positive
 }
 }
 
 
 void CMapHandler::initBorderGraphics()
 void CMapHandler::initBorderGraphics()
@@ -321,19 +306,21 @@ void CMapHandler::initObjectRects()
 					obj->coveringAt(currTile.x, currTile.y) // object is visible here
 					obj->coveringAt(currTile.x, currTile.y) // object is visible here
 				  )
 				  )
 				{
 				{
-					ttiles[currTile.x][currTile.y][currTile.z].objects.push_back(toAdd);
+					ttiles[currTile.z][currTile.x][currTile.y].objects.push_back(toAdd);
 				}
 				}
 			}
 			}
 		}
 		}
 	}
 	}
 
 
-	for(int ix=0; ix<ttiles.size()-frameW; ++ix)
+	auto shape = ttiles.shape();
+	for(size_t z = 0; z < shape[0]; z++)
 	{
 	{
-		for(int iy=0; iy<ttiles[0].size()-frameH; ++iy)
+		for(size_t x = 0; x < shape[1] - frameW; x++)
 		{
 		{
-			for(int iz=0; iz<ttiles[0][0].size(); ++iz)
+			for(size_t y = 0; y < shape[2] - frameH; y++)
 			{
 			{
-				stable_sort(ttiles[ix][iy][iz].objects.begin(), ttiles[ix][iy][iz].objects.end(), objectBlitOrderSorter);
+				auto & objects = ttiles[z][x][y].objects;
+				stable_sort(objects.begin(), objects.end(), objectBlitOrderSorter);
 			}
 			}
 		}
 		}
 	}
 	}
@@ -351,7 +338,7 @@ void CMapHandler::init()
 	//sizes of terrain
 	//sizes of terrain
 	sizes.x = map->width;
 	sizes.x = map->width;
 	sizes.y = map->height;
 	sizes.y = map->height;
-	sizes.z = map->twoLevel ? 2 : 1;
+	sizes.z = map->levels();
 
 
 	// Total number of visible tiles. Subtract the center tile, then
 	// Total number of visible tiles. Subtract the center tile, then
 	// compute the number of tiles on each side, and reassemble.
 	// compute the number of tiles on each side, and reassemble.
@@ -557,7 +544,9 @@ void CMapHandler::CMapWorldViewBlitter::drawTileOverlay(SDL_Surface * targetSurf
 		const CGObjectInstance * obj = object.obj;
 		const CGObjectInstance * obj = object.obj;
 
 
 		const bool sameLevel = obj->pos.z == pos.z;
 		const bool sameLevel = obj->pos.z == pos.z;
-		const bool isVisible = settings["session"]["spectate"].Bool() ? true : (*info->visibilityMap)[pos.x][pos.y][pos.z];
+
+		//FIXME: Don't read options in  a loop :v
+		const bool isVisible = settings["session"]["spectate"].Bool() ? true : (*info->visibilityMap)[pos.z][pos.x][pos.y];
 		const bool isVisitable = obj->visitableAt(pos.x, pos.y);
 		const bool isVisitable = obj->visitableAt(pos.x, pos.y);
 
 
 		if(sameLevel && isVisible && isVisitable)
 		if(sameLevel && isVisible && isVisitable)
@@ -826,11 +815,11 @@ void CMapHandler::CMapBlitter::drawRiver(SDL_Surface * targetSurf, const Terrain
 
 
 void CMapHandler::CMapBlitter::drawFow(SDL_Surface * targetSurf) const
 void CMapHandler::CMapBlitter::drawFow(SDL_Surface * targetSurf) const
 {
 {
-	const NeighborTilesInfo neighborInfo(pos, parent->sizes, *info->visibilityMap);
+	const NeighborTilesInfo neighborInfo(pos, parent->sizes, info->visibilityMap);
 
 
 	int retBitmapID = neighborInfo.getBitmapID();// >=0 -> partial hide, <0 - full hide
 	int retBitmapID = neighborInfo.getBitmapID();// >=0 -> partial hide, <0 - full hide
 	if (retBitmapID < 0)
 	if (retBitmapID < 0)
-		retBitmapID = - parent->hideBitmap[pos.x][pos.y][pos.z] - 1; //fully hidden
+		retBitmapID = - parent->hideBitmap[pos.z][pos.x][pos.y] - 1; //fully hidden
 
 
 	std::shared_ptr<IImage> image;
 	std::shared_ptr<IImage> image;
 
 
@@ -865,7 +854,7 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn
 			realTileRect.x = realPos.x;
 			realTileRect.x = realPos.x;
 			realTileRect.y = realPos.y;
 			realTileRect.y = realPos.y;
 
 
-			const TerrainTile2 & tile = parent->ttiles[pos.x][pos.y][pos.z];
+			const TerrainTile2 & tile = parent->ttiles[pos.z][pos.x][pos.y];
 			const TerrainTile & tinfo = parent->map->getTile(pos);
 			const TerrainTile & tinfo = parent->map->getTile(pos);
 			const TerrainTile * tinfoUpper = pos.y > 0 ? &parent->map->getTile(int3(pos.x, pos.y - 1, pos.z)) : nullptr;
 			const TerrainTile * tinfoUpper = pos.y > 0 ? &parent->map->getTile(int3(pos.x, pos.y - 1, pos.z)) : nullptr;
 
 
@@ -896,9 +885,9 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn
 			}
 			}
 			else
 			else
 			{
 			{
-				const TerrainTile2 & tile = parent->ttiles[pos.x][pos.y][pos.z];
+				const TerrainTile2 & tile = parent->ttiles[pos.z][pos.x][pos.y];
 
 
-				if(!settings["session"]["spectate"].Bool() && !(*info->visibilityMap)[pos.x][pos.y][topTile.z] && !info->showAllTerrain)
+				if(!settings["session"]["spectate"].Bool() && !(*info->visibilityMap)[topTile.z][pos.x][pos.y] && !info->showAllTerrain)
 					drawFow(targetSurf);
 					drawFow(targetSurf);
 
 
 				// overlay needs to be drawn over fow, because of artifacts-aura-like spells
 				// overlay needs to be drawn over fow, because of artifacts-aura-like spells
@@ -1105,7 +1094,7 @@ bool CMapHandler::CMapBlitter::canDrawCurrentTile() const
 	if(settings["session"]["spectate"].Bool())
 	if(settings["session"]["spectate"].Bool())
 		return true;
 		return true;
 
 
-	const NeighborTilesInfo neighbors(pos, parent->sizes, *info->visibilityMap);
+	const NeighborTilesInfo neighbors(pos, parent->sizes, info->visibilityMap);
 	return !neighbors.areAllHidden();
 	return !neighbors.areAllHidden();
 }
 }
 
 
@@ -1136,7 +1125,7 @@ bool CMapHandler::updateObjectsFade()
 			++iter;
 			++iter;
 		else // fade finished
 		else // fade finished
 		{
 		{
-			auto &objs = ttiles[pos.x][pos.y][pos.z].objects;
+			auto &objs = ttiles[pos.z][pos.x][pos.y].objects;
 			for (auto objIter = objs.begin(); objIter != objs.end(); ++objIter)
 			for (auto objIter = objs.begin(); objIter != objs.end(); ++objIter)
 			{
 			{
 				if ((*objIter).fadeAnimKey == (*iter).first)
 				if ((*objIter).fadeAnimKey == (*iter).first)
@@ -1210,6 +1199,9 @@ bool CMapHandler::printObject(const CGObjectInstance * obj, bool fadein)
 	const int tilesW = bitmap->width()/32;
 	const int tilesW = bitmap->width()/32;
 	const int tilesH = bitmap->height()/32;
 	const int tilesH = bitmap->height()/32;
 
 
+	auto ttilesWidth = ttiles.shape()[1];
+	auto ttilesHeight = ttiles.shape()[2];
+
 	for(int fx=0; fx<tilesW; ++fx)
 	for(int fx=0; fx<tilesW; ++fx)
 	{
 	{
 		for(int fy=0; fy<tilesH; ++fy)
 		for(int fy=0; fy<tilesH; ++fy)
@@ -1220,10 +1212,13 @@ bool CMapHandler::printObject(const CGObjectInstance * obj, bool fadein)
 			cr.x = fx*32;
 			cr.x = fx*32;
 			cr.y = fy*32;
 			cr.y = fy*32;
 
 
-			if((obj->pos.x + fx - tilesW+1)>=0 && (obj->pos.x + fx - tilesW+1)<ttiles.size()-frameW && (obj->pos.y + fy - tilesH+1)>=0 && (obj->pos.y + fy - tilesH+1)<ttiles[0].size()-frameH)
+			if((obj->pos.x + fx - tilesW + 1) >= 0 &&
+				(obj->pos.x + fx - tilesW + 1) < ttilesWidth - frameW &&
+				(obj->pos.y + fy - tilesH + 1) >= 0 &&
+				(obj->pos.y + fy - tilesH + 1) < ttilesHeight - frameH)
 			{
 			{
 				int3 pos(obj->pos.x + fx - tilesW + 1, obj->pos.y + fy - tilesH + 1, obj->pos.z);
 				int3 pos(obj->pos.x + fx - tilesW + 1, obj->pos.y + fy - tilesH + 1, obj->pos.z);
-				TerrainTile2 & curt = ttiles[pos.x][pos.y][pos.z];
+				TerrainTile2 & curt = ttiles[pos.z][pos.x][pos.y];
 
 
 				TerrainTileObject toAdd(obj, cr, obj->visitableAt(pos.x, pos.y));
 				TerrainTileObject toAdd(obj, cr, obj->visitableAt(pos.x, pos.y));
 				if (fadein && ADVOPT.objectFading)
 				if (fadein && ADVOPT.objectFading)
@@ -1252,59 +1247,26 @@ bool CMapHandler::printObject(const CGObjectInstance * obj, bool fadein)
 
 
 bool CMapHandler::hideObject(const CGObjectInstance * obj, bool fadeout)
 bool CMapHandler::hideObject(const CGObjectInstance * obj, bool fadeout)
 {
 {
-	//optimized version which reveals weird bugs with missing def name
-	//auto pos = obj->pos;
-
-	//for (size_t i = pos.x; i > pos.x - obj->getWidth(); i--)
-	//{
-	//	for (size_t j = pos.y; j > pos.y - obj->getHeight(); j--)
-	//	{
-	//		int3 t(i, j, pos.z);
-	//		if (!map->isInTheMap(t))
-	//			continue;
-
-	//		auto &objs = ttiles[i][j][pos.z].objects;
-	//		for (size_t x = 0; x < objs.size(); x++)
-	//		{
-	//			auto ourObj = objs[x].obj;
-	//			if (ourObj && ourObj->id == obj->id)
-	//			{
-	//				if (fadeout && ADVOPT.objectFading) // object should be faded == erase is delayed until the end of fadeout
-	//				{
-	//					if (startObjectFade(objs[x], false, t))
-	//						objs[x].obj = nullptr; //set original pointer to null
-	//					else
-	//						objs.erase(objs.begin() + x);
-	//				}
-	//				else
-	//					objs.erase(objs.begin() + x);
-	//				break;
-	//			}
-	//		}
-	//	}
-
-	//}
-
-	for (size_t i = 0; i<map->width; i++)
+	for(size_t z = 0; z < map->levels(); z++)
 	{
 	{
-		for (size_t j = 0; j<map->height; j++)
+		for(size_t x = 0; x < map->width; x++)
 		{
 		{
-			for (size_t k = 0; k<(map->twoLevel ? 2 : 1); k++)
+			for(size_t y = 0; y < map->height; y++)
 			{
 			{
-				auto &objs = ttiles[(int)i][(int)j][(int)k].objects;
-				for (size_t x = 0; x < objs.size(); x++)
+				auto &objs = ttiles[(int)z][(int)x][(int)y].objects;
+				for(size_t i = 0; i < objs.size(); i++)
 				{
 				{
-					if (objs[x].obj && objs[x].obj->id == obj->id)
+					if (objs[i].obj && objs[i].obj->id == obj->id)
 					{
 					{
 						if (fadeout && ADVOPT.objectFading) // object should be faded == erase is delayed until the end of fadeout
 						if (fadeout && ADVOPT.objectFading) // object should be faded == erase is delayed until the end of fadeout
 						{
 						{
-							if (startObjectFade(objs[x], false, int3((si32)i, (si32)j, (si32)k)))
-								objs[x].obj = nullptr;
+							if (startObjectFade(objs[i], false, int3((si32)x, (si32)y, (si32)z)))
+								objs[i].obj = nullptr;
 							else
 							else
-								objs.erase(objs.begin() + x);
+								objs.erase(objs.begin() + i);
 						}
 						}
 						else
 						else
-							objs.erase(objs.begin() + x);
+							objs.erase(objs.begin() + i);
 						break;
 						break;
 					}
 					}
 				}
 				}
@@ -1392,7 +1354,7 @@ CMapHandler::CMapHandler()
 
 
 bool CMapHandler::hasObjectHole(const int3 & pos) const
 bool CMapHandler::hasObjectHole(const int3 & pos) const
 {
 {
-	const TerrainTile2 & tt = ttiles[pos.x][pos.y][pos.z];
+	const TerrainTile2 & tt = ttiles[pos.z][pos.x][pos.y];
 
 
 	for(auto & elem : tt.objects)
 	for(auto & elem : tt.objects)
 	{
 	{
@@ -1411,7 +1373,7 @@ void CMapHandler::getTerrainDescr(const int3 & pos, std::string & out, bool isRM
 		out = CGI->objtypeh->getObjectName(Obj::FAVORABLE_WINDS);
 		out = CGI->objtypeh->getObjectName(Obj::FAVORABLE_WINDS);
 		return;
 		return;
 	}
 	}
-	const TerrainTile2 & tt = ttiles[pos.x][pos.y][pos.z];
+	const TerrainTile2 & tt = ttiles[pos.z][pos.x][pos.y];
 	bool isTile2Terrain = false;
 	bool isTile2Terrain = false;
 	out.clear();
 	out.clear();
 
 

+ 5 - 4
client/mapHandler.h

@@ -92,7 +92,7 @@ struct MapDrawingInfo
 {
 {
 	bool scaled;
 	bool scaled;
 	int3 &topTile; // top-left tile in viewport [in tiles]
 	int3 &topTile; // top-left tile in viewport [in tiles]
-	const std::vector< std::vector< std::vector<ui8> > > * visibilityMap;
+	std::shared_ptr<const boost::multi_array<ui8, 3>> visibilityMap;
 	SDL_Rect * drawBounds; // map rect drawing bounds on screen
 	SDL_Rect * drawBounds; // map rect drawing bounds on screen
 	std::shared_ptr<CAnimation> icons; // holds overlay icons for world view mode
 	std::shared_ptr<CAnimation> icons; // holds overlay icons for world view mode
 	float scale; // map scale for world view mode (only if scaled == true)
 	float scale; // map scale for world view mode (only if scaled == true)
@@ -110,9 +110,10 @@ struct MapDrawingInfo
 
 
 	bool showAllTerrain; //for expert viewEarth
 	bool showAllTerrain; //for expert viewEarth
 
 
-	MapDrawingInfo(int3 &topTile_, const std::vector< std::vector< std::vector<ui8> > > * visibilityMap_, SDL_Rect * drawBounds_, std::shared_ptr<CAnimation> icons_ = nullptr)
+	MapDrawingInfo(int3 &topTile_, std::shared_ptr<const boost::multi_array<ui8, 3>> visibilityMap_, SDL_Rect * drawBounds_, std::shared_ptr<CAnimation> icons_ = nullptr)
 		: scaled(false),
 		: scaled(false),
 		  topTile(topTile_),
 		  topTile(topTile_),
+
 		  visibilityMap(visibilityMap_),
 		  visibilityMap(visibilityMap_),
 		  drawBounds(drawBounds_),
 		  drawBounds(drawBounds_),
 		  icons(icons_),
 		  icons(icons_),
@@ -332,7 +333,7 @@ class CMapHandler
 	void initTerrainGraphics();
 	void initTerrainGraphics();
 	void prepareFOWDefs();
 	void prepareFOWDefs();
 public:
 public:
-	PseudoV< PseudoV< PseudoV<TerrainTile2> > > ttiles; //informations about map tiles
+	boost::multi_array<TerrainTile2, 3> ttiles; //informations about map tiles [z][x][y]
 	int3 sizes; //map size (x = width, y = height, z = number of levels)
 	int3 sizes; //map size (x = width, y = height, z = number of levels)
 	const CMap * map;
 	const CMap * map;
 
 
@@ -368,7 +369,7 @@ public:
 
 
 	//Fog of War cache (not owned)
 	//Fog of War cache (not owned)
 	std::vector<std::shared_ptr<IImage>> FoWfullHide;
 	std::vector<std::shared_ptr<IImage>> FoWfullHide;
-	std::vector<std::vector<std::vector<ui8> > > hideBitmap; //frame indexes (in FoWfullHide) of graphic that should be used to fully hide a tile
+	boost::multi_array<ui8, 3> hideBitmap; //frame indexes (in FoWfullHide) of graphic that should be used to fully hide a tile
 
 
 	std::vector<std::shared_ptr<IImage>> FoWpartialHide;
 	std::vector<std::shared_ptr<IImage>> FoWpartialHide;
 
 

+ 3 - 3
client/windows/CAdvmapInterface.cpp

@@ -380,7 +380,7 @@ void CTerrainRect::show(SDL_Surface * to)
 {
 {
 	if (adventureInt->mode == EAdvMapMode::NORMAL)
 	if (adventureInt->mode == EAdvMapMode::NORMAL)
 	{
 	{
-		MapDrawingInfo info(adventureInt->position, &LOCPLINT->cb->getVisibilityMap(), &pos);
+		MapDrawingInfo info(adventureInt->position, LOCPLINT->cb->getVisibilityMap(), &pos);
 		info.otherheroAnim = true;
 		info.otherheroAnim = true;
 		info.anim = adventureInt->anim;
 		info.anim = adventureInt->anim;
 		info.heroAnim = adventureInt->heroAnim;
 		info.heroAnim = adventureInt->heroAnim;
@@ -407,7 +407,7 @@ void CTerrainRect::showAll(SDL_Surface * to)
 	// world view map is static and doesn't need redraw every frame
 	// world view map is static and doesn't need redraw every frame
 	if (adventureInt->mode == EAdvMapMode::WORLD_VIEW)
 	if (adventureInt->mode == EAdvMapMode::WORLD_VIEW)
 	{
 	{
-		MapDrawingInfo info(adventureInt->position, &LOCPLINT->cb->getVisibilityMap(), &pos, adventureInt->worldViewIcons);
+		MapDrawingInfo info(adventureInt->position, LOCPLINT->cb->getVisibilityMap(), &pos, adventureInt->worldViewIcons);
 		info.scaled = true;
 		info.scaled = true;
 		info.scale = adventureInt->worldViewScale;
 		info.scale = adventureInt->worldViewScale;
 		adventureInt->worldViewOptions.adjustDrawingInfo(info);
 		adventureInt->worldViewOptions.adjustDrawingInfo(info);
@@ -757,7 +757,7 @@ void CAdvMapInt::fworldViewScale4x()
 void CAdvMapInt::fswitchLevel()
 void CAdvMapInt::fswitchLevel()
 {
 {
 	// with support for future multi-level maps :)
 	// with support for future multi-level maps :)
-	int maxLevels = CGI->mh->map->twoLevel ? 2 : 1;
+	int maxLevels = CGI->mh->map->levels();
 	if (maxLevels < 2)
 	if (maxLevels < 2)
 		return;
 		return;
 
 

+ 1 - 1
client/windows/GUIClasses.cpp

@@ -1514,7 +1514,7 @@ void CPuzzleWindow::showAll(SDL_Surface * to)
 	Rect mapRect = genRect(544, 591, pos.x + 8, pos.y + 7);
 	Rect mapRect = genRect(544, 591, pos.x + 8, pos.y + 7);
 	int3 topTile = grailPos - moveInt;
 	int3 topTile = grailPos - moveInt;
 
 
-	MapDrawingInfo info(topTile, &LOCPLINT->cb->getVisibilityMap(), &mapRect);
+	MapDrawingInfo info(topTile, LOCPLINT->cb->getVisibilityMap(), &mapRect);
 	info.puzzleMode = true;
 	info.puzzleMode = true;
 	info.grailPos = grailPos;
 	info.grailPos = grailPos;
 	CGI->mh->drawTerrainRectNew(to, &info);
 	CGI->mh->drawTerrainRectNew(to, &info);

+ 109 - 0
config/schemas/terrain.json

@@ -0,0 +1,109 @@
+{
+	"type":"object",
+	"$schema": "http://json-schema.org/draft-04/schema",
+	"title" : "VCMI terrain format",
+	"description" : "Format used to define new terrains in VCMI",
+	"required" : [ "tiles", "code", "moveCost" ],
+
+	"additionalProperties" : false,
+	"properties":{
+		"moveCost":
+		{
+			"type": "number",
+			"description": "How many movement points needed to move hero"
+		},
+		"minimapUnblocked":
+		{
+			"type": "array",
+			"description": "Color of terrain on minimap without unpassable objects",
+			"minItems": 3,
+			"maxItems": 3,
+			"items":
+			{
+				"type": "number"
+			}
+		},
+		"minimapBlocked":
+		{
+			"type": "array",
+			"description": "Color of terrain on minimap with unpassable objects",
+			"minItems": 3,
+			"maxItems": 3,
+			"items":
+			{
+				"type": "number"
+			}
+		},
+		"music":
+		{
+			"type": "string",
+			"description": "Music filename to play on this terrain on adventure map"
+		},
+		"tiles":
+		{
+			"type": "string",
+			"description": "Name of file with graphicks",
+			"format": "defFile"
+		},
+		"type":
+		{
+			"type": "string",
+			"description": "Type of this terrain. Can be land, water, subterranean or rock",
+			"enum": ["LAND", "WATER", "SUB", "ROCK"]
+		},
+		"rockTerrain":
+		{
+			"type": "string",
+			"description": "The name of tock type terrain which will be used as borders in the underground"
+		},
+		"river":
+		{
+			"type": "string",
+			"description": "River type which should be used for that terrain",
+			"enum": ["", "rw", "ri", "rm", "rl"]
+		},
+		"horseSoundId":
+		{
+			"type": "number",
+			"description": "Id of horse sound to be played when hero is moving across terrain"
+		},
+		"text":
+		{
+			"type": "string",
+			"description": "Text to be shown when mouse if over terrain"
+		},
+		"code":
+		{
+			"type": "string",
+			"description": "Two-letters unique indentifier for this terrain. Used for terrain serializaion"
+		},
+		"battleFields":
+		{
+			"type": "array",
+			"description": "array of battleFields for this terrain",
+			"items":
+			{
+				"type": "string"
+			}
+		},
+		"prohibitTransitions":
+		{
+			"type": "array",
+			"description": "array or terrain names, which is prohibited to make transition from/to",
+			"items":
+			{
+				"type": "string"
+			}
+		},
+		"transitionRequired":
+		{
+			"type": "boolean",
+			"description": "If sand/dirt transition required from/to other terrains"
+		},
+		"terrainViewPatterns":
+		{
+			"type": "string",
+			"description": "Can be normal, dirt, water, rock"
+		}
+	}
+}

+ 4 - 0
include/vcmi/Services.h

@@ -32,10 +32,12 @@ namespace spells
 	}
 	}
 }
 }
 
 
+#if SCRIPTING_ENABLED
 namespace scripting
 namespace scripting
 {
 {
 	class Service;
 	class Service;
 }
 }
+#endif
 
 
 class DLL_LINKAGE Services
 class DLL_LINKAGE Services
 {
 {
@@ -47,7 +49,9 @@ public:
 	virtual const FactionService * factions() const = 0;
 	virtual const FactionService * factions() const = 0;
 	virtual const HeroClassService * heroClasses() const = 0;
 	virtual const HeroClassService * heroClasses() const = 0;
 	virtual const HeroTypeService * heroTypes() const = 0;
 	virtual const HeroTypeService * heroTypes() const = 0;
+#if SCRIPTING_ENABLED
 	virtual const scripting::Service * scripts() const = 0;
 	virtual const scripting::Service * scripts() const = 0;
+#endif
 	virtual const spells::Service * spells() const = 0;
 	virtual const spells::Service * spells() const = 0;
 	virtual const SkillService * skills() const = 0;
 	virtual const SkillService * skills() const = 0;
 	virtual const BattleFieldService * battlefields() const = 0;
 	virtual const BattleFieldService * battlefields() const = 0;

+ 2 - 0
include/vcmi/scripting/Service.h

@@ -10,6 +10,7 @@
 
 
 #pragma once
 #pragma once
 
 
+#if SCRIPTING_ENABLED
 #include <vcmi/Environment.h>
 #include <vcmi/Environment.h>
 
 
 class Services;
 class Services;
@@ -78,3 +79,4 @@ public:
 
 
 
 
 }
 }
+#endif

+ 1 - 1
launcher/mainwindow_moc.cpp

@@ -28,7 +28,7 @@ void MainWindow::load()
 	QDir::setCurrent(QApplication::applicationDirPath());
 	QDir::setCurrent(QApplication::applicationDirPath());
 
 
 	console = new CConsoleHandler();
 	console = new CConsoleHandler();
-	CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() / "VCMI_Launcher_log.txt", console);
+	CBasicLogConfigurator logConfig(VCMIDirs::get().userLogsPath() / "VCMI_Launcher_log.txt", console);
 	logConfig.configureDefault();
 	logConfig.configureDefault();
 
 
 	CResourceHandler::initialize();
 	CResourceHandler::initialize();

+ 70 - 27
launcher/modManager/cmodlist.cpp

@@ -12,29 +12,53 @@
 
 
 #include "../../lib/JsonNode.h"
 #include "../../lib/JsonNode.h"
 #include "../../lib/filesystem/CFileInputStream.h"
 #include "../../lib/filesystem/CFileInputStream.h"
+#include "../../lib/GameConstants.h"
 
 
-bool CModEntry::compareVersions(QString lesser, QString greater)
+namespace
 {
 {
-	static const int maxSections = 3; // versions consist from up to 3 sections, major.minor.patch
-
-	QStringList lesserList = lesser.split(".");
-	QStringList greaterList = greater.split(".");
-
-	assert(lesserList.size() <= maxSections);
-	assert(greaterList.size() <= maxSections);
+bool isCompatible(const QString & verMin, const QString & verMax)
+{
+	const int maxSections = 3; // versions consist from up to 3 sections, major.minor.patch
+	QVersionNumber vcmiVersion(GameConstants::VCMI_VERSION_MAJOR,
+							   GameConstants::VCMI_VERSION_MINOR,
+							   GameConstants::VCMI_VERSION_PATCH);
+	
+	auto versionMin = QVersionNumber::fromString(verMin);
+	auto versionMax = QVersionNumber::fromString(verMax);
+	
+	auto buildVersion = [maxSections](QVersionNumber & ver)
+	{
+		if(ver.segmentCount() < maxSections)
+		{
+			auto segments = ver.segments();
+			for(int i = segments.size() - 1; i < maxSections; ++i)
+				segments.append(0);
+			ver = QVersionNumber(segments);
+		}
+	};
 
 
-	for(int i = 0; i < maxSections; i++)
+	if(!versionMin.isNull())
 	{
 	{
-		if(greaterList.size() <= i) // 1.1.1 > 1.1
+		buildVersion(versionMin);
+		if(vcmiVersion < versionMin)
 			return false;
 			return false;
-
-		if(lesserList.size() <= i) // 1.1 < 1.1.1
-			return true;
-
-		if(lesserList[i].toInt() != greaterList[i].toInt())
-			return lesserList[i].toInt() < greaterList[i].toInt(); // 1.1 < 1.2
 	}
 	}
-	return false;
+	
+	if(!versionMax.isNull())
+	{
+		buildVersion(versionMax);
+		if(vcmiVersion > versionMax)
+			return false;
+	}
+	return true;
+}
+}
+
+bool CModEntry::compareVersions(QString lesser, QString greater)
+{
+	auto versionLesser = QVersionNumber::fromString(lesser);
+	auto versionGreater = QVersionNumber::fromString(greater);
+	return versionLesser < versionGreater;
 }
 }
 
 
 QString CModEntry::sizeToString(double size)
 QString CModEntry::sizeToString(double size)
@@ -92,6 +116,15 @@ bool CModEntry::isUpdateable() const
 	return false;
 	return false;
 }
 }
 
 
+bool CModEntry::isCompatible() const
+{
+	if(!isInstalled())
+		return false;
+
+	auto compatibility = localData["compatibility"].toMap();
+	return ::isCompatible(compatibility["min"].toString(), compatibility["max"].toString());
+}
+
 bool CModEntry::isEssential() const
 bool CModEntry::isEssential() const
 {
 {
 	return getValue("storedLocaly").toBool();
 	return getValue("storedLocaly").toBool();
@@ -102,6 +135,11 @@ bool CModEntry::isInstalled() const
 	return !localData.isEmpty();
 	return !localData.isEmpty();
 }
 }
 
 
+bool CModEntry::isValid() const
+{
+	return !localData.isEmpty() || !repository.isEmpty();
+}
+
 int CModEntry::getModStatus() const
 int CModEntry::getModStatus() const
 {
 {
 	int status = 0;
 	int status = 0;
@@ -193,7 +231,11 @@ static QVariant getValue(QVariant input, QString path)
 		QString remainder = "/" + path.section('/', 2, -1);
 		QString remainder = "/" + path.section('/', 2, -1);
 
 
 		entryName.remove(0, 1);
 		entryName.remove(0, 1);
-		return getValue(input.toMap().value(entryName), remainder);
+		QMap<QString, QString> keyNormalize;
+		for(auto & key : input.toMap().keys())
+			keyNormalize[key.toLower()] = key;
+
+		return getValue(input.toMap().value(keyNormalize[entryName]), remainder);
 	}
 	}
 	else
 	else
 	{
 	{
@@ -203,6 +245,7 @@ static QVariant getValue(QVariant input, QString path)
 
 
 CModEntry CModList::getMod(QString modname) const
 CModEntry CModList::getMod(QString modname) const
 {
 {
+	modname = modname.toLower();
 	QVariantMap repo;
 	QVariantMap repo;
 	QVariantMap local = localModList[modname].toMap();
 	QVariantMap local = localModList[modname].toMap();
 	QVariantMap settings;
 	QVariantMap settings;
@@ -246,14 +289,14 @@ CModEntry CModList::getMod(QString modname) const
 		QVariant repoVal = getValue(entry, path);
 		QVariant repoVal = getValue(entry, path);
 		if(repoVal.isValid())
 		if(repoVal.isValid())
 		{
 		{
-			if(repo.empty())
-			{
-				repo = repoVal.toMap();
-			}
-			else
+			auto repoValMap = repoVal.toMap();
+			auto compatibility = repoValMap["compatibility"].toMap();
+			if(isCompatible(compatibility["min"].toString(), compatibility["max"].toString()))
 			{
 			{
-				if(CModEntry::compareVersions(repo["version"].toString(), repoVal.toMap()["version"].toString()))
-					repo = repoVal.toMap();
+				if(repo.empty() || CModEntry::compareVersions(repo["version"].toString(), repoValMap["version"].toString()))
+				{
+					repo = repoValMap;
+				}
 			}
 			}
 		}
 		}
 	}
 	}
@@ -297,12 +340,12 @@ QVector<QString> CModList::getModList() const
 	{
 	{
 		for(auto it = repo.begin(); it != repo.end(); it++)
 		for(auto it = repo.begin(); it != repo.end(); it++)
 		{
 		{
-			knownMods.insert(it.key());
+			knownMods.insert(it.key().toLower());
 		}
 		}
 	}
 	}
 	for(auto it = localModList.begin(); it != localModList.end(); it++)
 	for(auto it = localModList.begin(); it != localModList.end(); it++)
 	{
 	{
-		knownMods.insert(it.key());
+		knownMods.insert(it.key().toLower());
 	}
 	}
 
 
 	for(auto entry : knownMods)
 	for(auto entry : knownMods)

+ 9 - 9
launcher/modManager/cmodlistview_moc.ui

@@ -253,7 +253,7 @@
             </property>
             </property>
             <property name="html">
             <property name="html">
              <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
              <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
-&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;meta charset=&quot;utf-8&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
 p, li { white-space: pre-wrap; }
 p, li { white-space: pre-wrap; }
 &lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
 &lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
 &lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
 &lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@@ -386,7 +386,7 @@ p, li { white-space: pre-wrap; }
       </spacer>
       </spacer>
      </item>
      </item>
      <item>
      <item>
-      <widget class="QPushButton" name="enableButton">
+      <widget class="QPushButton" name="uninstallButton">
        <property name="sizePolicy">
        <property name="sizePolicy">
         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
          <horstretch>0</horstretch>
          <horstretch>0</horstretch>
@@ -406,12 +406,12 @@ p, li { white-space: pre-wrap; }
         </size>
         </size>
        </property>
        </property>
        <property name="text">
        <property name="text">
-        <string>Enable</string>
+        <string>Uninstall</string>
        </property>
        </property>
       </widget>
       </widget>
      </item>
      </item>
      <item>
      <item>
-      <widget class="QPushButton" name="disableButton">
+      <widget class="QPushButton" name="enableButton">
        <property name="sizePolicy">
        <property name="sizePolicy">
         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
          <horstretch>0</horstretch>
          <horstretch>0</horstretch>
@@ -431,12 +431,12 @@ p, li { white-space: pre-wrap; }
         </size>
         </size>
        </property>
        </property>
        <property name="text">
        <property name="text">
-        <string>Disable</string>
+        <string>Enable</string>
        </property>
        </property>
       </widget>
       </widget>
      </item>
      </item>
      <item>
      <item>
-      <widget class="QPushButton" name="updateButton">
+      <widget class="QPushButton" name="disableButton">
        <property name="sizePolicy">
        <property name="sizePolicy">
         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
          <horstretch>0</horstretch>
          <horstretch>0</horstretch>
@@ -456,12 +456,12 @@ p, li { white-space: pre-wrap; }
         </size>
         </size>
        </property>
        </property>
        <property name="text">
        <property name="text">
-        <string>Update</string>
+        <string>Disable</string>
        </property>
        </property>
       </widget>
       </widget>
      </item>
      </item>
      <item>
      <item>
-      <widget class="QPushButton" name="uninstallButton">
+      <widget class="QPushButton" name="updateButton">
        <property name="sizePolicy">
        <property name="sizePolicy">
         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
          <horstretch>0</horstretch>
          <horstretch>0</horstretch>
@@ -481,7 +481,7 @@ p, li { white-space: pre-wrap; }
         </size>
         </size>
        </property>
        </property>
        <property name="text">
        <property name="text">
-        <string>Uninstall</string>
+        <string>Update</string>
        </property>
        </property>
       </widget>
       </widget>
      </item>
      </item>

+ 13 - 12
lib/CGameInfoCallback.cpp

@@ -502,28 +502,29 @@ const TerrainTile * CGameInfoCallback::getTile( int3 tile, bool verbose) const
 }
 }
 
 
 //TODO: typedef?
 //TODO: typedef?
-std::shared_ptr<boost::multi_array<TerrainTile*, 3>> CGameInfoCallback::getAllVisibleTiles() const
+std::shared_ptr<const boost::multi_array<TerrainTile*, 3>> CGameInfoCallback::getAllVisibleTiles() const
 {
 {
 	assert(player.is_initialized());
 	assert(player.is_initialized());
 	auto team = getPlayerTeam(player.get());
 	auto team = getPlayerTeam(player.get());
 
 
 	size_t width = gs->map->width;
 	size_t width = gs->map->width;
 	size_t height = gs->map->height;
 	size_t height = gs->map->height;
-	size_t levels = (gs->map->twoLevel ? 2 : 1);
+	size_t levels = gs->map->levels();
 
 
+	auto ptr = new boost::multi_array<TerrainTile*, 3>(boost::extents[levels][width][height]);
 
 
-	boost::multi_array<TerrainTile*, 3> tileArray(boost::extents[width][height][levels]);
-
-	for (size_t x = 0; x < width; x++)
-		for (size_t y = 0; y < height; y++)
-			for (size_t z = 0; z < levels; z++)
+	int3 tile;
+	for(tile.z = 0; tile.z < levels; tile.z++)
+		for(tile.x = 0; tile.x < width; tile.x++)
+			for(tile.y = 0; tile.y < height; tile.y++)
 			{
 			{
-				if (team->fogOfWarMap[x][y][z])
-					tileArray[x][y][z] = &gs->map->getTile(int3((si32)x, (si32)y, (si32)z));
+				if ((*team->fogOfWarMap)[tile.z][tile.x][tile.y])
+					(*ptr)[tile.z][tile.x][tile.y] = &gs->map->getTile(tile);
 				else
 				else
-					tileArray[x][y][z] = nullptr;
+					(*ptr)[tile.z][tile.x][tile.y] = nullptr;
 			}
 			}
-	return std::make_shared<boost::multi_array<TerrainTile*, 3>>(tileArray);
+
+	return std::shared_ptr<const boost::multi_array<TerrainTile*, 3>>(ptr);
 }
 }
 
 
 EBuildingState::EBuildingState CGameInfoCallback::canBuildStructure( const CGTownInstance *t, BuildingID ID )
 EBuildingState::EBuildingState CGameInfoCallback::canBuildStructure( const CGTownInstance *t, BuildingID ID )
@@ -694,7 +695,7 @@ CGameInfoCallback::CGameInfoCallback(CGameState *GS, boost::optional<PlayerColor
 	player = Player;
 	player = Player;
 }
 }
 
 
-const std::vector< std::vector< std::vector<ui8> > > & CPlayerSpecificInfoCallback::getVisibilityMap() const
+std::shared_ptr<const boost::multi_array<ui8, 3>> CPlayerSpecificInfoCallback::getVisibilityMap() const
 {
 {
 	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
 	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
 	return gs->getPlayerTeam(*player)->fogOfWarMap;
 	return gs->getPlayerTeam(*player)->fogOfWarMap;

+ 2 - 2
lib/CGameInfoCallback.h

@@ -185,7 +185,7 @@ public:
 	virtual const CMapHeader * getMapHeader()const;
 	virtual const CMapHeader * getMapHeader()const;
 	virtual int3 getMapSize() const; //returns size of map - z is 1 for one - level map and 2 for two level map
 	virtual int3 getMapSize() const; //returns size of map - z is 1 for one - level map and 2 for two level map
 	virtual const TerrainTile * getTile(int3 tile, bool verbose = true) const;
 	virtual const TerrainTile * getTile(int3 tile, bool verbose = true) const;
-	virtual std::shared_ptr<boost::multi_array<TerrainTile*, 3>> getAllVisibleTiles() const;
+	virtual std::shared_ptr<const boost::multi_array<TerrainTile*, 3>> getAllVisibleTiles() const;
 	virtual bool isInTheMap(const int3 &pos) const;
 	virtual bool isInTheMap(const int3 &pos) const;
 	virtual void getVisibleTilesInRange(std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula = int3::DIST_2D) const;
 	virtual void getVisibleTilesInRange(std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula = int3::DIST_2D) const;
 	virtual void calculatePaths(std::shared_ptr<PathfinderConfig> config);
 	virtual void calculatePaths(std::shared_ptr<PathfinderConfig> config);
@@ -235,7 +235,7 @@ public:
 
 
 	virtual int getResourceAmount(Res::ERes type) const;
 	virtual int getResourceAmount(Res::ERes type) const;
 	virtual TResources getResourceAmount() const;
 	virtual TResources getResourceAmount() const;
-	virtual const std::vector< std::vector< std::vector<ui8> > > & getVisibilityMap()const; //returns visibility map
+	virtual std::shared_ptr<const boost::multi_array<ui8, 3>> getVisibilityMap() const; //returns visibility map
 	//virtual const PlayerSettings * getPlayerSettings(PlayerColor color) const;
 	//virtual const PlayerSettings * getPlayerSettings(PlayerColor color) const;
 };
 };
 
 

+ 2 - 0
lib/CGameInterface.cpp

@@ -126,10 +126,12 @@ std::shared_ptr<CBattleGameInterface> CDynLibHandler::getNewBattleAI(std::string
 	return createAnyAI<CBattleGameInterface>(dllname, "GetNewBattleAI");
 	return createAnyAI<CBattleGameInterface>(dllname, "GetNewBattleAI");
 }
 }
 
 
+#if SCRIPTING_ENABLED
 std::shared_ptr<scripting::Module> CDynLibHandler::getNewScriptingModule(const boost::filesystem::path & dllname)
 std::shared_ptr<scripting::Module> CDynLibHandler::getNewScriptingModule(const boost::filesystem::path & dllname)
 {
 {
 	return createAny<scripting::Module>(dllname, "GetNewModule");
 	return createAny<scripting::Module>(dllname, "GetNewModule");
 }
 }
+#endif
 
 
 BattleAction CGlobalAI::activeStack(const CStack * stack)
 BattleAction CGlobalAI::activeStack(const CStack * stack)
 {
 {

+ 6 - 0
lib/CGameInterface.h

@@ -56,10 +56,14 @@ class CSaveFile;
 class BinaryDeserializer;
 class BinaryDeserializer;
 class BinarySerializer;
 class BinarySerializer;
 struct ArtifactLocation;
 struct ArtifactLocation;
+
+#if SCRIPTING_ENABLED
 namespace scripting
 namespace scripting
 {
 {
 	class Module;
 	class Module;
 }
 }
+#endif
+
 
 
 class DLL_LINKAGE CBattleGameInterface : public IBattleEventsReceiver
 class DLL_LINKAGE CBattleGameInterface : public IBattleEventsReceiver
 {
 {
@@ -110,7 +114,9 @@ class DLL_LINKAGE CDynLibHandler
 public:
 public:
 	static std::shared_ptr<CGlobalAI> getNewAI(std::string dllname);
 	static std::shared_ptr<CGlobalAI> getNewAI(std::string dllname);
 	static std::shared_ptr<CBattleGameInterface> getNewBattleAI(std::string dllname);
 	static std::shared_ptr<CBattleGameInterface> getNewBattleAI(std::string dllname);
+#if SCRIPTING_ENABLED
 	static std::shared_ptr<scripting::Module> getNewScriptingModule(const boost::filesystem::path & dllname);
 	static std::shared_ptr<scripting::Module> getNewScriptingModule(const boost::filesystem::path & dllname);
+#endif
 };
 };
 
 
 class DLL_LINKAGE CGlobalAI : public CGameInterface // AI class (to derivate)
 class DLL_LINKAGE CGlobalAI : public CGameInterface // AI class (to derivate)

+ 17 - 21
lib/CGameState.cpp

@@ -954,19 +954,20 @@ void CGameState::initGrailPosition()
 		static const int BORDER_WIDTH = 9; // grail must be at least 9 tiles away from border
 		static const int BORDER_WIDTH = 9; // grail must be at least 9 tiles away from border
 
 
 		// add all not blocked tiles in range
 		// add all not blocked tiles in range
-		for (int i = BORDER_WIDTH; i < map->width - BORDER_WIDTH ; i++)
+
+		for (int z = 0; z < map->levels(); z++)
 		{
 		{
-			for (int j = BORDER_WIDTH; j < map->height - BORDER_WIDTH; j++)
+			for(int x = BORDER_WIDTH; x < map->width - BORDER_WIDTH ; x++)
 			{
 			{
-				for (int k = 0; k < (map->twoLevel ? 2 : 1); k++)
+				for(int y = BORDER_WIDTH; y < map->height - BORDER_WIDTH; y++)
 				{
 				{
-					const TerrainTile &t = map->getTile(int3(i, j, k));
+					const TerrainTile &t = map->getTile(int3(x, y, z));
 					if(!t.blocked
 					if(!t.blocked
 						&& !t.visitable
 						&& !t.visitable
 						&& t.terType.isLand()
 						&& t.terType.isLand()
 						&& t.terType.isPassable()
 						&& t.terType.isPassable()
-						&& (int)map->grailPos.dist2dSQ(int3(i, j, k)) <= (map->grailRadius * map->grailRadius))
-						allowedPos.push_back(int3(i,j,k));
+						&& (int)map->grailPos.dist2dSQ(int3(x, y, z)) <= (map->grailRadius * map->grailRadius))
+						allowedPos.push_back(int3(x,y,z));
 				}
 				}
 			}
 			}
 		}
 		}
@@ -1593,20 +1594,13 @@ void CGameState::giveCampaignBonusToHero(CGHeroInstance * hero)
 void CGameState::initFogOfWar()
 void CGameState::initFogOfWar()
 {
 {
 	logGlobal->debug("\tFog of war"); //FIXME: should be initialized after all bonuses are set
 	logGlobal->debug("\tFog of war"); //FIXME: should be initialized after all bonuses are set
+
+	int layers = map->levels();
 	for(auto & elem : teams)
 	for(auto & elem : teams)
 	{
 	{
-		elem.second.fogOfWarMap.resize(map->width);
-		for(int g=0; g<map->width; ++g)
-			elem.second.fogOfWarMap[g].resize(map->height);
-
-		for(int g=-0; g<map->width; ++g)
-			for(int h=0; h<map->height; ++h)
-				elem.second.fogOfWarMap[g][h].resize(map->twoLevel ? 2 : 1, 0);
-
-		for(int g=0; g<map->width; ++g)
-			for(int h=0; h<map->height; ++h)
-				for(int v = 0; v < (map->twoLevel ? 2 : 1); ++v)
-					elem.second.fogOfWarMap[g][h][v] = 0;
+		auto fow = elem.second.fogOfWarMap;
+		fow->resize(boost::extents[layers][map->width][map->height]);
+		std::fill(fow->data(), fow->data() + fow->num_elements(), 0);
 
 
 		for(CGObjectInstance *obj : map->objects)
 		for(CGObjectInstance *obj : map->objects)
 		{
 		{
@@ -1616,7 +1610,7 @@ void CGameState::initFogOfWar()
 			getTilesInRange(tiles, obj->getSightCenter(), obj->getSightRadius(), obj->tempOwner, 1);
 			getTilesInRange(tiles, obj->getSightCenter(), obj->getSightRadius(), obj->tempOwner, 1);
 			for(int3 tile : tiles)
 			for(int3 tile : tiles)
 			{
 			{
-				elem.second.fogOfWarMap[tile.x][tile.y][tile.z] = 1;
+				(*elem.second.fogOfWarMap)[tile.z][tile.x][tile.y] = 1;
 			}
 			}
 		}
 		}
 	}
 	}
@@ -2019,6 +2013,7 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out)
 
 
 void CGameState::calculatePaths(std::shared_ptr<PathfinderConfig> config)
 void CGameState::calculatePaths(std::shared_ptr<PathfinderConfig> config)
 {
 {
+	//FIXME: creating pathfinder is costly, maybe reset / clear is enough?
 	CPathfinder pathfinder(this, config);
 	CPathfinder pathfinder(this, config);
 	pathfinder.calculatePaths();
 	pathfinder.calculatePaths();
 }
 }
@@ -2080,7 +2075,7 @@ std::vector<CGObjectInstance*> CGameState::guardingCreatures (int3 pos) const
 
 
 int3 CGameState::guardingCreaturePosition (int3 pos) const
 int3 CGameState::guardingCreaturePosition (int3 pos) const
 {
 {
-	return gs->map->guardingCreaturePositions[pos.x][pos.y][pos.z];
+	return gs->map->guardingCreaturePositions[pos.z][pos.x][pos.y];
 }
 }
 
 
 void CGameState::updateRumor()
 void CGameState::updateRumor()
@@ -2164,7 +2159,7 @@ bool CGameState::isVisible(int3 pos, PlayerColor player)
 	if(player.isSpectator())
 	if(player.isSpectator())
 		return true;
 		return true;
 
 
-	return getPlayerTeam(player)->fogOfWarMap[pos.x][pos.y][pos.z];
+	return (*getPlayerTeam(player)->fogOfWarMap)[pos.z][pos.x][pos.y];
 }
 }
 
 
 bool CGameState::isVisible( const CGObjectInstance *obj, boost::optional<PlayerColor> player )
 bool CGameState::isVisible( const CGObjectInstance *obj, boost::optional<PlayerColor> player )
@@ -3096,6 +3091,7 @@ int ArmyDescriptor::getStrength() const
 TeamState::TeamState()
 TeamState::TeamState()
 {
 {
 	setNodeType(TEAM);
 	setNodeType(TEAM);
+	fogOfWarMap = std::make_shared<boost::multi_array<ui8, 3>>();
 }
 }
 
 
 TeamState::TeamState(TeamState && other):
 TeamState::TeamState(TeamState && other):

+ 18 - 6
lib/CModHandler.cpp

@@ -434,7 +434,9 @@ void CContentHandler::init()
 	handlers.insert(std::make_pair("spells", ContentTypeHandler(VLC->spellh, "spell")));
 	handlers.insert(std::make_pair("spells", ContentTypeHandler(VLC->spellh, "spell")));
 	handlers.insert(std::make_pair("skills", ContentTypeHandler(VLC->skillh, "skill")));
 	handlers.insert(std::make_pair("skills", ContentTypeHandler(VLC->skillh, "skill")));
 	handlers.insert(std::make_pair("templates", ContentTypeHandler((IHandlerBase *)VLC->tplh, "template")));
 	handlers.insert(std::make_pair("templates", ContentTypeHandler((IHandlerBase *)VLC->tplh, "template")));
+#if SCRIPTING_ENABLED
 	handlers.insert(std::make_pair("scripts", ContentTypeHandler(VLC->scriptHandler, "script")));
 	handlers.insert(std::make_pair("scripts", ContentTypeHandler(VLC->scriptHandler, "script")));
+#endif
 	handlers.insert(std::make_pair("battlefields", ContentTypeHandler(VLC->battlefieldsHandler, "battlefield")));
 	handlers.insert(std::make_pair("battlefields", ContentTypeHandler(VLC->battlefieldsHandler, "battlefield")));
 	handlers.insert(std::make_pair("obstacles", ContentTypeHandler(VLC->obstacleHandler, "obstacle")));
 	handlers.insert(std::make_pair("obstacles", ContentTypeHandler(VLC->obstacleHandler, "obstacle")));
 	//TODO: any other types of moddables?
 	//TODO: any other types of moddables?
@@ -544,10 +546,14 @@ CModInfo::Version CModInfo::Version::fromString(std::string from)
 	{
 	{
 		auto pointPos = from.find('.');
 		auto pointPos = from.find('.');
 		major = std::stoi(from.substr(0, pointPos));
 		major = std::stoi(from.substr(0, pointPos));
-		from = from.substr(pointPos);
-		pointPos = from.find('.');
-		minor = std::stoi(from.substr(0, pointPos));
-		patch = std::stoi(from.substr(pointPos));
+		if(pointPos != std::string::npos)
+		{
+			from = from.substr(pointPos + 1);
+			pointPos = from.find('.');
+			minor = std::stoi(from.substr(0, pointPos));
+			if(pointPos != std::string::npos)
+				patch = std::stoi(from.substr(pointPos + 1));
+		}
 	}
 	}
 	catch(const std::invalid_argument & e)
 	catch(const std::invalid_argument & e)
 	{
 	{
@@ -650,8 +656,12 @@ void CModInfo::loadLocalData(const JsonNode & data)
 	}
 	}
 	
 	
 	//check compatibility
 	//check compatibility
-	enabled &= vcmiCompatibleMin.isNull() || Version::GameVersion().compatible(vcmiCompatibleMin);
-	enabled &= vcmiCompatibleMax.isNull() || vcmiCompatibleMax.compatible(Version::GameVersion());
+	bool wasEnabled = enabled;
+	enabled = enabled && (vcmiCompatibleMin.isNull() || Version::GameVersion().compatible(vcmiCompatibleMin));
+	enabled = enabled && (vcmiCompatibleMax.isNull() || vcmiCompatibleMax.compatible(Version::GameVersion()));
+
+	if(wasEnabled && !enabled)
+		logGlobal->warn("Mod %s is incompatible with current version of VCMI and cannot be enabled", name);
 
 
 	if (enabled)
 	if (enabled)
 		validation = validated ? PASSED : PENDING;
 		validation = validated ? PASSED : PENDING;
@@ -1037,7 +1047,9 @@ void CModHandler::load()
 	for(const TModID & modName : activeMods)
 	for(const TModID & modName : activeMods)
 		content->load(allMods[modName]);
 		content->load(allMods[modName]);
 
 
+#if SCRIPTING_ENABLED
 	VLC->scriptHandler->performRegistration(VLC);//todo: this should be done before any other handlers load
 	VLC->scriptHandler->performRegistration(VLC);//todo: this should be done before any other handlers load
+#endif
 
 
 	content->loadCustom();
 	content->loadCustom();
 
 

+ 21 - 40
lib/CModHandler.h

@@ -212,7 +212,8 @@ public:
 	/// version of the mod
 	/// version of the mod
 	Version version;
 	Version version;
 	
 	
-	///The  vcmi versions compatible with the mod
+	/// vcmi versions compatible with the mod
+
 	Version vcmiCompatibleMin, vcmiCompatibleMax;
 	Version vcmiCompatibleMin, vcmiCompatibleMax;
 
 
 	/// list of mods that should be loaded before this one
 	/// list of mods that should be loaded before this one
@@ -240,19 +241,6 @@ public:
 	static std::string getModDir(std::string name);
 	static std::string getModDir(std::string name);
 	static std::string getModFile(std::string name);
 	static std::string getModFile(std::string name);
 
 
-	//TODO: remove as soon as backward compatilibity for versions earlier 806 is not preserved.
-	template <typename Handler> void serialize(Handler &h, const int ver)
-	{
-		h & identifier;
-		h & description;
-		h & name;
-		h & dependencies;
-		h & conflicts;
-		h & config;
-		h & checksum;
-		h & validation;
-		h & enabled;
-	}
 private:
 private:
 	void loadLocalData(const JsonNode & data);
 	void loadLocalData(const JsonNode & data);
 };
 };
@@ -374,41 +362,34 @@ public:
 
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
-		if(version < 806)
+		if(h.saving)
 		{
 		{
-			h & allMods; //don't serialize mods
 			h & activeMods;
 			h & activeMods;
+			for(const auto & m : activeMods)
+
+				h & allMods[m].version;
 		}
 		}
 		else
 		else
 		{
 		{
-			if(h.saving)
+			std::vector<TModID> newActiveMods;
+			h & newActiveMods;
+			for(auto & m : newActiveMods)
 			{
 			{
-				h & activeMods;
-				for(auto & m : activeMods)
-					h & allMods[m].version;
-			}
-			else
-			{
-				std::vector<TModID> newActiveMods;
-				h & newActiveMods;
-				for(auto & m : newActiveMods)
+				if(!allMods.count(m))
+					throw Incompatibility(m + " unkown mod");
+				
+				CModInfo::Version mver;
+				h & mver;
+				if(!allMods[m].version.isNull() && !mver.isNull() && !allMods[m].version.compatible(mver))
 				{
 				{
-					if(!allMods.count(m))
-						throw Incompatibility(m + " unkown mod");
-					
-					CModInfo::Version mver;
-					h & mver;
-					if(!allMods[m].version.isNull() && !mver.isNull() && !allMods[m].version.compatible(mver))
-					{
-						std::string err = allMods[m].name +
-						": version needed " + mver.toString() +
-						"but you have installed " + allMods[m].version.toString();
-						throw Incompatibility(err);
-					}
-					allMods[m].enabled = true;
+					std::string err = allMods[m].name +
+					": version needed " + mver.toString() +
+					"but you have installed " + allMods[m].version.toString();
+					throw Incompatibility(err);
 				}
 				}
-				std::swap(activeMods, newActiveMods);
+				allMods[m].enabled = true;
 			}
 			}
+			std::swap(activeMods, newActiveMods);
 		}
 		}
 				
 				
 		h & settings;
 		h & settings;

+ 7 - 7
lib/CPathfinder.cpp

@@ -33,17 +33,17 @@ void NodeStorage::initialize(const PathfinderOptions & options, const CGameState
 	int3 pos;
 	int3 pos;
 	const PlayerColor player = out.hero->tempOwner;
 	const PlayerColor player = out.hero->tempOwner;
 	const int3 sizes = gs->getMapSize();
 	const int3 sizes = gs->getMapSize();
-	const auto & fow = static_cast<const CGameInfoCallback *>(gs)->getPlayerTeam(player)->fogOfWarMap;
+	const auto fow = static_cast<const CGameInfoCallback *>(gs)->getPlayerTeam(player)->fogOfWarMap;
 
 
 	//make 200% sure that these are loop invariants (also a bit shorter code), let compiler do the rest(loop unswitching)
 	//make 200% sure that these are loop invariants (also a bit shorter code), let compiler do the rest(loop unswitching)
 	const bool useFlying = options.useFlying;
 	const bool useFlying = options.useFlying;
 	const bool useWaterWalking = options.useWaterWalking;
 	const bool useWaterWalking = options.useWaterWalking;
 
 
-	for(pos.x=0; pos.x < sizes.x; ++pos.x)
+	for(pos.z=0; pos.z < sizes.z; ++pos.z)
 	{
 	{
-		for(pos.y=0; pos.y < sizes.y; ++pos.y)
+		for(pos.x=0; pos.x < sizes.x; ++pos.x)
 		{
 		{
-			for(pos.z=0; pos.z < sizes.z; ++pos.z)
+			for(pos.y=0; pos.y < sizes.y; ++pos.y)
 			{
 			{
 				const TerrainTile * tile = &gs->map->getTile(pos);
 				const TerrainTile * tile = &gs->map->getTile(pos);
 				if(tile->terType.isWater())
 				if(tile->terType.isWater())
@@ -1304,7 +1304,7 @@ void CGPath::convert(ui8 mode)
 CPathsInfo::CPathsInfo(const int3 & Sizes, const CGHeroInstance * hero_)
 CPathsInfo::CPathsInfo(const int3 & Sizes, const CGHeroInstance * hero_)
 	: sizes(Sizes), hero(hero_)
 	: sizes(Sizes), hero(hero_)
 {
 {
-	nodes.resize(boost::extents[sizes.x][sizes.y][sizes.z][ELayer::NUM_LAYERS]);
+	nodes.resize(boost::extents[ELayer::NUM_LAYERS][sizes.z][sizes.x][sizes.y]);
 }
 }
 
 
 CPathsInfo::~CPathsInfo() = default;
 CPathsInfo::~CPathsInfo() = default;
@@ -1336,11 +1336,11 @@ bool CPathsInfo::getPath(CGPath & out, const int3 & dst) const
 
 
 const CGPathNode * CPathsInfo::getNode(const int3 & coord) const
 const CGPathNode * CPathsInfo::getNode(const int3 & coord) const
 {
 {
-	auto landNode = &nodes[coord.x][coord.y][coord.z][ELayer::LAND];
+	auto landNode = &nodes[ELayer::LAND][coord.z][coord.x][coord.y];
 	if(landNode->reachable())
 	if(landNode->reachable())
 		return landNode;
 		return landNode;
 	else
 	else
-		return &nodes[coord.x][coord.y][coord.z][ELayer::SAIL];
+		return &nodes[ELayer::SAIL][coord.z][coord.x][coord.y];
 }
 }
 
 
 PathNodeInfo::PathNodeInfo()
 PathNodeInfo::PathNodeInfo()

+ 2 - 2
lib/CPathfinder.h

@@ -178,7 +178,7 @@ struct DLL_LINKAGE CPathsInfo
 	const CGHeroInstance * hero;
 	const CGHeroInstance * hero;
 	int3 hpos;
 	int3 hpos;
 	int3 sizes;
 	int3 sizes;
-	boost::multi_array<CGPathNode, 4> nodes; //[w][h][level][layer]
+	boost::multi_array<CGPathNode, 4> nodes; //[layer][level][w][h]
 
 
 	CPathsInfo(const int3 & Sizes, const CGHeroInstance * hero_);
 	CPathsInfo(const int3 & Sizes, const CGHeroInstance * hero_);
 	~CPathsInfo();
 	~CPathsInfo();
@@ -189,7 +189,7 @@ struct DLL_LINKAGE CPathsInfo
 	STRONG_INLINE
 	STRONG_INLINE
 	CGPathNode * getNode(const int3 & coord, const ELayer layer)
 	CGPathNode * getNode(const int3 & coord, const ELayer layer)
 	{
 	{
-		return &nodes[coord.x][coord.y][coord.z][layer];
+		return &nodes[layer][coord.z][coord.x][coord.y];
 	}
 	}
 };
 };
 
 

+ 1 - 1
lib/CPlayerState.h

@@ -81,7 +81,7 @@ public:
 	TeamID id; //position in gameState::teams
 	TeamID id; //position in gameState::teams
 	std::set<PlayerColor> players; // members of this team
 	std::set<PlayerColor> players; // members of this team
 	//TODO: boost::array, bool if possible
 	//TODO: boost::array, bool if possible
-	std::vector<std::vector<std::vector<ui8> > >  fogOfWarMap; //true - visible, false - hidden
+	std::shared_ptr<boost::multi_array<ui8, 3>> fogOfWarMap; //[z][x][y] true - visible, false - hidden
 
 
 	TeamState();
 	TeamState();
 	TeamState(TeamState && other);
 	TeamState(TeamState && other);

+ 2 - 0
lib/CScriptingModule.cpp

@@ -11,6 +11,7 @@
 
 
 #include "CScriptingModule.h"
 #include "CScriptingModule.h"
 
 
+#if SCRIPTING_ENABLED
 namespace scripting
 namespace scripting
 {
 {
 
 
@@ -30,3 +31,4 @@ Module::Module()
 Module::~Module() = default;
 Module::~Module() = default;
 
 
 }
 }
+#endif

+ 2 - 0
lib/CScriptingModule.h

@@ -9,6 +9,7 @@
  */
  */
 #pragma once
 #pragma once
 
 
+#if SCRIPTING_ENABLED
 #include <vcmi/scripting/Service.h>
 #include <vcmi/scripting/Service.h>
 
 
 namespace spells
 namespace spells
@@ -45,3 +46,4 @@ public:
 };
 };
 
 
 }
 }
+#endif

+ 4 - 5
lib/IGameCallback.cpp

@@ -38,14 +38,13 @@
 void CPrivilegedInfoCallback::getFreeTiles(std::vector<int3> & tiles) const
 void CPrivilegedInfoCallback::getFreeTiles(std::vector<int3> & tiles) const
 {
 {
 	std::vector<int> floors;
 	std::vector<int> floors;
-	for (int b = 0; b < (gs->map->twoLevel ? 2 : 1); ++b)
+	for(int b = 0; b < gs->map->levels(); ++b)
 	{
 	{
 		floors.push_back(b);
 		floors.push_back(b);
 	}
 	}
 	const TerrainTile *tinfo;
 	const TerrainTile *tinfo;
 	for (auto zd : floors)
 	for (auto zd : floors)
 	{
 	{
-
 		for (int xd = 0; xd < gs->map->width; xd++)
 		for (int xd = 0; xd < gs->map->width; xd++)
 		{
 		{
 			for (int yd = 0; yd < gs->map->height; yd++)
 			for (int yd = 0; yd < gs->map->height; yd++)
@@ -80,8 +79,8 @@ void CPrivilegedInfoCallback::getTilesInRange(std::unordered_set<int3, ShashInt3
 				if(distance <= radious)
 				if(distance <= radious)
 				{
 				{
 					if(!player
 					if(!player
-						|| (mode == 1  && team->fogOfWarMap[xd][yd][pos.z]==0)
-						|| (mode == -1 && team->fogOfWarMap[xd][yd][pos.z]==1)
+						|| (mode == 1  && (*team->fogOfWarMap)[pos.z][xd][yd] == 0)
+						|| (mode == -1 && (*team->fogOfWarMap)[pos.z][xd][yd] == 1)
 					)
 					)
 						tiles.insert(int3(xd,yd,pos.z));
 						tiles.insert(int3(xd,yd,pos.z));
 				}
 				}
@@ -103,7 +102,7 @@ void CPrivilegedInfoCallback::getAllTiles(std::unordered_set<int3, ShashInt3> &
 	std::vector<int> floors;
 	std::vector<int> floors;
 	if(level == -1)
 	if(level == -1)
 	{
 	{
-		for(int b = 0; b < (gs->map->twoLevel ? 2 : 1); ++b)
+		for(int b = 0; b < gs->map->levels(); ++b)
 		{
 		{
 			floors.push_back(b);
 			floors.push_back(b);
 		}
 		}

+ 5 - 2
lib/IGameCallback.h

@@ -27,12 +27,13 @@ class CStackBasicDescriptor;
 class CGCreature;
 class CGCreature;
 struct ShashInt3;
 struct ShashInt3;
 
 
+#if SCRIPTING_ENABLED
 namespace scripting
 namespace scripting
 {
 {
-	class Context;
 	class Pool;
 	class Pool;
-	class Script;
 }
 }
+#endif
+
 
 
 class DLL_LINKAGE CPrivilegedInfoCallback : public CGameInfoCallback
 class DLL_LINKAGE CPrivilegedInfoCallback : public CGameInfoCallback
 {
 {
@@ -132,7 +133,9 @@ class DLL_LINKAGE IGameCallback : public CPrivilegedInfoCallback, public IGameEv
 public:
 public:
 	virtual ~IGameCallback(){};
 	virtual ~IGameCallback(){};
 
 
+#if SCRIPTING_ENABLED
 	virtual scripting::Pool * getGlobalContextPool() const = 0;
 	virtual scripting::Pool * getGlobalContextPool() const = 0;
+#endif
 
 
 	//get info
 	//get info
 	virtual bool isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, const CGHeroInstance *hero);
 	virtual bool isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, const CGHeroInstance *hero);

+ 5 - 3
lib/NetPacksLib.cpp

@@ -177,8 +177,9 @@ DLL_LINKAGE void SetMovePoints::applyGs(CGameState *gs)
 DLL_LINKAGE void FoWChange::applyGs(CGameState *gs)
 DLL_LINKAGE void FoWChange::applyGs(CGameState *gs)
 {
 {
 	TeamState * team = gs->getPlayerTeam(player);
 	TeamState * team = gs->getPlayerTeam(player);
+	auto fogOfWarMap = team->fogOfWarMap;
 	for(int3 t : tiles)
 	for(int3 t : tiles)
-		team->fogOfWarMap[t.x][t.y][t.z] = mode;
+		(*fogOfWarMap)[t.z][t.x][t.y] = mode;
 	if (mode == 0) //do not hide too much
 	if (mode == 0) //do not hide too much
 	{
 	{
 		std::unordered_set<int3, ShashInt3> tilesRevealed;
 		std::unordered_set<int3, ShashInt3> tilesRevealed;
@@ -200,7 +201,7 @@ DLL_LINKAGE void FoWChange::applyGs(CGameState *gs)
 			}
 			}
 		}
 		}
 		for(int3 t : tilesRevealed) //probably not the most optimal solution ever
 		for(int3 t : tilesRevealed) //probably not the most optimal solution ever
-			team->fogOfWarMap[t.x][t.y][t.z] = 1;
+			(*fogOfWarMap)[t.z][t.x][t.y] = 1;
 	}
 	}
 }
 }
 
 
@@ -561,8 +562,9 @@ void TryMoveHero::applyGs(CGameState *gs)
 		gs->map->addBlockVisTiles(h);
 		gs->map->addBlockVisTiles(h);
 	}
 	}
 
 
+	auto fogOfWarMap = gs->getPlayerTeam(h->getOwner())->fogOfWarMap;
 	for(int3 t : fowRevealed)
 	for(int3 t : fowRevealed)
-		gs->getPlayerTeam(h->getOwner())->fogOfWarMap[t.x][t.y][t.z] = 1;
+		(*fogOfWarMap)[t.z][t.x][t.y] = 1;
 }
 }
 
 
 DLL_LINKAGE void NewStructures::applyGs(CGameState *gs)
 DLL_LINKAGE void NewStructures::applyGs(CGameState *gs)

+ 3 - 3
lib/PathfinderUtil.h

@@ -14,13 +14,13 @@
 
 
 namespace PathfinderUtil
 namespace PathfinderUtil
 {
 {
-	using FoW = std::vector<std::vector<std::vector<ui8> > >;
+	using FoW = std::shared_ptr<const boost::multi_array<ui8, 3>>;
 	using ELayer = EPathfindingLayer;
 	using ELayer = EPathfindingLayer;
 
 
 	template<EPathfindingLayer::EEPathfindingLayer layer>
 	template<EPathfindingLayer::EEPathfindingLayer layer>
-	CGPathNode::EAccessibility evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo, const FoW & fow, const PlayerColor player, const CGameState * gs)
+	CGPathNode::EAccessibility evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo, FoW fow, const PlayerColor player, const CGameState * gs)
 	{
 	{
-		if(!fow[pos.x][pos.y][pos.z])
+		if(!(*fow)[pos.z][pos.x][pos.y])
 			return CGPathNode::BLOCKED;
 			return CGPathNode::BLOCKED;
 
 
 		switch(layer)
 		switch(layer)

+ 2 - 0
lib/ScriptHandler.cpp

@@ -11,6 +11,7 @@
 
 
 #include "ScriptHandler.h"
 #include "ScriptHandler.h"
 
 
+#if SCRIPTING_ENABLED
 #include <vcmi/Services.h>
 #include <vcmi/Services.h>
 #include <vcmi/Environment.h>
 #include <vcmi/Environment.h>
 
 
@@ -311,3 +312,4 @@ void ScriptHandler::saveState(JsonNode & state)
 
 
 
 
 }
 }
+#endif

+ 2 - 0
lib/ScriptHandler.h

@@ -10,6 +10,7 @@
 
 
 #pragma once
 #pragma once
 
 
+#if SCRIPTING_ENABLED
 #include <vcmi/scripting/Service.h>
 #include <vcmi/scripting/Service.h>
 #include "IHandlerBase.h"
 #include "IHandlerBase.h"
 #include "JsonNode.h"
 #include "JsonNode.h"
@@ -131,3 +132,4 @@ private:
 };
 };
 
 
 }
 }
+#endif

+ 31 - 43
lib/VCMIDirs.cpp

@@ -13,6 +13,8 @@
 
 
 namespace bfs = boost::filesystem;
 namespace bfs = boost::filesystem;
 
 
+bfs::path IVCMIDirs::userLogsPath() const { return userCachePath(); }
+
 bfs::path IVCMIDirs::userSavePath() const { return userDataPath() / "Saves"; }
 bfs::path IVCMIDirs::userSavePath() const { return userDataPath() / "Saves"; }
 
 
 bfs::path IVCMIDirs::fullLibraryPath(const std::string &desiredFolder, const std::string &baseLibName) const
 bfs::path IVCMIDirs::fullLibraryPath(const std::string &desiredFolder, const std::string &baseLibName) const
@@ -20,12 +22,32 @@ bfs::path IVCMIDirs::fullLibraryPath(const std::string &desiredFolder, const std
 	return libraryPath() / desiredFolder / libraryName(baseLibName);
 	return libraryPath() / desiredFolder / libraryName(baseLibName);
 }
 }
 
 
+std::string IVCMIDirs::genHelpString() const
+{
+	std::vector<std::string> tempVec;
+	for (const bfs::path & path : dataPaths())
+		tempVec.push_back(path.string());
+	const auto gdStringA = boost::algorithm::join(tempVec, ":");
+
+	return
+		"  game data:   " + gdStringA + "\n"
+		"  libraries:   " + libraryPath().string() + "\n"
+		"  server:      " + serverPath().string() + "\n"
+		"\n"
+		"  user data:   " + userDataPath().string() + "\n"
+		"  user cache:  " + userCachePath().string() + "\n"
+		"  user config: " + userConfigPath().string() + "\n"
+		"  user logs:   " + userLogsPath().string() + "\n"
+		"  user saves:  " + userSavePath().string() + "\n"; // Should end without new-line?
+}
+
 void IVCMIDirs::init()
 void IVCMIDirs::init()
 {
 {
 	// TODO: Log errors
 	// TODO: Log errors
 	bfs::create_directories(userDataPath());
 	bfs::create_directories(userDataPath());
 	bfs::create_directories(userCachePath());
 	bfs::create_directories(userCachePath());
 	bfs::create_directories(userConfigPath());
 	bfs::create_directories(userConfigPath());
+	bfs::create_directories(userLogsPath());
 	bfs::create_directories(userSavePath());
 	bfs::create_directories(userSavePath());
 }
 }
 
 
@@ -134,8 +156,6 @@ class VCMIDirsWIN32 final : public IVCMIDirs
 
 
 		std::string libraryName(const std::string& basename) const override;
 		std::string libraryName(const std::string& basename) const override;
 
 
-		std::string genHelpString() const override;
-
 		void init() override;
 		void init() override;
 	protected:
 	protected:
 		boost::filesystem::path oldUserDataPath() const;
 		boost::filesystem::path oldUserDataPath() const;
@@ -321,26 +341,6 @@ bfs::path VCMIDirsWIN32::serverPath() const { return binaryPath() / "VCMI_server
 bfs::path VCMIDirsWIN32::libraryPath() const { return "."; }
 bfs::path VCMIDirsWIN32::libraryPath() const { return "."; }
 bfs::path VCMIDirsWIN32::binaryPath() const { return ".";  }
 bfs::path VCMIDirsWIN32::binaryPath() const { return ".";  }
 
 
-std::string VCMIDirsWIN32::genHelpString() const
-{
-
-	std::vector<std::string> tempVec;
-	for (const bfs::path& path : dataPaths())
-		tempVec.push_back(path.string());
-	std::string gdStringA = boost::algorithm::join(tempVec, ";");
-
-
-	return
-		"  game data:   " + gdStringA + "\n"
-		"  libraries:   " + libraryPath().string() + "\n"
-		"  server:      " + serverPath().string() + "\n"
-		"\n"
-		"  user data:   " + userDataPath().string() + "\n"
-		"  user cache:  " + userCachePath().string() + "\n"
-		"  user config: " + userConfigPath().string() + "\n"
-		"  user saves:  " + userSavePath().string() + "\n"; // Should end without new-line?
-}
-
 std::string VCMIDirsWIN32::libraryName(const std::string& basename) const { return basename + ".dll"; }
 std::string VCMIDirsWIN32::libraryName(const std::string& basename) const { return basename + ".dll"; }
 #elif defined(VCMI_UNIX)
 #elif defined(VCMI_UNIX)
 class IVCMIDirsUNIX : public IVCMIDirs
 class IVCMIDirsUNIX : public IVCMIDirs
@@ -349,8 +349,6 @@ class IVCMIDirsUNIX : public IVCMIDirs
 		boost::filesystem::path clientPath() const override;
 		boost::filesystem::path clientPath() const override;
 		boost::filesystem::path serverPath() const override;
 		boost::filesystem::path serverPath() const override;
 
 
-		std::string genHelpString() const override;
-
 		bool developmentMode() const;
 		bool developmentMode() const;
 };
 };
 
 
@@ -363,25 +361,6 @@ bool IVCMIDirsUNIX::developmentMode() const
 bfs::path IVCMIDirsUNIX::clientPath() const { return binaryPath() / "vcmiclient"; }
 bfs::path IVCMIDirsUNIX::clientPath() const { return binaryPath() / "vcmiclient"; }
 bfs::path IVCMIDirsUNIX::serverPath() const { return binaryPath() / "vcmiserver"; }
 bfs::path IVCMIDirsUNIX::serverPath() const { return binaryPath() / "vcmiserver"; }
 
 
-std::string IVCMIDirsUNIX::genHelpString() const
-{
-	std::vector<std::string> tempVec;
-	for (const bfs::path& path : dataPaths())
-		tempVec.push_back(path.string());
-	std::string gdStringA = boost::algorithm::join(tempVec, ":");
-
-
-	return
-		"  game data:   " + gdStringA + "\n"
-		"  libraries:   " + libraryPath().string() + "\n"
-		"  server:      " + serverPath().string() + "\n"
-		"\n"
-		"  user data:   " + userDataPath().string() + "\n"
-		"  user cache:  " + userCachePath().string() + "\n"
-		"  user config: " + userConfigPath().string() + "\n"
-		"  user saves:  " + userSavePath().string() + "\n"; // Should end without new-line?
-}
-
 #ifdef VCMI_APPLE
 #ifdef VCMI_APPLE
 class VCMIDirsOSX final : public IVCMIDirsUNIX
 class VCMIDirsOSX final : public IVCMIDirsUNIX
 {
 {
@@ -389,6 +368,7 @@ class VCMIDirsOSX final : public IVCMIDirsUNIX
 		boost::filesystem::path userDataPath() const override;
 		boost::filesystem::path userDataPath() const override;
 		boost::filesystem::path userCachePath() const override;
 		boost::filesystem::path userCachePath() const override;
 		boost::filesystem::path userConfigPath() const override;
 		boost::filesystem::path userConfigPath() const override;
+		boost::filesystem::path userLogsPath() const override;
 
 
 		std::vector<boost::filesystem::path> dataPaths() const override;
 		std::vector<boost::filesystem::path> dataPaths() const override;
 
 
@@ -458,6 +438,14 @@ bfs::path VCMIDirsOSX::userDataPath() const
 bfs::path VCMIDirsOSX::userCachePath() const { return userDataPath(); }
 bfs::path VCMIDirsOSX::userCachePath() const { return userDataPath(); }
 bfs::path VCMIDirsOSX::userConfigPath() const { return userDataPath() / "config"; }
 bfs::path VCMIDirsOSX::userConfigPath() const { return userDataPath() / "config"; }
 
 
+bfs::path VCMIDirsOSX::userLogsPath() const
+{
+	// TODO: use proper objc code from Foundation framework
+	if(const auto homeDir = std::getenv("HOME"))
+		return bfs::path{homeDir} / "Library" / "Logs" / "vcmi";
+	return IVCMIDirsUNIX::userLogsPath();
+}
+
 std::vector<bfs::path> VCMIDirsOSX::dataPaths() const
 std::vector<bfs::path> VCMIDirsOSX::dataPaths() const
 {
 {
 	std::vector<bfs::path> ret;
 	std::vector<bfs::path> ret;

+ 4 - 1
lib/VCMIDirs.h

@@ -21,6 +21,9 @@ public:
 	// Path to writeable directory with user configs
 	// Path to writeable directory with user configs
 	virtual boost::filesystem::path userConfigPath() const = 0;
 	virtual boost::filesystem::path userConfigPath() const = 0;
 
 
+	// Path to writeable directory to store log files
+	virtual boost::filesystem::path userLogsPath() const;
+
 	// Path to saved games
 	// Path to saved games
 	virtual boost::filesystem::path userSavePath() const;
 	virtual boost::filesystem::path userSavePath() const;
 
 
@@ -49,7 +52,7 @@ public:
 	// virtual std::string libraryName(const char* basename) const = 0; ?
 	// virtual std::string libraryName(const char* basename) const = 0; ?
 	// virtual std::string libraryName(std::string&& basename) const = 0;?
 	// virtual std::string libraryName(std::string&& basename) const = 0;?
 
 
-	virtual std::string genHelpString() const = 0;
+	virtual std::string genHelpString() const;
 
 
 	// Creates not existed, but required directories.
 	// Creates not existed, but required directories.
 	// Updates directories what change name/path between versions.
 	// Updates directories what change name/path between versions.

+ 10 - 0
lib/VCMI_Lib.cpp

@@ -82,10 +82,12 @@ const HeroTypeService * LibClasses::heroTypes() const
 	return heroh;
 	return heroh;
 }
 }
 
 
+#if SCRIPTING_ENABLED
 const scripting::Service * LibClasses::scripts() const
 const scripting::Service * LibClasses::scripts() const
 {
 {
 	return scriptHandler;
 	return scriptHandler;
 }
 }
+#endif
 
 
 const spells::Service * LibClasses::spells() const
 const spells::Service * LibClasses::spells() const
 {
 {
@@ -215,7 +217,9 @@ void LibClasses::init(bool onlyEssential)
 
 
 	createHandler(tplh, "Template", pomtime); //templates need already resolved identifiers (refactor?)
 	createHandler(tplh, "Template", pomtime); //templates need already resolved identifiers (refactor?)
 
 
+#if SCRIPTING_ENABLED
 	createHandler(scriptHandler, "Script", pomtime);
 	createHandler(scriptHandler, "Script", pomtime);
+#endif
 
 
 	createHandler(battlefieldsHandler, "Battlefields", pomtime);
 	createHandler(battlefieldsHandler, "Battlefields", pomtime);
 	
 	
@@ -246,7 +250,9 @@ void LibClasses::clear()
 	delete bth;
 	delete bth;
 	delete tplh;
 	delete tplh;
 	delete terviewh;
 	delete terviewh;
+#if SCRIPTING_ENABLED
 	delete scriptHandler;
 	delete scriptHandler;
+#endif
 	delete battlefieldsHandler;
 	delete battlefieldsHandler;
 	makeNull();
 	makeNull();
 }
 }
@@ -266,7 +272,9 @@ void LibClasses::makeNull()
 	bth = nullptr;
 	bth = nullptr;
 	tplh = nullptr;
 	tplh = nullptr;
 	terviewh = nullptr;
 	terviewh = nullptr;
+#if SCRIPTING_ENABLED
 	scriptHandler = nullptr;
 	scriptHandler = nullptr;
+#endif
 	battlefieldsHandler = nullptr;
 	battlefieldsHandler = nullptr;
 }
 }
 
 
@@ -287,10 +295,12 @@ void LibClasses::callWhenDeserializing()
 	//modh->loadConfigFromFile ("defaultMods"); //TODO: remember last saved config
 	//modh->loadConfigFromFile ("defaultMods"); //TODO: remember last saved config
 }
 }
 
 
+#if SCRIPTING_ENABLED
 void LibClasses::scriptsLoaded()
 void LibClasses::scriptsLoaded()
 {
 {
 	scriptHandler->performRegistration(this);
 	scriptHandler->performRegistration(this);
 }
 }
+#endif
 
 
 LibClasses::~LibClasses()
 LibClasses::~LibClasses()
 {
 {

+ 11 - 0
lib/VCMI_Lib.h

@@ -32,10 +32,13 @@ class CTerrainViewPatternConfig;
 class CRmgTemplateStorage;
 class CRmgTemplateStorage;
 class IHandlerBase;
 class IHandlerBase;
 
 
+#if SCRIPTING_ENABLED
 namespace scripting
 namespace scripting
 {
 {
 	class ScriptHandler;
 	class ScriptHandler;
 }
 }
+#endif
+
 
 
 /// Loads and constructs several handlers
 /// Loads and constructs several handlers
 class DLL_LINKAGE LibClasses : public Services
 class DLL_LINKAGE LibClasses : public Services
@@ -55,7 +58,9 @@ public:
 	const FactionService * factions() const override;
 	const FactionService * factions() const override;
 	const HeroClassService * heroClasses() const override;
 	const HeroClassService * heroClasses() const override;
 	const HeroTypeService * heroTypes() const override;
 	const HeroTypeService * heroTypes() const override;
+#if SCRIPTING_ENABLED
 	const scripting::Service * scripts() const override;
 	const scripting::Service * scripts() const override;
+#endif
 	const spells::Service * spells() const override;
 	const spells::Service * spells() const override;
 	const SkillService * skills() const override;
 	const SkillService * skills() const override;
 	const BattleFieldService * battlefields() const override;
 	const BattleFieldService * battlefields() const override;
@@ -82,7 +87,9 @@ public:
 	CRmgTemplateStorage * tplh;
 	CRmgTemplateStorage * tplh;
 	BattleFieldHandler * battlefieldsHandler;
 	BattleFieldHandler * battlefieldsHandler;
 	ObstacleHandler * obstacleHandler;
 	ObstacleHandler * obstacleHandler;
+#if SCRIPTING_ENABLED
 	scripting::ScriptHandler * scriptHandler;
 	scripting::ScriptHandler * scriptHandler;
+#endif
 
 
 	LibClasses(); //c-tor, loads .lods and NULLs handlers
 	LibClasses(); //c-tor, loads .lods and NULLs handlers
 	~LibClasses();
 	~LibClasses();
@@ -92,15 +99,19 @@ public:
 
 
 	void loadFilesystem(bool onlyEssential);// basic initialization. should be called before init()
 	void loadFilesystem(bool onlyEssential);// basic initialization. should be called before init()
 
 
+#if SCRIPTING_ENABLED
 	void scriptsLoaded();
 	void scriptsLoaded();
+#endif
 
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
+#if SCRIPTING_ENABLED
 		h & scriptHandler;//must be first (or second after modh), it can modify factories other handlers depends on
 		h & scriptHandler;//must be first (or second after modh), it can modify factories other handlers depends on
 		if(!h.saving)
 		if(!h.saving)
 		{
 		{
 			scriptsLoaded();
 			scriptsLoaded();
 		}
 		}
+#endif
 
 
 		h & heroh;
 		h & heroh;
 		h & arth;
 		h & arth;

+ 11 - 4
lib/battle/BattleInfo.cpp

@@ -522,10 +522,15 @@ CStack * BattleInfo::getStack(int stackID, bool onlyAlive)
 	return const_cast<CStack *>(battleGetStackByID(stackID, onlyAlive));
 	return const_cast<CStack *>(battleGetStackByID(stackID, onlyAlive));
 }
 }
 
 
-BattleInfo::BattleInfo()
-	: round(-1), activeStack(-1), town(nullptr), tile(-1,-1,-1),
-	battlefieldType(BattleField::NONE), terrainType(),
-	tacticsSide(0), tacticDistance(0)
+BattleInfo::BattleInfo():
+	round(-1),
+	activeStack(-1),
+	town(nullptr),
+	tile(-1,-1,-1),
+	battlefieldType(BattleField::NONE),
+	terrainType(),
+	tacticsSide(0),
+	tacticDistance(0)
 {
 {
 	setBattle(this);
 	setBattle(this);
 	setNodeType(BATTLE);
 	setNodeType(BATTLE);
@@ -960,12 +965,14 @@ CGHeroInstance * BattleInfo::battleGetFightingHero(ui8 side) const
 	return const_cast<CGHeroInstance*>(CBattleInfoEssentials::battleGetFightingHero(side));
 	return const_cast<CGHeroInstance*>(CBattleInfoEssentials::battleGetFightingHero(side));
 }
 }
 
 
+#if SCRIPTING_ENABLED
 scripting::Pool * BattleInfo::getContextPool() const
 scripting::Pool * BattleInfo::getContextPool() const
 {
 {
 	//this is real battle, use global scripting context pool
 	//this is real battle, use global scripting context pool
 	//TODO: make this line not ugly
 	//TODO: make this line not ugly
 	return IObjectInterface::cb->getGlobalContextPool();
 	return IObjectInterface::cb->getGlobalContextPool();
 }
 }
+#endif
 
 
 bool CMP_stack::operator()(const battle::Unit * a, const battle::Unit * b)
 bool CMP_stack::operator()(const battle::Unit * a, const battle::Unit * b)
 {
 {

+ 2 - 0
lib/battle/BattleInfo.h

@@ -144,7 +144,9 @@ public:
 	ui8 whatSide(PlayerColor player) const;
 	ui8 whatSide(PlayerColor player) const;
 
 
 protected:
 protected:
+#if SCRIPTING_ENABLED
 	scripting::Pool * getContextPool() const override;
 	scripting::Pool * getContextPool() const override;
+#endif
 };
 };
 
 
 
 

+ 0 - 7
lib/battle/CBattleInfoCallback.h

@@ -23,13 +23,6 @@ struct CObstacleInstance;
 class IBonusBearer;
 class IBonusBearer;
 class CRandomGenerator;
 class CRandomGenerator;
 
 
-namespace scripting
-{
-	class Context;
-	class Pool;
-	class Script;
-}
-
 namespace spells
 namespace spells
 {
 {
 	class Caster;
 	class Caster;

+ 4 - 0
lib/battle/IBattleInfoCallback.h

@@ -24,15 +24,19 @@ namespace battle
 	using UnitFilter = std::function<bool(const Unit *)>;
 	using UnitFilter = std::function<bool(const Unit *)>;
 }
 }
 
 
+#if SCRIPTING_ENABLED
 namespace scripting
 namespace scripting
 {
 {
 	class Pool;
 	class Pool;
 }
 }
+#endif
 
 
 class DLL_LINKAGE IBattleInfoCallback
 class DLL_LINKAGE IBattleInfoCallback
 {
 {
 public:
 public:
+#if SCRIPTING_ENABLED
 	virtual scripting::Pool * getContextPool() const = 0;
 	virtual scripting::Pool * getContextPool() const = 0;
+#endif
 
 
 	virtual Terrain battleTerrainType() const = 0;
 	virtual Terrain battleTerrainType() const = 0;
 	virtual BattleField battleGetBattlefieldType() const = 0;
 	virtual BattleField battleGetBattlefieldType() const = 0;

+ 3 - 3
lib/mapObjects/ObjectTemplate.cpp

@@ -322,15 +322,15 @@ void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain)
 
 
 	size_t height = mask.size();
 	size_t height = mask.size();
 	size_t width  = 0;
 	size_t width  = 0;
-	for (auto & line : mask)
+	for(auto & line : mask)
 		vstd::amax(width, line.String().size());
 		vstd::amax(width, line.String().size());
 
 
 	setSize((ui32)width, (ui32)height);
 	setSize((ui32)width, (ui32)height);
 
 
-	for (size_t i=0; i<mask.size(); i++)
+	for(size_t i = 0; i < mask.size(); i++)
 	{
 	{
 		const std::string & line = mask[i].String();
 		const std::string & line = mask[i].String();
-		for (size_t j=0; j < line.size(); j++)
+		for(size_t j = 0; j < line.size(); j++)
 			usedTiles[mask.size() - 1 - i][line.size() - 1 - j] = charToTile(line[j]);
 			usedTiles[mask.size() - 1 - i][line.size() - 1 - j] = charToTile(line[j]);
 	}
 	}
 
 

+ 7 - 3
lib/mapping/CDrawRoadsOperation.cpp

@@ -149,19 +149,23 @@ static bool ruleIsAny(const std::string & rule)
 
 
 ///CDrawLinesOperation
 ///CDrawLinesOperation
 CDrawLinesOperation::CDrawLinesOperation(CMap * map, const CTerrainSelection & terrainSel, CRandomGenerator * gen):
 CDrawLinesOperation::CDrawLinesOperation(CMap * map, const CTerrainSelection & terrainSel, CRandomGenerator * gen):
-	CMapOperation(map), terrainSel(terrainSel), gen(gen)
+	CMapOperation(map),
+	terrainSel(terrainSel),
+	gen(gen)
 {
 {
 }
 }
 
 
 ///CDrawRoadsOperation
 ///CDrawRoadsOperation
 CDrawRoadsOperation::CDrawRoadsOperation(CMap * map, const CTerrainSelection & terrainSel, const std::string & roadType, CRandomGenerator * gen):
 CDrawRoadsOperation::CDrawRoadsOperation(CMap * map, const CTerrainSelection & terrainSel, const std::string & roadType, CRandomGenerator * gen):
-	CDrawLinesOperation(map, terrainSel, gen), roadType(roadType)
+	CDrawLinesOperation(map, terrainSel,gen),
+	roadType(roadType)
 {
 {
 }
 }
 
 
 ///CDrawRiversOperation
 ///CDrawRiversOperation
 CDrawRiversOperation::CDrawRiversOperation(CMap * map, const CTerrainSelection & terrainSel, const std::string & riverType, CRandomGenerator * gen):
 CDrawRiversOperation::CDrawRiversOperation(CMap * map, const CTerrainSelection & terrainSel, const std::string & riverType, CRandomGenerator * gen):
-	CDrawLinesOperation(map, terrainSel, gen), riverType(riverType)
+	CDrawLinesOperation(map, terrainSel, gen),
+	riverType(riverType)
 {
 {
 }
 }
 
 

+ 41 - 35
lib/mapping/CMap.cpp

@@ -228,7 +228,11 @@ CMapHeader::CMapHeader() : version(EMapFormat::SOD), height(72), width(72),
 
 
 CMapHeader::~CMapHeader()
 CMapHeader::~CMapHeader()
 {
 {
+}
 
 
+ui8 CMapHeader::levels() const
+{
+	return (twoLevel ? 2 : 1);
 }
 }
 
 
 CMap::CMap()
 CMap::CMap()
@@ -246,15 +250,15 @@ CMap::~CMap()
 {
 {
 	if(terrain)
 	if(terrain)
 	{
 	{
-		for (int i=0; i<width; i++)
+		for(int z = 0; z < levels(); z++)
 		{
 		{
-			for(int j=0; j<height; j++)
+			for(int x = 0; x < width; x++)
 			{
 			{
-				delete [] terrain[i][j];
-				delete [] guardingCreaturePositions[i][j];
+				delete[] terrain[z][x];
+				delete[] guardingCreaturePositions[z][x];
 			}
 			}
-			delete [] terrain[i];
-			delete [] guardingCreaturePositions[i];
+			delete[] terrain[z];
+			delete[] guardingCreaturePositions[z];
 		}
 		}
 		delete [] terrain;
 		delete [] terrain;
 		delete [] guardingCreaturePositions;
 		delete [] guardingCreaturePositions;
@@ -271,16 +275,16 @@ CMap::~CMap()
 
 
 void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total)
 void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total)
 {
 {
-	for(int fx=0; fx<obj->getWidth(); ++fx)
+	const int zVal = obj->pos.z;
+	for(int fx = 0; fx < obj->getWidth(); ++fx)
 	{
 	{
-		for(int fy=0; fy<obj->getHeight(); ++fy)
+		int xVal = obj->pos.x - fx;
+		for(int fy = 0; fy < obj->getHeight(); ++fy)
 		{
 		{
-			int xVal = obj->pos.x - fx;
 			int yVal = obj->pos.y - fy;
 			int yVal = obj->pos.y - fy;
-			int zVal = obj->pos.z;
-			if(xVal>=0 && xVal<width && yVal>=0 && yVal<height)
+			if(xVal>=0 && xVal < width && yVal>=0 && yVal < height)
 			{
 			{
-				TerrainTile & curt = terrain[xVal][yVal][zVal];
+				TerrainTile & curt = terrain[zVal][xVal][yVal];
 				if(total || obj->visitableAt(xVal, yVal))
 				if(total || obj->visitableAt(xVal, yVal))
 				{
 				{
 					curt.visitableObjects -= obj;
 					curt.visitableObjects -= obj;
@@ -298,22 +302,22 @@ void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total)
 
 
 void CMap::addBlockVisTiles(CGObjectInstance * obj)
 void CMap::addBlockVisTiles(CGObjectInstance * obj)
 {
 {
-	for(int fx=0; fx<obj->getWidth(); ++fx)
+	const int zVal = obj->pos.z;
+	for(int fx = 0; fx < obj->getWidth(); ++fx)
 	{
 	{
-		for(int fy=0; fy<obj->getHeight(); ++fy)
+		int xVal = obj->pos.x - fx;
+		for(int fy = 0; fy < obj->getHeight(); ++fy)
 		{
 		{
-			int xVal = obj->pos.x - fx;
 			int yVal = obj->pos.y - fy;
 			int yVal = obj->pos.y - fy;
-			int zVal = obj->pos.z;
-			if(xVal>=0 && xVal<width && yVal>=0 && yVal<height)
+			if(xVal>=0 && xVal < width && yVal >= 0 && yVal < height)
 			{
 			{
-				TerrainTile & curt = terrain[xVal][yVal][zVal];
-				if( obj->visitableAt(xVal, yVal))
+				TerrainTile & curt = terrain[zVal][xVal][yVal];
+				if(obj->visitableAt(xVal, yVal))
 				{
 				{
 					curt.visitableObjects.push_back(obj);
 					curt.visitableObjects.push_back(obj);
 					curt.visitable = true;
 					curt.visitable = true;
 				}
 				}
-				if( obj->blockingAt(xVal, yVal))
+				if(obj->blockingAt(xVal, yVal))
 				{
 				{
 					curt.blockingObjects.push_back(obj);
 					curt.blockingObjects.push_back(obj);
 					curt.blocked = true;
 					curt.blocked = true;
@@ -326,12 +330,14 @@ void CMap::addBlockVisTiles(CGObjectInstance * obj)
 void CMap::calculateGuardingGreaturePositions()
 void CMap::calculateGuardingGreaturePositions()
 {
 {
 	int levels = twoLevel ? 2 : 1;
 	int levels = twoLevel ? 2 : 1;
-	for (int i=0; i<width; i++)
+	for(int z = 0; z < levels; z++)
 	{
 	{
-		for(int j=0; j<height; j++)
+		for(int x = 0; x < width; x++)
 		{
 		{
-			for (int k = 0; k < levels; k++)
-				guardingCreaturePositions[i][j][k] = guardingCreaturePosition(int3(i,j,k));
+			for(int y = 0; y < height; y++)
+			{
+				guardingCreaturePositions[z][x][y] = guardingCreaturePosition(int3(x, y, z));
+			}
 		}
 		}
 	}
 	}
 }
 }
@@ -389,13 +395,13 @@ bool CMap::isInTheMap(const int3 & pos) const
 TerrainTile & CMap::getTile(const int3 & tile)
 TerrainTile & CMap::getTile(const int3 & tile)
 {
 {
 	assert(isInTheMap(tile));
 	assert(isInTheMap(tile));
-	return terrain[tile.x][tile.y][tile.z];
+	return terrain[tile.z][tile.x][tile.y];
 }
 }
 
 
 const TerrainTile & CMap::getTile(const int3 & tile) const
 const TerrainTile & CMap::getTile(const int3 & tile) const
 {
 {
 	assert(isInTheMap(tile));
 	assert(isInTheMap(tile));
-	return terrain[tile.x][tile.y][tile.z];
+	return terrain[tile.z][tile.x][tile.y];
 }
 }
 
 
 bool CMap::isWaterTile(const int3 &pos) const
 bool CMap::isWaterTile(const int3 &pos) const
@@ -679,17 +685,17 @@ void CMap::removeObject(CGObjectInstance * obj)
 
 
 void CMap::initTerrain()
 void CMap::initTerrain()
 {
 {
-	int level = twoLevel ? 2 : 1;
-	terrain = new TerrainTile**[width];
-	guardingCreaturePositions = new int3**[width];
-	for (int i = 0; i < width; ++i)
+	int level = levels();
+	terrain = new TerrainTile**[level];
+	guardingCreaturePositions = new int3**[level];
+	for(int z = 0; z < level; ++z)
 	{
 	{
-		terrain[i] = new TerrainTile*[height];
-		guardingCreaturePositions[i] = new int3*[height];
-		for (int j = 0; j < height; ++j)
+		terrain[z] = new TerrainTile*[width];
+		guardingCreaturePositions[z] = new int3*[width];
+		for(int x = 0; x < width; ++x)
 		{
 		{
-			terrain[i][j] = new TerrainTile[level];
-			guardingCreaturePositions[i][j] = new int3[level];
+			terrain[z][x] = new TerrainTile[height];
+			guardingCreaturePositions[z][x] = new int3[height];
 		}
 		}
 	}
 	}
 }
 }

+ 22 - 19
lib/mapping/CMap.h

@@ -286,6 +286,8 @@ public:
 	CMapHeader();
 	CMapHeader();
 	virtual ~CMapHeader();
 	virtual ~CMapHeader();
 
 
+	ui8 levels() const;
+
 	EMapFormat::EMapFormat version; /// The default value is EMapFormat::SOD.
 	EMapFormat::EMapFormat version; /// The default value is EMapFormat::SOD.
 	si32 height; /// The default value is 72.
 	si32 height; /// The default value is 72.
 	si32 width; /// The default value is 72.
 	si32 width; /// The default value is 72.
@@ -432,18 +434,18 @@ public:
 		h & questIdentifierToId;
 		h & questIdentifierToId;
 
 
 		//TODO: viccondetails
 		//TODO: viccondetails
-		int level = twoLevel ? 2 : 1;
+		const int level = levels();
 		if(h.saving)
 		if(h.saving)
 		{
 		{
 			// Save terrain
 			// Save terrain
-			for(int i = 0; i < width ; ++i)
+			for(int z = 0; z < level; ++z)
 			{
 			{
-				for(int j = 0; j < height ; ++j)
+				for(int x = 0; x < width; ++x)
 				{
 				{
-					for(int k = 0; k < level; ++k)
+					for(int y = 0; y < height; ++y)
 					{
 					{
-						h & terrain[i][j][k];
-						h & guardingCreaturePositions[i][j][k];
+						h & terrain[z][x][y];
+						h & guardingCreaturePositions[z][x][y];
 					}
 					}
 				}
 				}
 			}
 			}
@@ -451,26 +453,27 @@ public:
 		else
 		else
 		{
 		{
 			// Load terrain
 			// Load terrain
-			terrain = new TerrainTile**[width];
-			guardingCreaturePositions = new int3**[width];
-			for(int i = 0; i < width; ++i)
+			terrain = new TerrainTile**[level];
+			guardingCreaturePositions = new int3**[level];
+			for(int z = 0; z < level; ++z)
 			{
 			{
-				terrain[i] = new TerrainTile*[height];
-				guardingCreaturePositions[i] = new int3*[height];
-				for(int j = 0; j < height; ++j)
+				terrain[z] = new TerrainTile*[width];
+				guardingCreaturePositions[z] = new int3*[width];
+				for(int x = 0; x < width; ++x)
 				{
 				{
-					terrain[i][j] = new TerrainTile[level];
-					guardingCreaturePositions[i][j] = new int3[level];
+					terrain[z][x] = new TerrainTile[height];
+					guardingCreaturePositions[z][x] = new int3[height];
 				}
 				}
 			}
 			}
-			for(int i = 0; i < width ; ++i)
+			for(int z = 0; z < level; ++z)
 			{
 			{
-				for(int j = 0; j < height ; ++j)
+				for(int x = 0; x < width; ++x)
 				{
 				{
-					for(int k = 0; k < level; ++k)
+					for(int y = 0; y < height; ++y)
 					{
 					{
-						h & terrain[i][j][k];
-						h & guardingCreaturePositions[i][j][k];
+
+						h & terrain[z][x][y];
+						h & guardingCreaturePositions[z][x][y];
 					}
 					}
 				}
 				}
 			}
 			}

+ 1 - 1
lib/mapping/CMapDefines.h

@@ -84,7 +84,7 @@ struct DLL_LINKAGE TerrainTile
 	ui8 terView;
 	ui8 terView;
 	std::string riverType;
 	std::string riverType;
 	ui8 riverDir;
 	ui8 riverDir;
-	std::string roadType;
+	std::string roadType; //TODO: switch to ui8
 	ui8 roadDir;
 	ui8 roadDir;
 	/// first two bits - how to rotate terrain graphic (next two - river graphic, next two - road);
 	/// 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 - Favorable Winds effect
 	///	7th bit - whether tile is coastal (allows disembarking if land or block movement if water); 8th bit - Favorable Winds effect

+ 7 - 5
lib/mapping/MapFormatH3M.cpp

@@ -923,18 +923,20 @@ void CMapLoaderH3M::readTerrain()
 	map->initTerrain();
 	map->initTerrain();
 
 
 	// Read terrain
 	// Read terrain
-	for(int a = 0; a < 2; ++a)
+	int3 pos;
+	for(pos.z = 0; pos.z < 2; ++pos.z)
 	{
 	{
-		if(a == 1 && !map->twoLevel)
+		if(pos.z == 1 && !map->twoLevel)
 		{
 		{
 			break;
 			break;
 		}
 		}
 
 
-		for(int c = 0; c < map->width; c++)
+		//OH3 format is [z][y][x]
+		for(pos.y = 0; pos.y < map->height; pos.y++)
 		{
 		{
-			for(int z = 0; z < map->height; z++)
+			for(pos.x = 0; pos.x < map->width; pos.x++)
 			{
 			{
-				auto & tile = map->getTile(int3(z, c, a));
+				auto & tile = map->getTile(pos);
 				tile.terType = Terrain::createTerrainTypeH3M(reader.readUInt8());
 				tile.terType = Terrain::createTerrainTypeH3M(reader.readUInt8());
 				tile.terView = reader.readUInt8();
 				tile.terView = reader.readUInt8();
 				tile.riverType = RIVER_NAMES[reader.readUInt8()];
 				tile.riverType = RIVER_NAMES[reader.readUInt8()];

+ 2 - 2
lib/rmg/CMapGenerator.cpp

@@ -162,8 +162,8 @@ std::string CMapGenerator::getMapDescription() const
 
 
     std::stringstream ss;
     std::stringstream ss;
     ss << boost::str(boost::format(std::string("Map created by the Random Map Generator.\nTemplate was %s, Random seed was %d, size %dx%d") +
     ss << boost::str(boost::format(std::string("Map created by the Random Map Generator.\nTemplate was %s, Random seed was %d, size %dx%d") +
-        ", levels %s, players %d, computers %d, water %s, monster %s, VCMI map") % mapTemplate->getName() %
-		randomSeed % map->map().width % map->map().height % (map->map().twoLevel ? "2" : "1") % static_cast<int>(mapGenOptions.getPlayerCount()) %
+        ", levels %d, players %d, computers %d, water %s, monster %s, VCMI map") % mapTemplate->getName() %
+		randomSeed % map->map().width % map->map().height % map->map().levels() % static_cast<int>(mapGenOptions.getPlayerCount()) %
 		static_cast<int>(mapGenOptions.getCompOnlyPlayerCount()) % waterContentStr[mapGenOptions.getWaterContent()] %
 		static_cast<int>(mapGenOptions.getCompOnlyPlayerCount()) % waterContentStr[mapGenOptions.getWaterContent()] %
 		monsterStrengthStr[monsterStrengthIndex]);
 		monsterStrengthStr[monsterStrengthIndex]);
 
 

+ 11 - 12
lib/rmg/CZonePlacer.cpp

@@ -498,24 +498,24 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
 		zone->setPos(int3(total.x / size, total.y / size, total.z / size));
 		zone->setPos(int3(total.x / size, total.y / size, total.z / size));
 	};
 	};
 
 
-	int levels = map.map().twoLevel ? 2 : 1;
+	int levels = map.map().levels();
 
 
 	/*
 	/*
 	1. Create Voronoi diagram
 	1. Create Voronoi diagram
 	2. find current center of mass for each zone. Move zone to that center to balance zones sizes
 	2. find current center of mass for each zone. Move zone to that center to balance zones sizes
 	*/
 	*/
 
 
-	for (int i = 0; i<width; i++)
+	int3 pos;
+	for(pos.z = 0; pos.z < levels; pos.z++)
 	{
 	{
-		for (int j = 0; j<height; j++)
+		for(pos.x = 0; pos.x < width; pos.x++)
 		{
 		{
-			for (int k = 0; k < levels; k++)
+			for(pos.y = 0; pos.y < height; pos.y++)
 			{
 			{
 				distances.clear();
 				distances.clear();
-				int3 pos(i, j, k);
-				for (auto zone : zones)
+				for(auto zone : zones)
 				{
 				{
-					if (zone.second->getPos().z == k)
+					if (zone.second->getPos().z == pos.z)
 						distances.push_back(std::make_pair(zone.second, (float)pos.dist2dSQ(zone.second->getPos())));
 						distances.push_back(std::make_pair(zone.second, (float)pos.dist2dSQ(zone.second->getPos())));
 					else
 					else
 						distances.push_back(std::make_pair(zone.second, std::numeric_limits<float>::max()));
 						distances.push_back(std::make_pair(zone.second, std::numeric_limits<float>::max()));
@@ -533,17 +533,16 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
 	for (auto zone : zones)
 	for (auto zone : zones)
 		zone.second->clearTiles(); //now populate them again
 		zone.second->clearTiles(); //now populate them again
 
 
-	for (int i=0; i<width; i++)
+	for (pos.z = 0; pos.z < levels; pos.z++)
 	{
 	{
-		for(int j=0; j<height; j++)
+		for (pos.x = 0; pos.x < width; pos.x++)
 		{
 		{
-			for (int k = 0; k < levels; k++)
+			for (pos.y = 0; pos.y < height; pos.y++)
 			{
 			{
 				distances.clear();
 				distances.clear();
-				int3 pos(i, j, k);
 				for (auto zone : zones)
 				for (auto zone : zones)
 				{
 				{
-					if (zone.second->getPos().z == k)
+					if (zone.second->getPos().z == pos.z)
 						distances.push_back (std::make_pair(zone.second, metric(pos, zone.second->getPos())));
 						distances.push_back (std::make_pair(zone.second, metric(pos, zone.second->getPos())));
 					else
 					else
 						distances.push_back (std::make_pair(zone.second, std::numeric_limits<float>::max()));
 						distances.push_back (std::make_pair(zone.second, std::numeric_limits<float>::max()));

+ 0 - 42
lib/rmg/Functions.cpp

@@ -175,46 +175,4 @@ void createObstaclesCommon2(RmgMap & map, CRandomGenerator & generator)
 			}
 			}
 		}
 		}
 	}
 	}
-	
-	//tighten obstacles to improve visuals
-	
-	/*for (int i = 0; i < 3; ++i)
-	{
-		int blockedTiles = 0;
-		int freeTiles = 0;
-		
-		for (int z = 0; z < (map.map().twoLevel ? 2 : 1); z++)
-		{
-			for (int x = 0; x < map.map().width; x++)
-			{
-				for (int y = 0; y < map.map().height; y++)
-				{
-					int3 tile(x, y, z);
-					if (!map.isPossible(tile)) //only possible tiles can change
-						continue;
-					
-					int blockedNeighbours = 0;
-					int freeNeighbours = 0;
-					map.foreach_neighbour(tile, [&map, &blockedNeighbours, &freeNeighbours](int3 &pos)
-					{
-						if (map.isBlocked(pos))
-							blockedNeighbours++;
-						if (map.isFree(pos))
-							freeNeighbours++;
-					});
-					if (blockedNeighbours > 4)
-					{
-						map.setOccupied(tile, ETileType::BLOCKED);
-						blockedTiles++;
-					}
-					else if (freeNeighbours > 4)
-					{
-						map.setOccupied(tile, ETileType::FREE);
-						freeTiles++;
-					}
-				}
-			}
-		}
-		logGlobal->trace("Set %d tiles to BLOCKED and %d tiles to FREE", blockedTiles, freeTiles);
-	}*/
 }
 }

+ 9 - 8
lib/rmg/ObjectManager.h

@@ -39,29 +39,30 @@ public:
 	
 	
 public:
 public:
 	MODIFICATOR(ObjectManager);
 	MODIFICATOR(ObjectManager);
-	
+
+
 	void process() override;
 	void process() override;
 	void init() override;
 	void init() override;
-	
+
 	void addRequiredObject(CGObjectInstance * obj, si32 guardStrength=0);
 	void addRequiredObject(CGObjectInstance * obj, si32 guardStrength=0);
 	void addCloseObject(CGObjectInstance * obj, si32 guardStrength = 0);
 	void addCloseObject(CGObjectInstance * obj, si32 guardStrength = 0);
 	void addNearbyObject(CGObjectInstance * obj, CGObjectInstance * nearbyTarget);
 	void addNearbyObject(CGObjectInstance * obj, CGObjectInstance * nearbyTarget);
-		
+
 	bool createRequiredObjects();
 	bool createRequiredObjects();
-	
+
 	int3 findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, OptimizeType optimizer) const;
 	int3 findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, OptimizeType optimizer) const;
 	int3 findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, OptimizeType optimizer) const;
 	int3 findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, OptimizeType optimizer) const;
-	
+
 	rmg::Path placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool isGuarded, bool onlyStraight, OptimizeType optimizer) const;
 	rmg::Path placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool isGuarded, bool onlyStraight, OptimizeType optimizer) const;
 	rmg::Path placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, bool isGuarded, bool onlyStraight, OptimizeType optimizer) const;
 	rmg::Path placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, bool isGuarded, bool onlyStraight, OptimizeType optimizer) const;
-	
+
 	CGCreature * chooseGuard(si32 strength, bool zoneGuard = false);
 	CGCreature * chooseGuard(si32 strength, bool zoneGuard = false);
 	bool addGuard(rmg::Object & object, si32 strength, bool zoneGuard = false);
 	bool addGuard(rmg::Object & object, si32 strength, bool zoneGuard = false);
 	void placeObject(rmg::Object & object, bool guarded, bool updateDistance);
 	void placeObject(rmg::Object & object, bool guarded, bool updateDistance);
-	
+
 	void updateDistances(const rmg::Object & obj);
 	void updateDistances(const rmg::Object & obj);
 	void createDistancesPriorityQueue();
 	void createDistancesPriorityQueue();
-	
+
 	const rmg::Area & getVisitableArea() const;
 	const rmg::Area & getVisitableArea() const;
 
 
 	std::vector<CGObjectInstance*> getMines() const;
 	std::vector<CGObjectInstance*> getMines() const;

+ 5 - 12
lib/rmg/ObstaclePlacer.cpp

@@ -171,10 +171,7 @@ void ObstacleProxy::finalInsertion(CMapEditManager * manager, std::set<CGObjectI
 
 
 std::pair<bool, bool> ObstacleProxy::verifyCoverage(const int3 & t) const
 std::pair<bool, bool> ObstacleProxy::verifyCoverage(const int3 & t) const
 {
 {
-	std::pair<bool, bool> result(false, false);
-	if(blockedArea.contains(t))
-		result.first = true;
-	return result;
+	return {blockedArea.contains(t), false};
 }
 }
 
 
 void ObstacleProxy::placeObject(rmg::Object & object, std::set<CGObjectInstance*> & instances)
 void ObstacleProxy::placeObject(rmg::Object & object, std::set<CGObjectInstance*> & instances)
@@ -230,12 +227,7 @@ void ObstaclePlacer::init()
 
 
 std::pair<bool, bool> ObstaclePlacer::verifyCoverage(const int3 & t) const
 std::pair<bool, bool> ObstaclePlacer::verifyCoverage(const int3 & t) const
 {
 {
-	std::pair<bool, bool> result(false, false);
-	if(map.shouldBeBlocked(t))
-		result.first = true;
-	if(zone.areaPossible().contains(t))
-		result.second = true;
-	return result;
+	return {map.shouldBeBlocked(t), zone.areaPossible().contains(t)};
 }
 }
 
 
 void ObstaclePlacer::placeObject(rmg::Object & object, std::set<CGObjectInstance*> &)
 void ObstaclePlacer::placeObject(rmg::Object & object, std::set<CGObjectInstance*> &)
@@ -248,9 +240,10 @@ void ObstaclePlacer::postProcess(const rmg::Object & object)
 	//river processing
 	//river processing
 	if(riverManager)
 	if(riverManager)
 	{
 	{
-		if(object.instances().front()->object().typeName == "mountain")
+		const auto objTypeName = object.instances().front()->object().typeName;
+		if(objTypeName == "mountain")
 			riverManager->riverSource().unite(object.getArea());
 			riverManager->riverSource().unite(object.getArea());
-		if(object.instances().front()->object().typeName == "lake")
+		else if(objTypeName == "lake")
 			riverManager->riverSink().unite(object.getArea());
 			riverManager->riverSink().unite(object.getArea());
 	}
 	}
 }
 }

+ 3 - 3
lib/rmg/RmgMap.cpp

@@ -73,8 +73,8 @@ void RmgMap::initTiles(CMapGenerator & generator)
 {
 {
 	mapInstance->initTerrain();
 	mapInstance->initTerrain();
 	
 	
-	tiles.resize(boost::extents[mapInstance->width][mapInstance->height][mapInstance->twoLevel ? 2 : 1]);
-	zoneColouring.resize(boost::extents[mapInstance->width][mapInstance->height][mapInstance->twoLevel ? 2 : 1]);
+	tiles.resize(boost::extents[mapInstance->width][mapInstance->height][mapInstance->levels()]);
+	zoneColouring.resize(boost::extents[mapInstance->width][mapInstance->height][mapInstance->levels()]);
 	
 	
 	//init native town count with 0
 	//init native town count with 0
 	for (auto faction : VLC->townh->getAllowedFactions())
 	for (auto faction : VLC->townh->getAllowedFactions())
@@ -302,7 +302,7 @@ void RmgMap::dump(bool zoneId) const
 {
 {
 	static int id = 0;
 	static int id = 0;
 	std::ofstream out(boost::to_string(boost::format("zone_%d.txt") % id++));
 	std::ofstream out(boost::to_string(boost::format("zone_%d.txt") % id++));
-	int levels = mapInstance->twoLevel ? 2 : 1;
+	int levels = mapInstance->levels();
 	int width =  mapInstance->width;
 	int width =  mapInstance->width;
 	int height = mapInstance->height;
 	int height = mapInstance->height;
 	for (int k = 0; k < levels; k++)
 	for (int k = 0; k < levels; k++)

+ 4 - 4
lib/rmg/Zone.cpp

@@ -369,16 +369,16 @@ void Modificator::dump()
 {
 {
 	std::ofstream out(boost::to_string(boost::format("seed_%d_modzone_%d_%s.txt") % generator.getRandomSeed() % zone.getId() % getName()));
 	std::ofstream out(boost::to_string(boost::format("seed_%d_modzone_%d_%s.txt") % generator.getRandomSeed() % zone.getId() % getName()));
 	auto & mapInstance = map.map();
 	auto & mapInstance = map.map();
-	int levels = mapInstance.twoLevel ? 2 : 1;
+	int levels = mapInstance.levels();
 	int width =  mapInstance.width;
 	int width =  mapInstance.width;
 	int height = mapInstance.height;
 	int height = mapInstance.height;
-	for (int k = 0; k < levels; k++)
+	for(int z = 0; z < levels; z++)
 	{
 	{
 		for(int j=0; j<height; j++)
 		for(int j=0; j<height; j++)
 		{
 		{
-			for (int i=0; i<width; i++)
+			for(int i=0; i<width; i++)
 			{
 			{
-				out << dump(int3(i, j, k));
+				out << dump(int3(i, j, z));
 			}
 			}
 			out << std::endl;
 			out << std::endl;
 		}
 		}

+ 14 - 0
lib/serializer/BinaryDeserializer.h

@@ -560,6 +560,20 @@ public:
 			data = boost::optional<T>();
 			data = boost::optional<T>();
 		}
 		}
 	}
 	}
+
+	template <typename T>
+	void load(boost::multi_array<T, 3> & data)
+	{
+		ui32 length = readAndCheckLength();
+		ui32 x, y, z;
+		load(x);
+		load(y);
+		load(z);
+		data.resize(boost::extents[x][y][z]);
+		assert(length == data.num_elements()); //x*y*z should be equal to number of elements
+		for(ui32 i = 0; i < length; i++)
+			load(data.data()[i]);
+	}
 };
 };
 
 
 class DLL_LINKAGE CLoadFile : public IBinaryReader
 class DLL_LINKAGE CLoadFile : public IBinaryReader

+ 12 - 0
lib/serializer/BinarySerializer.h

@@ -350,6 +350,18 @@ public:
 			save((ui8)0);
 			save((ui8)0);
 		}
 		}
 	}
 	}
+
+	template <typename T>
+	void save(const boost::multi_array<T, 3> &data)
+	{
+		ui32 length = data.num_elements();
+		*this & length;
+		auto shape = data.shape();
+		ui32 x = shape[0], y = shape[1], z = shape[2];
+		*this & x & y & z;
+		for(ui32 i = 0; i < length; i++)
+			save(data.data()[i]);
+	}
 };
 };
 
 
 class DLL_LINKAGE CSaveFile : public IBinaryWriter
 class DLL_LINKAGE CSaveFile : public IBinaryWriter

+ 2 - 2
lib/spells/AdventureSpellMechanics.cpp

@@ -571,7 +571,7 @@ ESpellCastResult ViewMechanics::applyAdventureEffects(SpellCastEnvironment * env
 
 
 	const auto spellLevel = parameters.caster->getSpellSchoolLevel(owner);
 	const auto spellLevel = parameters.caster->getSpellSchoolLevel(owner);
 
 
-	const auto & fowMap = env->getCb()->getPlayerTeam(parameters.caster->getOwner())->fogOfWarMap;
+	const auto fowMap = env->getCb()->getPlayerTeam(parameters.caster->getOwner())->fogOfWarMap;
 
 
 	for(const CGObjectInstance * obj : env->getMap()->objects)
 	for(const CGObjectInstance * obj : env->getMap()->objects)
 	{
 	{
@@ -580,7 +580,7 @@ ESpellCastResult ViewMechanics::applyAdventureEffects(SpellCastEnvironment * env
 		{
 		{
 			ObjectPosInfo posInfo(obj);
 			ObjectPosInfo posInfo(obj);
 
 
-			if(fowMap[posInfo.pos.x][posInfo.pos.y][posInfo.pos.z] == 0)
+			if((*fowMap)[posInfo.pos.x][posInfo.pos.y][posInfo.pos.z] == 0)
 				pack.objectPositions.push_back(posInfo);
 				pack.objectPositions.push_back(posInfo);
 		}
 		}
 	}
 	}

+ 2 - 0
lib/spells/ISpellMechanics.cpp

@@ -720,10 +720,12 @@ const CreatureService * BaseMechanics::creatures() const
 	return VLC->creatures(); //todo: redirect
 	return VLC->creatures(); //todo: redirect
 }
 }
 
 
+#if SCRIPTING_ENABLED
 const scripting::Service * BaseMechanics::scripts() const
 const scripting::Service * BaseMechanics::scripts() const
 {
 {
 	return VLC->scripts(); //todo: redirect
 	return VLC->scripts(); //todo: redirect
 }
 }
+#endif
 
 
 const Service * BaseMechanics::spells() const
 const Service * BaseMechanics::spells() const
 {
 {

+ 6 - 0
lib/spells/ISpellMechanics.h

@@ -35,10 +35,12 @@ namespace vstd
 	class RNG;
 	class RNG;
 }
 }
 
 
+#if SCRIPTING_ENABLED
 namespace scripting
 namespace scripting
 {
 {
 	class Service;
 	class Service;
 }
 }
+#endif
 
 
 
 
 ///callback to be provided by server
 ///callback to be provided by server
@@ -238,7 +240,9 @@ public:
 
 
 	//Global environment facade
 	//Global environment facade
 	virtual const CreatureService * creatures() const = 0;
 	virtual const CreatureService * creatures() const = 0;
+#if SCRIPTING_ENABLED
 	virtual const scripting::Service * scripts() const = 0;
 	virtual const scripting::Service * scripts() const = 0;
+#endif
 	virtual const Service * spells() const = 0;
 	virtual const Service * spells() const = 0;
 
 
 	virtual const IGameInfoCallback * game() const = 0;
 	virtual const IGameInfoCallback * game() const = 0;
@@ -296,7 +300,9 @@ public:
 	std::vector<AimType> getTargetTypes() const override;
 	std::vector<AimType> getTargetTypes() const override;
 
 
 	const CreatureService * creatures() const override;
 	const CreatureService * creatures() const override;
+#if SCRIPTING_ENABLED
 	const scripting::Service * scripts() const override;
 	const scripting::Service * scripts() const override;
+#endif
 	const Service * spells() const override;
 	const Service * spells() const override;
 
 
 	const IGameInfoCallback * game() const override;
 	const IGameInfoCallback * game() const override;

+ 23 - 13
server/CGameHandler.cpp

@@ -1652,7 +1652,9 @@ CGameHandler::~CGameHandler()
 void CGameHandler::reinitScripting()
 void CGameHandler::reinitScripting()
 {
 {
 	serverEventBus = make_unique<events::EventBus>();
 	serverEventBus = make_unique<events::EventBus>();
+#if SCRIPTING_ENABLED
 	serverScripts.reset(new scripting::PoolImpl(this, spellEnv));
 	serverScripts.reset(new scripting::PoolImpl(this, spellEnv));
+#endif
 }
 }
 
 
 void CGameHandler::init(StartInfo *si)
 void CGameHandler::init(StartInfo *si)
@@ -1993,12 +1995,14 @@ void CGameHandler::newTurn()
 				fw.mode = 1;
 				fw.mode = 1;
 				fw.player = player;
 				fw.player = player;
 				// find all hidden tiles
 				// find all hidden tiles
-				const auto & fow = getPlayerTeam(player)->fogOfWarMap;
-				for (size_t i=0; i<fow.size(); i++)
-					for (size_t j=0; j<fow.at(i).size(); j++)
-						for (size_t k=0; k<fow.at(i).at(j).size(); k++)
-							if (!fow.at(i).at(j).at(k))
-								fw.tiles.insert(int3((si32)i,(si32)j,(si32)k));
+				const auto fow = getPlayerTeam(player)->fogOfWarMap;
+
+				auto shape = fow->shape();
+				for(size_t z = 0; z < shape[0]; z++)
+					for(size_t x = 0; x < shape[1]; x++)
+						for(size_t y = 0; y < shape[2]; y++)
+							if (!(*fow)[z][x][y])
+								fw.tiles.insert(int3(x, y, z));
 
 
 				sendAndApply (&fw);
 				sendAndApply (&fw);
 			}
 			}
@@ -2110,7 +2114,9 @@ void CGameHandler::run(bool resume)
 		logGlobal->info(sbuffer.str());
 		logGlobal->info(sbuffer.str());
 	}
 	}
 
 
+#if SCRIPTING_ENABLED
 	services()->scripts()->run(serverScripts);
 	services()->scripts()->run(serverScripts);
+#endif
 
 
 	if(resume)
 	if(resume)
 		events::GameResumed::defaultExecute(serverEventBus.get());
 		events::GameResumed::defaultExecute(serverEventBus.get());
@@ -7012,13 +7018,15 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons
 		fc.mode = (cheat == "vcmieagles" ? 1 : 0);
 		fc.mode = (cheat == "vcmieagles" ? 1 : 0);
 		fc.player = player;
 		fc.player = player;
 		const auto & fowMap = gs->getPlayerTeam(player)->fogOfWarMap;
 		const auto & fowMap = gs->getPlayerTeam(player)->fogOfWarMap;
-		auto hlp_tab = new int3[gs->map->width * gs->map->height * (gs->map->twoLevel ? 2 : 1)];
+		auto hlp_tab = new int3[gs->map->width * gs->map->height * (gs->map->levels())];
 		int lastUnc = 0;
 		int lastUnc = 0;
-		for (int i = 0; i < gs->map->width; i++)
-			for (int j = 0; j < gs->map->height; j++)
-				for (int k = 0; k < (gs->map->twoLevel ? 2 : 1); k++)
-					if (!fowMap.at(i).at(j).at(k) || !fc.mode)
-						hlp_tab[lastUnc++] = int3(i, j, k);
+
+		for(int z = 0; z < gs->map->levels(); z++)
+			for(int x = 0; x < gs->map->width; x++)
+				for(int y = 0; y < gs->map->height; y++)
+					if(!(*fowMap)[z][x][y] || !fc.mode)
+						hlp_tab[lastUnc++] = int3(x, y, z);
+
 		fc.tiles.insert(hlp_tab, hlp_tab + lastUnc);
 		fc.tiles.insert(hlp_tab, hlp_tab + lastUnc);
 		delete [] hlp_tab;
 		delete [] hlp_tab;
 		sendAndApply(&fc);
 		sendAndApply(&fc);
@@ -7315,15 +7323,17 @@ CRandomGenerator & CGameHandler::getRandomGenerator()
 	return CRandomGenerator::getDefault();
 	return CRandomGenerator::getDefault();
 }
 }
 
 
+#if SCRIPTING_ENABLED
 scripting::Pool * CGameHandler::getGlobalContextPool() const
 scripting::Pool * CGameHandler::getGlobalContextPool() const
 {
 {
 	return serverScripts.get();
 	return serverScripts.get();
 }
 }
 
 
-scripting::Pool *  CGameHandler::getContextPool() const
+scripting::Pool * CGameHandler::getContextPool() const
 {
 {
 	return serverScripts.get();
 	return serverScripts.get();
 }
 }
+#endif
 
 
 const ObjectInstanceID CGameHandler::putNewObject(Obj ID, int subID, int3 pos)
 const ObjectInstanceID CGameHandler::putNewObject(Obj ID, int subID, int3 pos)
 {
 {

+ 8 - 0
server/CGameHandler.h

@@ -34,10 +34,12 @@ class IMarket;
 
 
 class SpellCastEnvironment;
 class SpellCastEnvironment;
 
 
+#if SCRIPTING_ENABLED
 namespace scripting
 namespace scripting
 {
 {
 	class PoolImpl;
 	class PoolImpl;
 }
 }
+#endif
 
 
 
 
 template<typename T> class CApplier;
 template<typename T> class CApplier;
@@ -274,12 +276,14 @@ public:
 		h & finishingBattle;
 		h & finishingBattle;
 		h & getRandomGenerator();
 		h & getRandomGenerator();
 
 
+#if SCRIPTING_ENABLED
 		JsonNode scriptsState;
 		JsonNode scriptsState;
 		if(h.saving)
 		if(h.saving)
 			serverScripts->serializeState(h.saving, scriptsState);
 			serverScripts->serializeState(h.saving, scriptsState);
 		h & scriptsState;
 		h & scriptsState;
 		if(!h.saving)
 		if(!h.saving)
 			serverScripts->serializeState(h.saving, scriptsState);
 			serverScripts->serializeState(h.saving, scriptsState);
+#endif
 	}
 	}
 
 
 	void sendMessageToAll(const std::string &message);
 	void sendMessageToAll(const std::string &message);
@@ -326,13 +330,17 @@ public:
 
 
 	CRandomGenerator & getRandomGenerator();
 	CRandomGenerator & getRandomGenerator();
 
 
+#if SCRIPTING_ENABLED
 	scripting::Pool * getGlobalContextPool() const override;
 	scripting::Pool * getGlobalContextPool() const override;
 	scripting::Pool * getContextPool() const override;
 	scripting::Pool * getContextPool() const override;
+#endif
 
 
 	friend class CVCMIServer;
 	friend class CVCMIServer;
 private:
 private:
 	std::unique_ptr<events::EventBus> serverEventBus;
 	std::unique_ptr<events::EventBus> serverEventBus;
+#if SCRIPTING_ENABLED
 	std::shared_ptr<scripting::PoolImpl> serverScripts;
 	std::shared_ptr<scripting::PoolImpl> serverScripts;
+#endif
 
 
 	void reinitScripting();
 	void reinitScripting();
 
 

+ 1 - 1
server/CVCMIServer.cpp

@@ -915,7 +915,7 @@ int main(int argc, char * argv[])
 #endif
 #endif
 
 
 	console = new CConsoleHandler();
 	console = new CConsoleHandler();
-	CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() / "VCMI_Server_log.txt", console);
+	CBasicLogConfigurator logConfig(VCMIDirs::get().userLogsPath() / "VCMI_Server_log.txt", console);
 	logConfig.configureDefault();
 	logConfig.configureDefault();
 	logGlobal->info(NAME);
 	logGlobal->info(NAME);