Переглянути джерело

Merge branch 'develop' into SDL2

AlexVinS 11 роки тому
батько
коміт
3b8d0e44d4
100 змінених файлів з 8851 додано та 14374 видалено
  1. 4 4
      AI/VCAI/AIUtility.cpp
  2. 1 40
      AI/VCAI/AIUtility.h
  3. 18 44
      AI/VCAI/Fuzzy.cpp
  4. 2 1
      AI/VCAI/Fuzzy.h
  5. 0 1
      AI/VCAI/Goals.h
  6. 1 1
      AI/VCAI/VCAI.cpp
  7. 0 2
      AI/VCAI/VCAI.h
  8. 1 2
      CCallback.cpp
  9. 6 1
      CMakeLists.txt
  10. 38 0
      Global.h
  11. 1 1
      client/AdventureMapClasses.cpp
  12. 2 2
      client/CAdvmapInterface.cpp
  13. 1 1
      client/CCastleInterface.cpp
  14. 1 1
      client/CGameInfo.cpp
  15. 2 3
      client/CGameInfo.h
  16. 1 1
      client/CHeroWindow.cpp
  17. 0 2
      client/CHeroWindow.h
  18. 3 3
      client/CKingdomInterface.cpp
  19. 0 2
      client/CMT.cpp
  20. 0 2
      client/CMakeLists.txt
  21. 1 1
      client/CMusicHandler.cpp
  22. 1 1
      client/CMusicHandler.h
  23. 0 3
      client/CPlayerInterface.cpp
  24. 4 3
      client/CPreGame.cpp
  25. 0 1
      client/CQuestLog.cpp
  26. 0 1
      client/CSpellWindow.cpp
  27. 0 2
      client/Client.cpp
  28. 0 1
      client/GUIClasses.cpp
  29. 6 10
      client/Graphics.cpp
  30. 1 3
      client/NetPacksClient.cpp
  31. 0 1
      client/battle/CBattleInterface.cpp
  32. 0 1
      client/battle/CBattleInterfaceClasses.cpp
  33. 0 1
      client/battle/CCreatureAnimation.h
  34. 5 7
      client/mapHandler.cpp
  35. 2 683
      config/bankconfig.json
  36. 2 1
      config/defaultMods.json
  37. 0 108
      config/dwellings.json
  38. 6 4
      config/factions/castle.json
  39. 6 4
      config/factions/conflux.json
  40. 6 4
      config/factions/dungeon.json
  41. 6 4
      config/factions/fortress.json
  42. 6 4
      config/factions/inferno.json
  43. 6 4
      config/factions/necropolis.json
  44. 6 4
      config/factions/rampart.json
  45. 6 4
      config/factions/stronghold.json
  46. 6 4
      config/factions/tower.json
  47. 9 0
      config/gameConfig.json
  48. 36 90
      config/heroClasses.json
  49. 973 0
      config/objects/creatureBanks.json
  50. 126 0
      config/objects/dwellings.json
  51. 133 0
      config/objects/generic.json
  52. 208 0
      config/objects/moddables.json
  53. 44 0
      config/objects/rewardable.json
  54. 186 41
      config/rmg.json
  55. 6 36
      config/schemas/faction.json
  56. 10 20
      config/schemas/heroClass.json
  57. 31 0
      config/schemas/object.json
  58. 1 9
      config/schemas/objectTemplate.json
  59. 27 0
      config/schemas/objectType.json
  60. 1 1
      lib/BattleState.cpp
  61. 3 1
      lib/BattleState.h
  62. 51 29
      lib/CArtHandler.cpp
  63. 8 2
      lib/CArtHandler.h
  64. 10 8
      lib/CCreatureHandler.cpp
  65. 1 1
      lib/CCreatureSet.cpp
  66. 1 1
      lib/CGameInfoCallback.cpp
  67. 3569 3594
      lib/CGameState.cpp
  68. 2 2
      lib/CGameState.h
  69. 0 1
      lib/CGeneralTextHandler.cpp
  70. 0 1
      lib/CGeneralTextHandler.h
  71. 30 19
      lib/CHeroHandler.cpp
  72. 35 16
      lib/CMakeLists.txt
  73. 37 10
      lib/CModHandler.cpp
  74. 12 1
      lib/CModHandler.h
  75. 0 7777
      lib/CObjectHandler.cpp
  76. 0 1533
      lib/CObjectHandler.h
  77. 10 0
      lib/CRandomGenerator.h
  78. 0 0
      lib/CSoundBase.h
  79. 76 132
      lib/CTownHandler.cpp
  80. 1 5
      lib/CTownHandler.h
  81. 1 2
      lib/Connection.h
  82. 1 1
      lib/GameConstants.cpp
  83. 2 1
      lib/IGameCallback.cpp
  84. 1 1
      lib/JsonNode.cpp
  85. 8 13
      lib/JsonNode.h
  86. 39 12
      lib/NetPacks.h
  87. 3 3
      lib/NetPacksBase.h
  88. 27 19
      lib/NetPacksLib.cpp
  89. 7 7
      lib/VCMI_Lib.cpp
  90. 3 3
      lib/VCMI_Lib.h
  91. 32 4
      lib/VCMI_lib.cbp
  92. 128 0
      lib/mapObjects/CArmedInstance.cpp
  93. 40 0
      lib/mapObjects/CArmedInstance.h
  94. 329 0
      lib/mapObjects/CBank.cpp
  95. 49 0
      lib/mapObjects/CBank.h
  96. 1372 0
      lib/mapObjects/CGHeroInstance.cpp
  97. 227 0
      lib/mapObjects/CGHeroInstance.h
  98. 335 0
      lib/mapObjects/CGMarket.cpp
  99. 88 0
      lib/mapObjects/CGMarket.h
  100. 364 0
      lib/mapObjects/CGPandoraBox.cpp

+ 4 - 4
AI/VCAI/AIUtility.cpp

@@ -3,9 +3,9 @@
 #include "VCAI.h"
 
 #include "../../lib/UnlockGuard.h"
-#include "../../lib/CObjectHandler.h"
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CHeroHandler.h"
+#include "../../lib/mapObjects/CBank.h"
 
 /*
  * AIUtility.cpp, part of VCMI engine
@@ -303,11 +303,11 @@ ui64 evaluateDanger(const CGObjectInstance *obj)
 	case Obj::SHIPWRECK: //shipwreck
 	case Obj::DERELICT_SHIP: //derelict ship
 //	case Obj::PYRAMID:
-		return fh->estimateBankDanger (VLC->objh->bankObjToIndex(obj));
+		return fh->estimateBankDanger (dynamic_cast<const CBank *>(obj));
 	case Obj::PYRAMID:
 		{
 		    if(obj->subID == 0)
-				return fh->estimateBankDanger (VLC->objh->bankObjToIndex(obj));
+				return fh->estimateBankDanger (dynamic_cast<const CBank *>(obj));
 			else
 				return 0;
 		}
@@ -377,7 +377,7 @@ int3 whereToExplore(HeroPtr h)
 			}
 		}
 	}
-	removeDuplicates (nearbyVisitableObjs); //one object may occupy multiple tiles
+	vstd::removeDuplicates (nearbyVisitableObjs); //one object may occupy multiple tiles
 	boost::sort(nearbyVisitableObjs, isCloser);
 	if(nearbyVisitableObjs.size())
 		return nearbyVisitableObjs.back()->visitablePos();

+ 1 - 40
AI/VCAI/AIUtility.h

@@ -5,7 +5,6 @@
 #include "../../lib/CCreatureHandler.h"
 #include "../../lib/CTownHandler.h"
 #include "../../lib/CSpellHandler.h"
-#include "../../lib/CObjectHandler.h"
 #include "../../lib/Connection.h"
 #include "../../lib/CGameState.h"
 #include "../../lib/mapping/CMap.h"
@@ -135,44 +134,6 @@ bool objWithID(const CGObjectInstance *obj)
         return obj->ID == id;
 }
 
-template <typename Container, typename Item>
-bool erase_if_present(Container &c, const Item &item)
-{
-	auto i = std::find(c.begin(), c.end(), item);
-	if (i != c.end())
-	{
-		c.erase(i);
-		return true;
-	}
-
-	return false;
-}
-
-template <typename V, typename Item, typename Item2>
-bool erase_if_present(std::map<Item,V> & c, const Item2 &item)
-{
-	auto i = c.find(item);
-	if (i != c.end())
-	{
-		c.erase(i);
-		return true;
-	}
-	return false;
-}
-
-template <typename Container, typename Pred>
-void erase(Container &c, Pred pred)
-{
-	c.erase(boost::remove_if(c, pred), c.end());
-}
-
-template<typename T>
-void removeDuplicates(std::vector<T> &vec)
-{
-	boost::sort(vec);
-	vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
-}
-
 std::string strFromInt3(int3 pos);
 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
@@ -200,4 +161,4 @@ bool compareMovement(HeroPtr lhs, HeroPtr rhs);
 bool compareHeroStrength(HeroPtr h1, HeroPtr h2);
 bool compareArmyStrength(const CArmedInstance *a1, const CArmedInstance *a2);
 ui64 howManyReinforcementsCanGet(HeroPtr h, const CGTownInstance *t);
-int3 whereToExplore(HeroPtr h);
+int3 whereToExplore(HeroPtr h);

+ 18 - 44
AI/VCAI/Fuzzy.cpp

@@ -2,11 +2,11 @@
 #include "Fuzzy.h"
 #include <limits>
 
-#include "../../lib/CObjectHandler.h"
+#include "../../lib/mapObjects/MapObjects.h"
+#include "../../lib/mapObjects/CommonConstructors.h"
 #include "../../lib/CCreatureHandler.h"
 #include "../../lib/VCMI_Lib.h"
 #include "../../CCallback.h"
-//#include "Goals.cpp"
 #include "VCAI.h"
 
 /*
@@ -42,16 +42,6 @@ struct armyStructure
 	ui32 maxSpeed;
 };
 
-ui64 evaluateBankConfig (BankConfig * bc)
-{
-	ui64 danger = 0;
-	for (auto opt : bc->guards)
-	{
-		danger += VLC->creh->creatures[opt.first]->fightValue * opt.second;
-	}
-	return danger;
-}
-
 armyStructure evaluateArmyStructure (const CArmedInstance * army)
 {
 	ui64 totalStrenght = army->getArmyStrength();
@@ -209,42 +199,26 @@ void FuzzyHelper::initTacticalAdvantage()
 	}
 }
 
-ui64 FuzzyHelper::estimateBankDanger (int ID)
+ui64 FuzzyHelper::estimateBankDanger (const CBank * bank)
 {
-	std::vector <ConstTransitivePtr<BankConfig>> & configs = VLC->objh->banksInfo[ID];
+	auto info = VLC->objtypeh->getHandlerFor(bank->ID, bank->subID)->getObjectInfo(bank->appearance);
+
 	ui64 val = std::numeric_limits<ui64>::max();
 	try
 	{
-		switch (configs.size())
-		{
-			case 4:
-				try
-				{
-					for (int i = 0; i < 4; ++i)
-					{
-						int bankVal = evaluateBankConfig (VLC->objh->banksInfo[ID][i]);
-						bankDanger->term("Bank" + boost::lexical_cast<std::string>(i))->setMinimum(bankVal * 0.5f);
-						bankDanger->term("Bank" + boost::lexical_cast<std::string>(i))->setMaximum(bankVal * 1.5f);
-					}
-					//comparison purposes
-					//int averageValue = (evaluateBankConfig (VLC->objh->banksInfo[ID][0]) + evaluateBankConfig (VLC->objh->banksInfo[ID][3])) * 0.5;
-					//dynamic_cast<fl::SingletonTerm*>(bankInput->term("SET"))->setValue(0.5);
-					bankInput->setInput (0.5);
-					engine.process (BANK_DANGER);
-					//engine.process();
-					val = bankDanger->output().defuzzify(); //some expected value of this bank
-				}
-				catch (fl::FuzzyException & fe)
-				{
-                    logAi->errorStream() << fe.name() << ": " << fe.message();
-				}
-				break;
-			case 1: //rare case - Pyramid
-				val = evaluateBankConfig (VLC->objh->banksInfo[ID][0]);
-				break;
-			default:
-                logAi->warnStream() << ("Uhnandled bank config!");
-		}
+		bankDanger->term("Bank0")->setMinimum(info->minGuards().totalStrength * 0.5f);
+		bankDanger->term("Bank0")->setMaximum(info->minGuards().totalStrength * 1.5f);
+
+		bankDanger->term("Bank1")->setMinimum(info->maxGuards().totalStrength * 0.5f);
+		bankDanger->term("Bank1")->setMaximum(info->maxGuards().totalStrength * 1.5f);
+
+		//comparison purposes
+		//int averageValue = (evaluateBankConfig (VLC->objh->banksInfo[ID][0]) + evaluateBankConfig (VLC->objh->banksInfo[ID][3])) * 0.5;
+		//dynamic_cast<fl::SingletonTerm*>(bankInput->term("SET"))->setValue(0.5);
+		bankInput->setInput (0.5);
+		engine.process (BANK_DANGER);
+		//engine.process();
+		val = bankDanger->output().defuzzify(); //some expected value of this bank
 	}
 	catch (fl::FuzzyException & fe)
 	{

+ 2 - 1
AI/VCAI/Fuzzy.h

@@ -14,6 +14,7 @@
 
 class VCAI;
 class CArmedInstance;
+class CBank;
 
 class FuzzyHelper
 {
@@ -72,7 +73,7 @@ public:
 	float evaluate (Goals::AbstractGoal & g);
 	void setPriority (Goals::TSubgoal & g);
 
-	ui64 estimateBankDanger (int ID);
+	ui64 estimateBankDanger (const CBank * bank);
 	float getTacticalAdvantage (const CArmedInstance *we, const CArmedInstance *enemy); //returns factor how many times enemy is stronger than us
 
 	Goals::TSubgoal chooseSolution (Goals::TGoalVec vec);

+ 0 - 1
AI/VCAI/Goals.h

@@ -1,7 +1,6 @@
 #pragma once
 
 #include "../../lib/VCMI_Lib.h"
-#include "../../lib/CObjectHandler.h"
 #include "../../lib/CBuildingHandler.h"
 #include "../../lib/CCreatureHandler.h"
 #include "../../lib/CTownHandler.h"

+ 1 - 1
AI/VCAI/VCAI.cpp

@@ -2,7 +2,7 @@
 #include "VCAI.h"
 #include "Goals.h"
 #include "../../lib/UnlockGuard.h"
-#include "../../lib/CObjectHandler.h"
+#include "../../lib/mapObjects/MapObjects.h"
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CHeroHandler.h"
 

+ 0 - 2
AI/VCAI/VCAI.h

@@ -4,7 +4,6 @@
 #include "Goals.h"
 #include "../../lib/AI_Base.h"
 #include "../../CCallback.h"
-#include "../../lib/CDefObjInfoHandler.h"
 
 #include "../../lib/CThreadHelper.h"
 
@@ -14,7 +13,6 @@
 #include "../../lib/CCreatureHandler.h"
 #include "../../lib/CTownHandler.h"
 #include "../../lib/CSpellHandler.h"
-#include "../../lib/CObjectHandler.h"
 #include "../../lib/Connection.h"
 #include "../../lib/CGameState.h"
 #include "../../lib/mapping/CMap.h"

+ 1 - 2
CCallback.cpp

@@ -9,10 +9,9 @@
 #include "client/Client.h"
 #include "lib/mapping/CMap.h"
 #include "lib/CBuildingHandler.h"
-#include "lib/CDefObjInfoHandler.h"
+#include "lib/mapObjects/CObjectClassesHandler.h"
 #include "lib/CGeneralTextHandler.h"
 #include "lib/CHeroHandler.h"
-#include "lib/CObjectHandler.h"
 #include "lib/Connection.h"
 #include "lib/NetPacks.h"
 #include "client/mapHandler.h"

+ 6 - 1
CMakeLists.txt

@@ -117,9 +117,14 @@ endif()
 
 if(CMAKE_COMPILER_IS_GNUCXX OR NOT WIN32) #so far all *nix compilers support such parameters
 	if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
-                set(CLANG_SPECIFIC_FLAGS "-Wno-mismatched-tags")
+		set(CLANG_SPECIFIC_FLAGS "-Wno-mismatched-tags")
 	endif()
 	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall -Wextra -Wpointer-arith -Wno-switch -Wno-sign-compare -Wno-unused-parameter -Wuninitialized -Wno-overloaded-virtual ${CLANG_SPECIFIC_FLAGS}")
+
+	if(UNIX)
+		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
+	endif()
+
 endif()
 
 if(WIN32) # on Win everything goes into H3 root directory

+ 38 - 0
Global.h

@@ -608,6 +608,44 @@ namespace vstd
 		return defaultValue;
 	}
 
+	template <typename Container, typename Item>
+	bool erase_if_present(Container &c, const Item &item)
+	{
+		auto i = std::find(c.begin(), c.end(), item);
+		if (i != c.end())
+		{
+			c.erase(i);
+			return true;
+		}
+
+		return false;
+	}
+
+	template <typename V, typename Item, typename Item2>
+	bool erase_if_present(std::map<Item,V> & c, const Item2 &item)
+	{
+		auto i = c.find(item);
+		if (i != c.end())
+		{
+			c.erase(i);
+			return true;
+		}
+		return false;
+	}
+
+	template <typename Container, typename Pred>
+	void erase(Container &c, Pred pred)
+	{
+		c.erase(boost::remove_if(c, pred), c.end());
+	}
+
+	template<typename T>
+	void removeDuplicates(std::vector<T> &vec)
+	{
+		boost::sort(vec);
+		vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
+	}
+
 	using boost::math::round;
 }
 using vstd::operator-=;

+ 1 - 1
client/AdventureMapClasses.cpp

@@ -6,7 +6,7 @@
 #include "../lib/filesystem/Filesystem.h"
 #include "../lib/mapping/CMap.h"
 #include "../lib/CModHandler.h"
-#include "../lib/CObjectHandler.h"
+#include "../lib/mapObjects/CGHeroInstance.h"
 #include "../lib/CGameState.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CTownHandler.h"

+ 2 - 2
client/CAdvmapInterface.cpp

@@ -17,7 +17,7 @@
 #include "CDefHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CHeroHandler.h"
-#include "../lib/CObjectHandler.h"
+#include "../lib/mapObjects/CGHeroInstance.h"
 #include "../lib/CTownHandler.h"
 #include "../lib/mapping/CMap.h"
 #include "../lib/JsonNode.h"
@@ -25,7 +25,7 @@
 #include "CPreGame.h"
 #include "../lib/VCMI_Lib.h"
 #include "../lib/CSpellHandler.h"
-#include "CSoundBase.h"
+#include "../lib/CSoundBase.h"
 #include "../lib/CGameState.h"
 #include "CMusicHandler.h"
 #include "gui/CGuiHandler.h"

+ 1 - 1
client/CCastleInterface.cpp

@@ -7,7 +7,7 @@
 #include "../lib/CCreatureHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CModHandler.h"
-#include "../lib/CObjectHandler.h"
+#include "../lib/mapObjects/CGHeroInstance.h"
 #include "../lib/CSpellHandler.h"
 #include "../lib/CTownHandler.h"
 #include "CAdvmapInterface.h"

+ 1 - 1
client/CGameInfo.cpp

@@ -31,5 +31,5 @@ void CGameInfo::setFromLib()
 	heroh = VLC->heroh;
 	objh = VLC->objh;
 	spellh = VLC->spellh;
-	dobjinfo = VLC->dobjinfo;
+	objtypeh = VLC->objtypeh;
 }

+ 2 - 3
client/CGameInfo.h

@@ -23,7 +23,7 @@ class CBuildingHandler;
 class CObjectHandler;
 class CSoundHandler;
 class CMusicHandler;
-class CDefObjInfoHandler;
+class CObjectClassesHandler;
 class CTownHandler;
 class CGeneralTextHandler;
 class CConsoleHandler;
@@ -57,11 +57,10 @@ public:
 	ConstTransitivePtr<CCreatureHandler> creh;
 	ConstTransitivePtr<CSpellHandler> spellh;
 	ConstTransitivePtr<CObjectHandler> objh;
-	ConstTransitivePtr<CDefObjInfoHandler> dobjinfo;
+	ConstTransitivePtr<CObjectClassesHandler> objtypeh;
 	CGeneralTextHandler * generaltexth;
 	CMapHandler * mh;
 	CTownHandler * townh;
-	//CTownHandler * townh;
 
 	void setFromLib();
 

+ 1 - 1
client/CHeroWindow.cpp

@@ -20,7 +20,7 @@
 #include "CDefHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CHeroHandler.h"
-#include "../lib/CObjectHandler.h"
+#include "../lib/mapObjects/CGHeroInstance.h"
 #include "../lib/NetPacksBase.h"
 
 #include "gui/CGuiHandler.h"

+ 0 - 2
client/CHeroWindow.h

@@ -4,8 +4,6 @@
 #include "gui/CIntObjectClasses.h"
 #include "GUIClasses.h"
 
-//#include "CPlayerInterface.h"
-
 /*
  * CHeroWindow.h, part of VCMI engine
  *

+ 3 - 3
client/CKingdomInterface.cpp

@@ -5,7 +5,7 @@
 #include "../lib/CCreatureHandler.h" //creatures name for objects list
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CModHandler.h" //for buildings per turn
-#include "../lib/CObjectHandler.h" //Hero/Town objects
+#include "../lib/mapObjects/CGHeroInstance.h"
 #include "../lib/CHeroHandler.h" // only for calculating required xp? worth it?
 #include "../lib/CTownHandler.h"
 #include "CAnimation.h" //CAnimImage
@@ -500,7 +500,7 @@ void CKingdomInterface::generateObjectsList(const std::vector<const CGObjectInst
 			OwnedObjectInfo &info = visibleObjects[object->subID];
 			if (info.count++ == 0)
 			{
-				info.hoverText = CGI->creh->creatures[CGI->objh->cregens.find(object->subID)->second]->namePl;
+				info.hoverText = object->getHoverText();
 				info.imageID = object->subID;
 			}
 		}
@@ -953,4 +953,4 @@ void CHeroItem::onArtChange(int tabIndex)
 	//redraw item after background change
 	if (active)
 		redraw();
-}
+}

+ 0 - 2
client/CMT.cpp

@@ -34,7 +34,6 @@
 #include "CMessage.h"
 #include "../lib/CModHandler.h"
 #include "../lib/CTownHandler.h"
-#include "../lib/CObjectHandler.h"
 #include "../lib/CArtHandler.h"
 #include "../lib/CScriptingModule.h"
 #include "../lib/GameConstants.h"
@@ -44,7 +43,6 @@
 #ifdef _WIN32
 #include "SDL_syswm.h"
 #endif
-#include "../lib/CDefObjInfoHandler.h"
 #include "../lib/UnlockGuard.h"
 #include "CMT.h"
 

+ 0 - 2
client/CMakeLists.txt

@@ -48,8 +48,6 @@ set(client_SRCS
 )
 
 set(client_HEADERS
-		CSoundBase.h
-
 		gui/SDL_Pixels.h
 )
 

+ 1 - 1
client/CMusicHandler.cpp

@@ -2,9 +2,9 @@
 #include <SDL_mixer.h>
 
 #include "CMusicHandler.h"
+#include "CGameInfo.h"
 #include "../lib/CCreatureHandler.h"
 #include "../lib/CSpellHandler.h"
-#include "../client/CGameInfo.h"
 #include "../lib/JsonNode.h"
 #include "../lib/GameConstants.h"
 #include "../lib/filesystem/Filesystem.h"

+ 1 - 1
client/CMusicHandler.h

@@ -1,7 +1,7 @@
 #pragma once
 
 #include "../lib/CConfigHandler.h"
-#include "CSoundBase.h"
+#include "../lib/CSoundBase.h"
 #include "../lib/CCreatureHandler.h"
 
 /*

+ 0 - 3
client/CPlayerInterface.cpp

@@ -1,5 +1,4 @@
 #include "StdInc.h"
-#include "../lib/CDefObjInfoHandler.h"
 #include "CAdvmapInterface.h"
 #include "battle/CBattleInterface.h"
 #include "battle/CBattleInterfaceClasses.h"
@@ -13,7 +12,6 @@
 #include "CQuestLog.h"
 #include "CMessage.h"
 #include "CPlayerInterface.h"
-//#include "gui/SDL_Extensions.h"
 #include "gui/SDL_Extensions.h"
 #include "../lib/CConfigHandler.h"
 #include "battle/CCreatureAnimation.h"
@@ -21,7 +19,6 @@
 #include "../lib/CArtHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CHeroHandler.h"
-#include "../lib/CObjectHandler.h"
 #include "../lib/Connection.h"
 #include "../lib/CSpellHandler.h"
 #include "../lib/CTownHandler.h"

+ 4 - 3
client/CPreGame.cpp

@@ -11,11 +11,9 @@
 #include "gui/CCursorHandler.h"
 #include "CAnimation.h"
 #include "CDefHandler.h"
-#include "../lib/CDefObjInfoHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CTownHandler.h"
 #include "../lib/CHeroHandler.h"
-#include "../lib/CObjectHandler.h"
 #include "../lib/mapping/CCampaignHandler.h"
 #include "../lib/CCreatureHandler.h"
 #include "../lib/JsonNode.h"
@@ -1716,7 +1714,10 @@ CRandomMapTab::CRandomMapTab()
 	addButtonsWithRandToGroup(monsterStrengthGroup, monsterStrengthBtns, 0, 2, WIDE_BTN_WIDTH, 248, 251);
 	monsterStrengthGroup->onChange = [&](int btnId)
 	{
-		mapGenOptions.setMonsterStrength(static_cast<EMonsterStrength::EMonsterStrength>(btnId));
+		if (btnId < 0)
+			mapGenOptions.setMonsterStrength(EMonsterStrength::RANDOM);
+		else
+			mapGenOptions.setMonsterStrength(static_cast<EMonsterStrength::EMonsterStrength>(btnId + EMonsterStrength::GLOBAL_WEAK)); //value 2 to 4
 	};
 
 	// Show random maps btn

+ 0 - 1
client/CQuestLog.cpp

@@ -16,7 +16,6 @@
 #include "../lib/CGameState.h"
 #include "../lib/CArtHandler.h"
 #include "../lib/NetPacksBase.h"
-#include "../lib/CObjectHandler.h"
 
 #include "gui/CGuiHandler.h"
 #include "gui/CIntObjectClasses.h"

+ 0 - 1
client/CSpellWindow.cpp

@@ -3,7 +3,6 @@
 
 #include "Graphics.h"
 #include "CDefHandler.h"
-#include "../lib/CObjectHandler.h"
 #include "../lib/CSpellHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "CVideoHandler.h"

+ 0 - 2
client/Client.cpp

@@ -11,11 +11,9 @@
 #include "../lib/BattleState.h"
 #include "../lib/CModHandler.h"
 #include "../lib/CArtHandler.h"
-#include "../lib/CDefObjInfoHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CHeroHandler.h"
 #include "../lib/CTownHandler.h"
-#include "../lib/CObjectHandler.h"
 #include "../lib/CBuildingHandler.h"
 #include "../lib/CSpellHandler.h"
 #include "../lib/Connection.h"

+ 0 - 1
client/GUIClasses.cpp

@@ -24,7 +24,6 @@
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CHeroHandler.h"
 #include "../lib/CModHandler.h"
-#include "../lib/CObjectHandler.h"
 #include "../lib/CSpellHandler.h"
 #include "../lib/CTownHandler.h"
 #include "../lib/CondSh.h"

+ 6 - 10
client/Graphics.cpp

@@ -12,19 +12,17 @@
 #include "../CCallback.h"
 #include "../lib/CHeroHandler.h"
 #include "../lib/CTownHandler.h"
-#include "../lib/CObjectHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CCreatureHandler.h"
 #include "CBitmapHandler.h"
-#include "../lib/CObjectHandler.h"
 #include "../lib/CSpellHandler.h"
-#include "../lib/CDefObjInfoHandler.h"
 #include "../lib/CGameState.h"
 #include "../lib/JsonNode.h"
 #include "../lib/vcmi_endian.h"
 #include "../lib/GameConstants.h"
 #include "../lib/CStopWatch.h"
 #include "CAnimation.h"
+#include "../lib/mapObjects/CObjectClassesHandler.h"
 
 using namespace boost::assign;
 using namespace CSDL_Ext;
@@ -166,13 +164,11 @@ void Graphics::loadHeroAnims()
 
 	for(auto & elem : CGI->heroh->classes.heroClasses)
 	{
-		const CHeroClass * hc = elem;
-
-		if (!vstd::contains(heroAnims, hc->imageMapFemale))
-			heroAnims[hc->imageMapFemale] = loadHeroAnim(hc->imageMapFemale, rotations);
-
-		if (!vstd::contains(heroAnims, hc->imageMapMale))
-			heroAnims[hc->imageMapMale] = loadHeroAnim(hc->imageMapMale, rotations);
+		for (auto & templ : VLC->objtypeh->getHandlerFor(Obj::HERO, elem->id)->getTemplates())
+		{
+			if (!heroAnims.count(templ.animationFile))
+			heroAnims[templ.animationFile] = loadHeroAnim(templ.animationFile, rotations);
+		}
 	}
 
 	boatAnims.push_back(loadHeroAnim("AB01_.DEF", rotations));

+ 1 - 3
client/NetPacksClient.cpp

@@ -9,14 +9,12 @@
 #include "CGameInfo.h"
 #include "../lib/Connection.h"
 #include "../lib/CGeneralTextHandler.h"
-#include "../lib/CDefObjInfoHandler.h"
 #include "../lib/CHeroHandler.h"
-#include "../lib/CObjectHandler.h"
 #include "../lib/VCMI_Lib.h"
 #include "../lib/mapping/CMap.h"
 #include "../lib/VCMIDirs.h"
 #include "../lib/CSpellHandler.h"
-#include "CSoundBase.h"
+#include "../lib/CSoundBase.h"
 #include "mapHandler.h"
 #include "GUIClasses.h"
 #include "../lib/CConfigHandler.h"

+ 0 - 1
client/battle/CBattleInterface.cpp

@@ -6,7 +6,6 @@
 #include "../CAdvmapInterface.h"
 #include "../CAnimation.h"
 #include "../CBitmapHandler.h"
-#include "../../lib/CObjectHandler.h"
 #include "../../lib/CHeroHandler.h"
 # include "../CDefHandler.h"
 #include "../../lib/CSpellHandler.h"

+ 0 - 1
client/battle/CBattleInterfaceClasses.cpp

@@ -16,7 +16,6 @@
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/NetPacks.h"
 #include "../../lib/CCreatureHandler.h"
-#include "../../lib/CObjectHandler.h"
 #include "../../lib/BattleState.h"
 #include "../CMusicHandler.h"
 #include "../CVideoHandler.h"

+ 0 - 1
client/battle/CCreatureAnimation.h

@@ -1,7 +1,6 @@
 #pragma once
 
 #include "../../lib/FunctionList.h"
-//#include "../CDefHandler.h"
 #include "../CAnimation.h"
 
 /*

+ 5 - 7
client/mapHandler.cpp

@@ -14,12 +14,12 @@
 #include "CBitmapHandler.h"
 #include "gui/SDL_Extensions.h"
 #include "CGameInfo.h"
-#include "../lib/CDefObjInfoHandler.h"
+#include "../lib/mapObjects/CGHeroInstance.h"
+#include "../lib/mapObjects/CObjectClassesHandler.h"
 #include "../lib/CGameState.h"
 #include "../lib/CHeroHandler.h"
 #include "../lib/CTownHandler.h"
 #include "Graphics.h"
-#include "../lib/CObjectHandler.h"
 #include "../lib/mapping/CMap.h"
 #include "CDefHandler.h"
 #include "../lib/CConfigHandler.h"
@@ -207,7 +207,7 @@ void CMapHandler::borderAndTerrainBitmapInit()
 						terBitmapNum = 17;
 					else if(i==(sizes.x) && j==(sizes.y))
 						terBitmapNum = 18;
-					else if(j == -1 && i > -1 && i < sizes.y)
+					else if(j == -1 && i > -1 && i < sizes.x)
 						terBitmapNum = rand.nextInt(22, 23);
 					else if(i == -1 && j > -1 && j < sizes.y)
 						terBitmapNum = rand.nextInt(33, 34);
@@ -541,10 +541,8 @@ void CMapHandler::terrainRect( int3 top_tile, ui8 anim, const std::vector< std::
 						//pick graphics of hero (or boat if hero is sailing)
 						if (themp->boat)
 							iv = &graphics->boatAnims[themp->boat->subID]->ourImages;
-						else if (themp->sex)
-							iv = &graphics->heroAnims[themp->type->heroClass->imageMapFemale]->ourImages;
 						else
-							iv = &graphics->heroAnims[themp->type->heroClass->imageMapMale]->ourImages;
+							iv = &graphics->heroAnims[themp->appearance.animationFile]->ourImages;
 
 						//pick appropriate flag set
 						if(themp->boat)
@@ -1073,7 +1071,7 @@ void CMapHandler::getTerrainDescr( const int3 &pos, std::string & out, bool terN
 	}
 
 	if(t.hasFavourableWinds())
-		out = CGI->generaltexth->names[Obj::FAVORABLE_WINDS];
+		out = CGI->objtypeh->getObjectName(Obj::FAVORABLE_WINDS);
 	else if(terName)
         out = CGI->generaltexth->terrainNames[t.terType];
 }

+ 2 - 683
config/bankconfig.json

@@ -1,675 +1,9 @@
 //Resources:  Wood, Mercury, Ore, Sulfur, Crystal, Gems, Gold
 //Artifacts:  Treasure, Minor, Major, Relic
+//NOTE: all H3M banks were moved to objects/creatureBanks.json
+//Remaining part should be moved to WoG mod
 {
 	"banks": [
-		{
-			"name" : "Cyclops Stockpile",
-			"levels": [
-				{
-					"chance": 30,
-					"guards": [ { "number": 20, "id": 94 } ],
-					"upgrade_chance": 50,
-					"combat_value": 506,
-					"reward_resources": 
-					{
-						"wood" : 4,
-						"mercury" : 4,
-						"ore" : 4,
-						"sulfur" : 4,
-						"crystal" : 4,
-						"gems" : 4,
-						"gold" : 0
-					},
-					"value": 10000,
-					"profitability": 20,
-					"easiest": 100
-				},
-
-				{
-					"chance": 30,
-					"guards": [ { "number": 30, "id": 94 } ],
-					"upgrade_chance": 50,
-					"combat_value": 760,
-					"reward_resources":
-					{
-						"wood" : 6,
-						"mercury" : 6,
-						"ore" : 6,
-						"sulfur" : 6,
-						"crystal" : 6,
-						"gems" : 6
-					},
-					"value": 15000,
-					"profitability": 20,
-					"easiest": 150
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 40, "id": 94 } ],
-					"upgrade_chance": 50,
-					"combat_value": 1013,
-					"reward_resources":
-					{
-						"wood" : 8,
-						"mercury" : 8,
-						"ore" : 8,
-						"sulfur" : 8,
-						"crystal" : 8,
-						"gems" : 8
-					},
-					"value": 20000,
-					"profitability": 20,
-					"easiest": 200
-				},
-				{
-					"chance": 10,
-					"guards": [ { "number": 50, "id": 94 } ],
-					"upgrade_chance": 50,
-					"combat_value": 1266,
-					"reward_resources":
-					{
-						"wood" : 10,
-						"mercury" : 10,
-						"ore" : 10,
-						"sulfur" : 10,
-						"crystal" : 10,
-						"gems" : 10
-					},
-					"value": 25000,
-					"profitability": 20,
-					"easiest": 250
-				}
-			]
-		},
-
-		{
-			"name" : "Dwarven Treasury",
-			"levels": [
-				{
-					"chance": 30,
-					"guards": [ { "number": 50, "id": 16 } ],
-					"upgrade_chance": 50,
-					"combat_value": 194,
-					"reward_resources":
-					{
-						"crystal" : 2,
-						"gold" : 2500
-					},
-					"value": 3500,
-					"profitability": 18,
-					"easiest": 100
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 75, "id": 16 } ],
-					"upgrade_chance": 50,
-					"combat_value": 291,
-					"reward_resources":
-					{
-						"crystal" : 3,
-						"gold" : 4000
-					},
-					"value": 5500,
-					"profitability": 19,
-					"easiest": 150
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 100, "id": 16 } ],
-					"upgrade_chance": 50,
-					"combat_value": 388,
-					"reward_resources":
-					{
-						"crystal" : 5,
-						"gold" : 5000
-					},
-					"value": 7500,
-					"profitability": 19,
-					"easiest": 200
-				},
-				{
-					"chance": 10,
-					"guards": [ { "number": 150, "id": 16 } ],
-					"upgrade_chance": 50,
-					"combat_value": 582,
-					"reward_resources":
-					{
-						"crystal" : 10,
-						"gold" : 7500
-					},
-					"value": 12500,
-					"profitability": 21,
-					"easiest": 300
-				}
-			]
-		},
-
-		{
-			"name" : "Griffin Conservatory",
-			"levels": [
-				{
-					"chance": 30,
-					"guards": [ { "number": 50, "id": 4 } ],
-					"upgrade_chance": 50,
-					"combat_value": 351,
-					"reward_creatures": [ { "number": 1, "id": 12 } ],
-					"value": 3000,
-					"profitability": 9,
-					"easiest": 100
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 100, "id": 4 } ],
-					"upgrade_chance": 50,
-					"combat_value": 702,
-					"reward_creatures": [ { "number": 2, "id": 12 } ],
-					"value": 6000,
-					"profitability": 9,
-					"easiest": 200
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 150, "id": 4 } ],
-					"upgrade_chance": 50,
-					"combat_value": 1053,
-					"reward_creatures": [ { "number": 3, "id": 12 } ],
-					"value": 9000,
-					"profitability": 9,
-					"easiest": 300
-				},
-				{
-					"chance": 10,
-					"guards": [ { "number": 200, "id": 4 } ],
-					"upgrade_chance": 50,
-					"combat_value": 1404,
-					"reward_creatures": [ { "number": 4, "id": 12 } ],
-					"value": 12000,
-					"profitability": 9,
-					"easiest": 400
-				}
-			]
-		},
-
-		{
-			"name" : "Imp Cache",
-			"levels": [
-				{
-					"chance": 30,
-					"guards": [ { "number": 100, "id": 42 } ],
-					"upgrade_chance": 50,
-					"combat_value": 100,
-					"reward_resources":
-					{
-						"wood" : 0,
-						"mercury" : 2,
-						"ore" : 0,
-						"sulfur" : 0,
-						"crystal" : 0,
-						"gems" : 0,
-						"gold" : 1000
-					},
-					"value": 2000,
-					"profitability": 20,
-					"easiest": 100
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 150, "id": 42 } ],
-					"upgrade_chance": 50,
-					"combat_value": 150,
-					"reward_resources":
-					{
-						"mercury" : 3,
-						"gold" : 1500
-					},
-					"value": 3000,
-					"profitability": 20,
-					"easiest": 150
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 200, "id": 42 } ],
-					"upgrade_chance": 50,
-					"combat_value": 200,
-					"reward_resources":
-					{
-						"mercury" : 4,
-						"gold" : 2000
-					},
-					"value": 4000,
-					"profitability": 20,
-					"easiest": 200
-				},
-				{
-					"chance": 10,
-					"guards": [ { "number": 300, "id": 42 } ],
-					"upgrade_chance": 50,
-					"combat_value": 300,
-					"reward_resources":
-					{
-						"mercury" : 6,
-						"gold" : 3000
-					},
-					"value": 6000,
-					"profitability": 20,
-					"easiest": 300
-				}
-			]
-		},
-
-		{
-			"name" : "Medusa Stores",
-			"levels": [
-				{
-					"chance": 30,
-					"guards": [ { "number": 20, "id": 76 } ],
-					"upgrade_chance": 50,
-					"combat_value": 207,
-					"reward_resources":
-					{
-						"sulfur" : 5,
-						"gold" : 2000
-					},
-					"value": 4500,
-					"profitability": 22,
-					"easiest": 100
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 30, "id": 76 } ],
-					"upgrade_chance": 50,
-					"combat_value": 310,
-					"reward_resources":
-					{
-						"sulfur" : 6,
-						"gold" : 3000
-					},
-					"value": 6000,
-					"profitability": 19,
-					"easiest": 150
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 40, "id": 76 } ],
-					"upgrade_chance": 50,
-					"combat_value": 414,
-					"reward_resources":
-					{
-						"sulfur" : 8,
-						"gold" : 4000
-					},
-					"value": 8000,
-					"profitability": 19,
-					"easiest": 200
-				},
-				{
-					"chance": 10,
-					"guards": [ { "number": 50, "id": 76 } ],
-					"upgrade_chance": 50,
-					"combat_value": 517,
-					"reward_resources":
-					{
-						"sulfur" : 10,
-						"gold" : 5000
-					},
-					"value": 10000,
-					"profitability": 19,
-					"easiest": 250
-				}
-			]
-		},
-
-		{
-			"name" : "Naga Bank",
-			"levels": [
-				{
-					"chance": 30,
-					"guards": [ { "number": 10, "id": 38 } ],
-					"upgrade_chance": 50,
-					"combat_value": 403,
-					"reward_resources":
-					{
-						"gems" : 8,
-						"gold" : 4000
-					},
-					"value": 8000,
-					"profitability": 20,
-					"easiest": 100
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 15, "id": 38 } ],
-					"upgrade_chance": 50,
-					"combat_value": 605,
-					"reward_resources":
-					{
-						"gems" : 12,
-						"gold" : 6000
-					},
-					"value": 12000,
-					"profitability": 20,
-					"easiest": 150
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 20, "id": 38 } ],
-					"upgrade_chance": 50,
-					"combat_value": 806,
-					"reward_resources":
-					{
-						"gems" : 16,
-						"gold" : 8000
-					},
-					"value": 16000,
-					"profitability": 20,
-					"easiest": 200
-				},
-				{
-					"chance": 10,
-					"guards": [ { "number": 30, "id": 38 } ],
-					"upgrade_chance": 50,
-					"combat_value": 1210,
-					"reward_resources":
-					{
-						"gems" : 24,
-						"gold" : 12000
-					},
-					"value": 24000,
-					"profitability": 20,
-					"easiest": 300
-				}
-			]
-		},
-
-		{
-			"name" : "Dragon Fly Hive",
-			"levels": [
-				{
-					"chance": 30,
-					"guards": [ { "number": 30, "id": 105} ],
-					"upgrade_chance": 0,
-					"combat_value": 154,
-					"reward_creatures": [ { "number": 4, "id": 108 } ],
-					"value": 3200,
-					"profitability": 21,
-					"easiest": 100
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 45, "id": 105 } ],
-					"upgrade_chance": 0,
-					"combat_value": 230,
-					"reward_creatures": [ { "number": 6, "id": 108 } ],
-					"value": 4800,
-					"profitability": 21,
-					"easiest": 150
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 60, "id": 105 } ],
-					"upgrade_chance": 0,
-					"combat_value": 307,
-					"reward_creatures": [ { "number": 8, "id": 108 } ],
-					"value": 6400,
-					"profitability": 21,
-					"easiest": 200
-				},
-				{
-					"chance": 10,
-					"guards": [ { "number": 90, "id": 105 } ],
-					"upgrade_chance": 0,
-					"combat_value": 461,
-					"reward_creatures": [ { "number": 12, "id": 108 } ],
-					"value": 9600,
-					"profitability": 21,
-					"easiest": 300
-				}
-			]
-		},
-
-		{
-			"name" : "Shipwreck",
-			"levels": [
-				{
-					"chance": 30,
-					"guards": [ { "number": 10, "id": 60 } ],
-					"upgrade_chance": 0,
-					"combat_value": 31,
-					"reward_resources":
-					{
-						"gold" : 2000
-					},
-					"value": 2000,
-					"profitability": 65,
-					"easiest": 100
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 15, "id": 60 } ],
-					"upgrade_chance": 0,
-					"combat_value": 46,
-					"reward_resources":
-					{
-						"gold" : 3000
-					},
-					"value": 3000,
-					"profitability": 65,
-					"easiest": 150
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 25, "id": 60 } ],
-					"upgrade_chance": 0,
-					"combat_value": 77,
-					"reward_resources":
-					{
-						"gold" : 4000
-					},
-					"reward_artifacts": [ 1, 0, 0, 0 ],
-					"value": 5000,
-					"profitability": 65,
-					"easiest": 250
-				},
-				{
-					"chance": 10,
-					"guards": [ { "number": 50, "id": 60 } ],
-					"upgrade_chance": 0,
-					"combat_value": 154,
-					"reward_resources":
-					{
-						"gold" : 5000
-					},
-					"reward_artifacts": [ 0, 1, 0, 0 ],
-					"value": 7000,
-					"profitability": 45,
-					"easiest": 500
-				}
-			]
-		},
-
-		{
-			"name" : "Derelict Ship",
-			"levels": [
-				{
-					"chance": 30,
-					"guards": [ { "number": 20, "id": 115 } ],
-					"upgrade_chance": 0,
-					"combat_value": 138,
-					"reward_resources":
-					{
-						"gold" : 3000
-					},
-					"value": 3000,
-					"profitability": 22,
-					"easiest": 100
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 30, "id": 115 } ],
-					"upgrade_chance": 0,
-					"combat_value": 207,
-					"reward_resources":
-					{
-						"gold" : 3000
-					},
-					"reward_artifacts": [ 1, 0, 0, 0 ],
-					"value": 4000,
-					"profitability": 19,
-					"easiest": 150
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 40, "id": 115 } ],
-					"upgrade_chance": 0,
-					"combat_value": 276,
-					"reward_resources":
-					{
-						"gold" : 4000
-					},
-					"reward_artifacts": [ 1, 0, 0, 0 ],
-					"value": 5000,
-					"profitability": 18,
-					"easiest": 200
-				},
-				{
-					"chance": 10,
-					"guards": [ { "number": 60, "id": 115 } ],
-					"upgrade_chance": 0,
-					"combat_value": 414,
-					"reward_resources":
-					{
-						"gold" : 6000
-					},
-					"reward_artifacts": [ 0, 1, 0, 0 ],
-					"value": 8000,
-					"profitability": 19,
-					"easiest": 300
-				}
-			]
-		},
-
-		{
-			"name" : "Crypt",
-			"levels": [
-				{
-					"chance": 30,
-					"guards": [ { "number": 30, "id": 56 }, { "number": 20, "id": 58 }, { "number": 0, "id": 60 } , { "number": 0, "id": 62 } ],
-					"upgrade_chance": 0,
-					"combat_value": 75,
-					"reward_resources":
-					{
-						"gold" : 1500
-					},
-					"value": 1500,
-					"profitability": 20,
-					"easiest": 100
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 25, "id": 56 }, { "number": 20, "id": 58 }, { "number": 5, "id": 60 }, { "number": 0, "id": 62 } ],
-					"upgrade_chance": 0,
-					"combat_value": 94,
-					"reward_resources":
-					{
-						"gold" : 2000
-					},
-					"value": 2000,
-					"profitability": 21,
-					"easiest": 126
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 20, "id": 56 }, { "number": 20, "id": 58 }, { "number": 10, "id": 60 }, { "number": 5, "id": 62 } ],
-					"upgrade_chance": 0,
-					"combat_value": 169,
-					"reward_resources":
-					{
-						"gold" : 2500
-					},
-					"reward_artifacts": [ 1, 0, 0, 0 ],
-					"value": 3500,
-					"profitability": 21,
-					"easiest": 225
-				},
-				{
-					"chance": 10,
-					"guards": [ { "number": 20, "id": 56 }, { "number": 20, "id": 58 }, { "number": 10, "id": 60 }, { "number": 10, "id": 62 } ],
-					"upgrade_chance": 0,
-					"combat_value": 225,
-					"reward_resources":
-					{
-						"gold" : 5000
-					},
-					"reward_artifacts": [ 1, 0, 0, 0 ],
-					"value": 6000,
-					"profitability": 27,
-					"easiest": 299
-				}
-			]
-		},
-
-		{
-			"name" : "Dragon Utopia",
-			"levels": [
-				{
-					"chance": 30,
-					"guards": [ { "number": 8, "id": 26 }, { "number": 5, "id": 82 }, { "number": 2, "id": 27 }, { "number": 1, "id": 83 }	],
-					"upgrade_chance": 0,
-					"combat_value": 769,
-					"reward_resources":
-					{
-						"gold" : 20000
-					},
-					"reward_artifacts": [ 1, 1, 1, 1 ],
-					"value": 38000,
-					"profitability": 21,
-					"easiest": 100
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 8, "id": 26 }, { "number": 6, "id": 82 }, { "number": 3, "id": 27 }, { "number": 2, "id": 83 }	],
-					"upgrade_chance": 0,
-					"combat_value": 209,
-					"reward_resources":
-					{
-						"gold" : 30000
-					},
-					"reward_artifacts": [ 0, 1, 1, 2 ],
-					"value": 57000,
-					"profitability": 26,
-					"easiest": 125
-				},
-				{
-					"chance": 30,
-					"guards": [ { "number": 8, "id": 26 }, { "number": 6, "id": 82 }, { "number": 4, "id": 27 }, { "number": 3, "id": 83 }	],
-					"upgrade_chance": 0,
-					"combat_value": 556,
-					"reward_resources":
-					{
-						"gold" : 40000
-					},
-					"reward_artifacts": [ 0, 0, 1, 3 ],
-					"value": 75000,
-					"profitability": 29,
-					"easiest": 145
-				},
-				{
-					"chance": 10,
-					"guards": [ { "number": 8, "id": 26 }, { "number": 7, "id": 82 }, { "number": 6, "id": 27 }, { "number": 5, "id": 83 } ],
-					"upgrade_chance": 0,
-					"combat_value": 343,
-					"reward_resources":
-					{
-						"gold" : 50000
-					},
-					"reward_artifacts": [ 0, 0, 0, 4 ],
-					"value": 90000,
-					"profitability": 27,
-					"easiest": 189
-				}
-			]
-		},
-
 		{
 			"name" : "Hunting Lodge",
 			"levels": [
@@ -1252,21 +586,6 @@
 					"easiest": 250
 				}
 			]
-		},
-
-		{
-			"name" : "Pyramid",
-			"levels": [
-				{
-					"chance": 100,
-					"guards": [	{ "number": 20, "id": 116 }, { "number": 10, "id": 117 }, { "number": 20, "id": 116 }, { "number": 10, "id": 117 } ],
-					"upgrade_chance": 0,
-					"combat_value": 786,
-					"value": 15000,
-					"profitability": 19,
-					"easiest": 100
-				}
-			]
 		}
 	]
 }

+ 2 - 1
config/defaultMods.json

@@ -8,7 +8,8 @@
 		"creature"   : 150,
 		"faction"    : 9,
 		"hero"       : 156,
-                "spell"      : 81,
+		"spell"      : 81,
+		"object"     : 256,
 		"mapVersion" : 28 // max supported version, SoD
 	},
 

+ 0 - 108
config/dwellings.json

@@ -1,108 +0,0 @@
-{
-	// Indicate which dwelling produces which creature
-	// Note that it is 1<->n connection since
-	// a creature can be produced by more than one dwelling.
-	"dwellings": [
-		{ "dwelling": 0, "creature": 106 },
-		{ "dwelling": 1, "creature": 96 },
-		{ "dwelling": 2, "creature": 74 },
-		{ "dwelling": 3, "creature": 66 },
-		{ "dwelling": 4, "creature": 68 },
-		{ "dwelling": 5, "creature": 10 },
-		{ "dwelling": 6, "creature": 14 },
-		{ "dwelling": 7, "creature": 112 },
-		{ "dwelling": 8, "creature": 12 },
-		{ "dwelling": 9, "creature": 94 },
-		{ "dwelling": 10, "creature": 54 },
-		{ "dwelling": 11, "creature": 104 },
-		{ "dwelling": 12, "creature": 16 },
-		{ "dwelling": 13, "creature": 113 },
-		{ "dwelling": 14, "creature": 52 },
-		{ "dwelling": 15, "creature": 18 },
-		{ "dwelling": 16, "creature": 114 },
-		{ "dwelling": 17, "creature": 30 },
-		{ "dwelling": 18, "creature": 36 },
-		{ "dwelling": 19, "creature": 86 },
-		{ "dwelling": 20, "creature": 98 },
-		{ "dwelling": 21, "creature": 84 },
-		{ "dwelling": 22, "creature": 44 },
-		{ "dwelling": 23, "creature": 102 },
-		{ "dwelling": 24, "creature": 26 },
-		{ "dwelling": 25, "creature": 4 },
-		{ "dwelling": 26, "creature": 72 },
-		{ "dwelling": 27, "creature": 46 },
-		{ "dwelling": 28, "creature": 110 },
-		{ "dwelling": 29, "creature": 42 },
-		{ "dwelling": 30, "creature": 100 },
-		{ "dwelling": 31, "creature": 34 },
-		{ "dwelling": 32, "creature": 80 },
-		{ "dwelling": 33, "creature": 76 },
-		{ "dwelling": 34, "creature": 78 },
-		{ "dwelling": 35, "creature": 8 },
-		{ "dwelling": 36, "creature": 38 },
-		{ "dwelling": 37, "creature": 48 },
-		{ "dwelling": 38, "creature": 90 },
-		{ "dwelling": 39, "creature": 88 },
-		{ "dwelling": 40, "creature": 50 },
-		{ "dwelling": 41, "creature": 82 },
-		{ "dwelling": 42, "creature": 92 },
-		{ "dwelling": 43, "creature": 28 },
-		{ "dwelling": 44, "creature": 40 },
-		{ "dwelling": 45, "creature": 22 },
-		{ "dwelling": 46, "creature": 70 },
-		{ "dwelling": 47, "creature": 115 },
-		{ "dwelling": 48, "creature": 60 },
-		{ "dwelling": 49, "creature": 108 },
-		{ "dwelling": 50, "creature": 20 },
-		{ "dwelling": 51, "creature": 24 },
-		{ "dwelling": 52, "creature": 64 },
-		{ "dwelling": 53, "creature": 62 },
-		{ "dwelling": 54, "creature": 56 },
-		{ "dwelling": 55, "creature": 58 },
-		{ "dwelling": 56, "creature": 0 },
-		{ "dwelling": 57, "creature": 2 },
-		{ "dwelling": 58, "creature": 6 },
-		{ "dwelling": 59, "creature": 118 },
-		{ "dwelling": 60, "creature": 120 },
-		{ "dwelling": 61, "creature": 130 },
-		{ "dwelling": 62, "creature": 132 },
-		{ "dwelling": 63, "creature": 133 },
-		{ "dwelling": 64, "creature": 134 },
-		{ "dwelling": 65, "creature": 135 },
-		{ "dwelling": 66, "creature": 136 },
-		{ "dwelling": 67, "creature": 137 },
-		{ "dwelling": 68, "creature": 24 },
-		{ "dwelling": 69, "creature": 112 },
-		{ "dwelling": 70, "creature": 113 },
-		{ "dwelling": 71, "creature": 114 },
-		{ "dwelling": 72, "creature": 115 },
-		{ "dwelling": 73, "creature": 138 },
-		{ "dwelling": 74, "creature": 139 },
-		{ "dwelling": 75, "creature": 140 },
-		{ "dwelling": 76, "creature": 141 },
-		{ "dwelling": 77, "creature": 142 },
-		{ "dwelling": 78, "creature": 143 },
-		{ "dwelling": 79, "creature": 144 },
-		{ "dwelling": 80, "creature": 150 },
-		{ "dwelling": 81, "creature": 151 },
-		{ "dwelling": 82, "creature": 152 },
-		{ "dwelling": 83, "creature": 153 },
-		{ "dwelling": 84, "creature": 154 },
-		{ "dwelling": 85, "creature": 155 },
-		{ "dwelling": 86, "creature": 156 },
-		{ "dwelling": 87, "creature": 157 },
-		{ "dwelling": 88, "creature": 158 },
-		{ "dwelling": 89, "creature": 171 },
-		{ "dwelling": 90, "creature": 170 },
-		{ "dwelling": 91, "creature": 168 },
-		{ "dwelling": 92, "creature": 172 },
-		{ "dwelling": 93, "creature": 164 },
-		{ "dwelling": 94, "creature": 169 },
-		{ "dwelling": 95, "creature": 173 },
-		{ "dwelling": 96, "creature": 192 },
-		{ "dwelling": 97, "creature": 193 },
-		{ "dwelling": 98, "creature": 194 },
-		{ "dwelling": 99, "creature": 195 },
-		{ "dwelling": 100, "creature": 196 }
-	]
-}

+ 6 - 4
config/factions/castle.json

@@ -66,11 +66,13 @@
 		},
 		"town" :
 		{
-			"adventureMap" :
+			"mapObject" :
 			{
-				"castle" : "AVCcasx0.def",
-				"village" : "AVCCAST0.DEF",
-				"capitol" : "AVCCASZ0.DEF"
+				"templates" : {
+					"castle" :  { "animation" : "AVCcasx0.def" },
+					"village" : { "animation" : "AVCCAST0.DEF" },
+					"capitol" : { "animation" : "AVCCASZ0.DEF" }
+				}
 			},
 			"structures" :
 			{

+ 6 - 4
config/factions/conflux.json

@@ -66,11 +66,13 @@
 		},
 		"town" :
 		{
-			"adventureMap" :
+			"mapObject" :
 			{
-				"castle" : "avchforx.def",
-				"village" : "AVCHFOR0.DEF",
-				"capitol" : "AVCHFORZ.DEF"
+				"templates" : {
+					"castle" :  { "animation" : "avchforx.def" },
+					"village" : { "animation" : "AVCHFOR0.DEF" },
+					"capitol" : { "animation" : "AVCHFORZ.DEF" }
+				}
 			},
 			"structures" :
 			{

+ 6 - 4
config/factions/dungeon.json

@@ -66,11 +66,13 @@
 		},
 		"town" :
 		{
-			"adventureMap" :
+			"mapObject" :
 			{
-				"castle" : "AVCdunx0.def",
-				"village" : "AVCDUNG0.DEF",
-				"capitol" : "AVCDUNZ0.DEF"
+				"templates" : {
+					"castle" :  { "animation" : "AVCdunx0.def" },
+					"village" : { "animation" : "AVCDUNG0.DEF" },
+					"capitol" : { "animation" : "AVCDUNZ0.DEF" }
+				}
 			},
 			"structures" :
 			{

+ 6 - 4
config/factions/fortress.json

@@ -66,11 +66,13 @@
 		},
 		"town" :
 		{
-			"adventureMap" :
+			"mapObject" :
 			{
-				"castle" : "AVCftrx0.def",
-				"village" : "AVCFTRT0.DEF",
-				"capitol" : "AVCFORZ0.DEF"
+				"templates" : {
+					"castle" :  { "animation" : "AVCftrx0.def" },
+					"village" : { "animation" : "AVCFTRT0.DEF" },
+					"capitol" : { "animation" : "AVCFORZ0.DEF" }
+				}
 			},
 			"structures" :
 			{

+ 6 - 4
config/factions/inferno.json

@@ -66,11 +66,13 @@
 		},
 		"town" :
 		{
-			"adventureMap" :
+			"mapObject" :
 			{
-				"castle" : "AVCinfx0.def",
-				"village" : "AVCINFT0.DEF",
-				"capitol" : "AVCINFZ0.DEF"
+				"templates" : {
+					"castle" :  { "animation" : "AVCinfx0.def" },
+					"village" : { "animation" : "AVCINFT0.DEF" },
+					"capitol" : { "animation" : "AVCINFZ0.DEF" }
+				}
 			},
 			"structures" :
 			{

+ 6 - 4
config/factions/necropolis.json

@@ -66,11 +66,13 @@
 		},
 		"town" :
 		{
-			"adventureMap" :
+			"mapObject" :
 			{
-				"castle" : "AVCnecx0.def",
-				"village" : "AVCNECR0.DEF",
-				"capitol" : "AVCNECZ0.DEF"
+				"templates" : {
+					"castle" :  { "animation" : "AVCnecx0.def" },
+					"village" : { "animation" : "AVCNECR0.DEF" },
+					"capitol" : { "animation" : "AVCNECZ0.DEF" }
+				}
 			},
 			"structures" :
 			{

+ 6 - 4
config/factions/rampart.json

@@ -66,11 +66,13 @@
 		},
 		"town" :
 		{
-			"adventureMap" :
+			"mapObject" :
 			{
-				"castle" : "AVCramx0.def",
-				"village" : "AVCRAMP0.DEF",
-				"capitol" : "AVCRAMZ0.DEF"
+				"templates" : {
+					"castle" :  { "animation" : "AVCramx0.def" },
+					"village" : { "animation" : "AVCRAMP0.DEF" },
+					"capitol" : { "animation" : "AVCRAMZ0.DEF" }
+				}
 			},
 			"structures" :
 			{

+ 6 - 4
config/factions/stronghold.json

@@ -66,11 +66,13 @@
 		},
 		"town" :
 		{
-			"adventureMap" :
+			"mapObject" :
 			{
-				"castle" : "AVCstrx0.def",
-				"village" : "AVCSTRO0.DEF",
-				"capitol" : "AVCSTRZ0.DEF"
+				"templates" : {
+					"castle" :  { "animation" : "AVCstrx0.def" },
+					"village" : { "animation" : "AVCSTRO0.DEF" },
+					"capitol" : { "animation" : "AVCSTRZ0.DEF" }
+				}
 			},
 			"structures" :
 			{

+ 6 - 4
config/factions/tower.json

@@ -66,11 +66,13 @@
 		},
 		"town" :
 		{
-			"adventureMap" :
+			"mapObject" :
 			{
-				"castle" : "AVCtowx0.def",
-				"village" : "AVCTOWR0.DEF",
-				"capitol" : "AVCTOWZ0.DEF"
+				"templates" : {
+					"castle" :  { "animation" : "AVCtowx0.def" },
+					"village" : { "animation" : "AVCTOWR0.DEF" },
+					"capitol" : { "animation" : "AVCTOWZ0.DEF" }
+				}
 			},
 			"structures" :
 			{

+ 9 - 0
config/gameConfig.json

@@ -45,6 +45,15 @@
 		"config/heroes/special.json"
 	],
 
+	"objects" :
+	[
+		"config/objects/generic.json",
+		"config/objects/moddables.json",
+		"config/objects/creatureBanks.json",
+		"config/objects/dwellings.json",
+		"config/objects/rewardable.json"
+	],
+
 	"artifacts" :
 	[
 		"config/artifacts.json"

+ 36 - 90
config/heroClasses.json

@@ -6,11 +6,8 @@
 		"defaultTavern" : 5,
 		"affinity" : "might",
 		"commander" : "zealot",
-		"animation":
-		{
-			"battle" : { "male" : "CH00.DEF",  "female" : "CH01.DEF" },
-			"map":     { "male" : "AH00_.def", "female" : "AH00_.def" }
-		}
+		"mapObject" : { "templates" : { "default" : { "animation" : "AH00_.def" } } },
+		"animation":  { "battle" : { "male" : "CH00.DEF",  "female" : "CH01.DEF" } }
 	},
 	"cleric" :
 	{
@@ -19,11 +16,8 @@
 		"defaultTavern" : 5,
 		"affinity" : "magic",
 		"commander" : "zealot",
-		"animation":
-		{
-			"battle" : { "male" : "CH00.DEF",  "female" : "CH01.DEF" },
-			"map":     { "male" : "AH01_.def", "female" : "AH01_.def" }
-		}
+		"mapObject" : { "templates" : { "default" : { "animation" : "AH01_.def" } } },
+		"animation":  { "battle" : { "male" : "CH00.DEF",  "female" : "CH01.DEF" } }
 	},
 	"ranger" :
 	{
@@ -32,11 +26,8 @@
 		"defaultTavern" : 5,
 		"affinity" : "might",
 		"commander" : "grandElf",
-		"animation":
-		{
-			"battle" : { "male" : "CH02.DEF",  "female" : "CH03.DEF" },
-			"map":     { "male" : "AH02_.def", "female" : "AH02_.def" }
-		}
+		"mapObject" : { "templates" : { "default" : { "animation" : "AH02_.def" } } },
+		"animation":  { "battle" : { "male" : "CH02.DEF",  "female" : "CH03.DEF" } }
 	},
 	"druid" :
 	{
@@ -45,11 +36,8 @@
 		"defaultTavern" : 5,
 		"affinity" : "magic",
 		"commander" : "grandElf",
-		"animation":
-		{
-			"battle" : { "male" : "CH02.DEF",  "female" : "CH03.DEF" },
-			"map":     { "male" : "AH03_.def", "female" : "AH03_.def" }
-		}
+		"mapObject" : { "templates" : { "default" : { "animation" : "AH03_.def" } } },
+		"animation":  { "battle" : { "male" : "CH02.DEF",  "female" : "CH03.DEF" } }
 	},
 	"alchemist" :
 	{
@@ -58,11 +46,8 @@
 		"defaultTavern" : 5,
 		"affinity" : "might",
 		"commander" : "archMage",
-		"animation":
-		{
-			"battle" : { "male" : "CH05.DEF",  "female" : "CH04.DEF" },
-			"map":     { "male" : "AH04_.def", "female" : "AH04_.def" }
-		}
+		"mapObject" : { "templates" : { "default" : { "animation" : "AH04_.def" } } },
+		"animation":  { "battle" : { "male" : "CH05.DEF",  "female" : "CH04.DEF" } }
 	},
 	"wizard" :
 	{
@@ -71,11 +56,8 @@
 		"defaultTavern" : 5,
 		"affinity" : "magic",
 		"commander" : "archMage",
-		"animation":
-		{
-			"battle" : { "male" : "CH05.DEF",  "female" : "CH04.DEF" },
-			"map":     { "male" : "AH05_.def", "female" : "AH05_.def" }
-		}
+		"mapObject" : { "templates" : { "default" : { "animation" : "AH05_.def" } } },
+		"animation":  { "battle" : { "male" : "CH05.DEF",  "female" : "CH04.DEF" } }
 	},
 	"demoniac" :
 	{
@@ -84,11 +66,8 @@
 		"defaultTavern" : 5,
 		"affinity" : "might",
 		"commander" : "magog",
-		"animation":
-		{
-			"battle" : { "male" : "CH06.DEF",  "female" : "CH07.DEF" },
-			"map":     { "male" : "AH06_.def", "female" : "AH06_.def" }
-		}
+		"mapObject" : { "templates" : { "default" : { "animation" : "AH06_.def" } } },
+		"animation":  { "battle" : { "male" : "CH06.DEF",  "female" : "CH07.DEF" } }
 	},
 	"heretic" :
 	{
@@ -97,11 +76,8 @@
 		"defaultTavern" : 5,
 		"affinity" : "magic",
 		"commander" : "magog",
-		"animation":
-		{
-			"battle" : { "male" : "CH06.DEF",  "female" : "CH07.DEF" },
-			"map":     { "male" : "AH07_.def", "female" : "AH07_.def" }
-		}
+		"mapObject" : { "templates" : { "default" : { "animation" : "AH07_.def" } } },
+		"animation":  { "battle" : { "male" : "CH06.DEF",  "female" : "CH07.DEF" } }
 	},
 	"deathknight" :
 	{
@@ -110,11 +86,8 @@
 		"defaultTavern" : 5,
 		"affinity" : "might",
 		"commander" : "powerLich",
-		"animation":
-		{
-			"battle" : { "male" : "CH08.DEF",  "female" : "CH09.DEF" },
-			"map":     { "male" : "AH08_.def", "female" : "AH08_.def" }
-		}
+		"mapObject" : { "templates" : { "default" : { "animation" : "AH08_.def" } } },
+		"animation":  { "battle" : { "male" : "CH08.DEF",  "female" : "CH09.DEF" } }
 	},
 	"necromancer" :
 	{
@@ -123,11 +96,8 @@
 		"defaultTavern" : 5,
 		"affinity" : "magic",
 		"commander" : "powerLich",
-		"animation":
-		{
-			"battle" : { "male" : "CH08.DEF",  "female" : "CH09.DEF" },
-			"map":     { "male" : "AH09_.def", "female" : "AH09_.def" }
-		}
+		"mapObject" : { "templates" : { "default" : { "animation" : "AH09_.def" } } },
+		"animation":  { "battle" : { "male" : "CH08.DEF",  "female" : "CH09.DEF" } }
 	},
 	"warlock" :
 	{
@@ -136,11 +106,8 @@
 		"defaultTavern" : 5,
 		"affinity" : "might",
 		"commander" : "medusaQueen",
-		"animation":
-		{
-			"battle" : { "male" : "CH010.DEF", "female" : "CH11.DEF" },
-			"map":     { "male" : "AH10_.def", "female" : "AH10_.def" }
-		}
+		"mapObject" : { "templates" : { "default" : { "animation" : "AH10_.def" } } },
+		"animation":  { "battle" : { "male" : "CH010.DEF",  "female" : "CH11.DEF" } }
 	},
 	"overlord" :
 	{
@@ -149,11 +116,8 @@
 		"defaultTavern" : 5,
 		"affinity" : "magic",
 		"commander" : "medusaQueen",
-		"animation":
-		{
-			"battle" : { "male" : "CH010.DEF", "female" : "CH11.DEF" },
-			"map":     { "male" : "AH11_.def", "female" : "AH11_.def" }
-		}
+		"mapObject" : { "templates" : { "default" : { "animation" : "AH11_.def" } } },
+		"animation":  { "battle" : { "male" : "CH010.DEF",  "female" : "CH11.DEF" } }
 	},
 	"barbarian" :
 	{
@@ -162,11 +126,8 @@
 		"defaultTavern" : 5,
 		"affinity" : "might",
 		"commander" : "orcChieftain",
-		"animation":
-		{
-			"battle" : { "male" : "CH013.DEF", "female" : "CH012.DEF" },
-			"map":     { "male" : "AH12_.def", "female" : "AH12_.def" }
-		}
+		"mapObject" : { "templates" : { "default" : { "animation" : "AH12_.def" } } },
+		"animation":  { "battle" : { "male" : "CH013.DEF",  "female" : "CH012.DEF" } }
 	},
 	"battlemage" :
 	{
@@ -175,11 +136,8 @@
 		"defaultTavern" : 5,
 		"affinity" : "magic",
 		"commander" : "orcChieftain",
-		"animation":
-		{
-			"battle" : { "male" : "CH013.DEF", "female" : "CH012.DEF" },
-			"map":     { "male" : "AH13_.def", "female" : "AH13_.def" }
-		}
+		"mapObject" : { "templates" : { "default" : { "animation" : "AH13_.def" } } },
+		"animation":  { "battle" : { "male" : "CH013.DEF",  "female" : "CH012.DEF" } }
 	},
 	"beastmaster" :
 	{
@@ -188,11 +146,8 @@
 		"defaultTavern" : 5,
 		"affinity" : "might",
 		"commander" : "lizardWarrior",
-		"animation":
-		{
-			"battle" : { "male" : "CH014.DEF", "female" : "CH015.DEF" },
-			"map":     { "male" : "AH14_.def", "female" : "AH14_.def" }
-		}
+		"mapObject" : { "templates" : { "default" : { "animation" : "AH14_.def" } } },
+		"animation":  { "battle" : { "male" : "CH014.DEF",  "female" : "CH015.DEF" } }
 	},
 	"witch" :
 	{
@@ -201,11 +156,8 @@
 		"defaultTavern" : 5,
 		"affinity" : "magic",
 		"commander" : "lizardWarrior",
-		"animation":
-		{
-			"battle" : { "male" : "CH014.DEF", "female" : "CH015.DEF" },
-			"map":     { "male" : "AH15_.def", "female" : "AH15_.def" }
-		}
+		"mapObject" : { "templates" : { "default" : { "animation" : "AH15_.def" } } },
+		"animation":  { "battle" : { "male" : "CH014.DEF",  "female" : "CH015.DEF" } }
 	},
 	"planeswalker" :
 	{
@@ -214,11 +166,8 @@
 		"defaultTavern" : 5,
 		"affinity" : "might",
 		"commander" : "iceElemental",
-		"animation":
-		{
-			"battle" : { "male" : "CH16.DEF",  "female" : "CH16.DEF" },
-			"map":     { "male" : "AH16_.def", "female" : "AH16_.def" }
-		}
+		"mapObject" : { "templates" : { "default" : { "animation" : "AH16_.def" } } },
+		"animation":  { "battle" : { "male" : "CH16.DEF",  "female" : "CH16.DEF" } }
 	},
 	"elementalist" :
 	{
@@ -227,10 +176,7 @@
 		"defaultTavern" : 5,
 		"affinity" : "magic",
 		"commander" : "iceElemental",
-		"animation":
-		{
-			"battle" : { "male" : "CH17.DEF",  "female" : "CH17.DEF" },
-			"map":     { "male" : "AH17_.def", "female" : "AH17_.def" }
-		}
+		"mapObject" : { "templates" : { "default" : { "animation" : "AH17_.def" } } },
+		"animation":  { "battle" : { "male" : "CH17.DEF",  "female" : "CH17.DEF" } }
 	}
 }

+ 973 - 0
config/objects/creatureBanks.json

@@ -0,0 +1,973 @@
+{
+	"creatureBank" : {
+		"index" :16,
+		"handler": "bank",
+		"types" : {
+			"cyclopsStockpile" :
+			{
+				"index" : 0,
+				"name" : "Cyclops Stockpile",
+				"levels": [
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 4, "type": "cyclop" },
+							{ "amount": 4, "type": "cyclop" },
+							{ "amount": 4, "type": "cyclop", "upgradeChance": 50 },
+							{ "amount": 4, "type": "cyclop" },
+							{ "amount": 4, "type": "cyclop" }
+						],
+						
+						"combat_value": 506,
+						"reward" : {
+							"value": 10000,
+							"resources": 
+							{
+								"wood" : 4,
+								"mercury" : 4,
+								"ore" : 4,
+								"sulfur" : 4,
+								"crystal" : 4,
+								"gems" : 4,
+								"gold" : 0
+							}
+						}
+					},
+
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 6, "type": "cyclop" },
+							{ "amount": 6, "type": "cyclop" },
+							{ "amount": 6, "type": "cyclop", "upgradeChance": 50 },
+							{ "amount": 6, "type": "cyclop" },
+							{ "amount": 6, "type": "cyclop" }
+						],
+						"combat_value": 760,
+						"reward" : {
+							"value": 15000,
+							"resources": 
+							{
+								"wood" : 6,
+								"mercury" : 6,
+								"ore" : 6,
+								"sulfur" : 6,
+								"crystal" : 6,
+								"gems" : 6
+							}
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 8, "type": "cyclop" },
+							{ "amount": 8, "type": "cyclop" },
+							{ "amount": 8, "type": "cyclop", "upgradeChance": 50 },
+							{ "amount": 8, "type": "cyclop" },
+							{ "amount": 8, "type": "cyclop" }
+						],
+						"combat_value": 1013,
+						"reward" : {
+							"value": 20000,
+							"resources":
+							{
+								"wood" : 8,
+								"mercury" : 8,
+								"ore" : 8,
+								"sulfur" : 8,
+								"crystal" : 8,
+								"gems" : 8
+							}
+						}
+					},
+					{
+						"chance": 10,
+						"guards": [
+							{ "amount": 10, "type": "cyclop" },
+							{ "amount": 10, "type": "cyclop" },
+							{ "amount": 10, "type": "cyclop", "upgradeChance": 50 },
+							{ "amount": 10, "type": "cyclop" },
+							{ "amount": 10, "type": "cyclop" }
+						],
+						"combat_value": 1266,
+						"reward" : {
+							"value": 25000,
+							"resources":
+							{
+								"wood" : 10,
+								"mercury" : 10,
+								"ore" : 10,
+								"sulfur" : 10,
+								"crystal" : 10,
+								"gems" : 10
+							}
+						}
+					}
+				]
+			},
+			"dwarvenTreasury" : {
+				"index" : 1,
+				"resetDuraition" : 28,
+				"name" : "Dwarven Treasury",
+				"levels": [
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 10, "type": "dwarf" },
+							{ "amount": 10, "type": "dwarf" },
+							{ "amount": 10, "type": "dwarf", "upgradeChance": 50 },
+							{ "amount": 10, "type": "dwarf" },
+							{ "amount": 10, "type": "dwarf" }
+						],
+						"combat_value": 194,
+						"reward" : {
+						"value": 3500,
+							"resources":
+							{
+								"crystal" : 2,
+								"gold" : 2500
+							}
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 15, "type": "dwarf" },
+							{ "amount": 15, "type": "dwarf" },
+							{ "amount": 15, "type": "dwarf", "upgradeChance": 50 },
+							{ "amount": 15, "type": "dwarf" },
+							{ "amount": 15, "type": "dwarf" }
+						],
+						"combat_value": 291,
+						"reward" : {
+							"value": 5500,
+							"resources":
+							{
+								"crystal" : 3,
+								"gold" : 4000
+							}
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 20, "type": "dwarf" },
+							{ "amount": 20, "type": "dwarf" },
+							{ "amount": 20, "type": "dwarf", "upgradeChance": 50 },
+							{ "amount": 20, "type": "dwarf" },
+							{ "amount": 20, "type": "dwarf" }
+						],
+						"combat_value": 388,
+						"reward" : {
+							"value": 7500,
+							"resources":
+							{
+								"crystal" : 5,
+								"gold" : 5000
+							}
+						}
+					},
+					{
+						"chance": 10,
+						"guards": [
+							{ "amount": 30, "type": "dwarf" },
+							{ "amount": 30, "type": "dwarf" },
+							{ "amount": 30, "type": "dwarf", "upgradeChance": 50 },
+							{ "amount": 30, "type": "dwarf" },
+							{ "amount": 30, "type": "dwarf" }
+						],
+						"combat_value": 582,
+						"reward" : {
+							"value": 12500,
+							"resources":
+							{
+								"crystal" : 10,
+								"gold" : 7500
+							}
+						}
+					}
+				]
+			},
+			"griffinConservatory" : {
+				"index" : 2,
+				"resetDuraition" : 28,
+				"name" : "Griffin Conservatory",
+				"levels": [
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 10, "type": "griffin" },
+							{ "amount": 10, "type": "griffin" },
+							{ "amount": 10, "type": "griffin", "upgradeChance": 50 },
+							{ "amount": 10, "type": "griffin" },
+							{ "amount": 10, "type": "griffin" }
+						],
+						"combat_value": 351,
+						"reward" : {
+							"value": 3000,
+							"creatures": [ { "amount": 1, "type": "angel" } ]
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 20, "type": "griffin" },
+							{ "amount": 20, "type": "griffin" },
+							{ "amount": 20, "type": "griffin", "upgradeChance": 50 },
+							{ "amount": 20, "type": "griffin" },
+							{ "amount": 20, "type": "griffin" }
+						],
+						"combat_value": 702,
+						"reward" : {
+							"value": 6000,
+							"creatures": [ { "amount": 2, "type": "angel" } ]
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 30, "type": "griffin" },
+							{ "amount": 30, "type": "griffin" },
+							{ "amount": 30, "type": "griffin", "upgradeChance": 50 },
+							{ "amount": 30, "type": "griffin" },
+							{ "amount": 30, "type": "griffin" }
+						],
+						"combat_value": 1053,
+						"reward" : {
+							"value": 9000,
+							"creatures": [ { "amount": 3, "type": "angel" } ]
+						}
+					},
+					{
+						"chance": 10,
+						"guards": [
+							{ "amount": 40, "type": "griffin" },
+							{ "amount": 40, "type": "griffin" },
+							{ "amount": 40, "type": "griffin", "upgradeChance": 50 },
+							{ "amount": 40, "type": "griffin" },
+							{ "amount": 40, "type": "griffin" }
+						],
+						"combat_value": 1404,
+						"reward" : {
+							"value": 12000,
+							"creatures": [ { "amount": 4, "type": "angel" } ]
+						}
+					}
+				]
+			},
+			"inpCache" :  {
+				"index" : 3,
+				"resetDuraition" : 28,
+				"name" : "Imp Cache",
+				"levels": [
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 20, "type": "imp" },
+							{ "amount": 20, "type": "imp" },
+							{ "amount": 20, "type": "imp", "upgradeChance": 50 },
+							{ "amount": 20, "type": "imp" },
+							{ "amount": 20, "type": "imp" }
+						],
+						"combat_value": 100,
+						"reward" : {
+							"value": 2000,
+							"resources":
+							{
+								"wood" : 0,
+								"mercury" : 2,
+								"ore" : 0,
+								"sulfur" : 0,
+								"crystal" : 0,
+								"gems" : 0,
+								"gold" : 1000
+							}
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 30, "type": "imp" },
+							{ "amount": 30, "type": "imp" },
+							{ "amount": 30, "type": "imp", "upgradeChance": 50 },
+							{ "amount": 30, "type": "imp" },
+							{ "amount": 30, "type": "imp" }
+						],
+						"combat_value": 150,
+						"reward" : {
+							"value": 3000,
+							"resources":
+							{
+								"mercury" : 3,
+								"gold" : 1500
+							}
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 40, "type": "imp" },
+							{ "amount": 40, "type": "imp" },
+							{ "amount": 40, "type": "imp", "upgradeChance": 50 },
+							{ "amount": 40, "type": "imp" },
+							{ "amount": 40, "type": "imp" }
+						],
+						"combat_value": 200,
+						"reward" : {
+							"value": 4000,
+							"resources":
+							{
+								"mercury" : 4,
+								"gold" : 2000
+							}
+						}
+					},
+					{
+						"chance": 10,
+						"guards": [
+							{ "amount": 60, "type": "imp" },
+							{ "amount": 60, "type": "imp" },
+							{ "amount": 60, "type": "imp", "upgradeChance": 50 },
+							{ "amount": 60, "type": "imp" },
+							{ "amount": 60, "type": "imp" }
+						],
+						"combat_value": 300,
+						"reward" : {
+							"value": 6000,
+							"resources":
+							{
+								"mercury" : 6,
+								"gold" : 3000
+							}
+						}
+					}
+				]
+			},
+			"medusaStore" : {
+				"index" : 4,
+				"resetDuraition" : 28,
+				"name" : "Medusa Stores",
+				"levels": [
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 4, "type": "medusa" },
+							{ "amount": 4, "type": "medusa" },
+							{ "amount": 4, "type": "medusa", "upgradeChance": 50 },
+							{ "amount": 4, "type": "medusa" },
+							{ "amount": 4, "type": "medusa" }
+						],
+						"combat_value": 207,
+						"reward" : {
+							"value": 4500,
+							"resources":
+							{
+								"sulfur" : 5,
+								"gold" : 2000
+							}
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 6, "type": "medusa" },
+							{ "amount": 6, "type": "medusa" },
+							{ "amount": 6, "type": "medusa", "upgradeChance": 50 },
+							{ "amount": 6, "type": "medusa" },
+							{ "amount": 6, "type": "medusa" }
+						],
+						"combat_value": 310,
+						"reward" : {
+							"value": 6000,
+							"resources":
+							{
+								"sulfur" : 6,
+								"gold" : 3000
+							}
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 8, "type": "medusa" },
+							{ "amount": 8, "type": "medusa" },
+							{ "amount": 8, "type": "medusa", "upgradeChance": 50 },
+							{ "amount": 8, "type": "medusa" },
+							{ "amount": 8, "type": "medusa" }
+						],
+						"combat_value": 414,
+						"reward" : {
+							"value": 8000,
+							"resources":
+							{
+								"sulfur" : 8,
+								"gold" : 4000
+							}
+						}
+					},
+					{
+						"chance": 10,
+						"guards": [
+							{ "amount": 10, "type": "medusa" },
+							{ "amount": 10, "type": "medusa" },
+							{ "amount": 10, "type": "medusa", "upgradeChance": 50 },
+							{ "amount": 10, "type": "medusa" },
+							{ "amount": 10, "type": "medusa" }
+						],
+						"combat_value": 517,
+						"reward" : {
+							"value": 10000,
+							"resources":
+							{
+								"sulfur" : 10,
+								"gold" : 5000
+							}
+						}
+					}
+				]
+			},
+			"nagaBank" : {
+				"index" : 5,
+				"resetDuraition" : 28,
+				"name" : "Naga Bank",
+				"levels": [
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 2, "type": "naga" },
+							{ "amount": 2, "type": "naga" },
+							{ "amount": 2, "type": "naga", "upgradeChance": 50 },
+							{ "amount": 2, "type": "naga" },
+							{ "amount": 2, "type": "naga" }
+						],
+						"combat_value": 403,
+						"reward" : {
+							"value": 8000,
+							"resources":
+							{
+								"gems" : 8,
+								"gold" : 4000
+							}
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 3, "type": "naga" },
+							{ "amount": 3, "type": "naga" },
+							{ "amount": 3, "type": "naga", "upgradeChance": 50 },
+							{ "amount": 3, "type": "naga" },
+							{ "amount": 3, "type": "naga" }
+						],
+						"combat_value": 605,
+						"reward" : {
+							"value": 12000,
+							"resources":
+							{
+								"gems" : 12,
+								"gold" : 6000
+							}
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 4, "type": "naga" },
+							{ "amount": 4, "type": "naga" },
+							{ "amount": 4, "type": "naga", "upgradeChance": 50 },
+							{ "amount": 4, "type": "naga" },
+							{ "amount": 4, "type": "naga" }
+						],
+						"combat_value": 806,
+						"reward" : {
+							"value": 16000,
+							"resources":
+							{
+								"gems" : 16,
+								"gold" : 8000
+							}
+						}
+					},
+					{
+						"chance": 10,
+						"guards": [
+							{ "amount": 6, "type": "naga" },
+							{ "amount": 6, "type": "naga" },
+							{ "amount": 6, "type": "naga", "upgradeChance": 50 },
+							{ "amount": 6, "type": "naga" },
+							{ "amount": 6, "type": "naga" }
+						],
+						"combat_value": 1210,
+						"reward" : {
+							"value": 24000,
+							"resources":
+							{
+								"gems" : 24,
+								"gold" : 12000
+							}
+						}
+					}
+				]
+			},
+			"dragonflyHive" : {
+				"index" : 6,
+				"resetDuraition" : 28,
+				"name" : "Dragon Fly Hive",
+				"levels": [
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 6, "type": "dragonFly" },
+							{ "amount": 6, "type": "dragonFly" },
+							{ "amount": 6, "type": "dragonFly" },
+							{ "amount": 6, "type": "dragonFly" },
+							{ "amount": 6, "type": "dragonFly" }
+						],
+						"combat_value": 154,
+						"reward" : {
+							"value": 3200,
+							"creatures": [ { "amount": 4, "type": "vyvern" } ]
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 9, "type": "dragonFly" },
+							{ "amount": 9, "type": "dragonFly" },
+							{ "amount": 9, "type": "dragonFly" },
+							{ "amount": 9, "type": "dragonFly" },
+							{ "amount": 9, "type": "dragonFly" }
+						],
+						"combat_value": 230,
+						"reward" : {
+							"value": 4800,
+							"creatures": [ { "amount": 6, "type": "vyvern" } ]
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 12, "type": "dragonFly" },
+							{ "amount": 12, "type": "dragonFly" },
+							{ "amount": 12, "type": "dragonFly" },
+							{ "amount": 12, "type": "dragonFly" },
+							{ "amount": 12, "type": "dragonFly" }
+						],
+						"combat_value": 307,
+						"reward" : {
+							"value": 6400,
+							"creatures": [ { "amount": 8, "type": "vyvern" } ]
+						}
+					},
+					{
+						"chance": 10,
+						"guards": [
+							{ "amount": 18, "type": "dragonFly" },
+							{ "amount": 18, "type": "dragonFly" },
+							{ "amount": 18, "type": "dragonFly" },
+							{ "amount": 18, "type": "dragonFly" },
+							{ "amount": 18, "type": "dragonFly" }
+						],
+						"combat_value": 461,
+						"reward" : {
+							"value": 9600,
+							"creatures": [ { "amount": 12, "type": "vyvern" } ]
+						}
+					}
+				]
+			}
+		}
+	},
+	"shipwreck" : {
+		"index" :85,
+		"handler": "bank",
+		"types" : {
+			"shipwreck" : {
+				"index" : 0,
+				"resetDuraition" : 28,
+				"name" : "Shipwreck",
+				"levels": [
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 2, "type": "wight" },
+							{ "amount": 2, "type": "wight" },
+							{ "amount": 2, "type": "wight" },
+							{ "amount": 2, "type": "wight" },
+							{ "amount": 2, "type": "wight" }
+						],
+						"combat_value": 31,
+						"reward" : {
+							"value": 2000,
+							"resources":
+							{
+								"gold" : 2000
+							}
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 3, "type": "wight" },
+							{ "amount": 3, "type": "wight" },
+							{ "amount": 3, "type": "wight" },
+							{ "amount": 3, "type": "wight" },
+							{ "amount": 3, "type": "wight" }
+						],
+						"combat_value": 46,
+						"reward" : {
+							"value": 3000,
+							"resources":
+							{
+								"gold" : 3000
+							}
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 5, "type": "wight" },
+							{ "amount": 5, "type": "wight" },
+							{ "amount": 5, "type": "wight" },
+							{ "amount": 5, "type": "wight" },
+							{ "amount": 5, "type": "wight" }
+						],
+						"combat_value": 77,
+						"reward" : {
+							"value": 5000,
+							"resources":
+							{
+								"gold" : 4000
+							},
+							"artifacts": [ { "class" : "TREASURE" } ]
+						}
+					},
+					{
+						"chance": 10,
+						"guards": [
+							{ "amount": 10, "type": "wight" },
+							{ "amount": 10, "type": "wight" },
+							{ "amount": 10, "type": "wight" },
+							{ "amount": 10, "type": "wight" },
+							{ "amount": 10, "type": "wight" }
+						],
+						"combat_value": 154,
+						"reward" : {
+							"value": 7000,
+							"resources":
+							{
+								"gold" : 5000
+							},
+							"artifacts": [ { "class" : "MINOR" } ]
+						}
+					}
+				]
+			}
+		}
+	},
+	"derelictShip" : {
+		"index" :24,
+		"handler": "bank",
+		"types" : {
+			"derelictShip" : {
+				"index" : 0,
+				"resetDuraition" : 28,
+				"name" : "Derelict Ship",
+				"levels": [
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 4, "type": "waterElemental" },
+							{ "amount": 4, "type": "waterElemental" },
+							{ "amount": 4, "type": "waterElemental" },
+							{ "amount": 4, "type": "waterElemental" },
+							{ "amount": 4, "type": "waterElemental" }
+						],
+						"combat_value": 138,
+						"reward" : {
+							"value": 3000,
+							"resources":
+							{
+								"gold" : 3000
+							}
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 6, "type": "waterElemental" },
+							{ "amount": 6, "type": "waterElemental" },
+							{ "amount": 6, "type": "waterElemental" },
+							{ "amount": 6, "type": "waterElemental" },
+							{ "amount": 6, "type": "waterElemental" }
+						],
+						"combat_value": 207,
+						"reward" : {
+							"value": 4000,
+							"resources":
+							{
+								"gold" : 3000
+							},
+							"artifacts": [ { "class" : "TREASURE" } ]
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 8, "type": "waterElemental" },
+							{ "amount": 8, "type": "waterElemental" },
+							{ "amount": 8, "type": "waterElemental" },
+							{ "amount": 8, "type": "waterElemental" },
+							{ "amount": 8, "type": "waterElemental" }
+						],
+						"combat_value": 276,
+						"reward" : {
+							"value": 5000,
+							"resources":
+							{
+								"gold" : 4000
+							},
+							"artifacts": [ { "class" : "TREASURE" } ]
+						}
+					},
+					{
+						"chance": 10,
+						"guards": [
+							{ "amount": 12, "type": "waterElemental" },
+							{ "amount": 12, "type": "waterElemental" },
+							{ "amount": 12, "type": "waterElemental" },
+							{ "amount": 12, "type": "waterElemental" },
+							{ "amount": 12, "type": "waterElemental" }
+						],
+						"combat_value": 414,
+						"reward" : {
+							"value": 8000,
+							"resources":
+							{
+								"gold" : 6000
+							},
+							"artifacts": [ { "class" : "MINOR" } ]
+						}
+					}
+				]
+			}
+		}
+	},
+	"crypt" : {
+		"index" :84,
+		"handler": "bank",
+		"types" : {
+			"crypt" : {
+				"index" : 0,
+				"resetDuraition" : 28,
+				"name" : "Crypt",
+				"levels": [
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 10, "type": "skeleton" },
+							{ "amount": 10, "type": "walkingDead" },
+							{ "amount": 10, "type": "walkingDead" },
+							{ "amount": 10, "type": "skeleton" },
+							{ "amount": 10, "type": "skeleton" }
+						],
+						"combat_value": 75,
+						"reward" : {
+							"value": 1500,
+							"resources":
+							{
+								"gold" : 1500
+							}
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 13, "type": "skeleton" },
+							{ "amount": 10, "type": "walkingDead" },
+							{ "amount": 5, "type": "wight" },
+							{ "amount": 10, "type": "walkingDead" },
+							{ "amount": 12, "type": "skeleton" }
+						],
+						"combat_value": 94,
+						"reward" : {
+							"value": 2000,
+							"resources":
+							{
+								"gold" : 2000
+							}
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 20, "type": "skeleton" },
+							{ "amount": 20, "type": "walkingDead" },
+							{ "amount": 10, "type": "wight" },
+							{ "amount": 5, "type": "vampire" }
+						],
+						"combat_value": 169,
+						"reward" : {
+							"value": 3500,
+							"resources":
+							{
+								"gold" : 2500
+							},
+							"artifacts": [ { "class" : "TREASURE" } ]
+						}
+					},
+					{
+						"chance": 10,
+						"guards": [
+							{ "amount": 20, "type": "skeleton" },
+							{ "amount": 20, "type": "walkingDead" },
+							{ "amount": 10, "type": "wight" },
+							{ "amount": 10, "type": "vampire" }
+						],
+						"combat_value": 225,
+						"reward" : {
+							"value": 6000,
+							"resources":
+							{
+								"gold" : 5000
+							},
+							"artifacts": [ { "class" : "TREASURE" } ]
+						}
+					}
+				]
+			}
+		}
+	},
+	"dragonUtopia" : {
+		"index" :25,
+		"handler": "bank",
+		"types" : {
+			"dragonUtopia" : {
+				"index" : 0,
+				"resetDuraition" : 28,
+				"name" : "Dragon Utopia",
+				"levels": [
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 8, "type": "greenDragon" },
+							{ "amount": 5, "type": "redDragon" },
+							{ "amount": 2, "type": "goldDragon" },
+							{ "amount": 1, "type": "blackDragon" }
+						],
+						"combat_value": 769,
+						"reward" : {
+							"value": 38000,
+							"resources":
+							{
+								"gold" : 20000
+							},
+							"artifacts": [
+								{ "class" : "TREASURE" },
+								{ "class" : "MINOR" },
+								{ "class" : "MAJOR" },
+								{ "class" : "RELIC" }
+							]
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 8, "type": "greenDragon" },
+							{ "amount": 6, "type": "redDragon" },
+							{ "amount": 3, "type": "goldDragon" },
+							{ "amount": 2, "type": "blackDragon" }
+						],
+						"combat_value": 209,
+						"reward" : {
+							"value": 57000,
+							"resources":
+							{
+								"gold" : 30000
+							},
+							"artifacts": [
+								{ "class" : "MINOR" },
+								{ "class" : "MAJOR" },
+								{ "class" : "RELIC" },
+								{ "class" : "RELIC" }
+							]
+						}
+					},
+					{
+						"chance": 30,
+						"guards": [
+							{ "amount": 8, "type": "greenDragon" },
+							{ "amount": 6, "type": "redDragon" },
+							{ "amount": 4, "type": "goldDragon" },
+							{ "amount": 3, "type": "blackDragon" }
+						],
+						"combat_value": 556,
+						"reward" : {
+							"value": 75000,
+							"resources":
+							{
+								"gold" : 40000
+							},
+							"artifacts": [
+								{ "class" : "MAJOR" },
+								{ "class" : "RELIC" },
+								{ "class" : "RELIC" },
+								{ "class" : "RELIC" }
+							]
+						}
+					},
+					{
+						"chance": 10,
+						"guards": [
+							{ "amount": 8, "type": "greenDragon" },
+							{ "amount": 7, "type": "redDragon" },
+							{ "amount": 6, "type": "goldDragon" },
+							{ "amount": 5, "type": "blackDragon" }
+						],
+						"combat_value": 343,
+						"reward" : {
+							"value": 90000,
+							"resources":
+							{
+								"gold" : 50000
+							},
+							"artifacts": [
+								{ "class" : "RELIC" },
+								{ "class" : "RELIC" },
+								{ "class" : "RELIC" },
+								{ "class" : "RELIC" }
+							]
+						}
+					}
+				]
+			}
+		}
+	},
+	"pyramid" : {
+		"index" :63,
+		"handler": "bank",
+		"types" : {
+			"pyramid" : {
+				"index" : 0,
+				"resetDuraition" : 28,
+				"name" : "Pyramid",
+				"levels": [
+					{
+						"chance": 100,
+						"guards": [
+							{ "amount": 20, "type": "goldGolem" },
+							{ "amount": 10, "type": "diamondGolem" },
+							{ "amount": 20, "type": "goldGolem" },
+							{ "amount": 10, "type": "diamondGolem" }
+						],
+						"combat_value": 786,
+						"reward" : {
+							"value": 15000,
+							"spells" : [ { "level" : 5 } ]
+						}
+					}
+				]
+			}
+		}
+	}
+}

+ 126 - 0
config/objects/dwellings.json

@@ -0,0 +1,126 @@
+{
+	"creatureGeneratorCommon" : {
+		"index" :17,
+		"handler": "dwelling",
+		"base" : {
+			"base" : {
+				"visitableFrom" : [ "---", "+++", "+++" ],
+				"mask" : [ "VVV", "VBB", "VAA" ]
+			}
+		},
+		"types" : {
+			"basiliskPit" :      { "index" : 0,  "creatures" :  [ [ "basilisk" ] ] },
+			"behemothCrag" :     { "index" : 1,  "creatures" :  [ [ "behemoth" ] ], "guards" : true },
+			"pillarOfEyes" :     { "index" : 2,  "creatures" :  [ [ "beholder" ] ] },
+			"hallOfDarkness" :   { "index" : 3,  "creatures" :  [ [ "blackKnight" ] ], "guards" : true },
+			"dragonVault" :      { "index" : 4,  "creatures" :  [ [ "boneDragon" ] ], "guards" : true },
+			"trainingGrounds" :  { "index" : 5,  "creatures" :  [ [ "cavalier" ] ], "guards" : true },
+			"centaurStables" :   { "index" : 6,  "creatures" :  [ [ "centaur" ] ] },
+			"airConflux" :       { "index" : 7,  "creatures" :  [ [ "airElemental" ] ] },
+			"portalOfGlory" :    { "index" : 8,  "creatures" :  [ [ "angel" ] ], "guards" : true },
+			"cyclopsCave" :      { "index" : 9,  "creatures" :  [ [ "cyclop" ] ], "guards" : true },
+			"forsakenPalace" :   { "index" : 10, "creatures" : [ [ "devil" ] ], "guards" : true },
+			"serpentFlyHive" :   { "index" : 11, "creatures" : [ [ "serpentFly" ] ] },
+			"dwarfCottage" :     { "index" : 12, "creatures" : [ [ "dwarf" ] ] },
+			"earthConflux" :     { "index" : 13, "creatures" : [ [ "earthElemental" ] ], "guards" : true },
+			"fireLake" :         { "index" : 14, "creatures" : [ [ "efreet" ] ], "guards" : true },
+			"homestead" :        { "index" : 15, "creatures" : [ [ "woodElf" ] ] },
+			"fireConflux" :      { "index" : 16, "creatures" : [ [ "fireElemental" ] ] },
+			"parapet" :          { "index" : 17, "creatures" : [ [ "stoneGargoyle" ] ] },
+			"altarOfWishes" :    { "index" : 18, "creatures" : [ [ "genie" ] ], "guards" : true },
+			"wolfPen" :          { "index" : 19, "creatures" : [ [ "goblinWolfRider" ] ] },
+			"gnollHut" :         { "index" : 20, "creatures" : [ [ "gnoll" ] ] },
+			"goblinBarracks" :   { "index" : 21, "creatures" : [ [ "goblin" ] ] },
+			"hallOfSins" :       { "index" : 22, "creatures" : [ [ "gog" ] ] },
+			"gorgonLair" :       { "index" : 23, "creatures" : [ [ "gorgon" ] ], "guards" : true },
+			"dragonCliffs" :     { "index" : 24, "creatures" : [ [ "greenDragon" ] ], "guards" : true },
+			"griffinTower" :     { "index" : 25, "creatures" : [ [ "griffin" ] ] },
+			"harpyLoft" :        { "index" : 26, "creatures" : [ [ "harpy" ] ] },
+			"kennels" :          { "index" : 27, "creatures" : [ [ "hellHound" ] ] },
+			"hydraPond" :        { "index" : 28, "creatures" : [ [ "hydra" ] ], "guards" : true },
+			"impCrucible" :      { "index" : 29, "creatures" : [ [ "imp" ] ] },
+			"lizardDen" :        { "index" : 30, "creatures" : [ [ "lizardman" ] ] },
+			"mageTower" :        { "index" : 31, "creatures" : [ [ "mage" ] ] },
+			"manticoreLair" :    { "index" : 32, "creatures" : [ [ "manticore" ] ], "guards" : true },
+			"medusaChapel" :     { "index" : 33, "creatures" : [ [ "medusa" ] ] },
+			"labyrinth" :        { "index" : 34, "creatures" : [ [ "minotaur" ] ], "guards" : true },
+			"monastery" :        { "index" : 35, "creatures" : [ [ "monk" ] ], "guards" : true },
+			"goldenPavilion" :   { "index" : 36, "creatures" : [ [ "naga" ] ], "guards" : true },
+			"demonGate" :        { "index" : 37, "creatures" : [ [ "demon" ] ] },
+			"ogreFort" :         { "index" : 38, "creatures" : [ [ "ogre" ] ] },
+			"orcTower" :         { "index" : 39, "creatures" : [ [ "orc" ] ] },
+			"hellHole" :         { "index" : 40, "creatures" : [ [ "pitFiend" ] ], "guards" : true },
+			"dragonCave" :       { "index" : 41, "creatures" : [ [ "redDragon" ] ], "guards" : true },
+			"cliffNest" :        { "index" : 42, "creatures" : [ [ "roc" ] ], "guards" : true },
+			"workshop" :         { "index" : 43, "creatures" : [ [ "gremlin" ] ] },
+			"cloudTemple" :      { "index" : 44, "creatures" : [ [ "giant" ] ], "guards" : true },
+			"dendroidArches" :   { "index" : 45, "creatures" : [ [ "dendroidGuard" ] ], "guards" : true },
+			"warren" :           { "index" : 46, "creatures" : [ [ "troglodyte" ] ] },
+			"waterConflux" :     { "index" : 47, "creatures" : [ [ "waterElemental" ] ] },
+			"tombOfSouls" :      { "index" : 48, "creatures" : [ [ "wight" ] ] },
+			"wyvernNest" :       { "index" : 49, "creatures" : [ [ "wyvern" ] ], "guards" : true },
+			"enchantedSpring" :  { "index" : 50, "creatures" : [ [ "pegasus" ] ] },
+			"unicornGladeBig" :  { "index" : 51, "creatures" : [ [ "unicorn" ] ], "guards" : true },
+			"mausoleum" :        { "index" : 52, "creatures" : [ [ "lich" ] ], "guards" : true },
+			"estate" :           { "index" : 53, "creatures" : [ [ "vampire" ] ] },
+			"cursedTemple" :     { "index" : 54, "creatures" : [ [ "skeleton" ] ] },
+			"graveyard" :        { "index" : 55, "creatures" : [ [ "walkingDead" ] ] },
+			"guardhouse" :       { "index" : 56, "creatures" : [ [ "pikeman" ] ] },
+			"archersTower" :     { "index" : 57, "creatures" : [ [ "archer" ] ] },
+			"barracks" :         { "index" : 58, "creatures" : [ [ "swordsman" ] ] },
+			"magicLantern" :     { "index" : 59, "creatures" : [ [ "pixie" ] ] },
+			"altarOfThought" :   { "index" : 60, "creatures" : [ [ "psychicElemental" ] ], "guards" : true },
+			"pyre" :             { "index" : 61, "creatures" : [ [ "firebird" ] ], "guards" : true },
+			"frozenCliffs" :     { "index" : 62, "creatures" : [ [ "azureDragon" ] ], "guards" : true },
+			"crystalCavern" :    { "index" : 63, "creatures" : [ [ "crystalDragon" ] ], "guards" : true },
+			"magicForest" :      { "index" : 64, "creatures" : [ [ "fairieDragon" ] ], "guards" : true },
+			"sulfurousLair" :    { "index" : 65, "creatures" : [ [ "rustDragon" ] ], "guards" : true },
+			"enchantersHollow" : { "index" : 66, "creatures" : [ [ "enchanter" ] ], "guards" : true },
+			"treetopTower" :     { "index" : 67, "creatures" : [ [ "sharpshooter" ] ], "guards" : true },
+			"unicornGlade" :     { "index" : 68, "creatures" : [ [ "unicorn" ] ], "guards" : true },
+			"altarOfAir" :       { "index" : 69, "creatures" : [ [ "airElemental" ] ] },
+			"altarOfEarth" :     { "index" : 70, "creatures" : [ [ "earthElemental" ] ], "guards" : true },
+			"altarOfFire" :      { "index" : 71, "creatures" : [ [ "fireElemental" ] ] },
+			"altarOfWater" :     { "index" : 72, "creatures" : [ [ "waterElemental" ] ] },
+			"thatchedHut" :      { "index" : 73, "creatures" : [ [ "halfling" ] ] },
+			"hovel" :            { "index" : 74, "creatures" : [ [ "peasant" ] ] },
+			"boarGlen" :         { "index" : 75, "creatures" : [ [ "boar" ] ] },
+			"tombOfCurses" :     { "index" : 76, "creatures" : [ [ "mummy" ] ] },
+			"nomadTent" :        { "index" : 77, "creatures" : [ [ "nomad" ] ] },
+			"rogueCavern" :      { "index" : 78, "creatures" : [ [ "rogue" ] ] },
+			"trollBridge" :      { "index" : 79, "creatures" : [ [ "troll" ] ], "guards" : true }
+		}
+	},
+	// subtype: unique special dwellings - golem factory and elemental conflux
+	"creatureGeneratorSpecial" : {
+		"index" :20,
+		"handler": "dwelling",
+		"types" : {
+			"elementalConflux" : {
+				"index" : 0,
+				"creatures" : [ // 4 separate "levels" to give them separate growth
+					[ "airElemental" ],
+					[ "waterElemental" ],
+					[ "fireElemental" ],
+					[ "earthElemental" ]
+				],
+				"guards" : [
+					{ "amount" : 12, "type" : "earthElemental" }
+				]
+			},
+			"golemFactory" : {
+				"index" : 1,
+				"creatures" : [ // 4 separate "levels" to give them separate growth
+					[ "ironGolem" ],
+					[ "stoneGolem" ],
+					[ "goldGolem" ],
+					[ "diamondGolem" ]
+				],
+				"guards" : [
+					{ "amount" : 9, "type" : "goldGolem" },
+					{ "amount" : 6, "type" : "diamondGolem" }
+				]
+			}
+		}
+	},
+}

+ 133 - 0
config/objects/generic.json

@@ -0,0 +1,133 @@
+{
+	/// These are objects that can not be configured, either due to
+	/// their hardcoded status or because they don't have any configurable functionality
+	"prison" : {
+		"index" :62,
+		"handler": "prison",
+		"types" : {
+			"prison" : { "index" : 0 }
+		}
+	},
+	
+	"altarOfSacrifice"				: { "index" :2,  "handler": "market" },
+	"tradingPost"					: { "index" :221, "handler": "market" },
+	"tradingPostDUPLICATE"			: { "index" :99, "handler": "market" },
+	"freelancersGuild"				: { "index" :213, "handler": "market" },
+
+	"blackMarket"					: { "index" :7,  "handler": "blackMarket" },
+
+	"pandoraBox"					: { "index" :6,  "handler": "pandora" },
+	"event"							: { "index" :26, "handler": "event" },
+
+	"redwoodObservatory"			: { "index" :58, "handler": "observatory" },
+	"pillarOfFire"					: { "index" :60, "handler": "observatory" },
+	"coverOfDarkness"				: { "index" :15, "handler": "observatory" },
+	
+	"subterraneanGate"				: { "index" :103, "handler": "teleport" },
+	"whirlpool"						: { "index" :111, "handler": "teleport" },
+
+	"refugeeCamp"					: { "index" :78, "handler": "dwelling" },
+	"warMachineFactory"				: { "index" :106, "handler": "dwelling" },
+
+	"shrineOfMagicLevel1"			: { "index" :88, "handler": "shrine" },
+	"shrineOfMagicLevel2"			: { "index" :89, "handler": "shrine" },
+	"shrineOfMagicLevel3"			: { "index" :90, "handler": "shrine" },
+
+	"eyeOfTheMagi"					: { "index" :27, "handler": "magi" },
+	"hutOfTheMagi"					: { "index" :37, "handler": "magi" },
+
+	"lighthouse"					: { "index" :42, "handler": "lighthouse" },
+	"magicWell"						: { "index" :49, "handler": "magicWell" },
+	"obelisk"						: { "index" :57, "handler": "obelisk" },
+	"oceanBottle"					: { "index" :59, "handler": "sign" },
+	"scholar"						: { "index" :81, "handler": "scholar" },
+	"shipyard"						: { "index" :87, "handler": "shipyard" },
+	"sign"							: { "index" :91, "handler": "sign" },
+	"sirens"						: { "index" :92, "handler": "siren" },
+	"denOfThieves"					: { "index" :97, "handler": "denOfThieves" },
+	"university"					: { "index" :104, "handler": "university" },
+	"witchHut"						: { "index" :113, "handler": "witch" },
+	"questGuard"					: { "index" :215, "handler": "questGuard" },
+
+	/// Random objects
+	"randomResource"				: { "index" :76, "handler": "resource" },
+	"randomTown"					: { "index" :77, "handler": "town" },
+	"randomHero"					: { "index" :70, "handler": "hero" },
+	
+	"randomDwelling"				: { "index" :216, "handler": "dwelling" },
+
+	"randomArtifact"				: { "index" :65, "handler": "artifact" },
+	"randomArtifactTreasure"		: { "index" :66, "handler": "artifact" },
+	"randomArtifactMinor"			: { "index" :67, "handler": "artifact" },
+	"randomArtifactMajor"			: { "index" :68, "handler": "artifact" },
+	"randomArtifactRelic"			: { "index" :69, "handler": "artifact" },
+
+	"randomMonster"					: { "index" :71, "handler": "monster" },
+	"randomMonsterLevel1"			: { "index" :72, "handler": "monster" },
+	"randomMonsterLevel2"			: { "index" :73, "handler": "monster" },
+	"randomMonsterLevel3"			: { "index" :74, "handler": "monster" },
+	"randomMonsterLevel4"			: { "index" :75, "handler": "monster" },
+	"randomMonsterLevel5"			: { "index" :162, "handler": "monster" },
+	"randomMonsterLevel6"			: { "index" :163, "handler": "monster" },
+	"randomMonsterLevel7"			: { "index" :164, "handler": "monster" },
+
+	/// Classes without dedicated object
+	"hillFort"						: { "index" :35, "handler": "generic" },
+	"grail"							: { "index" :36, "handler": "generic" },
+	"tavern"						: { "index" :95, "handler": "generic" },
+	"sanctuary"						: { "index" :80, "handler": "generic" },
+
+	/// Passive objects, terrain overlays
+	"cursedGround"					: { "index" :21, "handler": "generic" },
+	"magicPlains"					: { "index" :46, "handler": "generic" },
+	"swampFoliage"					: { "index" :211, "handler": "generic" },
+	"cloverField"					: { "index" :222, "handler": "generic" },
+	"cursedGroundDUPLICATE"			: { "index" :223, "handler": "generic" },
+	"evilFog"						: { "index" :224, "handler": "generic" },
+	"favorableWinds"				: { "index" :225, "handler": "generic" },
+	"fieryFields"					: { "index" :226, "handler": "generic" },
+	"holyGround"					: { "index" :227, "handler": "generic" },
+	"lucidPools"					: { "index" :228, "handler": "generic" },
+	"magicClouds"					: { "index" :229, "handler": "generic" },
+	"magicPlainsDUPLICATE"			: { "index" :230, "handler": "generic" },
+	"rocklands"						: { "index" :231, "handler": "generic" },
+
+	/// Decorations
+	"cactus"						: { "index" :116, "handler": "static" },
+	"canyon"						: { "index" :117, "handler": "static" },
+	"crater"						: { "index" :118, "handler": "static" },
+	"deadVegetation"				: { "index" :119, "handler": "static" },
+	"flowers"						: { "index" :120, "handler": "static" },
+	"frozenLake"					: { "index" :121, "handler": "static" },
+	"hole"							: { "index" :124, "handler": "static" },
+	"kelp"							: { "index" :125, "handler": "static" },
+	"lake"							: { "index" :126, "handler": "static" },
+	"lavaFlow"						: { "index" :127, "handler": "static" },
+	"lavaLake"						: { "index" :128, "handler": "static" },
+	"mushrooms"						: { "index" :129, "handler": "static" },
+	"log"							: { "index" :130, "handler": "static" },
+	"mandrake"						: { "index" :131, "handler": "static" },
+	"moss"							: { "index" :132, "handler": "static" },
+	"mound"							: { "index" :133, "handler": "static" },
+	"mountain"						: { "index" :134, "handler": "static" },
+	"oakTrees"						: { "index" :135, "handler": "static" },
+	"outcropping"					: { "index" :136, "handler": "static" },
+	"pineTrees"						: { "index" :137, "handler": "static" },
+	"riverDelta"					: { "index" :143, "handler": "static" },
+	"rock"							: { "index" :147, "handler": "static" },
+	"sandDune"						: { "index" :148, "handler": "static" },
+	"sandPit"						: { "index" :149, "handler": "static" },
+	"shrub"							: { "index" :150, "handler": "static" },
+	"skull"							: { "index" :151, "handler": "static" },
+	"stump"							: { "index" :153, "handler": "static" },
+	"trees"							: { "index" :155, "handler": "static" },
+	"volcano"						: { "index" :158, "handler": "static" },
+	"reef"							: { "index" :161, "handler": "static" },
+	"lakeDUPLICATE"					: { "index" :177, "handler": "static" },
+	"treesDUPLICATE"				: { "index" :199, "handler": "static" },
+	"desertHills"					: { "index" :206, "handler": "static" },
+	"dirtHills"						: { "index" :207, "handler": "static" },
+	"grassHills"					: { "index" :208, "handler": "static" },
+	"roughHills"					: { "index" :209, "handler": "static" },
+	"subterraneanRocks"				: { "index" :210, "handler": "static" }
+}

+ 208 - 0
config/objects/moddables.json

@@ -0,0 +1,208 @@
+{
+	/// These are objects that have subtypes that change various aspects of their mechanics
+	/// Should be made configurable (either directly or via other parts of modding system ASAP)
+	/// Editing these objects either directly or via mod may have negative effect on game since they are handled by engine
+	
+	// subtype: artifact ID
+	"artifact" : {
+		"index" :5, 
+		"handler": "artifact",
+		"base" : {
+			"base" : {
+				"visitableFrom" : [ "+++", "+-+", "+++" ],
+				"mask" : [ "VV", "VA"]
+			}
+		}
+	},
+	
+	// subtype: hero CLASS (not hero).
+	"hero" : {
+		"index" :34,
+		"handler": "hero",
+		"base" : {
+			"base" : {
+				"visitableFrom" : [ "+++", "+-+", "+++" ],
+				"mask" : [ "VV", "AV"]
+			}
+		}
+	},
+	
+	// subtype: creatures
+	"monster" : {
+		"index" :54,
+		"handler": "monster",
+		"base" : {
+			"base" : {
+				"visitableFrom" : [ "+++", "+-+", "+++" ],
+				"mask" : [ "VV", "VA"]
+			}
+		}
+	},
+
+	// subtype: resource ID
+	"resource" : {
+		"index" :79,
+		"handler": "resource",
+		"base" : {
+			"base" : {
+				"visitableFrom" : [ "+++", "+-+", "+++" ],
+				"mask" : [ "VA" ]
+			}
+		},
+		"types" : {
+			"wood" :    { "index" : 0, "templates" : { "res" : { "animation" : "AVTwood0.def" } } },
+			"mercury" : { "index" : 1, "templates" : { "res" : { "animation" : "AVTmerc0.def" } } },
+			"ore" :     { "index" : 2, "templates" : { "res" : { "animation" : "AVTore0.def"  } } },
+			"sulfur" :  { "index" : 3, "templates" : { "res" : { "animation" : "AVTsulf0.def" } } },
+			"crystal" : { "index" : 4, "templates" : { "res" : { "animation" : "AVTcrys0.def" } } },
+			"gems" :    { "index" : 5, "templates" : { "res" : { "animation" : "AVTgems0.def" } } },
+			"gold" :    { "index" : 6, "templates" : { "res" : { "animation" : "AVTgold0.def" } } },
+			"mithril" : { "index" : 7 } // TODO: move to WoG?
+		}
+	},
+	
+	// subtype: faction
+	"town" : {
+		"index" :98,
+		"handler": "town",
+		"base" : {
+			"filters" : {
+				// village image - fort not present
+				"village" : [ "noneOf", [ "fort" ] ],
+				// fort image - fort is here but not capitol
+				"fort" : [ "allOf", [ "fort" ], [ "noneOf", ["capitol" ] ] ],
+				// capitol image only when capitol is built
+				"capitol" : [ "capitol" ]
+			},
+			
+			// "faction" : "stringID", // should be set by engine
+			"base" : {
+				"visitableFrom" : [ "---", "+++", "+++" ],
+				"mask" : [
+					"VVVVVV", // a LOT of just visible rows due to towns like Tower
+					"VVVVVV",
+					"VVVVVV",
+					"VVBBBV",
+					"VBBBBB",
+					"VBBABB"
+				]
+			}
+		}
+	},
+
+	// subtype: one of 3 possible boats
+	"boat" : {
+		"index" :8,
+		"handler": "boat",
+		"base" : {
+			"base" : {
+				"visitableFrom" : [ "+++", "+-+", "+++" ],
+				"mask" : [ "VVV", "VAV" ]
+			}
+		},
+		"types" : {
+			"evil" : { "index" : 0 },
+			"good" : { "index" : 1 },
+			"neutral" : { "index" : 2 },
+		}
+	},
+
+	// subtype: color of guard
+	"borderGuard" : {
+		"index" :9,
+		"handler": "borderGuard",
+		"types" : {
+			"lblue" :  { "index" : 0 },
+			"green" :  { "index" : 1 },
+			"red" :    { "index" : 2 },
+			"dblue" :  { "index" : 3 },
+			"brown" :  { "index" : 4 },
+			"purple" : { "index" : 5 },
+			"white" :  { "index" : 6 },
+			"black" :  { "index" : 7 }
+		}
+	},
+	"borderGate" : {
+		"index" :212,
+		"handler": "borderGate",
+		"types" : {
+			"lblue" :  { "index" : 0 },
+			"green" :  { "index" : 1 },
+			"red" :    { "index" : 2 },
+			"dblue" :  { "index" : 3 },
+			"brown" :  { "index" : 4 },
+			"purple" : { "index" : 5 },
+			"white" :  { "index" : 6 },
+			"black" :  { "index" : 7 }
+		}
+	},
+	"keymasterTent" : {
+		"index" :10,
+		"handler": "keymaster",
+		"types" : {
+			"lblue" :  { "index" : 0 },
+			"green" :  { "index" : 1 },
+			"red" :    { "index" : 2 },
+			"dblue" :  { "index" : 3 },
+			"brown" :  { "index" : 4 },
+			"purple" : { "index" : 5 },
+			"white" :  { "index" : 6 },
+			"black" :  { "index" : 7 }
+		}
+	},
+
+	// subtype: different revealed areas
+	"cartographer" : {
+		"index" :13,
+		"handler": "cartographer",
+		"types" : {
+			"water" : { "index" : 0 },
+			"land" : { "index" : 1 },
+			"subterra" : { "index" : 2 }
+		}
+	},
+
+	// subtype: resource ID
+	"mine" : {
+		"index" :53,
+		"handler": "mine",
+		"types" : {
+			"sawmill" :       { "index" : 0 },
+			"alchemistLab" :  { "index" : 1 },
+			"orePit" :        { "index" : 2 },
+			"sulfurDune" :    { "index" : 3 },
+			"crystalCavern" : { "index" : 4 },
+			"gemPond" :       { "index" : 5 },
+			"goldMine" :      { "index" : 6 },
+		}
+	},
+	"abandonedMine" : {
+		"index" :220, 
+		"handler": "mine",
+		"types" : {
+			"mine" : { "index" : 7 }
+		}
+	},
+
+	// subtype: 0 = normal, 1 = anti-magic
+	"garrisonHorizontal"			: { "index" :33, "handler": "garrison" },
+	"garrisonVertical"				: { "index" :219, "handler": "garrison" },
+
+	// Subtype: paired monoliths
+	"monolithOneWayEntrance"		: { "index" :43, "handler": "teleport" },
+	"monolithOneWayExit"			: { "index" :44, "handler": "teleport" },
+	"monolithTwoWay"				: { "index" :45, "handler": "teleport" },
+
+	// subtype: different appearance. That's all?
+	"seerHut" : { "index" :83, "handler": "seerHut" },
+
+	// subtype: level
+	"randomDwellingLvl"				: { "index" :217, "handler": "dwelling" },
+	
+	// subtype: faction ID
+	"randomDwellingFaction"			: { "index" :218, "handler": "dwelling" },
+
+	// don't have subtypes (at least now), but closely connected to this objects
+	"spellScroll"					: { "index" :93, "handler": "artifact" },
+	"heroPlaceholder"				: { "index" :214, "handler": "heroPlaceholder" }
+}

+ 44 - 0
config/objects/rewardable.json

@@ -0,0 +1,44 @@
+{
+	/// These are objects that covered by concept of "configurable object"
+	/// Most or even all of their configuration located in this file
+	"magicSpring"					: { "index" :48, "handler": "magicSpring" },
+
+	"mysticalGarden"				: { "index" :55, "handler": "oncePerWeek" },
+	"windmill"						: { "index" :112, "handler": "oncePerWeek" },
+	"waterWheel"					: { "index" :109, "handler": "oncePerWeek" },
+	
+	"leanTo"						: { "index" :39, "handler": "onceVisitable" },
+	"corpse"						: { "index" :22, "handler": "onceVisitable" },
+	"wagon"							: { "index" :105, "handler": "onceVisitable" },
+	"warriorTomb"					: { "index" :108, "handler": "onceVisitable" },
+
+	"campfire"						: { "index" :12, "handler": "pickable" },
+	"flotsam"						: { "index" :29, "handler": "pickable" },
+	"seaChest"						: { "index" :82, "handler": "pickable" },
+	"shipwreckSurvivor"				: { "index" :86, "handler": "pickable" },
+	"treasureChest"					: { "index" :101, "handler": "pickable" },
+
+	"arena"							: { "index" :4,  "handler": "oncePerHero" },
+	"marlettoTower"					: { "index" :23, "handler": "oncePerHero" },
+	"gardenOfRevelation"			: { "index" :32, "handler": "oncePerHero" },
+	"libraryOfEnlightenment"		: { "index" :41, "handler": "oncePerHero" },
+	"mercenaryCamp"					: { "index" :51, "handler": "oncePerHero" },
+	"starAxis"						: { "index" :61, "handler": "oncePerHero" },
+	"learningStone"					: { "index" :100, "handler": "oncePerHero" },
+	"treeOfKnowledge"				: { "index" :102, "handler": "oncePerHero" },
+	"schoolOfMagic"					: { "index" :47, "handler": "oncePerHero" },
+	"schoolOfWar"					: { "index" :107, "handler": "oncePerHero" },
+	
+	"buoy"							: { "index" :11, "handler": "bonusingObject" },
+	"swanPond"						: { "index" :14, "handler": "bonusingObject" },
+	"faerieRing"					: { "index" :28, "handler": "bonusingObject" },
+	"fountainOfFortune"				: { "index" :30, "handler": "bonusingObject" },
+	"fountainOfYouth"				: { "index" :31, "handler": "bonusingObject" },
+	"idolOfFortune"					: { "index" :38, "handler": "bonusingObject" },
+	"mermaids"						: { "index" :52, "handler": "bonusingObject" },
+	"oasis"							: { "index" :56, "handler": "bonusingObject" },
+	"stables"						: { "index" :94, "handler": "bonusingObject" },
+	"temple"						: { "index" :96, "handler": "bonusingObject" },
+	"rallyFlag"						: { "index" :64, "handler": "bonusingObject" },
+	"wateringHole"					: { "index" :110, "handler": "bonusingObject" },	
+}

+ 186 - 41
config/rmg.json

@@ -9,26 +9,48 @@
 			"1" :
 			{
 				"type" : "playerStart", "size" : 1, "owner" : 1,
-				"playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true
+				"playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true,
+				"monsters" : "normal",
+				"mines" : {"wood" : 1, "ore" : 1, "gems" : 1, "crystal" : 1, "sulfur" : 1, "mercury" : 1},
+				"treasure" : [
+								{"min" : 2100, "max": 3000, "density" : 5},
+								{"min" : 300, "max": 1500, "density" : 10}
+							] 
 			},
 			"2" :
 			{
 				"type" : "playerStart", "size" : 1, "owner" : 2,
-				"playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true
+				"playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true,
+				"monsters" : "normal",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
 			},
 			"3" :
 			{
 				"type" : "playerStart", "size" : 1, "owner" : 3,
-				"playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true
+				"playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true,
+				"monsters" : "normal",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
 			},
 			"4" :
 			{
 				"type" : "playerStart", "size" : 1, "owner" : 4,
-				"playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true
+				"playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true,
+				"monsters" : "normal",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
 			},
 			"5" :
 			{
-				"type" : "treasure", "size" : 2, "terrainTypes" : [ "sand" ], "neutralTowns" : { "castles" : 1 }
+				"type" : "treasure", "size" : 2, "terrainTypes" : [ "sand" ], "matchTerrainToTown" : false,
+				"neutralTowns" : { "castles" : 1 },
+				"monsters" : "strong",
+				"mines" : {"gold" : 2},
+				"treasure" : [
+								{"min" : 9000, "max": 10000, "density" : 3},
+								{"min" : 6000, "max": 10000, "density" : 15}
+							]
 			}
 		},
 		"connections" :
@@ -48,34 +70,58 @@
 			"1" :
 			{
 				"type" : "playerStart", "size" : 1, "owner" : 1,
-				"playerTowns" : { "castles" : 1 }
+				"playerTowns" : { "castles" : 1 },
+				"monsters" : "normal",
+				"mines" : {"wood" : 1, "ore" : 1},
+				"treasure" : [
+								{"min" : 400, "max": 1500, "density" : 16},
+								{"min" : 1500, "max": 2500, "density" : 4}
+								]
 			},
 			"2" :
 			{
 				"type" : "playerStart", "size" : 1, "owner" : 2,
-				"playerTowns" : { "castles" : 1 }
+				"playerTowns" : { "castles" : 1 },
+				"monsters" : "normal",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
 			},
 			"3" :
 			{
-				"type" : "treasure", "size" : 2, "neutralTowns" : { "towns" : 1 }, "townTypeLikeZone" : "1"
+				"type" : "treasure", "size" : 2, "neutralTowns" : { "towns" : 1 }, "townTypeLikeZone" : 1,
+				"monsters" : "weak",
+				"mines" : {"gems" : 1, "crystal" : 1, "sulfur" : 1, "mercury" : 1, "gold" : 1},
+				"treasure" : [
+								{"min" : 2000, "max": 4000, "density" : 15},
+								{"min" : 4000, "max": 5000, "density" : 5}
+							]
 			},
 			"4" :
 			{
-				"type" : "treasure", "size" : 2, "neutralTowns" : { "towns" : 1 }, "townTypeLikeZone" : "2"
+				"type" : "treasure", "size" : 2, "neutralTowns" : { "towns" : 1 }, "townTypeLikeZone" : 2,
+				"monsters" : "weak",
+				"minesLikeZone" : 3,
+				"treasureLikeZone" : 3
 			},
 			"5" :
 			{
-				"type" : "treasure", "size" : 3, "neutralTowns" : { "castles" : 1 }, "terrainTypes" : [ "sand" ]
+				"type" : "treasure", "size" : 3, "neutralTowns" : { "castles" : 1 }, "terrainTypes" : [ "sand" ],
+				"monsters" : "strong",
+				"mines" : {"gold" : 2},
+				"treasure" : [
+								{"min" : 11000, "max": 12000, "density" : 5},
+								{"min" : 6000, "max": 11000, "density" : 10}
+							]
 			}
 		},
 		"connections" :
 		[
-			{ "a" : "1", "b" : "3", "guard" : 1000 },
-			{ "a" : "1", "b" : "5", "guard" : 4000 },
-			{ "a" : "2", "b" : "4", "guard" : 1000 },
-			{ "a" : "2", "b" : "5", "guard" : 4000 },
-			{ "a" : "3", "b" : "5", "guard" : 2000 },
-			{ "a" : "4", "b" : "5", "guard" : 2000 }
+			{ "a" : "1", "b" : "3", "guard" : 3000 },
+			{ "a" : "1", "b" : "5", "guard" : 9000 },
+			{ "a" : "2", "b" : "4", "guard" : 3000 },
+			{ "a" : "2", "b" : "5", "guard" : 9000 },
+			{ "a" : "3", "b" : "5", "guard" : 6000 },
+			{ "a" : "4", "b" : "5", "guard" : 6000 }
 		]
 	},
 	"Golden Ring" :
@@ -87,36 +133,63 @@
 			"1" :
 			{
 				"type" : "playerStart", "size" : 3, "owner" : 1,
-				"playerTowns" : { "castles" : 1 }
+				"playerTowns" : { "castles" : 1 },
+				"monsters" : "normal",
+				"mines" : {"wood" : 1, "ore" : 1},
+				"treasure" : [
+								{"min" : 300, "max": 2000, "density": 15},
+								{"min" : 2100, "max": 2500, "density": 5}
+							]
 			},
 			"2" :
 			{
 				"type" : "playerStart", "size" : 3, "owner" : 2,
-				"playerTowns" : { "castles" : 1 }
+				"playerTowns" : { "castles" : 1 },
+				"monsters" : "normal",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
 			},
 			"3" :
 			{
 				"type" : "playerStart", "size" : 3, "owner" : 3,
-				"playerTowns" : { "castles" : 1 }
-			},
-			"4" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : "1" },
-			"5" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : "1" },
-			"6" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : "2" },
-			"7" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : "2" },
-			"8" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : "3" },
-			"9" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : "3" },
-			"10" : { "type" : "treasure", "size" : 1, "neutralTowns" : { "towns" : 1 } },
-			"11" : { "type" : "treasure", "size" : 1, "neutralTowns" : { "towns" : 1 } },
-			"12" : { "type" : "treasure", "size" : 1, "neutralTowns" : { "towns" : 1 } }
+				"playerTowns" : { "castles" : 1 },
+				"monsters" : "normal",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"4" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : 1,
+					"monsters" : "normal",
+					"mines" : {"gems" : 1, "crystal" : 1},
+					"treasure" : [
+									{"min" : 3000, "max": 10000, "density" : 12},
+									{"min" : 6000, "max": 10000, "density" : 6}
+								]},
+			"5" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : 1,
+					"monsters" : "normal",
+					"mines" : {"sulfur" : 1, "mercury" : 1},
+					"treasureLikeZone" : 4},
+			"6" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : 2, "monsters" : "normal", "minesLikeZone" : 5, "treasureLikeZone" : 4 },
+			"7" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : 2, "monsters" : "normal", "minesLikeZone" : 4, "treasureLikeZone" : 4 },
+			"8" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : 3, "monsters" : "normal", "minesLikeZone" : 4, "treasureLikeZone" : 4 },
+			"9" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : 3, "monsters" : "normal", "minesLikeZone" : 5, "treasureLikeZone" : 4 },
+			"10" : { "type" : "treasure", "size" : 1, "neutralTowns" : { "towns" : 1 },
+					"monsters" : "strong",
+					"mines" : {"gold" : 1},
+					"treasure" : [
+									{"min" : 21000, "max": 25000, "density" : 3},
+									{"min" : 10000, "max": 21000, "density" : 10}
+								]},
+			"11" : { "type" : "treasure", "size" : 1, "neutralTowns" : { "towns" : 1 }, "monsters" : "strong", "minesLikeZone" : 10, "treasureLikeZone" : 10 },
+			"12" : { "type" : "treasure", "size" : 1, "neutralTowns" : { "towns" : 1 }, "monsters" : "strong", "minesLikeZone" : 10, "treasureLikeZone" : 10 }
 		},
 		"connections" :
 		[
-			{ "a" : "1", "b" : "4", "guard" : 2000 },
-			{ "a" : "1", "b" : "5", "guard" : 2000 },
-			{ "a" : "2", "b" : "6", "guard" : 2000 },
-			{ "a" : "2", "b" : "7", "guard" : 2000 },
-			{ "a" : "3", "b" : "8", "guard" : 2000 },
-			{ "a" : "3", "b" : "9", "guard" : 2000 },
+			{ "a" : "1", "b" : "4", "guard" : 2500 },
+			{ "a" : "1", "b" : "5", "guard" : 2500 },
+			{ "a" : "2", "b" : "6", "guard" : 2500 },
+			{ "a" : "2", "b" : "7", "guard" : 2500 },
+			{ "a" : "3", "b" : "8", "guard" : 2500 },
+			{ "a" : "3", "b" : "9", "guard" : 2500 },
 			{ "a" : "4", "b" : "10", "guard" : 20000 },
 			{ "a" : "5", "b" : "12", "guard" : 20000 },
 			{ "a" : "6", "b" : "10", "guard" : 20000 },
@@ -134,25 +207,29 @@
 			"1" :
 			{
 				"type" : "playerStart", "size" : 2, "owner" : 1,
-				"playerTowns" : { "castles" : 1 }
+				"playerTowns" : { "castles" : 1 },
+				"monsters" : "normal",
 			},
 			"2" :
 			{
 				"type" : "playerStart", "size" : 2, "owner" : 2,
-				"playerTowns" : { "castles" : 1 }
+				"playerTowns" : { "castles" : 1 },
+				"monsters" : "normal",
 			},
 			"3" :
 			{
 				"type" : "cpuStart", "size" : 3, "owner" : 3,
-				"playerTowns" : { "castles" : 1 }
+				"playerTowns" : { "castles" : 1 },
+				"monsters" : "weak",
 			},
 			"4" :
 			{
 				"type" : "cpuStart", "size" : 3, "owner" : 4,
-				"playerTowns" : { "castles" : 1 }
+				"playerTowns" : { "castles" : 1 },
+				"monsters" : "weak",
 			},
-			"5" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : "3" },
-			"6" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : "4" }
+			"5" : { "type" : "treasure", "size" : 1, "monsters" : "strong", "terrainTypeLikeZone" : 3 },
+			"6" : { "type" : "treasure", "size" : 1, "monsters" : "strong", "terrainTypeLikeZone" : 4 }
 		},
 		"connections" :
 		[
@@ -163,5 +240,73 @@
 			{ "a" : "3", "b" : "5", "guard" : 2000 },
 			{ "a" : "4", "b" : "6", "guard" : 2000 }
 		]
+	},
+	"Jebus Cross":
+	{
+		"minSize" : "l", "maxSize" : "xl",
+		"players" : "4",
+		"zones":
+		{
+			"1":
+			{
+				"type" : "playerStart", "size" : 30, "owner" : 1,
+				"playerTowns" : { "castles" : 1 },
+				"neutralTowns" : { "towns" : 2 },
+				"monsters" : "weak",
+				"mines" : {"wood" : 4, "ore" : 4, "gems" : 1, "crystal" : 1, "sulfur" : 1, "mercury" : 1, "gold" : 2},
+				"treasure" : [
+								{"min" : 12000, "max": 22000, "density": 1},
+								{"min" : 500, "max": 1600, "density": 6},
+								{"min" : 300, "max": 3000, "density": 14}
+							]
+			},
+			"2":
+			{
+				"type" : "playerStart", "size" : 30, "owner" : 2,
+				"playerTowns" : { "castles" : 1 },
+				"neutralTowns" : { "towns" : 2 },
+				"monsters" : "weak",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"3":
+			{
+				"type" : "playerStart", "size" : 30, "owner" : 3,
+				"playerTowns" : { "castles" : 1 },
+				"neutralTowns" : { "towns" : 2 },
+				"monsters" : "weak",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"4":
+			{
+				"type" : "playerStart", "size" : 30, "owner" : 4,
+				"playerTowns" : { "castles" : 1 },
+				"neutralTowns" : { "towns" : 2 },
+				"monsters" : "weak",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"5" :
+			{
+				"type" : "treasure", "size" : 40,
+				"neutralTowns" : { "castles" : 2 },
+				"terrainTypes" : [ "sand" ], "matchTerrainToTown" : false,
+				"monsters" : "strong",
+				"mines" : {"gold" : 4},
+				"treasure" : [
+								{"min" : 35000, "max": 55000, "density" : 3},
+								{"min" : 25000, "max": 35000, "density" : 10},
+								{"min" : 10000, "max": 25000, "density" : 10}
+							]
+			}
+		},
+		"connections" :
+		[
+			{ "a" : "1", "b" : "5", "guard" : 45000 },
+			{ "a" : "2", "b" : "5", "guard" : 45000 },
+			{ "a" : "3", "b" : "5", "guard" : 45000 },
+			{ "a" : "4", "b" : "5", "guard" : 45000 }
+		]
 	}
 }

+ 6 - 36
config/schemas/faction.json

@@ -104,46 +104,16 @@
 			"type":"object",
 			"additionalProperties" : false,
 			"required" : [
-				"adventureMap", "buildingsIcons", "buildings", "creatures", "guildWindow", "names",
+				"mapObject", "buildingsIcons", "buildings", "creatures", "guildWindow", "names",
 				"hallBackground", "hallSlots", "horde", "mageGuild", "moatDamage", "defaultTavern", "tavernVideo", "guildBackground", "musicTheme", "siege", "structures", "townBackground", "warMachine"
 			],
 			"description": "town",
 			"properties":{
-				"adventureMap": {
-					"type":"object",
-					"additionalProperties" : false,
-					"description": "Paths to images of object on adventure map",
-					"required" : [ "capitol", "castle", "village" ],
-					"properties":{
-						"capitol": {
-							"type":"string",
-							"description": "Town with capitol",
-							"format" : "defFile"
-						},
-						"castle": {
-							"type":"string",
-							"description": "Town with built fort",
-							"format" : "defFile"
-						},
-						"village": {
-							"type":"string",
-							"description": "Village without built fort",
-							"format" : "defFile"
-						},
-						"dwellings" : {
-							"type" : "array",
-							"minItems" : 7,
-							"maxItems" : 7,
-							"description" : "Dwellings on adventure map",
-							"items" : {
-								"type" : "object",
-								"additionalProperties" : false,
-								"required" : [ "name", "graphics" ],
-								"properties" : {
-									"name":     { "type":"string" },
-									"graphics": { "type":"string", "format" : "defFile" }
-								}
-							}
+				"mapObject" : {
+					"properties" : {
+						"filters" : {
+							"type" : "object",
+							"additionalProperties" : { "type" : "array" }
 						}
 					}
 				},

+ 10 - 20
config/schemas/heroClass.json

@@ -6,7 +6,7 @@
 	"required" : [
 		"animation", "faction", "highLevelChance", "lowLevelChance",
 		"name", "primarySkills", "secondarySkills", "tavern", "defaultTavern",
-		"affinity", "commander"
+		"affinity", "commander", "mapObject"
 	],
 
 	"additionalProperties" : false,
@@ -15,7 +15,7 @@
 			"type":"object",
 			"additionalProperties" : false,
 			"description": "Files related to hero animation",
-			"required": [ "battle", "map" ],
+			"required": [ "battle" ],
 			"properties":{
 				"battle": {
 					"type":"object",
@@ -34,24 +34,14 @@
 							"format" : "defFile"
 						}
 					}
-				},
-				"map": {
-					"type":"object",
-					"additionalProperties" : false,
-					"description": "Hero animations for adventure map",
-					"required": [ "female", "male" ],
-					"properties":{
-						"female": {
-							"type":"string",
-							"description": "Female version. Warning: not implemented!",
-							"format" : "defFile"
-						},
-						"male": {
-							"type":"string",
-							"description": "Male version",
-							"format" : "defFile"
-						}
-					}
+				}
+			}
+		},
+		"mapObject" : {
+			"properties" : {
+				"filters" : {
+					"type" : "object",
+					"additionalProperties" : { "type" : "array" }
 				}
 			}
 		},

+ 31 - 0
config/schemas/object.json

@@ -0,0 +1,31 @@
+{
+	"type":"object",
+	"$schema": "http://json-schema.org/draft-04/schema",
+	"title" : "VCMI map object format",
+	"description" : "Description of map object class",
+	"required": [ "handler", "name" ],
+	"additionalProperties" : false,
+
+	"properties":{
+		"index": {
+			"type":"number",
+		},
+		"name": {
+			"type":"string",
+		},
+
+		"handler": {
+			"type":"string",
+		},
+
+		"base": {
+			"type" : "object"
+		},
+		"types": {
+			"type":"object",
+			"additionalProperties": {
+				"$ref" : "vcmi:objectType"
+			}
+		}
+	}
+}

+ 1 - 9
config/schemas/objectTemplate.json

@@ -3,18 +3,10 @@
 	"$schema": "http://json-schema.org/draft-04/schema",
 	"title" : "VCMI map object template format",
 	"description" : "Description of map object tempate that describes appearence of object instance",
-	"required": ["basebase", "base", "animation", "mask" ],
+	"required": [ "animation", "mask" ],
 
 	"additionalProperties" : false,
 	"properties":{
-		"basebase": {
-			"type" : "number",
-			"description": "Base object type, e.g. town or hero"
-		},
-		"base": {
-			"type" : "number",
-			"description": "Object subtype, e.g. Castle, Rampart, Cleric, Demon"
-		},
 		"animation": {
 			"type" : "string",
 			"description": "Path to def file with animation of this object",

+ 27 - 0
config/schemas/objectType.json

@@ -0,0 +1,27 @@
+{
+	"type":"object",
+	"$schema": "http://json-schema.org/draft-04/schema",
+	"title" : "VCMI map object type format",
+	"description" : "Description of map object type, used only as sub-schema of object",
+	"required": [ ],
+	"additionalProperties" : true, // may have type-dependant properties
+
+	"properties":{
+		"index": {
+			"type":"number",
+		},
+		"name": {
+			"type":"string",
+		},
+
+		"base": {
+			"type" : "object"
+		},
+		"templates": {
+			"type":"object",
+			"additionalProperties": {
+				"$ref" : "vcmi:objectTemplate"
+			}
+		}
+	}
+}

+ 1 - 1
lib/BattleState.cpp

@@ -13,7 +13,7 @@
 
 #include <numeric>
 #include "VCMI_Lib.h"
-#include "CObjectHandler.h"
+#include "mapObjects/CObjectHandler.h"
 #include "CHeroHandler.h"
 #include "CCreatureHandler.h"
 #include "CSpellHandler.h"

+ 3 - 1
lib/BattleState.h

@@ -4,12 +4,14 @@
 #include "BattleHex.h"
 #include "HeroBonus.h"
 #include "CCreatureSet.h"
-#include "CObjectHandler.h"
+#include "mapObjects/CGTownInstance.h"
+#include "mapObjects/CGHeroInstance.h"
 #include "CCreatureHandler.h"
 #include "CObstacleInstance.h"
 #include "ConstTransitivePtr.h"
 #include "GameConstants.h"
 #include "CBattleCallback.h"
+#include "int3.h"
 
 /*
  * BattleState.h, part of VCMI engine

+ 51 - 29
lib/CArtHandler.cpp

@@ -16,11 +16,13 @@
 #include "VCMI_Lib.h"
 #include "CModHandler.h"
 #include "CSpellHandler.h"
-#include "CObjectHandler.h"
+#include "mapObjects/MapObjects.h"
 #include "NetPacksBase.h"
 #include "GameConstants.h"
 #include "CRandomGenerator.h"
 
+#include "mapObjects/CObjectClassesHandler.h"
+
 using namespace boost::assign;
 
 // Note: list must match entries in ArtTraits.txt
@@ -260,12 +262,21 @@ CArtifact * CArtHandler::loadFromJson(const JsonNode & node)
 	return art;
 }
 
-void CArtHandler::addSlot(CArtifact * art, const std::string & slotID)
+ArtifactPosition CArtHandler::stringToSlot(std::string slotName)
 {
 #define ART_POS(x) ( #x, ArtifactPosition::x )
 	static const std::map<std::string, ArtifactPosition> artifactPositionMap = boost::assign::map_list_of ART_POS_LIST;
 #undef ART_POS
+	auto it = artifactPositionMap.find (slotName);
+	if (it != artifactPositionMap.end())
+		return it->second;
+
+	logGlobal->warnStream() << "Warning! Artifact slot " << slotName << " not recognized!";
+	return ArtifactPosition::PRE_FIRST;
+}
 
+void CArtHandler::addSlot(CArtifact * art, const std::string & slotID)
+{
 	if (slotID == "MISC")
 	{
 		art->possibleSlots[ArtBearer::HERO] += ArtifactPosition::MISC1, ArtifactPosition::MISC2, ArtifactPosition::MISC3, ArtifactPosition::MISC4, ArtifactPosition::MISC5;
@@ -276,14 +287,9 @@ void CArtHandler::addSlot(CArtifact * art, const std::string & slotID)
 	}
 	else
 	{
-		auto it = artifactPositionMap.find (slotID);
-		if (it != artifactPositionMap.end())
-		{
-			auto slot = it->second;
+		auto slot = stringToSlot(slotID);
+		if (slot != ArtifactPosition::PRE_FIRST)
 			art->possibleSlots[ArtBearer::HERO].push_back (slot);
-		}
-		else
-            logGlobal->warnStream() << "Warning! Artifact slot " << slotID << " not recognized!";
 	}
 }
 
@@ -301,7 +307,7 @@ void CArtHandler::loadSlots(CArtifact * art, const JsonNode & node)
 	}
 }
 
-void CArtHandler::loadClass(CArtifact * art, const JsonNode & node)
+CArtifact::EartClass CArtHandler::stringToClass(std::string className)
 {
 	static const std::map<std::string, CArtifact::EartClass> artifactClassMap = boost::assign::map_list_of
 		("TREASURE", CArtifact::ART_TREASURE)
@@ -310,16 +316,17 @@ void CArtHandler::loadClass(CArtifact * art, const JsonNode & node)
 		("RELIC", CArtifact::ART_RELIC)
 		("SPECIAL", CArtifact::ART_SPECIAL);
 
-	auto it = artifactClassMap.find (node["class"].String());
+	auto it = artifactClassMap.find (className);
 	if (it != artifactClassMap.end())
-	{
-		art->aClass = it->second;
-	}
-	else
-	{
-        logGlobal->warnStream() << "Warning! Artifact rarity " << node["class"].String() << " not recognized!";
-		art->aClass = CArtifact::ART_SPECIAL;
-	}
+		return it->second;
+
+	logGlobal->warnStream() << "Warning! Artifact rarity " << className << " not recognized!";
+	return CArtifact::ART_SPECIAL;
+}
+
+void CArtHandler::loadClass(CArtifact * art, const JsonNode & node)
+{
+	art->aClass = stringToClass(node["class"].String());
 }
 
 void CArtHandler::loadType(CArtifact * art, const JsonNode & node)
@@ -422,7 +429,7 @@ CreatureID CArtHandler::machineIDToCreature(ArtifactID id)
 	return CreatureID::NONE; //this artifact is not a creature
 }
 
-ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, int flags)
+ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, int flags, std::function<bool(ArtifactID)> accepts)
 {
 	auto getAllowedArts = [&](std::vector<ConstTransitivePtr<CArtifact> > &out, std::vector<CArtifact*> *arts, CArtifact::EartClass flag)
 	{
@@ -431,8 +438,11 @@ ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, int flags)
 
 		for (auto & arts_i : *arts)
 		{
-			CArtifact *art = arts_i;
-			out.push_back(art);
+			if (accepts(arts_i->id))
+			{
+				CArtifact *art = arts_i;
+				out.push_back(art);
+			}
 		}
 	};
 
@@ -467,6 +477,16 @@ ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, int flags)
 	return artID;
 }
 
+ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, std::function<bool(ArtifactID)> accepts)
+{
+	return pickRandomArtifact(rand, 0xff, accepts);
+}
+
+ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, int flags)
+{
+	return pickRandomArtifact(rand, flags, [](ArtifactID){ return true;});
+}
+
 Bonus *createBonus(Bonus::BonusType type, int val, int subtype, Bonus::ValueType valType, shared_ptr<ILimiter> limiter = shared_ptr<ILimiter>(), int additionalInfo = 0)
 {
 	auto added = new Bonus(Bonus::PERMANENT,type,Bonus::ARTIFACT,val,-1,subtype);
@@ -647,20 +667,22 @@ void CArtHandler::afterLoadFinalization()
 		}
 	}
 
-	//Note: "10" is used here because H3 text files don't define any template for art with ID 0
-	ObjectTemplate base = VLC->dobjinfo->pickCandidates(Obj::ARTIFACT, 10).front();
 	for (CArtifact * art : artifacts)
 	{
+		VLC->objtypeh->loadSubObject(art->Name(), JsonNode(), Obj::ARTIFACT, art->id.num);
+
 		if (!art->advMapDef.empty())
 		{
-			base.animationFile = art->advMapDef;
-			base.subid = art->id;
+			JsonNode templ;
+			templ["animation"].String() = art->advMapDef;
 
-			// replace existing (if any) and add new template.
+			// add new template.
 			// Necessary for objects added via mods that don't have any templates in H3
-			VLC->dobjinfo->eraseAll(Obj::ARTIFACT, art->id);
-			VLC->dobjinfo->registerTemplate(base);
+			VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, art->id)->addTemplate(templ);
 		}
+		// object does not have any templates - this is not usable object (e.g. pseudo-art like lock)
+		if (VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, art->id)->getTemplates().empty())
+			VLC->objtypeh->removeSubObject(Obj::ARTIFACT, art->id);
 	}
 }
 

+ 8 - 2
lib/CArtHandler.h

@@ -203,11 +203,17 @@ public:
 
 	boost::optional<std::vector<CArtifact*>&> listFromClass(CArtifact::EartClass artifactClass);
 
+	ArtifactPosition stringToSlot(std::string slotName);
+	CArtifact::EartClass stringToClass(std::string className);
+
 	/// Gets a artifact ID randomly and removes the selected artifact from this handler.
 	ArtifactID pickRandomArtifact(CRandomGenerator & rand, int flags);
+	ArtifactID pickRandomArtifact(CRandomGenerator & rand, std::function<bool(ArtifactID)> accepts);
+	ArtifactID pickRandomArtifact(CRandomGenerator & rand, int flags, std::function<bool(ArtifactID)> accepts);
+
 	bool legalArtifact(ArtifactID id);
-	void getAllowedArts(std::vector<ConstTransitivePtr<CArtifact> > &out, std::vector<CArtifact*> *arts, int flag);
-	void getAllowed(std::vector<ConstTransitivePtr<CArtifact> > &out, int flags);
+	//void getAllowedArts(std::vector<ConstTransitivePtr<CArtifact> > &out, std::vector<CArtifact*> *arts, int flag);
+	//void getAllowed(std::vector<ConstTransitivePtr<CArtifact> > &out, int flags);
 	bool isBigArtifact (ArtifactID artID) const {return bigArtifacts.find(artID) != bigArtifacts.end();}
 	void initAllowedArtifactsList(const std::vector<bool> &allowed); //allowed[art_id] -> 0 if not allowed, 1 if allowed
 	static ArtifactID creatureToMachineID(CreatureID id);

+ 10 - 8
lib/CCreatureHandler.cpp

@@ -9,6 +9,8 @@
 #include "CModHandler.h"
 #include "StringConstants.h"
 
+#include "mapObjects/CObjectClassesHandler.h"
+
 using namespace boost::assign;
 
 /*
@@ -1114,19 +1116,19 @@ void CCreatureHandler::buildBonusTreeForTiers()
 
 void CCreatureHandler::afterLoadFinalization()
 {
-	ObjectTemplate base = VLC->dobjinfo->pickCandidates(Obj::MONSTER, 0).front();
 	for (CCreature * crea : creatures)
 	{
+		VLC->objtypeh->loadSubObject(crea->nameSing, JsonNode(), Obj::MONSTER, crea->idNumber.num);
 		if (!crea->advMapDef.empty())
 		{
-			base.animationFile = crea->advMapDef;
-			base.subid = crea->idNumber;
-
-			// replace existing (if any) and add new template.
-			// Necessary for objects added via mods that don't have any templates in H3
-			VLC->dobjinfo->eraseAll(Obj::MONSTER, crea->idNumber);
-			VLC->dobjinfo->registerTemplate(base);
+			JsonNode templ;
+			templ["animation"].String() = crea->advMapDef;
+			VLC->objtypeh->getHandlerFor(Obj::MONSTER, crea->idNumber)->addTemplate(templ);
 		}
+
+		// object does not have any templates - this is not usable object (e.g. pseudo-creature like Arrow Tower)
+		if (VLC->objtypeh->getHandlerFor(Obj::MONSTER, crea->idNumber.num)->getTemplates().empty())
+			VLC->objtypeh->removeSubObject(Obj::MONSTER, crea->idNumber.num);
 	}
 }
 

+ 1 - 1
lib/CCreatureSet.cpp

@@ -4,7 +4,7 @@
 #include "CCreatureHandler.h"
 #include "VCMI_Lib.h"
 #include "CModHandler.h"
-#include "CObjectHandler.h"
+#include "mapObjects/CGHeroInstance.h"
 #include "IGameCallback.h"
 #include "CGameState.h"
 #include "CGeneralTextHandler.h"

+ 1 - 1
lib/CGameInfoCallback.cpp

@@ -12,7 +12,7 @@
 #include "CGameInfoCallback.h"
 
 #include "CGameState.h" // PlayerState
-#include "CObjectHandler.h" // for CGObjectInstance
+#include "mapObjects/CObjectHandler.h" // for CGObjectInstance
 #include "StartInfo.h" // for StartInfo
 #include "BattleState.h" // for BattleInfo
 #include "NetPacks.h" // for InfoWindow

+ 3569 - 3594
lib/CGameState.cpp

@@ -1,3594 +1,3569 @@
-#include "StdInc.h"
-#include "CGameState.h"
-
-#include "mapping/CCampaignHandler.h"
-#include "CDefObjInfoHandler.h"
-#include "CArtHandler.h"
-#include "CBuildingHandler.h"
-#include "CGeneralTextHandler.h"
-#include "CTownHandler.h"
-#include "CSpellHandler.h"
-#include "CHeroHandler.h"
-#include "CObjectHandler.h"
-#include "CCreatureHandler.h"
-#include "CModHandler.h"
-#include "VCMI_Lib.h"
-#include "Connection.h"
-#include "mapping/CMap.h"
-#include "mapping/CMapService.h"
-#include "StartInfo.h"
-#include "NetPacks.h"
-#include "registerTypes/RegisterTypes.h"
-#include "mapping/CMapInfo.h"
-#include "BattleState.h"
-#include "JsonNode.h"
-#include "filesystem/Filesystem.h"
-#include "GameConstants.h"
-#include "rmg/CMapGenerator.h"
-#include "CStopWatch.h"
-#include "mapping/CMapEditManager.h"
-
-class CGObjectInstance;
-
-#ifdef min
-#undef min
-#endif
-#ifdef max
-#undef max
-#endif
-
-/*
- * CGameState.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
- *
- */
-
-template <typename T> class CApplyOnGS;
-
-class CBaseForGSApply
-{
-public:
-	virtual void applyOnGS(CGameState *gs, void *pack) const =0;
-	virtual ~CBaseForGSApply(){};
-	template<typename U> static CBaseForGSApply *getApplier(const U * t=nullptr)
-	{
-		return new CApplyOnGS<U>;
-	}
-};
-
-template <typename T> class CApplyOnGS : public CBaseForGSApply
-{
-public:
-	void applyOnGS(CGameState *gs, void *pack) const
-	{
-		T *ptr = static_cast<T*>(pack);
-
-		boost::unique_lock<boost::shared_mutex> lock(*gs->mx);
-		ptr->applyGs(gs);
-	}
-};
-
-static CApplier<CBaseForGSApply> *applierGs = nullptr;
-
-// class IObjectCaller
-// {
-// public:
-// 	virtual ~IObjectCaller(){};
-// 	virtual void preInit()=0;
-// 	virtual void postInit()=0;
-// };
-//
-// template <typename T>
-// class CObjectCaller : public IObjectCaller
-// {
-// public:
-// 	void preInit()
-// 	{
-// 		//T::preInit();
-// 	}
-// 	void postInit()
-// 	{
-// 		//T::postInit();
-// 	}
-// };
-
-// class CObjectCallersHandler
-// {
-// public:
-// 	std::vector<IObjectCaller*> apps;
-//
-// 	template<typename T> void registerType(const T * t=nullptr)
-// 	{
-// 		apps.push_back(new CObjectCaller<T>);
-// 	}
-//
-// 	CObjectCallersHandler()
-// 	{
-// 		registerTypesMapObjects(*this);
-// 	}
-//
-// 	~CObjectCallersHandler()
-// 	{
-// 		for (auto & elem : apps)
-// 			delete elem;
-// 	}
-//
-// 	void preInit()
-// 	{
-// // 		for (size_t i = 0; i < apps.size(); i++)
-// // 			apps[i]->preInit();
-// 	}
-//
-// 	void postInit()
-// 	{
-// 	//for (size_t i = 0; i < apps.size(); i++)
-// 	//apps[i]->postInit();
-// 	}
-// } *objCaller = nullptr;
-
-void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst) const
-{
-	int type = txt.first, ser = txt.second;
-
-	if(type == ART_NAMES)
-	{
-		dst = VLC->arth->artifacts[ser]->Name();
-	}
-	else if(type == CRE_PL_NAMES)
-	{
-		dst = VLC->creh->creatures[ser]->namePl;
-	}
-	else if(type == MINE_NAMES)
-	{
-		dst = VLC->generaltexth->mines[ser].first;
-	}
-	else if(type == MINE_EVNTS)
-	{
-		dst = VLC->generaltexth->mines[ser].second;
-	}
-	else if(type == SPELL_NAME)
-	{
-		dst = SpellID(ser).toSpell()->name;
-	}
-	else if(type == CRE_SING_NAMES)
-	{
-		dst = VLC->creh->creatures[ser]->nameSing;
-	}
-	else if(type == ART_DESCR)
-	{
-		dst = VLC->arth->artifacts[ser]->Description();
-	}
-	else if (type == ART_EVNTS)
-	{
-		dst = VLC->arth->artifacts[ser]->EventText();
-	}
-	else
-	{
-		std::vector<std::string> *vec;
-		switch(type)
-		{
-		case GENERAL_TXT:
-			vec = &VLC->generaltexth->allTexts;
-			break;
-		case XTRAINFO_TXT:
-			vec = &VLC->generaltexth->xtrainfo;
-			break;
-		case OBJ_NAMES:
-			vec = &VLC->generaltexth->names;
-			break;
-		case RES_NAMES:
-			vec = &VLC->generaltexth->restypes;
-			break;
-		case ARRAY_TXT:
-			vec = &VLC->generaltexth->arraytxt;
-			break;
-		case CREGENS:
-			vec = &VLC->generaltexth->creGens;
-			break;
-		case CREGENS4:
-			vec = &VLC->generaltexth->creGens4;
-			break;
-		case ADVOB_TXT:
-			vec = &VLC->generaltexth->advobtxt;
-			break;
-		case SEC_SKILL_NAME:
-			vec = &VLC->generaltexth->skillName;
-			break;
-		case COLOR:
-			vec = &VLC->generaltexth->capColors;
-			break;
-		default:
-            logGlobal->errorStream() << "Failed string substitution because type is " << type;
-			dst = "#@#";
-			return;
-		}
-		if(vec->size() <= ser)
-		{
-            logGlobal->errorStream() << "Failed string substitution with type " << type << " because index " << ser << " is out of bounds!";
-			dst = "#!#";
-		}
-		else
-			dst = (*vec)[ser];
-	}
-}
-
-DLL_LINKAGE void MetaString::toString(std::string &dst) const
-{
-	size_t exSt = 0, loSt = 0, nums = 0;
-	dst.clear();
-
-	for(auto & elem : message)
-	{//TEXACT_STRING, TLOCAL_STRING, TNUMBER, TREPLACE_ESTRING, TREPLACE_LSTRING, TREPLACE_NUMBER
-		switch(elem)
-		{
-		case TEXACT_STRING:
-			dst += exactStrings[exSt++];
-			break;
-		case TLOCAL_STRING:
-			{
-				std::string hlp;
-				getLocalString(localStrings[loSt++], hlp);
-				dst += hlp;
-			}
-			break;
-		case TNUMBER:
-			dst += boost::lexical_cast<std::string>(numbers[nums++]);
-			break;
-		case TREPLACE_ESTRING:
-			boost::replace_first(dst, "%s", exactStrings[exSt++]);
-			break;
-		case TREPLACE_LSTRING:
-			{
-				std::string hlp;
-				getLocalString(localStrings[loSt++], hlp);
-				boost::replace_first(dst, "%s", hlp);
-			}
-			break;
-		case TREPLACE_NUMBER:
-			boost::replace_first(dst, "%d", boost::lexical_cast<std::string>(numbers[nums++]));
-			break;
-		case TREPLACE_PLUSNUMBER:
-			boost::replace_first(dst, "%+d", '+' + boost::lexical_cast<std::string>(numbers[nums++]));
-			break;
-		default:
-            logGlobal->errorStream() << "MetaString processing error!";
-			break;
-		}
-	}
-}
-
-DLL_LINKAGE std::string MetaString::toString() const
-{
-	std::string ret;
-	toString(ret);
-	return ret;
-}
-
-DLL_LINKAGE std::string MetaString::buildList () const
-///used to handle loot from creature bank
-{
-
-	size_t exSt = 0, loSt = 0, nums = 0;
-	std::string lista;
-	for (int i = 0; i < message.size(); ++i)
-	{
-		if (i > 0 && (message[i] == TEXACT_STRING || message[i] == TLOCAL_STRING))
-		{
-			if (exSt == exactStrings.size() - 1)
-				lista += VLC->generaltexth->allTexts[141]; //" and "
-			else
-				lista += ", ";
-		}
-		switch (message[i])
-		{
-			case TEXACT_STRING:
-				lista += exactStrings[exSt++];
-				break;
-			case TLOCAL_STRING:
-			{
-				std::string hlp;
-				getLocalString (localStrings[loSt++], hlp);
-				lista += hlp;
-			}
-				break;
-			case TNUMBER:
-				lista += boost::lexical_cast<std::string>(numbers[nums++]);
-				break;
-			case TREPLACE_ESTRING:
-				lista.replace (lista.find("%s"), 2, exactStrings[exSt++]);
-				break;
-			case TREPLACE_LSTRING:
-			{
-				std::string hlp;
-				getLocalString (localStrings[loSt++], hlp);
-				lista.replace (lista.find("%s"), 2, hlp);
-			}
-				break;
-			case TREPLACE_NUMBER:
-				lista.replace (lista.find("%d"), 2, boost::lexical_cast<std::string>(numbers[nums++]));
-				break;
-			default:
-                logGlobal->errorStream() << "MetaString processing error!";
-		}
-
-	}
-	return lista;
-}
-
-
-void  MetaString::addCreReplacement(CreatureID id, TQuantity count) //adds sing or plural name;
-{
-	if (!count)
-		addReplacement (CRE_PL_NAMES, id); //no creatures - just empty name (eg. defeat Angels)
-	else if (count == 1)
-		addReplacement (CRE_SING_NAMES, id);
-	else
-		addReplacement (CRE_PL_NAMES, id);
-}
-
-void MetaString::addReplacement(const CStackBasicDescriptor &stack)
-{
-	assert(stack.type); //valid type
-	addCreReplacement(stack.type->idNumber, stack.count);
-}
-
-static CGObjectInstance * createObject(Obj id, int subid, int3 pos, PlayerColor owner)
-{
-	CGObjectInstance * nobj;
-	switch(id)
-	{
-	case Obj::HERO:
-		nobj = new CGHeroInstance();
-		nobj->appearance = VLC->dobjinfo->pickCandidates(id, VLC->heroh->heroes[subid]->heroClass->id).front();
-		break;
-	case Obj::TOWN:
-		nobj = new CGTownInstance;
-		break;
-	default: //rest of objects
-		nobj = new CGObjectInstance;
-		break;
-	}
-	nobj->ID = id;
-	nobj->subID = subid;
-	nobj->pos = pos;
-	nobj->tempOwner = owner;
-	if (id != Obj::HERO)
-		nobj->appearance = VLC->dobjinfo->pickCandidates(id, subid).front();
-
-	return nobj;
-}
-
-CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, PlayerColor player, const CTown *town,
-	std::map<ui32, ConstTransitivePtr<CGHeroInstance> > &available, CRandomGenerator & rand, const CHeroClass * bannedClass /*= nullptr*/) const
-{
-	CGHeroInstance *ret = nullptr;
-
-	if(player>=PlayerColor::PLAYER_LIMIT)
-	{
-        logGlobal->errorStream() << "Cannot pick hero for " << town->faction->index << ". Wrong owner!";
-		return nullptr;
-	}
-
-	std::vector<CGHeroInstance *> pool;
-
-	if(native)
-	{
-		for(auto & elem : available)
-		{
-			if(pavailable.find(elem.first)->second & 1<<player.getNum()
-				&& elem.second->type->heroClass->faction == town->faction->index)
-			{
-				pool.push_back(elem.second); //get all available heroes
-			}
-		}
-		if(!pool.size())
-		{
-            logGlobal->errorStream() << "Cannot pick native hero for " << player << ". Picking any...";
-			return pickHeroFor(false, player, town, available, rand);
-		}
-		else
-		{
-			ret = *RandomGeneratorUtil::nextItem(pool, rand);
-		}
-	}
-	else
-	{
-		int sum=0, r;
-
-		for(auto & elem : available)
-		{
-			if (pavailable.find(elem.first)->second & (1<<player.getNum()) &&    // hero is available
-			    ( !bannedClass || elem.second->type->heroClass != bannedClass) ) // and his class is not same as other hero
-			{
-				pool.push_back(elem.second);
-				sum += elem.second->type->heroClass->selectionProbability[town->faction->index]; //total weight
-			}
-		}
-		if(!pool.size() || sum == 0)
-		{
-            logGlobal->errorStream() << "There are no heroes available for player " << player<<"!";
-			return nullptr;
-		}
-
-		r = rand.nextInt(sum - 1);
-		for (auto & elem : pool)
-		{
-			r -= elem->type->heroClass->selectionProbability[town->faction->index];
-			if(r < 0)
-			{
-				ret = elem;
-				break;
-			}
-		}
-		if(!ret)
-			ret = pool.back();
-	}
-
-	available.erase(ret->subID);
-	return ret;
-}
-
-void CGameState::CrossoverHeroesList::addHeroToBothLists(CGHeroInstance * hero)
-{
-	heroesFromPreviousScenario.push_back(hero);
-	heroesFromAnyPreviousScenarios.push_back(hero);
-}
-
-void CGameState::CrossoverHeroesList::removeHeroFromBothLists(CGHeroInstance * hero)
-{
-	heroesFromPreviousScenario -= hero;
-	heroesFromAnyPreviousScenarios -= hero;
-}
-
-CGameState::CampaignHeroReplacement::CampaignHeroReplacement(CGHeroInstance * hero, ObjectInstanceID heroPlaceholderId) : hero(hero), heroPlaceholderId(heroPlaceholderId)
-{
-
-}
-
-int CGameState::pickNextHeroType(PlayerColor owner)
-{
-	const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(owner);
-	if(ps.hero >= 0 && !isUsedHero(HeroTypeID(ps.hero))) //we haven't used selected hero
-	{
-		return ps.hero;
-	}
-
-	return pickUnusedHeroTypeRandomly(owner);
-}
-
-int CGameState::pickUnusedHeroTypeRandomly(PlayerColor owner)
-{
-	//list of available heroes for this faction and others
-	std::vector<HeroTypeID> factionHeroes, otherHeroes;
-
-	const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(owner);
-	for(HeroTypeID hid : getUnusedAllowedHeroes())
-	{
-		if(VLC->heroh->heroes[hid.getNum()]->heroClass->faction == ps.castle)
-			factionHeroes.push_back(hid);
-		else
-			otherHeroes.push_back(hid);
-	}
-
-	// select random hero native to "our" faction
-	if(!factionHeroes.empty())
-	{
-		return RandomGeneratorUtil::nextItem(factionHeroes, rand)->getNum();
-	}
-
-	logGlobal->warnStream() << "Cannot find free hero of appropriate faction for player " << owner << " - trying to get first available...";
-	if(!otherHeroes.empty())
-	{
-		return RandomGeneratorUtil::nextItem(otherHeroes, rand)->getNum();
-	}
-
-	logGlobal->errorStream() << "No free allowed heroes!";
-	auto notAllowedHeroesButStillBetterThanCrash = getUnusedAllowedHeroes(true);
-	if(notAllowedHeroesButStillBetterThanCrash.size())
-		return notAllowedHeroesButStillBetterThanCrash.begin()->getNum();
-
-	logGlobal->errorStream() << "No free heroes at all!";
-	assert(0); //current code can't handle this situation
-	return -1; // no available heroes at all
-}
-
-std::pair<Obj,int> CGameState::pickObject (CGObjectInstance *obj)
-{
-	switch(obj->ID)
-	{
-	case Obj::RANDOM_ART:
-		return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(rand, CArtifact::ART_TREASURE | CArtifact::ART_MINOR | CArtifact::ART_MAJOR | CArtifact::ART_RELIC));
-	case Obj::RANDOM_TREASURE_ART:
-		return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(rand, CArtifact::ART_TREASURE));
-	case Obj::RANDOM_MINOR_ART:
-		return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MINOR));
-	case Obj::RANDOM_MAJOR_ART:
-		return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MAJOR));
-	case Obj::RANDOM_RELIC_ART:
-		return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(rand, CArtifact::ART_RELIC));
-	case Obj::RANDOM_HERO:
-		return std::make_pair(Obj::HERO, pickNextHeroType(obj->tempOwner));
-	case Obj::RANDOM_MONSTER:
-		return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand));
-	case Obj::RANDOM_MONSTER_L1:
-		return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 1));
-	case Obj::RANDOM_MONSTER_L2:
-		return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 2));
-	case Obj::RANDOM_MONSTER_L3:
-		return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 3));
-	case Obj::RANDOM_MONSTER_L4:
-		return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 4));
-	case Obj::RANDOM_RESOURCE:
-		return std::make_pair(Obj::RESOURCE,rand.nextInt(6)); //now it's OH3 style, use %8 for mithril
-	case Obj::RANDOM_TOWN:
-		{
-			PlayerColor align = PlayerColor((static_cast<CGTownInstance*>(obj))->alignment);
-			si32 f; // can be negative (for random)
-			if(align >= PlayerColor::PLAYER_LIMIT)//same as owner / random
-			{
-				if(obj->tempOwner >= PlayerColor::PLAYER_LIMIT)
-					f = -1; //random
-				else
-					f = scenarioOps->getIthPlayersSettings(obj->tempOwner).castle;
-			}
-			else
-			{
-				f = scenarioOps->getIthPlayersSettings(align).castle;
-			}
-			if(f<0)
-			{
-				do
-				{
-					f = rand.nextInt(VLC->townh->factions.size() - 1);
-				}
-				while (VLC->townh->factions[f]->town == nullptr); // find playable faction
-			}
-			return std::make_pair(Obj::TOWN,f);
-		}
-	case Obj::RANDOM_MONSTER_L5:
-		return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 5));
-	case Obj::RANDOM_MONSTER_L6:
-		return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 6));
-	case Obj::RANDOM_MONSTER_L7:
-		return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 7));
-	case Obj::RANDOM_DWELLING:
-	case Obj::RANDOM_DWELLING_LVL:
-	case Obj::RANDOM_DWELLING_FACTION:
-		{
-			CGDwelling * dwl = static_cast<CGDwelling*>(obj);
-			int faction;
-
-			//if castle alignment available
-			if (auto info = dynamic_cast<CCreGenAsCastleInfo*>(dwl->info))
-			{
-				faction = rand.nextInt(VLC->townh->factions.size() - 1);
-				if (info->asCastle)
-				{
-					for(auto & elem : map->objects)
-					{
-						if(!elem)
-							continue;
-
-						if(elem->ID==Obj::RANDOM_TOWN
-							&& dynamic_cast<CGTownInstance*>(elem.get())->identifier == info->identifier)
-						{
-							randomizeObject(elem); //we have to randomize the castle first
-							faction = elem->subID;
-							break;
-						}
-						else if(elem->ID==Obj::TOWN
-							&& dynamic_cast<CGTownInstance*>(elem.get())->identifier == info->identifier)
-						{
-							faction = elem->subID;
-							break;
-						}
-					}
-				}
-				else
-				{
-					while(!(info->castles[0]&(1<<faction)))
-					{
-						if((faction>7) && (info->castles[1]&(1<<(faction-8))))
-							break;
-						faction = rand.nextInt(GameConstants::F_NUMBER - 1);
-					}
-				}
-			}
-			else // castle alignment fixed
-				faction = obj->subID;
-
-			int level;
-
-			//if level set to range
-			if (auto info = dynamic_cast<CCreGenLeveledInfo*>(dwl->info))
-			{
-				level = rand.nextInt(info->minLevel, info->maxLevel);
-			}
-			else // fixed level
-			{
-				level = obj->subID;
-			}
-
-			delete dwl->info;
-			dwl->info = nullptr;
-
-			std::pair<Obj, int> result(Obj::NO_OBJ, -1);
-			CreatureID cid = VLC->townh->factions[faction]->town->creatures[level][0];
-
-			//golem factory is not in list of cregens but can be placed as random object
-			static const CreatureID factoryCreatures[] = {CreatureID::STONE_GOLEM, CreatureID::IRON_GOLEM,
-				CreatureID::GOLD_GOLEM, CreatureID::DIAMOND_GOLEM};
-			std::vector<CreatureID> factory(factoryCreatures, factoryCreatures + ARRAY_COUNT(factoryCreatures));
-			if (vstd::contains(factory, cid))
-				result = std::make_pair(Obj::CREATURE_GENERATOR4, 1);
-
-			//NOTE: this will pick last dwelling with this creature (Mantis #900)
-			//check for block map equality is better but more complex solution
-			for(auto &iter : VLC->objh->cregens)
-				if (iter.second == cid)
-					result = std::make_pair(Obj::CREATURE_GENERATOR1, iter.first);
-
-			if (result.first == Obj::NO_OBJ)
-			{
-                logGlobal->errorStream() << "Error: failed to find creature for dwelling of "<< int(faction) << " of level " << int(level);
-				result = std::make_pair(Obj::CREATURE_GENERATOR1, RandomGeneratorUtil::nextItem(VLC->objh->cregens, rand)->first);
-			}
-
-			return result;
-		}
-	}
-	return std::make_pair(Obj::NO_OBJ,-1);
-}
-
-void CGameState::randomizeObject(CGObjectInstance *cur)
-{
-	std::pair<Obj,int> ran = pickObject(cur);
-	if(ran.first == Obj::NO_OBJ || ran.second<0) //this is not a random object, or we couldn't find anything
-	{
-		if(cur->ID==Obj::TOWN) //town - set def
-		{
-			const TerrainTile &tile = map->getTile(cur->visitablePos());
-			CGTownInstance *t = dynamic_cast<CGTownInstance*>(cur);
-			t->town = VLC->townh->factions[t->subID]->town;
-			t->appearance = VLC->dobjinfo->pickCandidates(Obj::TOWN, t->subID, tile.terType).front();
-			t->updateAppearance();
-		}
-		return;
-	}
-	else if(ran.first==Obj::HERO)//special code for hero
-	{
-		CGHeroInstance *h = dynamic_cast<CGHeroInstance *>(cur);
-        if(!h) {logGlobal->warnStream()<<"Wrong random hero at "<<cur->pos; return;}
-		cur->ID = ran.first;
-		cur->subID = ran.second;
-		h->type = VLC->heroh->heroes[ran.second];
-		h->portrait = h->type->imageIndex;
-		h->randomizeArmy(h->type->heroClass->faction);
-		map->heroesOnMap.push_back(h);
-		return; //TODO: maybe we should do something with definfo?
-	}
-	else if(ran.first==Obj::TOWN)//special code for town
-	{
-		const TerrainTile &tile = map->getTile(cur->visitablePos());
-		CGTownInstance *t = dynamic_cast<CGTownInstance*>(cur);
-        if(!t) {logGlobal->warnStream()<<"Wrong random town at "<<cur->pos; return;}
-		cur->ID = ran.first;
-		cur->subID = ran.second;
-		//FIXME: copy-pasted from above
-		t->town = VLC->townh->factions[t->subID]->town;
-		t->appearance = VLC->dobjinfo->pickCandidates(Obj::TOWN,t->subID, tile.terType).front();
-		t->updateAppearance();
-
-		t->randomizeArmy(t->subID);
-		map->towns.push_back(t);
-		return;
-	}
-	else
-	{
-		if (ran.first  != cur->appearance.id ||
-			ran.second != cur->appearance.subid)
-		{
-			const TerrainTile &tile = map->getTile(cur->visitablePos());
-			cur->appearance = VLC->dobjinfo->pickCandidates(Obj(ran.first),ran.second, tile.terType).front();
-		}
-	}
-	//we have to replace normal random object
-	cur->ID = ran.first;
-	cur->subID = ran.second;
-	map->removeBlockVisTiles(cur, true); //recalculate blockvis tiles - picked object might have different than random placeholder
-	map->addBlockVisTiles(cur);
-}
-
-int CGameState::getDate(Date::EDateType mode) const
-{
-	int temp;
-	switch (mode)
-	{
-	case Date::DAY:
-		return day;
-	case Date::DAY_OF_WEEK: //day of week
-		temp = (day)%7; // 1 - Monday, 7 - Sunday
-		return temp ? temp : 7;
-	case Date::WEEK:  //current week
-		temp = ((day-1)/7)+1;
-		if (!(temp%4))
-			return 4;
-		else
-			return (temp%4);
-	case Date::MONTH: //current month
-		return ((day-1)/28)+1;
-	case Date::DAY_OF_MONTH: //day of month
-		temp = (day)%28;
-		if (temp)
-			return temp;
-		else return 28;
-	}
-	return 0;
-}
-
-CGameState::CGameState()
-{
-	gs = this;
-	mx = new boost::shared_mutex();
-	applierGs = new CApplier<CBaseForGSApply>;
-	registerTypesClientPacks1(*applierGs);
-	registerTypesClientPacks2(*applierGs);
-	//objCaller = new CObjectCallersHandler;
-	globalEffects.setDescription("Global effects");
-	globalEffects.setNodeType(CBonusSystemNode::GLOBAL_EFFECTS);
-}
-
-CGameState::~CGameState()
-{
-	//delete mx;//TODO: crash on Linux (mutex must be unlocked before destruction)
-	map.dellNull();
-	curB.dellNull();
-	//delete scenarioOps; //TODO: fix for loading ind delete
-	//delete initialOpts;
-	delete applierGs;
-	//delete objCaller;
-
-	for(auto ptr : hpool.heroesPool) // clean hero pool
-		ptr.second.dellNull();
-}
-
-BattleInfo * CGameState::setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town)
-{
-	const TerrainTile &t = map->getTile(tile);
-	ETerrainType terrain = t.terType;
-	if(t.isCoastal() && !t.isWater())
-		terrain = ETerrainType::SAND;
-
-	BFieldType terType = battleGetBattlefieldType(tile);
-	if (heroes[0] && heroes[0]->boat && heroes[1] && heroes[1]->boat)
-		terType = BFieldType::SHIP_TO_SHIP;
-	return BattleInfo::setupBattle(tile, terrain, terType, armies, heroes, creatureBank, town);
-}
-
-void CGameState::init(StartInfo * si)
-{
-    logGlobal->infoStream() << "\tUsing random seed: "<< si->seedToBeUsed;
-	rand.setSeed(si->seedToBeUsed);
-	scenarioOps = CMemorySerializer::deepCopy(*si).release();
-	initialOpts = CMemorySerializer::deepCopy(*si).release();
-	si = nullptr;
-
-	switch(scenarioOps->mode)
-	{
-	case StartInfo::NEW_GAME:
-		initNewGame();
-		break;
-	case StartInfo::CAMPAIGN:
-		initCampaign();
-		break;
-	case StartInfo::DUEL:
-		initDuel();
-		return;
-	default:
-        logGlobal->errorStream() << "Wrong mode: " << (int)scenarioOps->mode;
-		return;
-	}
-	VLC->arth->initAllowedArtifactsList(map->allowedArtifact);
-    logGlobal->infoStream() << "Map loaded!";
-
-	checkMapChecksum();
-
-	day = 0;
-
-    logGlobal->debugStream() << "Initialization:";
-
-	initPlayerStates();
-	placeCampaignHeroes();
-	initGrailPosition();
-	initRandomFactionsForPlayers();
-	randomizeMapObjects();
-	placeStartingHeroes();
-	initStartingResources();
-	initHeroes();
-	initStartingBonus();
-	initTowns();
-	initMapObjects();
-	buildBonusSystemTree();
-	initVisitingAndGarrisonedHeroes();
-	initFogOfWar();
-
-    logGlobal->debugStream() << "\tChecking objectives";
-	map->checkForObjectives(); //needs to be run when all objects are properly placed
-
-	auto seedAfterInit = rand.nextInt();
-    logGlobal->infoStream() << "Seed after init is " << seedAfterInit << " (before was " << scenarioOps->seedToBeUsed << ")";
-	if(scenarioOps->seedPostInit > 0)
-	{
-		//RNG must be in the same state on all machines when initialization is done (otherwise we have desync)
-		assert(scenarioOps->seedPostInit == seedAfterInit);
-	}
-	else
-	{
-		scenarioOps->seedPostInit = seedAfterInit; //store the post init "seed"
-	}
-}
-
-void CGameState::initNewGame()
-{
-	if(scenarioOps->createRandomMap())
-	{
-		logGlobal->infoStream() << "Create random map.";
-		CStopWatch sw;
-
-		// Gen map
-		CMapGenerator mapGenerator(scenarioOps->mapGenOptions, scenarioOps->seedToBeUsed);
-		map = mapGenerator.generate().release();
-
-		// Update starting options
-		for(int i = 0; i < map->players.size(); ++i)
-		{
-			const auto & playerInfo = map->players[i];
-			if(playerInfo.canAnyonePlay())
-			{
-				PlayerSettings & playerSettings = scenarioOps->playerInfos[PlayerColor(i)];
-				playerSettings.compOnly = !playerInfo.canHumanPlay;
-				playerSettings.team = playerInfo.team;
-				playerSettings.castle = playerInfo.defaultCastle();
-				if(playerSettings.playerID == PlayerSettings::PLAYER_AI && playerSettings.name.empty())
-				{
-					playerSettings.name = VLC->generaltexth->allTexts[468];
-				}
-				playerSettings.color = PlayerColor(i);
-			}
-			else
-			{
-				scenarioOps->playerInfos.erase(PlayerColor(i));
-			}
-		}
-
-		logGlobal->infoStream() << boost::format("Generated random map in %i ms.") % sw.getDiff();
-	}
-	else
-	{
-		logGlobal->infoStream() << "Open map file: " << scenarioOps->mapname;
-		map = CMapService::loadMap(scenarioOps->mapname).release();
-	}
-}
-
-void CGameState::initCampaign()
-{
-	logGlobal->infoStream() << "Open campaign map file: " << scenarioOps->campState->currentMap;
-	auto campaign = scenarioOps->campState;
-	assert(vstd::contains(campaign->camp->mapPieces, *scenarioOps->campState->currentMap));
-
-	std::string scenarioName = scenarioOps->mapname.substr(0, scenarioOps->mapname.find('.'));
-	boost::to_lower(scenarioName);
-	scenarioName += ':' + boost::lexical_cast<std::string>(*campaign->currentMap);
-
-	std::string & mapContent = campaign->camp->mapPieces[*campaign->currentMap];
-	auto buffer = reinterpret_cast<const ui8 *>(mapContent.data());
-	map = CMapService::loadMap(buffer, mapContent.size(), scenarioName).release();
-}
-
-void CGameState::initDuel()
-{
-	DuelParameters dp;
-	try //CLoadFile likes throwing
-	{
-		if(boost::algorithm::ends_with(scenarioOps->mapname, ".json"))
-		{
-            logGlobal->infoStream() << "Loading duel settings from JSON file: " << scenarioOps->mapname;
-			dp = DuelParameters::fromJSON(scenarioOps->mapname);
-            logGlobal->infoStream() << "JSON file has been successfully read!";
-		}
-		else
-		{
-			CLoadFile lf(scenarioOps->mapname);
-			lf >> dp;
-		}
-	}
-	catch(...)
-	{
-        logGlobal->errorStream() << "Cannot load duel settings from " << scenarioOps->mapname;
-		throw;
-	}
-
-	const CArmedInstance *armies[2] = {nullptr};
-	const CGHeroInstance *heroes[2] = {nullptr};
-	CGTownInstance *town = nullptr;
-
-	for(int i = 0; i < 2; i++)
-	{
-		CArmedInstance *obj = nullptr;
-		if(dp.sides[i].heroId >= 0)
-		{
-			const DuelParameters::SideSettings &ss = dp.sides[i];
-			auto h = new CGHeroInstance();
-			armies[i] = heroes[i] = h;
-			obj = h;
-			h->subID = ss.heroId;
-			for(int i = 0; i < ss.heroPrimSkills.size(); i++)
-				h->pushPrimSkill(static_cast<PrimarySkill::PrimarySkill>(i), ss.heroPrimSkills[i]);
-
-			if(!ss.spells.empty())
-			{
-				h->putArtifact(ArtifactPosition::SPELLBOOK, CArtifactInstance::createNewArtifactInstance(0));
-				boost::copy(ss.spells, std::inserter(h->spells, h->spells.begin()));
-			}
-
-			for(auto &parka : ss.artifacts)
-			{
-				h->putArtifact(ArtifactPosition(parka.first), parka.second);
-			}
-
-			typedef const std::pair<si32, si8> &TSecSKill;
-			for(TSecSKill secSkill : ss.heroSecSkills)
-				h->setSecSkillLevel(SecondarySkill(secSkill.first), secSkill.second, 1);
-
-			h->initHero(HeroTypeID(h->subID));
-			obj->initObj();
-		}
-		else
-		{
-			auto c = new CGCreature();
-			armies[i] = obj = c;
-			//c->subID = 34;
-		}
-
-		obj->setOwner(PlayerColor(i));
-
-		for(int j = 0; j < ARRAY_COUNT(dp.sides[i].stacks); j++)
-		{
-			CreatureID cre = dp.sides[i].stacks[j].type;
-			TQuantity count = dp.sides[i].stacks[j].count;
-			if(count || obj->hasStackAtSlot(SlotID(j)))
-				obj->setCreature(SlotID(j), cre, count);
-		}
-
-		for(const DuelParameters::CusomCreature &cc : dp.creatures)
-		{
-			CCreature *c = VLC->creh->creatures[cc.id];
-			if(cc.attack >= 0)
-				c->getBonusLocalFirst(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK))->val = cc.attack;
-			if(cc.defense >= 0)
-				c->getBonusLocalFirst(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE))->val = cc.defense;
-			if(cc.speed >= 0)
-				c->getBonusLocalFirst(Selector::type(Bonus::STACKS_SPEED))->val = cc.speed;
-			if(cc.HP >= 0)
-				c->getBonusLocalFirst(Selector::type(Bonus::STACK_HEALTH))->val = cc.HP;
-			if(cc.dmg >= 0)
-			{
-				c->getBonusLocalFirst(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 1))->val = cc.dmg;
-				c->getBonusLocalFirst(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 2))->val = cc.dmg;
-			}
-			if(cc.shoots >= 0)
-				c->getBonusLocalFirst(Selector::type(Bonus::SHOTS))->val = cc.shoots;
-		}
-	}
-
-	curB = BattleInfo::setupBattle(int3(), dp.terType, dp.bfieldType, armies, heroes, false, town);
-	curB->obstacles = dp.obstacles;
-	curB->localInit();
-}
-
-void CGameState::checkMapChecksum()
-{
-	logGlobal->infoStream() << "\tOur checksum for the map: "<< map->checksum;
-	if(scenarioOps->mapfileChecksum)
-	{
-		logGlobal->infoStream() << "\tServer checksum for " << scenarioOps->mapname <<": "<< scenarioOps->mapfileChecksum;
-		if(map->checksum != scenarioOps->mapfileChecksum)
-		{
-			logGlobal->errorStream() << "Wrong map checksum!!!";
-			throw std::runtime_error("Wrong checksum");
-		}
-	}
-	else
-	{
-		scenarioOps->mapfileChecksum = map->checksum;
-	}
-}
-
-void CGameState::initGrailPosition()
-{
-	logGlobal->debugStream() << "\tPicking grail position";
-	//pick grail location
-	if(map->grailPos.x < 0 || map->grailRadious) //grail not set or set within a radius around some place
-	{
-		if(!map->grailRadious) //radius not given -> anywhere on map
-			map->grailRadious = map->width * 2;
-
-		std::vector<int3> allowedPos;
-		static const int BORDER_WIDTH = 9; // grail must be at least 9 tiles away from border
-
-		// add all not blocked tiles in range
-		for (int i = BORDER_WIDTH; i < map->width - BORDER_WIDTH ; i++)
-		{
-			for (int j = BORDER_WIDTH; j < map->height - BORDER_WIDTH; j++)
-			{
-				for (int k = 0; k < (map->twoLevel ? 2 : 1); k++)
-				{
-					const TerrainTile &t = map->getTile(int3(i, j, k));
-					if(!t.blocked
-						&& !t.visitable
-						&& t.terType != ETerrainType::WATER
-						&& t.terType != ETerrainType::ROCK
-						&& map->grailPos.dist2dSQ(int3(i, j, k)) <= (map->grailRadious * map->grailRadious))
-						allowedPos.push_back(int3(i,j,k));
-				}
-			}
-		}
-
-		//remove tiles with holes
-		for(auto & elem : map->objects)
-			if(elem && elem->ID == Obj::HOLE)
-				allowedPos -= elem->pos;
-
-		if(!allowedPos.empty())
-		{
-			map->grailPos = *RandomGeneratorUtil::nextItem(allowedPos, rand);
-		}
-		else
-		{
-			logGlobal->warnStream() << "Warning: Grail cannot be placed, no appropriate tile found!";
-		}
-	}
-}
-
-void CGameState::initRandomFactionsForPlayers()
-{
-	logGlobal->debugStream() << "\tPicking random factions for players";
-	for(auto & elem : scenarioOps->playerInfos)
-	{
-		if(elem.second.castle==-1)
-		{
-			auto randomID = rand.nextInt(map->players[elem.first.getNum()].allowedFactions.size() - 1);
-			auto iter = map->players[elem.first.getNum()].allowedFactions.begin();
-			std::advance(iter, randomID);
-
-			elem.second.castle = *iter;
-		}
-	}
-}
-
-void CGameState::randomizeMapObjects()
-{
-	logGlobal->debugStream() << "\tRandomizing objects";
-	for(CGObjectInstance *obj : map->objects)
-	{
-		if(!obj) continue;
-
-		randomizeObject(obj);
-		obj->hoverName = VLC->generaltexth->names[obj->ID];
-
-		//handle Favouring Winds - mark tiles under it
-		if(obj->ID == Obj::FAVORABLE_WINDS)
-		{
-			for (int i = 0; i < obj->getWidth() ; i++)
-			{
-				for (int j = 0; j < obj->getHeight() ; j++)
-				{
-					int3 pos = obj->pos - int3(i,j,0);
-					if(map->isInTheMap(pos)) map->getTile(pos).extTileFlags |= 128;
-				}
-			}
-		}
-	}
-}
-
-void CGameState::initPlayerStates()
-{
-	logGlobal->debugStream() << "\tCreating player entries in gs";
-	for(auto & elem : scenarioOps->playerInfos)
-	{
-		std::pair<PlayerColor, PlayerState> ins(elem.first,PlayerState());
-		ins.second.color=ins.first;
-		ins.second.human = elem.second.playerID;
-		ins.second.team = map->players[ins.first.getNum()].team;
-		teams[ins.second.team].id = ins.second.team;//init team
-		teams[ins.second.team].players.insert(ins.first);//add player to team
-		players.insert(ins);
-	}
-}
-
-void CGameState::placeCampaignHeroes()
-{
-	if (scenarioOps->campState)
-	{
-		// place bonus hero
-		auto campaignBonus = scenarioOps->campState->getBonusForCurrentMap();
-		bool campaignGiveHero = campaignBonus && campaignBonus.get().type == CScenarioTravel::STravelBonus::HERO;
-
-		if(campaignGiveHero)
-		{
-			auto playerColor = PlayerColor(campaignBonus->info1);
-			auto it = scenarioOps->playerInfos.find(playerColor);
-			if(it != scenarioOps->playerInfos.end())
-			{
-				auto heroTypeId = campaignBonus->info2;
-				if(heroTypeId == 0xffff) // random bonus hero
-				{
-					heroTypeId = pickUnusedHeroTypeRandomly(playerColor);
-				}
-
-				placeStartingHero(playerColor, HeroTypeID(heroTypeId), map->players[playerColor.getNum()].posOfMainTown);
-			}
-		}
-
-		// replace heroes placeholders
-		auto crossoverHeroes = getCrossoverHeroesFromPreviousScenarios();
-
-		if(!crossoverHeroes.heroesFromAnyPreviousScenarios.empty())
-		{
-			logGlobal->debugStream() << "\tGenerate list of hero placeholders";
-			auto campaignHeroReplacements = generateCampaignHeroesToReplace(crossoverHeroes);
-
-			logGlobal->debugStream() << "\tPrepare crossover heroes";
-			prepareCrossoverHeroes(campaignHeroReplacements, scenarioOps->campState->getCurrentScenario().travelOptions);
-
-			// remove same heroes on the map which will be added through crossover heroes
-			// INFO: we will remove heroes because later it may be possible that the API doesn't allow having heroes
-			// with the same hero type id
-			std::vector<CGHeroInstance *> removedHeroes;
-
-			for(auto & campaignHeroReplacement : campaignHeroReplacements)
-			{
-				auto hero = getUsedHero(HeroTypeID(campaignHeroReplacement.hero->subID));
-				if(hero)
-				{
-					removedHeroes.push_back(hero);
-					map->heroesOnMap -= hero;
-					map->objects[hero->id.getNum()] = nullptr;
-					map->removeBlockVisTiles(hero, true);
-				}
-			}
-
-			logGlobal->debugStream() << "\tReplace placeholders with heroes";
-			replaceHeroesPlaceholders(campaignHeroReplacements);
-
-			// remove hero placeholders on map
-			for(auto obj : map->objects)
-			{
-				if(obj && obj->ID == Obj::HERO_PLACEHOLDER)
-				{
-					auto heroPlaceholder = dynamic_cast<CGHeroPlaceholder *>(obj.get());
-					map->removeBlockVisTiles(heroPlaceholder, true);
-					map->objects[heroPlaceholder->id.getNum()] = nullptr;
-					delete heroPlaceholder;
-				}
-			}
-
-			// now add removed heroes again with unused type ID
-			for(auto hero : removedHeroes)
-			{
-				si32 heroTypeId = 0;
-				if(hero->ID == Obj::HERO)
-				{
-					heroTypeId = pickUnusedHeroTypeRandomly(hero->tempOwner);
-				}
-				else if(hero->ID == Obj::PRISON)
-				{
-					auto unusedHeroTypeIds = getUnusedAllowedHeroes();
-					if(!unusedHeroTypeIds.empty())
-					{
-						heroTypeId = (*RandomGeneratorUtil::nextItem(unusedHeroTypeIds, rand)).getNum();
-					}
-					else
-					{
-						logGlobal->errorStream() << "No free hero type ID found to replace prison.";
-						assert(0);
-					}
-				}
-				else
-				{
-					assert(0); // should not happen
-				}
-
-				hero->subID = heroTypeId;
-				hero->portrait = hero->subID;
-				map->getEditManager()->insertObject(hero, hero->pos);
-			}
-		}
-	}
-}
-
-void CGameState::placeStartingHero(PlayerColor playerColor, HeroTypeID heroTypeId, int3 townPos)
-{
-	townPos.x += 1;
-
-	CGHeroInstance * hero =  static_cast<CGHeroInstance*>(createObject(Obj::HERO, heroTypeId.getNum(), townPos, playerColor));
-	map->getEditManager()->insertObject(hero, townPos);
-}
-
-CGameState::CrossoverHeroesList CGameState::getCrossoverHeroesFromPreviousScenarios() const
-{
-	CrossoverHeroesList crossoverHeroes;
-
-	auto campaignState = scenarioOps->campState;
-	auto bonus = campaignState->getBonusForCurrentMap();
-	if (bonus && bonus->type == CScenarioTravel::STravelBonus::HEROES_FROM_PREVIOUS_SCENARIO)
-	{
-		crossoverHeroes.heroesFromAnyPreviousScenarios = crossoverHeroes.heroesFromPreviousScenario = campaignState->camp->scenarios[bonus->info2].crossoverHeroes;
-	}
-	else
-	{
-		if(!campaignState->mapsConquered.empty())
-		{
-			crossoverHeroes.heroesFromPreviousScenario = campaignState->camp->scenarios[campaignState->mapsConquered.back()].crossoverHeroes;
-
-			for(auto mapNr : campaignState->mapsConquered)
-			{
-				// create a list of deleted heroes
-				auto & scenario = campaignState->camp->scenarios[mapNr];
-				auto lostCrossoverHeroes = scenario.getLostCrossoverHeroes();
-
-				// remove heroes which didn't reached the end of the scenario, but were available at the start
-				for(auto hero : lostCrossoverHeroes)
-				{
-					crossoverHeroes.heroesFromAnyPreviousScenarios.erase(range::remove_if(crossoverHeroes.heroesFromAnyPreviousScenarios,
-						CGObjectInstanceBySubIdFinder(hero)), crossoverHeroes.heroesFromAnyPreviousScenarios.end());
-				}
-
-				// now add heroes which completed the scenario
-				for(auto hero : scenario.crossoverHeroes)
-				{
-					// add new heroes and replace old heroes with newer ones
-					auto it = range::find_if(crossoverHeroes.heroesFromAnyPreviousScenarios, CGObjectInstanceBySubIdFinder(hero));
-					if (it != crossoverHeroes.heroesFromAnyPreviousScenarios.end())
-					{
-						// replace old hero with newer one
-						crossoverHeroes.heroesFromAnyPreviousScenarios[it - crossoverHeroes.heroesFromAnyPreviousScenarios.begin()] = hero;
-					}
-					else
-					{
-						// add new hero
-						crossoverHeroes.heroesFromAnyPreviousScenarios.push_back(hero);
-					}
-				}
-			}
-		}
-	}
-
-	return crossoverHeroes;
-}
-
-void CGameState::prepareCrossoverHeroes(std::vector<CGameState::CampaignHeroReplacement> & campaignHeroReplacements, const CScenarioTravel & travelOptions) const
-{
-	// create heroes list for convenience iterating
-	std::vector<CGHeroInstance *> crossoverHeroes;
-	for(auto & campaignHeroReplacement : campaignHeroReplacements)
-	{
-		crossoverHeroes.push_back(campaignHeroReplacement.hero);
-	}
-
-	// TODO replace magic numbers with named constants
-	// TODO this logic (what should be kept) should be part of CScenarioTravel and be exposed via some clean set of methods
-	if(!(travelOptions.whatHeroKeeps & 1))
-	{
-		//trimming experience
-		for(CGHeroInstance * cgh : crossoverHeroes)
-		{
-			cgh->initExp();
-		}
-	}
-
-	if(!(travelOptions.whatHeroKeeps & 2))
-	{
-		//trimming prim skills
-		for(CGHeroInstance * cgh : crossoverHeroes)
-		{
-			for(int g=0; g<GameConstants::PRIMARY_SKILLS; ++g)
-			{
-				auto sel = Selector::type(Bonus::PRIMARY_SKILL)
-					.And(Selector::subtype(g))
-					.And(Selector::sourceType(Bonus::HERO_BASE_SKILL));
-
-				cgh->getBonusLocalFirst(sel)->val = cgh->type->heroClass->primarySkillInitial[g];
-			}
-		}
-	}
-
-	if(!(travelOptions.whatHeroKeeps & 4))
-	{
-		//trimming sec skills
-		for(CGHeroInstance * cgh : crossoverHeroes)
-		{
-			cgh->secSkills = cgh->type->secSkillsInit;
-			cgh->recreateSecondarySkillsBonuses();
-		}
-	}
-
-	if(!(travelOptions.whatHeroKeeps & 8))
-	{
-		for(CGHeroInstance * cgh : crossoverHeroes)
-		{
-			// Trimming spells
-			cgh->spells.clear();
-
-			// Spellbook will also be removed
-			if (cgh->hasSpellbook())
-				ArtifactLocation(cgh, ArtifactPosition(ArtifactPosition::SPELLBOOK)).removeArtifact();
-		}
-	}
-
-	if(!(travelOptions.whatHeroKeeps & 16))
-	{
-		//trimming artifacts
-		for(CGHeroInstance * hero : crossoverHeroes)
-		{
-			size_t totalArts = GameConstants::BACKPACK_START + hero->artifactsInBackpack.size();
-			for (size_t i = 0; i < totalArts; i++ )
-			{
-				auto artifactPosition = ArtifactPosition(i);
-				if(artifactPosition == ArtifactPosition::SPELLBOOK) continue; // do not handle spellbook this way
-
-				const ArtSlotInfo *info = hero->getSlot(artifactPosition);
-				if(!info)
-					continue;
-
-				// TODO: why would there be nullptr artifacts?
-				const CArtifactInstance *art = info->artifact;
-				if(!art)
-					continue;
-
-				int id  = art->artType->id;
-				assert( 8*18 > id );//number of arts that fits into h3m format
-				bool takeable = travelOptions.artifsKeptByHero[id / 8] & ( 1 << (id%8) );
-
-				ArtifactLocation al(hero, artifactPosition);
-				if(!takeable  &&  !al.getSlot()->locked)  //don't try removing locked artifacts -> it crashes #1719
-					al.removeArtifact();
-			}
-		}
-	}
-
-	//trimming creatures
-	for(CGHeroInstance * cgh : crossoverHeroes)
-	{
-		auto shouldSlotBeErased = [&](const std::pair<SlotID, CStackInstance *> & j) -> bool
-		{
-			CreatureID::ECreatureID crid = j.second->getCreatureID().toEnum();
-			return !(travelOptions.monstersKeptByHero[crid / 8] & (1 << (crid % 8)));
-		};
-
-		auto stacksCopy = cgh->stacks; //copy of the map, so we can iterate iover it and remove stacks
-		for(auto &slotPair : stacksCopy)
-			if(shouldSlotBeErased(slotPair))
-				cgh->eraseStack(slotPair.first);
-	}
-
-	// Removing short-term bonuses
-	for(CGHeroInstance * cgh : crossoverHeroes)
-	{
-		cgh->popBonuses(Selector::durationType(Bonus::ONE_DAY));
-		cgh->popBonuses(Selector::durationType(Bonus::ONE_WEEK));
-		cgh->popBonuses(Selector::durationType(Bonus::N_TURNS));
-		cgh->popBonuses(Selector::durationType(Bonus::N_DAYS));
-		cgh->popBonuses(Selector::durationType(Bonus::ONE_BATTLE));
-	}
-
-}
-
-void CGameState::placeStartingHeroes()
-{
-	logGlobal->debugStream() << "\tGiving starting hero";
-
-	for(auto & playerSettingPair : scenarioOps->playerInfos)
-	{
-		auto playerColor = playerSettingPair.first;
-		auto & playerInfo = map->players[playerColor.getNum()];
-		if(playerInfo.generateHeroAtMainTown && playerInfo.hasMainTown)
-		{
-			// Do not place a starting hero if the hero was already placed due to a campaign bonus
-			if(scenarioOps->campState)
-			{
-				if(auto campaignBonus = scenarioOps->campState->getBonusForCurrentMap())
-				{
-					if(campaignBonus->type == CScenarioTravel::STravelBonus::HERO && playerColor == PlayerColor(campaignBonus->info1)) continue;
-				}
-			}
-
-			int heroTypeId = pickNextHeroType(playerColor);
-			if(playerSettingPair.second.hero == -1) playerSettingPair.second.hero = heroTypeId;
-
-			placeStartingHero(playerColor, HeroTypeID(heroTypeId), playerInfo.posOfMainTown);
-		}
-	}
-}
-
-void CGameState::initStartingResources()
-{
-	logGlobal->debugStream() << "\tSetting up resources";
-	const JsonNode config(ResourceID("config/startres.json"));
-	const JsonVector &vector = config["difficulty"].Vector();
-	const JsonNode &level = vector[scenarioOps->difficulty];
-
-	TResources startresAI(level["ai"]);
-	TResources startresHuman(level["human"]);
-
-	for (auto & elem : players)
-	{
-		PlayerState &p = elem.second;
-
-		if (p.human)
-			p.resources = startresHuman;
-		else
-			p.resources = startresAI;
-	}
-
-	auto getHumanPlayerInfo = [&]() -> std::vector<const PlayerSettings *>
-	{
-		std::vector<const PlayerSettings *> ret;
-		for(auto it = scenarioOps->playerInfos.cbegin();
-			it != scenarioOps->playerInfos.cend(); ++it)
-		{
-			if(it->second.playerID != PlayerSettings::PLAYER_AI)
-				ret.push_back(&it->second);
-		}
-
-		return ret;
-	};
-
-	//give start resource bonus in case of campaign
-	if (scenarioOps->mode == StartInfo::CAMPAIGN)
-	{
-		auto chosenBonus = scenarioOps->campState->getBonusForCurrentMap();
-		if(chosenBonus && chosenBonus->type == CScenarioTravel::STravelBonus::RESOURCE)
-		{
-			std::vector<const PlayerSettings *> people = getHumanPlayerInfo(); //players we will give resource bonus
-			for(const PlayerSettings *ps : people)
-			{
-				std::vector<int> res; //resources we will give
-				switch (chosenBonus->info1)
-				{
-				case 0: case 1: case 2: case 3: case 4: case 5: case 6:
-					res.push_back(chosenBonus->info1);
-					break;
-				case 0xFD: //wood+ore
-					res.push_back(Res::WOOD); res.push_back(Res::ORE);
-					break;
-				case 0xFE:  //rare
-					res.push_back(Res::MERCURY); res.push_back(Res::SULFUR); res.push_back(Res::CRYSTAL); res.push_back(Res::GEMS);
-					break;
-				default:
-					assert(0);
-					break;
-				}
-				//increasing resource quantity
-				for (auto & re : res)
-				{
-					players[ps->color].resources[re] += chosenBonus->info2;
-				}
-			}
-		}
-	}
-}
-
-void CGameState::initHeroes()
-{
-	for(auto hero : map->heroesOnMap)  //heroes instances initialization
-	{
-		if (hero->getOwner() == PlayerColor::UNFLAGGABLE)
-		{
-			logGlobal->warnStream() << "Warning - hero with uninitialized owner!";
-			continue;
-		}
-
-		hero->initHero();
-		getPlayer(hero->getOwner())->heroes.push_back(hero);
-		map->allHeroes[hero->type->ID.getNum()] = hero;
-	}
-
-	for(auto obj : map->objects) //prisons
-	{
-		if(obj && obj->ID == Obj::PRISON)
-			map->allHeroes[obj->subID] = dynamic_cast<CGHeroInstance*>(obj.get());
-	}
-
-	std::set<HeroTypeID> heroesToCreate = getUnusedAllowedHeroes(); //ids of heroes to be created and put into the pool
-	for(auto ph : map->predefinedHeroes)
-	{
-		if(!vstd::contains(heroesToCreate, HeroTypeID(ph->subID)))
-			continue;
-		ph->initHero();
-		hpool.heroesPool[ph->subID] = ph;
-		hpool.pavailable[ph->subID] = 0xff;
-		heroesToCreate.erase(ph->type->ID);
-
-		map->allHeroes[ph->subID] = ph;
-	}
-
-	for(HeroTypeID htype : heroesToCreate) //all not used allowed heroes go with default state into the pool
-	{
-		auto  vhi = new CGHeroInstance();
-		vhi->initHero(htype);
-
-		int typeID = htype.getNum();
-		map->allHeroes[typeID] = vhi;
-		hpool.heroesPool[typeID] = vhi;
-		hpool.pavailable[typeID] = 0xff;
-	}
-
-	for(auto & elem : map->disposedHeroes)
-	{
-		hpool.pavailable[elem.heroId] = elem.players;
-	}
-
-	if (scenarioOps->mode == StartInfo::CAMPAIGN) //give campaign bonuses for specific / best hero
-	{
-		auto chosenBonus = scenarioOps->campState->getBonusForCurrentMap();
-		if (chosenBonus && chosenBonus->isBonusForHero() && chosenBonus->info1 != 0xFFFE) //exclude generated heroes
-		{
-			//find human player
-			PlayerColor humanPlayer=PlayerColor::NEUTRAL;
-			for (auto & elem : players)
-			{
-				if(elem.second.human)
-				{
-					humanPlayer = elem.first;
-					break;
-				}
-			}
-			assert(humanPlayer != PlayerColor::NEUTRAL);
-
-			std::vector<ConstTransitivePtr<CGHeroInstance> > & heroes = players[humanPlayer].heroes;
-
-			if (chosenBonus->info1 == 0xFFFD) //most powerful
-			{
-				int maxB = -1;
-				for (int b=0; b<heroes.size(); ++b)
-				{
-					if (maxB == -1 || heroes[b]->getTotalStrength() > heroes[maxB]->getTotalStrength())
-					{
-						maxB = b;
-					}
-				}
-				if(maxB < 0)
-					logGlobal->warnStream() << "Warning - cannot give bonus to hero cause there are no heroes!";
-				else
-					giveCampaignBonusToHero(heroes[maxB]);
-			}
-			else //specific hero
-			{
-				for (auto & heroe : heroes)
-				{
-					if (heroe->subID == chosenBonus->info1)
-					{
-						giveCampaignBonusToHero(heroe);
-						break;
-					}
-				}
-			}
-		}
-	}
-}
-
-void CGameState::giveCampaignBonusToHero(CGHeroInstance * hero)
-{
-	const boost::optional<CScenarioTravel::STravelBonus> & curBonus = scenarioOps->campState->getBonusForCurrentMap();
-	if(!curBonus)
-		return;
-
-	if(curBonus->isBonusForHero())
-	{
-		//apply bonus
-		switch (curBonus->type)
-		{
-		case CScenarioTravel::STravelBonus::SPELL:
-			hero->spells.insert(SpellID(curBonus->info2));
-			break;
-		case CScenarioTravel::STravelBonus::MONSTER:
-			{
-				for(int i=0; i<GameConstants::ARMY_SIZE; i++)
-				{
-					if(hero->slotEmpty(SlotID(i)))
-					{
-						hero->addToSlot(SlotID(i), CreatureID(curBonus->info2), curBonus->info3);
-						break;
-					}
-				}
-			}
-			break;
-		case CScenarioTravel::STravelBonus::ARTIFACT:
-			gs->giveHeroArtifact(hero, static_cast<ArtifactID>(curBonus->info2));
-			break;
-		case CScenarioTravel::STravelBonus::SPELL_SCROLL:
-			{
-				CArtifactInstance * scroll = CArtifactInstance::createScroll(SpellID(curBonus->info2).toSpell());
-				scroll->putAt(ArtifactLocation(hero, scroll->firstAvailableSlot(hero)));
-			}
-			break;
-		case CScenarioTravel::STravelBonus::PRIMARY_SKILL:
-			{
-				const ui8* ptr = reinterpret_cast<const ui8*>(&curBonus->info2);
-				for (int g=0; g<GameConstants::PRIMARY_SKILLS; ++g)
-				{
-					int val = ptr[g];
-					if (val == 0)
-					{
-						continue;
-					}
-					auto bb = new Bonus(Bonus::PERMANENT, Bonus::PRIMARY_SKILL, Bonus::CAMPAIGN_BONUS, val, *scenarioOps->campState->currentMap, g);
-					hero->addNewBonus(bb);
-				}
-			}
-			break;
-		case CScenarioTravel::STravelBonus::SECONDARY_SKILL:
-			hero->setSecSkillLevel(SecondarySkill(curBonus->info2), curBonus->info3, true);
-			break;
-		}
-	}
-}
-
-void CGameState::initFogOfWar()
-{
-	logGlobal->debugStream() << "\tFog of war"; //FIXME: should be initialized after all bonuses are set
-	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;
-
-		for(CGObjectInstance *obj : map->objects)
-		{
-			if(!obj || !vstd::contains(elem.second.players, obj->tempOwner)) continue; //not a flagged object
-
-			std::unordered_set<int3, ShashInt3> tiles;
-			obj->getSightTiles(tiles);
-			for(int3 tile : tiles)
-			{
-				elem.second.fogOfWarMap[tile.x][tile.y][tile.z] = 1;
-			}
-		}
-	}
-}
-
-void CGameState::initStartingBonus()
-{
-	logGlobal->debugStream() << "\tStarting bonuses";
-	for(auto & elem : players)
-	{
-		//starting bonus
-		if(scenarioOps->playerInfos[elem.first].bonus==PlayerSettings::RANDOM)
-			scenarioOps->playerInfos[elem.first].bonus = static_cast<PlayerSettings::Ebonus>(rand.nextInt(2));
-		switch(scenarioOps->playerInfos[elem.first].bonus)
-		{
-		case PlayerSettings::GOLD:
-			elem.second.resources[Res::GOLD] += rand.nextInt(500, 1000);
-			break;
-		case PlayerSettings::RESOURCE:
-			{
-				int res = VLC->townh->factions[scenarioOps->playerInfos[elem.first].castle]->town->primaryRes;
-				if(res == Res::WOOD_AND_ORE)
-				{
-					elem.second.resources[Res::WOOD] += rand.nextInt(5, 10);
-					elem.second.resources[Res::ORE] += rand.nextInt(5, 10);
-				}
-				else
-				{
-					elem.second.resources[res] += rand.nextInt(3, 6);
-				}
-				break;
-			}
-		case PlayerSettings::ARTIFACT:
-			{
-				if(!elem.second.heroes.size())
-				{
-					logGlobal->debugStream() << "Cannot give starting artifact - no heroes!";
-					break;
-				}
-				CArtifact *toGive;
-				toGive = VLC->arth->artifacts[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_TREASURE)];
-
-				CGHeroInstance *hero = elem.second.heroes[0];
-				giveHeroArtifact(hero, toGive->id);
-			}
-			break;
-		}
-	}
-}
-
-void CGameState::initTowns()
-{
-	logGlobal->debugStream() << "\tTowns";
-
-	//campaign bonuses for towns
-	if (scenarioOps->mode == StartInfo::CAMPAIGN)
-	{
-		auto chosenBonus = scenarioOps->campState->getBonusForCurrentMap();
-
-		if (chosenBonus && chosenBonus->type == CScenarioTravel::STravelBonus::BUILDING)
-		{
-			for (int g=0; g<map->towns.size(); ++g)
-			{
-				PlayerState * owner = getPlayer(map->towns[g]->getOwner());
-				if (owner)
-				{
-					PlayerInfo & pi = map->players[owner->color.getNum()];
-
-					if (owner->human && //human-owned
-						map->towns[g]->pos == pi.posOfMainTown + int3(2, 0, 0))
-					{
-						map->towns[g]->builtBuildings.insert(
-							CBuildingHandler::campToERMU(chosenBonus->info1, map->towns[g]->subID, map->towns[g]->builtBuildings));
-						break;
-					}
-				}
-			}
-		}
-	}
-
-	CGTownInstance::universitySkills.clear();
-	for ( int i=0; i<4; i++)
-		CGTownInstance::universitySkills.push_back(14+i);//skills for university
-
-	for (auto & elem : map->towns)
-	{
-		CGTownInstance * vti =(elem);
-		if(!vti->town)
-		{
-			vti->town = VLC->townh->factions[vti->subID]->town;
-		}
-		if(vti->name.empty())
-		{
-			vti->name = *RandomGeneratorUtil::nextItem(vti->town->names, rand);
-		}
-
-		//init buildings
-		if(vstd::contains(vti->builtBuildings, BuildingID::DEFAULT)) //give standard set of buildings
-		{
-			vti->builtBuildings.erase(BuildingID::DEFAULT);
-			vti->builtBuildings.insert(BuildingID::VILLAGE_HALL);
-			vti->builtBuildings.insert(BuildingID::TAVERN);
-			vti->builtBuildings.insert(BuildingID::DWELL_FIRST);
-			if(rand.nextInt(1) == 1)
-			{
-				vti->builtBuildings.insert(BuildingID::DWELL_LVL_2);
-			}
-		}
-
-		//#1444 - remove entries that don't have buildings defined (like some unused extra town hall buildings)
-		vstd::erase_if(vti->builtBuildings, [vti](BuildingID bid){
-			return !vti->town->buildings.count(bid) || !vti->town->buildings.at(bid); });
-
-		if (vstd::contains(vti->builtBuildings, BuildingID::SHIPYARD) && vti->shipyardStatus()==IBoatGenerator::TILE_BLOCKED)
-			vti->builtBuildings.erase(BuildingID::SHIPYARD);//if we have harbor without water - erase it (this is H3 behaviour)
-
-		//init hordes
-		for (int i = 0; i<GameConstants::CREATURES_PER_TOWN; i++)
-			if (vstd::contains(vti->builtBuildings,(-31-i))) //if we have horde for this level
-			{
-				vti->builtBuildings.erase(BuildingID(-31-i));//remove old ID
-				if (vti->town->hordeLvl.at(0) == i)//if town first horde is this one
-				{
-					vti->builtBuildings.insert(BuildingID::HORDE_1);//add it
-					if (vstd::contains(vti->builtBuildings,(BuildingID::DWELL_UP_FIRST+i)))//if we have upgraded dwelling as well
-						vti->builtBuildings.insert(BuildingID::HORDE_1_UPGR);//add it as well
-				}
-				if (vti->town->hordeLvl.at(1) == i)//if town second horde is this one
-				{
-					vti->builtBuildings.insert(BuildingID::HORDE_2);
-					if (vstd::contains(vti->builtBuildings,(BuildingID::DWELL_UP_FIRST+i)))
-						vti->builtBuildings.insert(BuildingID::HORDE_2_UPGR);
-				}
-			}
-
-		//Early check for #1444-like problems
-		for(auto building : vti->builtBuildings)
-		{
-			assert(vti->town->buildings.at(building) != nullptr);
-			UNUSED(building);
-		}
-
-		//town events
-		for(CCastleEvent &ev : vti->events)
-		{
-			for (int i = 0; i<GameConstants::CREATURES_PER_TOWN; i++)
-				if (vstd::contains(ev.buildings,(-31-i))) //if we have horde for this level
-				{
-					ev.buildings.erase(BuildingID(-31-i));
-					if (vti->town->hordeLvl.at(0) == i)
-						ev.buildings.insert(BuildingID::HORDE_1);
-					if (vti->town->hordeLvl.at(1) == i)
-						ev.buildings.insert(BuildingID::HORDE_2);
-				}
-		}
-		//init spells
-		logGlobal->debugStream() << "\t\tTown init spells";
-		vti->spells.resize(GameConstants::SPELL_LEVELS);
-
-		for(ui32 z=0; z<vti->obligatorySpells.size();z++)
-		{
-			CSpell *s = vti->obligatorySpells[z].toSpell();
-			vti->spells[s->level-1].push_back(s->id);
-			vti->possibleSpells -= s->id;
-		}
-		logGlobal->debugStream() << "\t\tTown init spells2";
-		while(vti->possibleSpells.size())
-		{
-			ui32 total=0;
-			int sel = -1;
-
-			for(ui32 ps=0;ps<vti->possibleSpells.size();ps++)
-				total += vti->possibleSpells[ps].toSpell()->getProbability(vti->subID);
-
-			if (total == 0) // remaining spells have 0 probability
-				break;
-
-			auto r = rand.nextInt(total - 1);
-			for(ui32 ps=0; ps<vti->possibleSpells.size();ps++)
-			{
-				r -= vti->possibleSpells[ps].toSpell()->getProbability(vti->subID);
-				if(r<0)
-				{
-					sel = ps;
-					break;
-				}
-			}
-			if(sel<0)
-				sel=0;
-
-			CSpell *s = vti->possibleSpells[sel].toSpell();
-			vti->spells[s->level-1].push_back(s->id);
-			vti->possibleSpells -= s->id;
-		}
-		vti->possibleSpells.clear();
-		if(vti->getOwner() != PlayerColor::NEUTRAL)
-			getPlayer(vti->getOwner())->towns.push_back(vti);
-        logGlobal->debugStream() << "\t\tTown init spells3";
-
-	}
-}
-
-void CGameState::initMapObjects()
-{
-	logGlobal->debugStream() << "\tObject initialization";
-//	objCaller->preInit();
-	for(CGObjectInstance *obj : map->objects)
-	{
-		if(obj)
-			obj->initObj();
-	}
-	for(CGObjectInstance *obj : map->objects)
-	{
-		if(!obj)
-			continue;
-
-		switch (obj->ID)
-		{
-			case Obj::QUEST_GUARD:
-			case Obj::SEER_HUT:
-			{
-				auto q = static_cast<CGSeerHut*>(obj);
-				assert (q);
-				q->setObjToKill();
-			}
-		}
-	}
-	CGTeleport::postInit(); //pairing subterranean gates
-
-	map->calculateGuardingGreaturePositions(); //calculate once again when all the guards are placed and initialized
-}
-
-void CGameState::initVisitingAndGarrisonedHeroes()
-{
-	for(auto k=players.begin(); k!=players.end(); ++k)
-	{
-		if(k->first==PlayerColor::NEUTRAL)
-			continue;
-
-		//init visiting and garrisoned heroes
-		for(CGHeroInstance *h : k->second.heroes)
-		{
-			for(CGTownInstance *t : k->second.towns)
-			{
-				int3 vistile = t->pos; vistile.x--; //tile next to the entrance
-				if(vistile == h->pos || h->pos==t->pos)
-				{
-					t->setVisitingHero(h);
-					if(h->pos == t->pos) //visiting hero placed in the editor has same pos as the town - we need to correct it
-					{
-						map->removeBlockVisTiles(h);
-						h->pos.x -= 1;
-						map->addBlockVisTiles(h);
-					}
-					break;
-				}
-			}
-		}
-	}
-}
-
-BFieldType CGameState::battleGetBattlefieldType(int3 tile)
-{
-	if(tile==int3() && curB)
-		tile = curB->tile;
-	else if(tile==int3() && !curB)
-		return BFieldType::NONE;
-
-	const TerrainTile &t = map->getTile(tile);
-	//fight in mine -> subterranean
-	if(dynamic_cast<const CGMine *>(t.visitableObjects.front()))
-		return BFieldType::SUBTERRANEAN;
-
-	for(auto &obj : map->objects)
-	{
-		//look only for objects covering given tile
-		if( !obj || obj->pos.z != tile.z || !obj->coveringAt(tile.x, tile.y))
-			continue;
-
-		switch(obj->ID)
-		{
-		case Obj::CLOVER_FIELD:
-			return BFieldType::CLOVER_FIELD;
-		case Obj::CURSED_GROUND1: case Obj::CURSED_GROUND2:
-			return BFieldType::CURSED_GROUND;
-		case Obj::EVIL_FOG:
-			return BFieldType::EVIL_FOG;
-		case Obj::FAVORABLE_WINDS:
-			return BFieldType::FAVOURABLE_WINDS;
-		case Obj::FIERY_FIELDS:
-			return BFieldType::FIERY_FIELDS;
-		case Obj::HOLY_GROUNDS:
-			return BFieldType::HOLY_GROUND;
-		case Obj::LUCID_POOLS:
-			return BFieldType::LUCID_POOLS;
-		case Obj::MAGIC_CLOUDS:
-			return BFieldType::MAGIC_CLOUDS;
-		case Obj::MAGIC_PLAINS1: case Obj::MAGIC_PLAINS2:
-			return BFieldType::MAGIC_PLAINS;
-		case Obj::ROCKLANDS:
-			return BFieldType::ROCKLANDS;
-		}
-	}
-
-	if(!t.isWater() && t.isCoastal())
-		return BFieldType::SAND_SHORE;
-
-	switch(t.terType)
-	{
-	case ETerrainType::DIRT:
-		return BFieldType(rand.nextInt(3, 5));
-	case ETerrainType::SAND:
-		return BFieldType::SAND_MESAS; //TODO: coast support
-	case ETerrainType::GRASS:
-		return BFieldType(rand.nextInt(6, 7));
-	case ETerrainType::SNOW:
-		return BFieldType(rand.nextInt(10, 11));
-	case ETerrainType::SWAMP:
-		return BFieldType::SWAMP_TREES;
-	case ETerrainType::ROUGH:
-		return BFieldType::ROUGH;
-	case ETerrainType::SUBTERRANEAN:
-		return BFieldType::SUBTERRANEAN;
-	case ETerrainType::LAVA:
-		return BFieldType::LAVA;
-	case ETerrainType::WATER:
-		return BFieldType::SHIP;
-	case ETerrainType::ROCK:
-		return BFieldType::ROCKLANDS;
-	default:
-		return BFieldType::NONE;
-	}
-}
-
-UpgradeInfo CGameState::getUpgradeInfo(const CStackInstance &stack)
-{
-	UpgradeInfo ret;
-	const CCreature *base = stack.type;
-
-	const CGHeroInstance *h = stack.armyObj->ID == Obj::HERO ? static_cast<const CGHeroInstance*>(stack.armyObj) : nullptr;
-	const CGTownInstance *t = nullptr;
-
-	if(stack.armyObj->ID == Obj::TOWN)
-		t = static_cast<const CGTownInstance *>(stack.armyObj);
-	else if(h)
-	{	//hero specialty
-		TBonusListPtr lista = h->getBonuses(Selector::typeSubtype(Bonus::SPECIAL_UPGRADE, base->idNumber));
-		for(const Bonus *it : *lista)
-		{
-			auto nid = CreatureID(it->additionalInfo);
-			if (nid != base->idNumber) //in very specific case the upgrade is available by default (?)
-			{
-				ret.newID.push_back(nid);
-				ret.cost.push_back(VLC->creh->creatures[nid]->cost - base->cost);
-			}
-		}
-		t = h->visitedTown;
-	}
-	if(t)
-	{
-		for(const CGTownInstance::TCreaturesSet::value_type & dwelling : t->creatures)
-		{
-			if (vstd::contains(dwelling.second, base->idNumber)) //Dwelling with our creature
-			{
-				for(auto upgrID : dwelling.second)
-				{
-					if(vstd::contains(base->upgrades, upgrID)) //possible upgrade
-					{
-						ret.newID.push_back(upgrID);
-						ret.cost.push_back(VLC->creh->creatures[upgrID]->cost - base->cost);
-					}
-				}
-			}
-		}
-	}
-
-	//hero is visiting Hill Fort
-	if(h && map->getTile(h->visitablePos()).visitableObjects.front()->ID == Obj::HILL_FORT)
-	{
-		static const int costModifiers[] = {0, 25, 50, 75, 100}; //we get cheaper upgrades depending on level
-		const int costModifier = costModifiers[std::min<int>(std::max((int)base->level - 1, 0), ARRAY_COUNT(costModifiers) - 1)];
-
-		for(auto nid : base->upgrades)
-		{
-			ret.newID.push_back(nid);
-			ret.cost.push_back((VLC->creh->creatures[nid]->cost - base->cost) * costModifier / 100);
-		}
-	}
-
-	if(ret.newID.size())
-		ret.oldID = base->idNumber;
-
-	for (Res::ResourceSet &cost : ret.cost)
-		cost.positive(); //upgrade cost can't be negative, ignore missing resources
-
-	return ret;
-}
-
-PlayerRelations::PlayerRelations CGameState::getPlayerRelations( PlayerColor color1, PlayerColor color2 )
-{
-	if ( color1 == color2 )
-		return PlayerRelations::SAME_PLAYER;
-	if(color1 == PlayerColor::NEUTRAL || color2 == PlayerColor::NEUTRAL) //neutral player has no friends
-		return PlayerRelations::ENEMIES;
-
-	const TeamState * ts = getPlayerTeam(color1);
-	if (ts && vstd::contains(ts->players, color2))
-		return PlayerRelations::ALLIES;
-	return PlayerRelations::ENEMIES;
-}
-
-void CGameState::getNeighbours(const TerrainTile &srct, int3 tile, std::vector<int3> &vec, const boost::logic::tribool &onLand, bool limitCoastSailing)
-{
-	static const int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0),
-					int3(1,1,0),int3(-1,1,0),int3(1,-1,0),int3(-1,-1,0) };
-
-	//vec.reserve(8); //optimization
-	for (auto & dir : dirs)
-	{
-		const int3 hlp = tile + dir;
-		if(!map->isInTheMap(hlp))
-			continue;
-
-		const TerrainTile &hlpt = map->getTile(hlp);
-
-// 		//we cannot visit things from blocked tiles
-// 		if(srct.blocked && !srct.visitable && hlpt.visitable && srct.blockingObjects.front()->ID != HEROI_TYPE)
-// 		{
-// 			continue;
-// 		}
-
-        if(srct.terType == ETerrainType::WATER && limitCoastSailing && hlpt.terType == ETerrainType::WATER && dir.x && dir.y) //diagonal move through water
-		{
-			int3 hlp1 = tile,
-				hlp2 = tile;
-			hlp1.x += dir.x;
-			hlp2.y += dir.y;
-
-            if(map->getTile(hlp1).terType != ETerrainType::WATER || map->getTile(hlp2).terType != ETerrainType::WATER)
-				continue;
-		}
-
-        if((indeterminate(onLand)  ||  onLand == (hlpt.terType!=ETerrainType::WATER) )
-            && hlpt.terType != ETerrainType::ROCK)
-		{
-			vec.push_back(hlp);
-		}
-	}
-}
-
-int CGameState::getMovementCost(const CGHeroInstance *h, const int3 &src, const int3 &dest, bool flying, int remainingMovePoints, bool checkLast)
-{
-	if(src == dest) //same tile
-		return 0;
-
-	TerrainTile &s = map->getTile(src),
-		&d = map->getTile(dest);
-
-	//get basic cost
-	int ret = h->getTileCost(d,s);
-
-	if(d.blocked && flying)
-	{
-		bool freeFlying = h->getBonusesCount(Selector::typeSubtype(Bonus::FLYING_MOVEMENT, 1)) > 0;
-
-		if(!freeFlying)
-		{
-			ret *= 1.4; //40% penalty for movement over blocked tile
-		}
-	}
-    else if (d.terType == ETerrainType::WATER)
-	{
-		if(h->boat && s.hasFavourableWinds() && d.hasFavourableWinds()) //Favourable Winds
-			ret *= 0.666;
-		else if (!h->boat && h->getBonusesCount(Selector::typeSubtype(Bonus::WATER_WALKING, 1)) > 0)
-			ret *= 1.4; //40% penalty for water walking
-	}
-
-	if(src.x != dest.x  &&  src.y != dest.y) //it's diagonal move
-	{
-		int old = ret;
-		ret *= 1.414213;
-		//diagonal move costs too much but normal move is possible - allow diagonal move for remaining move points
-		if(ret > remainingMovePoints  &&  remainingMovePoints >= old)
-		{
-			return remainingMovePoints;
-		}
-	}
-
-
-	int left = remainingMovePoints-ret;
-	if(checkLast  &&  left > 0  &&  remainingMovePoints-ret < 250) //it might be the last tile - if no further move possible we take all move points
-	{
-		std::vector<int3> vec;
-		vec.reserve(8); //optimization
-        getNeighbours(d, dest, vec, s.terType != ETerrainType::WATER, true);
-		for(auto & elem : vec)
-		{
-			int fcost = getMovementCost(h,dest, elem, flying, left, false);
-			if(fcost <= left)
-			{
-				return ret;
-			}
-		}
-		ret = remainingMovePoints;
-	}
-	return ret;
-}
-
-void CGameState::apply(CPack *pack)
-{
-	ui16 typ = typeList.getTypeID(pack);
-	applierGs->apps[typ]->applyOnGS(this,pack);
-}
-
-void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int3 src, int movement)
-{
-	CPathfinder pathfinder(out, this, hero);
-	pathfinder.calculatePaths(src, movement);
-}
-
-/**
- * Tells if the tile is guarded by a monster as well as the position
- * of the monster that will attack on it.
- *
- * @return int3(-1, -1, -1) if the tile is unguarded, or the position of
- * the monster guarding the tile.
- */
-std::vector<CGObjectInstance*> CGameState::guardingCreatures (int3 pos) const
-{
-	std::vector<CGObjectInstance*> guards;
-	const int3 originalPos = pos;
-	if (!map->isInTheMap(pos))
-		return guards;
-
-	const TerrainTile &posTile = map->getTile(pos);
-	if (posTile.visitable)
-	{
-		for (CGObjectInstance* obj : posTile.visitableObjects)
-		{
-			if(obj->blockVisit)
-			{
-				if (obj->ID == Obj::MONSTER) // Monster
-					guards.push_back(obj);
-			}
-		}
-	}
-	pos -= int3(1, 1, 0); // Start with top left.
-	for (int dx = 0; dx < 3; dx++)
-	{
-		for (int dy = 0; dy < 3; dy++)
-		{
-			if (map->isInTheMap(pos))
-			{
-				const auto & tile = map->getTile(pos);
-                if (tile.visitable && (tile.isWater() == posTile.isWater()))
-				{
-					for (CGObjectInstance* obj : tile.visitableObjects)
-					{
-						if (obj->ID == Obj::MONSTER  &&  map->checkForVisitableDir(pos, &map->getTile(originalPos), originalPos)) // Monster being able to attack investigated tile
-						{
-							guards.push_back(obj);
-						}
-					}
-				}
-			}
-
-			pos.y++;
-		}
-		pos.y -= 3;
-		pos.x++;
-	}
-	return guards;
-
-}
-
-int3 CGameState::guardingCreaturePosition (int3 pos) const
-{
-	return gs->map->guardingCreaturePositions[pos.x][pos.y][pos.z];
-}
-
-bool CGameState::isVisible(int3 pos, PlayerColor player)
-{
-	if(player == PlayerColor::NEUTRAL)
-		return false;
-	return getPlayerTeam(player)->fogOfWarMap[pos.x][pos.y][pos.z];
-}
-
-bool CGameState::isVisible( const CGObjectInstance *obj, boost::optional<PlayerColor> player )
-{
-	if(!player)
-		return true;
-
-	if(*player == PlayerColor::NEUTRAL) //-> TODO ??? needed?
-		return false;
-	//object is visible when at least one blocked tile is visible
-	for(int fy=0; fy < obj->getHeight(); ++fy)
-	{
-		for(int fx=0; fx < obj->getWidth(); ++fx)
-		{
-			int3 pos = obj->pos + int3(-fx, -fy, 0);
-
-			if ( map->isInTheMap(pos) &&
-				 obj->coveringAt(pos.x, pos.y) &&
-				 isVisible(pos, *player))
-				return true;
-		}
-	}
-	return false;
-}
-
-bool CGameState::checkForVisitableDir(const int3 & src, const int3 & dst) const
-{
-	const TerrainTile * pom = &map->getTile(dst);
-	return map->checkForVisitableDir(src, pom, dst);
-}
-
-EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(PlayerColor player) const
-{
-	const std::string & messageWonSelf = VLC->generaltexth->allTexts[659];
-	const std::string & messageWonOther = VLC->generaltexth->allTexts[5];
-	const std::string & messageLostSelf = VLC->generaltexth->allTexts[7];
-	const std::string & messageLostOther = VLC->generaltexth->allTexts[8];
-
-	auto evaluateEvent = [=](const EventCondition & condition)
-	{
-		return this->checkForVictory(player, condition);
-	};
-
-	const PlayerState *p = CGameInfoCallback::getPlayer(player);
-
-	//cheater or tester, but has entered the code...
-	if (p->enteredWinningCheatCode)
-		return EVictoryLossCheckResult::victory(messageWonSelf, messageWonOther);
-
-	if (p->enteredLosingCheatCode)
-		return EVictoryLossCheckResult::defeat(messageLostSelf, messageLostOther);
-
-	for (const TriggeredEvent & event : map->triggeredEvents)
-	{
-		if ((event.trigger.test(evaluateEvent)))
-		{
-			if (event.effect.type == EventEffect::VICTORY)
-				return EVictoryLossCheckResult::victory(event.onFulfill, event.effect.toOtherMessage);
-
-			if (event.effect.type == EventEffect::DEFEAT)
-				return EVictoryLossCheckResult::defeat(event.onFulfill, event.effect.toOtherMessage);
-		}
-	}
-
-	if (checkForStandardLoss(player))
-	{
-		return EVictoryLossCheckResult::defeat(messageLostSelf, messageLostOther);
-	}
-	return EVictoryLossCheckResult();
-}
-
-bool CGameState::checkForVictory( PlayerColor player, const EventCondition & condition ) const
-{
-	const PlayerState *p = CGameInfoCallback::getPlayer(player);
-	switch (condition.condition)
-	{
-		case EventCondition::STANDARD_WIN:
-		{
-			return player == checkForStandardWin();
-		}
-		case EventCondition::HAVE_ARTIFACT: //check if any hero has winning artifact
-		{
-			for(auto & elem : p->heroes)
-				if(elem->hasArt(condition.objectType))
-					return true;
-			return false;
-		}
-		case EventCondition::HAVE_CREATURES:
-		{
-			//check if in players armies there is enough creatures
-			int total = 0; //creature counter
-			for(size_t i = 0; i < map->objects.size(); i++)
-			{
-				const CArmedInstance *ai = nullptr;
-				if(map->objects[i]
-					&& map->objects[i]->tempOwner == player //object controlled by player
-					&&  (ai = dynamic_cast<const CArmedInstance*>(map->objects[i].get()))) //contains army
-				{
-					for(auto & elem : ai->Slots()) //iterate through army
-						if(elem.second->type->idNumber == condition.objectType) //it's searched creature
-							total += elem.second->count;
-				}
-			}
-			return total >= condition.value;
-		}
-		case EventCondition::HAVE_RESOURCES:
-		{
-			return p->resources[condition.objectType] >= condition.value;
-		}
-		case EventCondition::HAVE_BUILDING:
-		{
-			if (condition.object) // specific town
-			{
-				const CGTownInstance *t = static_cast<const CGTownInstance *>(condition.object);
-				return (t->tempOwner == player && t->hasBuilt(BuildingID(condition.objectType)));
-			}
-			else // any town
-			{
-				for (const CGTownInstance * t : p->towns)
-				{
-					if (t->hasBuilt(BuildingID(condition.objectType)))
-						return true;
-				}
-				return false;
-			}
-		}
-		case EventCondition::DESTROY:
-		{
-			if (condition.object) // mode A - destroy specific object of this type
-			{
-				if (auto hero = dynamic_cast<const CGHeroInstance*>(condition.object))
-					return boost::range::find(gs->map->heroesOnMap, hero) == gs->map->heroesOnMap.end();
-				else
-					return getObj(condition.object->id) == nullptr;
-			}
-			else
-			{
-				for(auto & elem : map->objects) // mode B - destroy all objects of this type
-				{
-					if(elem && elem->ID == condition.objectType)
-						return false;
-				}
-				return true;
-			}
-		}
-		case EventCondition::CONTROL:
-		{
-			// list of players that need to control object to fulfull condition
-			// NOTE: cgameinfocallback specified explicitly in order to get const version
-			auto & team = CGameInfoCallback::getPlayerTeam(player)->players;
-
-			if (condition.object) // mode A - flag one specific object, like town
-			{
-				return team.count(condition.object->tempOwner) != 0;
-			}
-			else
-			{
-				for(auto & elem : map->objects) // mode B - flag all objects of this type
-				{
-					 //check not flagged objs
-					if ( elem && elem->ID == condition.objectType && team.count(elem->tempOwner) == 0 )
-						return false;
-				}
-				return true;
-			}
-		}
-		case EventCondition::TRANSPORT:
-		{
-			const CGTownInstance *t = static_cast<const CGTownInstance *>(condition.object);
-			if((t->visitingHero && t->visitingHero->hasArt(condition.objectType))
-				|| (t->garrisonHero && t->garrisonHero->hasArt(condition.objectType)))
-			{
-				return true;
-			}
-			return false;
-		}
-		case EventCondition::DAYS_PASSED:
-		{
-			return gs->day > condition.value;
-		}
-		case EventCondition::IS_HUMAN:
-		{
-			return p->human ? condition.value == 1 : condition.value == 0;
-		}
-		case EventCondition::DAYS_WITHOUT_TOWN:
-		{
-			if (p->daysWithoutCastle)
-				return p->daysWithoutCastle.get() >= condition.value;
-			else
-				return false;
-		}
-		case EventCondition::CONST_VALUE:
-		{
-			return condition.value; // just convert to bool
-		}
-	}
-	assert(0);
-	return false;
-}
-
-PlayerColor CGameState::checkForStandardWin() const
-{
-	//std victory condition is:
-	//all enemies lost
-	PlayerColor supposedWinner = PlayerColor::NEUTRAL;
-	TeamID winnerTeam = TeamID::NO_TEAM;
-	for(auto & elem : players)
-	{
-		if(elem.second.status == EPlayerStatus::INGAME && elem.first < PlayerColor::PLAYER_LIMIT)
-		{
-			if(supposedWinner == PlayerColor::NEUTRAL)
-			{
-				//first player remaining ingame - candidate for victory
-				supposedWinner = elem.second.color;
-				winnerTeam = elem.second.team;
-			}
-			else if(winnerTeam != elem.second.team)
-			{
-				//current candidate has enemy remaining in game -> no vicotry
-				return PlayerColor::NEUTRAL;
-			}
-		}
-	}
-
-	return supposedWinner;
-}
-
-bool CGameState::checkForStandardLoss( PlayerColor player ) const
-{
-	//std loss condition is: player lost all towns and heroes
-	const PlayerState &p = *CGameInfoCallback::getPlayer(player);
-	return !p.heroes.size() && !p.towns.size();
-}
-
-struct statsHLP
-{
-	typedef std::pair< PlayerColor, si64 > TStat;
-	//converts [<player's color, value>] to vec[place] -> platers
-	static std::vector< std::vector< PlayerColor > > getRank( std::vector<TStat> stats )
-	{
-		std::sort(stats.begin(), stats.end(), statsHLP());
-
-		//put first element
-		std::vector< std::vector<PlayerColor> > ret;
-		std::vector<PlayerColor> tmp;
-		tmp.push_back( stats[0].first );
-		ret.push_back( tmp );
-
-		//the rest of elements
-		for(int g=1; g<stats.size(); ++g)
-		{
-			if(stats[g].second == stats[g-1].second)
-			{
-				(ret.end()-1)->push_back( stats[g].first );
-			}
-			else
-			{
-				//create next occupied rank
-				std::vector<PlayerColor> tmp;
-				tmp.push_back(stats[g].first);
-				ret.push_back(tmp);
-			}
-		}
-
-		return ret;
-	}
-
-	bool operator()(const TStat & a, const TStat & b) const
-	{
-		return a.second > b.second;
-	}
-
-	static const CGHeroInstance * findBestHero(CGameState * gs, PlayerColor color)
-	{
-		std::vector<ConstTransitivePtr<CGHeroInstance> > &h = gs->players[color].heroes;
-		if(!h.size())
-			return nullptr;
-		//best hero will be that with highest exp
-		int best = 0;
-		for(int b=1; b<h.size(); ++b)
-		{
-			if(h[b]->exp > h[best]->exp)
-			{
-				best = b;
-			}
-		}
-		return h[best];
-	}
-
-	//calculates total number of artifacts that belong to given player
-	static int getNumberOfArts(const PlayerState * ps)
-	{
-		int ret = 0;
-		for(auto h : ps->heroes)
-		{
-			ret += h->artifactsInBackpack.size() + h->artifactsWorn.size();
-		}
-		return ret;
-	}
-
-	// get total strength of player army
-	static si64 getArmyStrength(const PlayerState * ps)
-	{
-		si64 str = 0;
-
-		for(auto h : ps->heroes)
-		{
-			if(!h->inTownGarrison)		//original h3 behavior
-				str += h->getArmyStrength();
-		}
-		return str;
-	}
-};
-
-void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
-{
-	auto playerInactive = [&](PlayerColor color)
-	{
-		return color == PlayerColor::NEUTRAL || players.at(color).status != EPlayerStatus::INGAME;
-	};
-
-#define FILL_FIELD(FIELD, VAL_GETTER) \
-	{ \
-		std::vector< std::pair< PlayerColor, si64 > > stats; \
-		for(auto g = players.begin(); g != players.end(); ++g) \
-		{ \
-			if(playerInactive(g->second.color)) \
-				continue; \
-			std::pair< PlayerColor, si64 > stat; \
-			stat.first = g->second.color; \
-			stat.second = VAL_GETTER; \
-			stats.push_back(stat); \
-		} \
-		tgi.FIELD = statsHLP::getRank(stats); \
-	}
-
-	for(auto & elem : players)
-	{
-		if(!playerInactive(elem.second.color))
-			tgi.playerColors.push_back(elem.second.color);
-	}
-
-	if(level >= 1) //num of towns & num of heroes
-	{
-		//num of towns
-		FILL_FIELD(numOfTowns, g->second.towns.size())
-		//num of heroes
-		FILL_FIELD(numOfHeroes, g->second.heroes.size())
-		//best hero's portrait
-		for(auto g = players.cbegin(); g != players.cend(); ++g)
-		{
-			if(playerInactive(g->second.color))
-				continue;
-			const CGHeroInstance * best = statsHLP::findBestHero(this, g->second.color);
-			InfoAboutHero iah;
-			iah.initFromHero(best, level >= 8);
-			iah.army.clear();
-			tgi.colorToBestHero[g->second.color] = iah;
-		}
-	}
-	if(level >= 2) //gold
-	{
-		FILL_FIELD(gold, g->second.resources[Res::GOLD])
-	}
-	if(level >= 2) //wood & ore
-	{
-		FILL_FIELD(woodOre, g->second.resources[Res::WOOD] + g->second.resources[Res::ORE])
-	}
-	if(level >= 3) //mercury, sulfur, crystal, gems
-	{
-		FILL_FIELD(mercSulfCrystGems, g->second.resources[Res::MERCURY] + g->second.resources[Res::SULFUR] + g->second.resources[Res::CRYSTAL] + g->second.resources[Res::GEMS])
-	}
-	if(level >= 4) //obelisks found
-	{
-		FILL_FIELD(obelisks, CGObelisk::visited[gs->getPlayerTeam(g->second.color)->id])
-	}
-	if(level >= 5) //artifacts
-	{
-		FILL_FIELD(artifacts, statsHLP::getNumberOfArts(&g->second))
-	}
-	if(level >= 6) //army strength
-	{
-		FILL_FIELD(army, statsHLP::getArmyStrength(&g->second))
-	}
-	if(level >= 7) //income
-	{
-		//TODO:obtainPlayersStats - income
-	}
-	if(level >= 8) //best hero's stats
-	{
-		//already set in  lvl 1 handling
-	}
-	if(level >= 9) //personality
-	{
-		for(auto g = players.cbegin(); g != players.cend(); ++g)
-		{
-			if(playerInactive(g->second.color)) //do nothing for neutral player
-				continue;
-			if(g->second.human)
-			{
-                tgi.personality[g->second.color] = EAiTactic::NONE;
-			}
-			else //AI
-			{
-                tgi.personality[g->second.color] = map->players[g->second.color.getNum()].aiTactic;
-			}
-
-		}
-	}
-	if(level >= 10) //best creature
-	{
-		//best creatures belonging to player (highest AI value)
-		for(auto g = players.cbegin(); g != players.cend(); ++g)
-		{
-			if(playerInactive(g->second.color)) //do nothing for neutral player
-				continue;
-			int bestCre = -1; //best creature's ID
-			for(auto & elem : g->second.heroes)
-			{
-				for(auto it = elem->Slots().begin(); it != elem->Slots().end(); ++it)
-				{
-					int toCmp = it->second->type->idNumber; //ID of creature we should compare with the best one
-					if(bestCre == -1 || VLC->creh->creatures[bestCre]->AIValue < VLC->creh->creatures[toCmp]->AIValue)
-					{
-						bestCre = toCmp;
-					}
-				}
-			}
-			tgi.bestCreature[g->second.color] = bestCre;
-		}
-	}
-
-#undef FILL_FIELD
-}
-
-std::map<ui32, ConstTransitivePtr<CGHeroInstance> > CGameState::unusedHeroesFromPool()
-{
-	std::map<ui32, ConstTransitivePtr<CGHeroInstance> > pool = hpool.heroesPool;
-	for ( auto i = players.cbegin() ; i != players.cend();i++)
-		for(auto j = i->second.availableHeroes.cbegin(); j != i->second.availableHeroes.cend(); j++)
-			if(*j)
-				pool.erase((**j).subID);
-
-	return pool;
-}
-
-void CGameState::buildBonusSystemTree()
-{
-	buildGlobalTeamPlayerTree();
-	attachArmedObjects();
-
-	for(CGTownInstance *t : map->towns)
-	{
-		t->deserializationFix();
-	}
-	// CStackInstance <-> CCreature, CStackInstance <-> CArmedInstance, CArtifactInstance <-> CArtifact
-	// are provided on initializing / deserializing
-}
-
-void CGameState::deserializationFix()
-{
-	buildGlobalTeamPlayerTree();
-	attachArmedObjects();
-}
-
-void CGameState::buildGlobalTeamPlayerTree()
-{
-	for(auto k=teams.begin(); k!=teams.end(); ++k)
-	{
-		TeamState *t = &k->second;
-		t->attachTo(&globalEffects);
-
-		for(PlayerColor teamMember : k->second.players)
-		{
-			PlayerState *p = getPlayer(teamMember);
-			assert(p);
-			p->attachTo(t);
-		}
-	}
-}
-
-void CGameState::attachArmedObjects()
-{
-	for(CGObjectInstance *obj : map->objects)
-	{
-		if(CArmedInstance *armed = dynamic_cast<CArmedInstance*>(obj))
-			armed->whatShouldBeAttached()->attachTo(armed->whereShouldBeAttached(this));
-	}
-}
-
-void CGameState::giveHeroArtifact(CGHeroInstance *h, ArtifactID aid)
-{
-	 CArtifact * const artifact = VLC->arth->artifacts[aid]; //pointer to constant object
-	 CArtifactInstance *ai = CArtifactInstance::createNewArtifactInstance(artifact);
-	 map->addNewArtifactInstance(ai);
-	 ai->putAt(ArtifactLocation(h, ai->firstAvailableSlot(h)));
-}
-
-std::set<HeroTypeID> CGameState::getUnusedAllowedHeroes(bool alsoIncludeNotAllowed /*= false*/) const
-{
-	std::set<HeroTypeID> ret;
-	for(int i = 0; i < map->allowedHeroes.size(); i++)
-		if(map->allowedHeroes[i] || alsoIncludeNotAllowed)
-			ret.insert(HeroTypeID(i));
-
-	for(auto hero : map->heroesOnMap)  //heroes instances initialization
-	{
-		if(hero->type)
-			ret -= hero->type->ID;
-		else
-			ret -= HeroTypeID(hero->subID);
-	}
-
-	for(auto obj : map->objects) //prisons
-		if(obj && obj->ID == Obj::PRISON)
-			ret -= HeroTypeID(obj->subID);
-
-	return ret;
-}
-
-std::vector<CGameState::CampaignHeroReplacement> CGameState::generateCampaignHeroesToReplace(CrossoverHeroesList & crossoverHeroes)
-{
-	std::vector<CampaignHeroReplacement> campaignHeroReplacements;
-
-	//selecting heroes by type
-	for(auto obj : map->objects)
-	{
-		if(obj && obj->ID == Obj::HERO_PLACEHOLDER)
-		{
-			auto heroPlaceholder = dynamic_cast<CGHeroPlaceholder *>(obj.get());
-			if(heroPlaceholder->subID != 0xFF) //select by type
-			{
-				auto it = range::find_if(crossoverHeroes.heroesFromAnyPreviousScenarios, [heroPlaceholder](CGHeroInstance * hero)
-				{
-					return hero->subID == heroPlaceholder->subID;
-				});
-
-				if(it != crossoverHeroes.heroesFromAnyPreviousScenarios.end())
-				{
-					auto hero = *it;
-					crossoverHeroes.removeHeroFromBothLists(hero);
-                    campaignHeroReplacements.push_back(CampaignHeroReplacement(CMemorySerializer::deepCopy(*hero).release(), heroPlaceholder->id));
-				}
-			}
-		}
-	}
-
-	//selecting heroes by power
-	range::sort(crossoverHeroes.heroesFromPreviousScenario, [](const CGHeroInstance * a, const CGHeroInstance * b)
-	{
-		return a->getHeroStrength() > b->getHeroStrength();
-	}); //sort, descending strength
-
-	// sort hero placeholders descending power
-	std::vector<CGHeroPlaceholder *> heroPlaceholders;
-	for(auto obj : map->objects)
-	{
-		if(obj && obj->ID == Obj::HERO_PLACEHOLDER)
-		{
-			auto heroPlaceholder = dynamic_cast<CGHeroPlaceholder *>(obj.get());
-			if(heroPlaceholder->subID == 0xFF) //select by power
-			{
-				heroPlaceholders.push_back(heroPlaceholder);
-			}
-		}
-	}
-	range::sort(heroPlaceholders, [](const CGHeroPlaceholder * a, const CGHeroPlaceholder * b)
-	{
-		return a->power > b->power;
-	});
-
-	for(int i = 0; i < heroPlaceholders.size(); ++i)
-	{
-		auto heroPlaceholder = heroPlaceholders[i];
-		if(crossoverHeroes.heroesFromPreviousScenario.size() > i)
-		{
-            auto hero = crossoverHeroes.heroesFromPreviousScenario[i];
-            campaignHeroReplacements.push_back(CampaignHeroReplacement(CMemorySerializer::deepCopy(*hero).release(), heroPlaceholder->id));
-		}
-	}
-
-	return campaignHeroReplacements;
-}
-
-void CGameState::replaceHeroesPlaceholders(const std::vector<CGameState::CampaignHeroReplacement> & campaignHeroReplacements)
-{
-	for(auto campaignHeroReplacement : campaignHeroReplacements)
-	{
-		auto heroPlaceholder = dynamic_cast<CGHeroPlaceholder*>(getObjInstance(campaignHeroReplacement.heroPlaceholderId));
-
-		CGHeroInstance *heroToPlace = campaignHeroReplacement.hero;
-		heroToPlace->id = campaignHeroReplacement.heroPlaceholderId;
-		heroToPlace->tempOwner = heroPlaceholder->tempOwner;
-		heroToPlace->pos = heroPlaceholder->pos;
-		heroToPlace->type = VLC->heroh->heroes[heroToPlace->subID];
-
-		for(auto &&i : heroToPlace->stacks)
-			i.second->type = VLC->creh->creatures[i.second->getCreatureID()];
-
-		auto fixArtifact = [&](CArtifactInstance * art)
-		{
-			art->artType = VLC->arth->artifacts[art->artType->id];
-			gs->map->artInstances.push_back(art);
-			art->id = ArtifactInstanceID(gs->map->artInstances.size() - 1);
-		};
-
-		for(auto &&i : heroToPlace->artifactsWorn)
-			fixArtifact(i.second.artifact);
-		for(auto &&i : heroToPlace->artifactsInBackpack)
-			fixArtifact(i.artifact);
-
-		map->heroesOnMap.push_back(heroToPlace);
-		map->objects[heroToPlace->id.getNum()] = heroToPlace;
-		map->addBlockVisTiles(heroToPlace);
-
-		scenarioOps->campState->getCurrentScenario().placedCrossoverHeroes.push_back(heroToPlace);
-	}
-}
-
-bool CGameState::isUsedHero(HeroTypeID hid) const
-{
-	return getUsedHero(hid);
-}
-
-CGHeroInstance * CGameState::getUsedHero(HeroTypeID hid) const
-{
-	for(auto hero : map->heroesOnMap)  //heroes instances initialization
-	{
-		if(hero->subID == hid.getNum())
-		{
-			return hero;
-		}
-	}
-
-	for(auto obj : map->objects) //prisons
-	{
-		if(obj && obj->ID == Obj::PRISON && obj->subID == hid.getNum())
-		{
-			return dynamic_cast<CGHeroInstance *>(obj.get());
-		}
-	}
-
-	return nullptr;
-}
-
-CGPathNode::CGPathNode()
-:coord(-1,-1,-1)
-{
-	accessible = NOT_SET;
-	land = 0;
-	moveRemains = 0;
-	turns = 255;
-	theNodeBefore = nullptr;
-}
-
-bool CGPathNode::reachable() const
-{
-	return turns < 255;
-}
-
-bool CPathsInfo::getPath( const int3 &dst, CGPath &out )
-{
-	assert(isValid);
-
-	out.nodes.clear();
-	const CGPathNode *curnode = &nodes[dst.x][dst.y][dst.z];
-	if(!curnode->theNodeBefore)
-		return false;
-
-
-	while(curnode)
-	{
-		CGPathNode cpn = *curnode;
-		curnode = curnode->theNodeBefore;
-		out.nodes.push_back(cpn);
-	}
-	return true;
-}
-
-CPathsInfo::CPathsInfo( const int3 &Sizes )
-:sizes(Sizes)
-{
-	hero = nullptr;
-	nodes = new CGPathNode**[sizes.x];
-	for(int i = 0; i < sizes.x; i++)
-	{
-		nodes[i] = new CGPathNode*[sizes.y];
-		for (int j = 0; j < sizes.y; j++)
-		{
-			nodes[i][j] = new CGPathNode[sizes.z];
-		}
-	}
-}
-
-CPathsInfo::~CPathsInfo()
-{
-	for(int i = 0; i < sizes.x; i++)
-	{
-		for (int j = 0; j < sizes.y; j++)
-		{
-			delete [] nodes[i][j];
-		}
-		delete [] nodes[i];
-	}
-	delete [] nodes;
-}
-
-int3 CGPath::startPos() const
-{
-	return nodes[nodes.size()-1].coord;
-}
-
-int3 CGPath::endPos() const
-{
-	return nodes[0].coord;
-}
-
-void CGPath::convert( ui8 mode )
-{
-	if(mode==0)
-	{
-		for(auto & elem : nodes)
-		{
-			elem.coord = CGHeroInstance::convertPosition(elem.coord,true);
-		}
-	}
-}
-
-PlayerState::PlayerState()
- : color(-1), currentSelection(0xffffffff), enteredWinningCheatCode(0),
-   enteredLosingCheatCode(0), status(EPlayerStatus::INGAME)
-{
-	setNodeType(PLAYER);
-}
-
-std::string PlayerState::nodeName() const
-{
-	return "Player " + (color.getNum() < VLC->generaltexth->capColors.size() ? VLC->generaltexth->capColors[color.getNum()] : boost::lexical_cast<std::string>(color));
-}
-
-InfoAboutArmy::InfoAboutArmy():
-    owner(PlayerColor::NEUTRAL)
-{}
-
-InfoAboutArmy::InfoAboutArmy(const CArmedInstance *Army, bool detailed)
-{
-	initFromArmy(Army, detailed);
-}
-
-void InfoAboutArmy::initFromArmy(const CArmedInstance *Army, bool detailed)
-{
-	army = ArmyDescriptor(Army, detailed);
-	owner = Army->tempOwner;
-	name = Army->getHoverText();
-}
-
-void InfoAboutHero::assign(const InfoAboutHero & iah)
-{
-	InfoAboutArmy::operator = (iah);
-
-	details = (iah.details ? new Details(*iah.details) : nullptr);
-	hclass = iah.hclass;
-	portrait = iah.portrait;
-}
-
-InfoAboutHero::InfoAboutHero():
-    details(nullptr),
-    hclass(nullptr),
-    portrait(-1)
-{}
-
-InfoAboutHero::InfoAboutHero(const InfoAboutHero & iah):
-    InfoAboutArmy()
-{
-	assign(iah);
-}
-
-InfoAboutHero::InfoAboutHero(const CGHeroInstance *h, bool detailed)
-	: details(nullptr),
-	hclass(nullptr),
-	portrait(-1)
-{
-	initFromHero(h, detailed);
-}
-
-InfoAboutHero::~InfoAboutHero()
-{
-	delete details;
-}
-
-InfoAboutHero & InfoAboutHero::operator=(const InfoAboutHero & iah)
-{
-	assign(iah);
-	return *this;
-}
-
-void InfoAboutHero::initFromHero(const CGHeroInstance *h, bool detailed)
-{
-	if(!h)
-		return;
-
-	initFromArmy(h, detailed);
-
-	hclass = h->type->heroClass;
-	name = h->name;
-	portrait = h->portrait;
-
-	if(detailed)
-	{
-		//include details about hero
-		details = new Details;
-		details->luck = h->LuckVal();
-		details->morale = h->MoraleVal();
-		details->mana = h->mana;
-		details->primskills.resize(GameConstants::PRIMARY_SKILLS);
-
-		for (int i = 0; i < GameConstants::PRIMARY_SKILLS ; i++)
-		{
-			details->primskills[i] = h->getPrimSkillLevel(static_cast<PrimarySkill::PrimarySkill>(i));
-		}
-	}
-}
-
-InfoAboutTown::InfoAboutTown():
-    details(nullptr),
-    tType(nullptr),
-    built(0),
-    fortLevel(0)
-{
-
-}
-
-InfoAboutTown::InfoAboutTown(const CGTownInstance *t, bool detailed)
-{
-	initFromTown(t, detailed);
-}
-
-InfoAboutTown::~InfoAboutTown()
-{
-	delete details;
-}
-
-void InfoAboutTown::initFromTown(const CGTownInstance *t, bool detailed)
-{
-	initFromArmy(t, detailed);
-	army = ArmyDescriptor(t->getUpperArmy(), detailed);
-	built = t->builded;
-	fortLevel = t->fortLevel();
-	name = t->name;
-	tType = t->town;
-
-	if(detailed)
-	{
-		//include details about hero
-		details = new Details;
-		TResources income = t->dailyIncome();
-		details->goldIncome = income[Res::GOLD];
-		details->customRes = t->hasBuilt(BuildingID::RESOURCE_SILO);
-		details->hallLevel = t->hallLevel();
-		details->garrisonedHero = t->garrisonHero;
-	}
-}
-
-ArmyDescriptor::ArmyDescriptor(const CArmedInstance *army, bool detailed)
-    : isDetailed(detailed)
-{
-	for(auto & elem : army->Slots())
-	{
-		if(detailed)
-			(*this)[elem.first] = *elem.second;
-		else
-			(*this)[elem.first] = CStackBasicDescriptor(elem.second->type, elem.second->getQuantityID());
-	}
-}
-
-ArmyDescriptor::ArmyDescriptor()
-    : isDetailed(false)
-{
-
-}
-
-int ArmyDescriptor::getStrength() const
-{
-	ui64 ret = 0;
-	if(isDetailed)
-	{
-		for(auto & elem : *this)
-			ret += elem.second.type->AIValue * elem.second.count;
-	}
-	else
-	{
-		for(auto & elem : *this)
-			ret += elem.second.type->AIValue * CCreature::estimateCreatureCount(elem.second.count);
-	}
-	return ret;
-}
-
-DuelParameters::SideSettings::StackSettings::StackSettings()
-	: type(CreatureID::NONE), count(0)
-{
-}
-
-DuelParameters::SideSettings::StackSettings::StackSettings(CreatureID Type, si32 Count)
-	: type(Type), count(Count)
-{
-}
-
-DuelParameters::SideSettings::SideSettings()
-{
-	heroId = -1;
-}
-
-DuelParameters::DuelParameters():
-    terType(ETerrainType::DIRT),
-    bfieldType(BFieldType::ROCKLANDS)
-{
-}
-
-DuelParameters DuelParameters::fromJSON(const std::string &fname)
-{
-	DuelParameters ret;
-
-	const JsonNode duelData(ResourceID("DATA/" + fname, EResType::TEXT));
-	ret.terType = ETerrainType((int)duelData["terType"].Float());
-	ret.bfieldType = BFieldType((int)duelData["bfieldType"].Float());
-	for(const JsonNode &n : duelData["sides"].Vector())
-	{
-		SideSettings &ss = ret.sides[(int)n["side"].Float()];
-		int i = 0;
-		for(const JsonNode &stackNode : n["army"].Vector())
-		{
-			ss.stacks[i].type = CreatureID((si32)stackNode.Vector()[0].Float());
-			ss.stacks[i].count = stackNode.Vector()[1].Float();
-			i++;
-		}
-
-		if(n["heroid"].getType() == JsonNode::DATA_FLOAT)
-			ss.heroId = n["heroid"].Float();
-		else
-			ss.heroId = -1;
-
-		for(const JsonNode &entry : n["heroPrimSkills"].Vector())
-			ss.heroPrimSkills.push_back(entry.Float());
-
-		for(const JsonNode &skillNode : n["heroSecSkills"].Vector())
-		{
-			std::pair<si32, si8> secSkill;
-			secSkill.first = skillNode.Vector()[0].Float();
-			secSkill.second = skillNode.Vector()[1].Float();
-			ss.heroSecSkills.push_back(secSkill);
-		}
-
-		assert(ss.heroPrimSkills.empty() || ss.heroPrimSkills.size() == GameConstants::PRIMARY_SKILLS);
-
-		if(ss.heroId != -1)
-		{
-			const JsonNode & spells = n["spells"];
-			if(spells.getType() == JsonNode::DATA_STRING  &&  spells.String() == "all")
-			{
-				for(auto spell : VLC->spellh->objects)
-					if(spell->id <= SpellID::SUMMON_AIR_ELEMENTAL)
-						ss.spells.insert(spell->id);
-			}
-			else
-				for(const JsonNode &spell : n["spells"].Vector())
-					ss.spells.insert(SpellID(spell.Float()));
-		}
-	}
-
-	for(const JsonNode &n : duelData["obstacles"].Vector())
-	{
-		auto oi = make_shared<CObstacleInstance>();
-		if(n.getType() == JsonNode::DATA_VECTOR)
-		{
-			oi->ID = n.Vector()[0].Float();
-			oi->pos = n.Vector()[1].Float();
-		}
-		else
-		{
-			assert(n.getType() == JsonNode::DATA_FLOAT);
-			oi->ID = 21;
-			oi->pos = n.Float();
-		}
-		oi->uniqueID = ret.obstacles.size();
-		ret.obstacles.push_back(oi);
-	}
-
-	for(const JsonNode &n : duelData["creatures"].Vector())
-	{
-		CusomCreature cc;
-		cc.id = n["id"].Float();
-
-#define retrieve(name)								\
-	if(n[ #name ].getType() == JsonNode::DATA_FLOAT)\
-	cc.name = n[ #name ].Float();			\
-	else											\
-	cc.name = -1;
-
-		retrieve(attack);
-		retrieve(defense);
-		retrieve(HP);
-		retrieve(dmg);
-		retrieve(shoots);
-		retrieve(speed);
-		ret.creatures.push_back(cc);
-	}
-
-	return ret;
-}
-
-TeamState::TeamState()
-{
-	setNodeType(TEAM);
-}
-
-void CPathfinder::initializeGraph()
-{
-	CGPathNode ***graph = out.nodes;
-	for(size_t i=0; i < out.sizes.x; ++i)
-	{
-		for(size_t j=0; j < out.sizes.y; ++j)
-		{
-			for(size_t k=0; k < out.sizes.z; ++k)
-			{
-				curPos = int3(i,j,k);
-				const TerrainTile *tinfo = &gs->map->getTile(int3(i, j, k));
-				CGPathNode &node = graph[i][j][k];
-
-				node.accessible = evaluateAccessibility(tinfo);
-				node.turns = 0xff;
-				node.moveRemains = 0;
-				node.coord.x = i;
-				node.coord.y = j;
-				node.coord.z = k;
-                node.land = tinfo->terType != ETerrainType::WATER;
-				node.theNodeBefore = nullptr;
-			}
-		}
-	}
-}
-
-void CPathfinder::calculatePaths(int3 src /*= int3(-1,-1,-1)*/, int movement /*= -1*/)
-{
-	assert(hero);
-	assert(hero == getHero(hero->id));
-	if(src.x < 0)
-		src = hero->getPosition(false);
-	if(movement < 0)
-		movement = hero->movement;
-	bool flying = hero->hasBonusOfType(Bonus::FLYING_MOVEMENT);
-	int maxMovePointsLand = hero->maxMovePoints(true);
-	int maxMovePointsWater = hero->maxMovePoints(false);
-
-	auto maxMovePoints = [&](CGPathNode *cp) -> int
-	{
-		return cp->land ? maxMovePointsLand : maxMovePointsWater;
-	};
-
-	out.hero = hero;
-	out.hpos = src;
-
-	if(!gs->map->isInTheMap(src)/* || !gs->map->isInTheMap(dest)*/) //check input
-	{
-        logGlobal->errorStream() << "CGameState::calculatePaths: Hero outside the gs->map? How dare you...";
-		return;
-	}
-
-	initializeGraph();
-
-
-	//initial tile - set cost on 0 and add to the queue
-	CGPathNode &initialNode = *getNode(src);
-	initialNode.turns = 0;
-	initialNode.moveRemains = movement;
-	mq.push_back(&initialNode);
-
-	std::vector<int3> neighbours;
-	neighbours.reserve(16);
-	while(!mq.empty())
-	{
-		cp = mq.front();
-		mq.pop_front();
-
-		const int3 sourceGuardPosition = gs->map->guardingCreaturePositions[cp->coord.x][cp->coord.y][cp->coord.z];
-		bool guardedSource = (sourceGuardPosition != int3(-1, -1, -1) && cp->coord != src);
-		ct = &gs->map->getTile(cp->coord);
-
-		int movement = cp->moveRemains, turn = cp->turns;
-		if(!movement)
-		{
-			movement = maxMovePoints(cp);
-			turn++;
-		}
-
-
-		//add accessible neighbouring nodes to the queue
-		neighbours.clear();
-
-		//handling subterranean gate => it's exit is the only neighbour
-        bool subterraneanEntry = (ct->topVisitableId() == Obj::SUBTERRANEAN_GATE && useSubterraneanGates);
-		if(subterraneanEntry)
-		{
-			//try finding the exit gate
-			if(const CGObjectInstance *outGate = getObj(CGTeleport::getMatchingGate(ct->visitableObjects.back()->id), false))
-			{
-				const int3 outPos = outGate->visitablePos();
-				//gs->getNeighbours(*getTile(outPos), outPos, neighbours, boost::logic::indeterminate, !cp->land);
-				neighbours.push_back(outPos);
-			}
-			else
-			{
-				//gate with no exit (blocked) -> do nothing with this node
-				continue;
-			}
-		}
-
-		gs->getNeighbours(*ct, cp->coord, neighbours, boost::logic::indeterminate, !cp->land);
-
-		for(auto & neighbour : neighbours)
-		{
-			const int3 &n = neighbour; //current neighbor
-			dp = getNode(n);
-			dt = &gs->map->getTile(n);
-            destTopVisObjID = dt->topVisitableId();
-
-			useEmbarkCost = 0; //0 - usual movement; 1 - embark; 2 - disembark
-
-			int turnAtNextTile = turn;
-
-
-			const bool destIsGuardian = sourceGuardPosition == n;
-
-			if(!goodForLandSeaTransition())
-				continue;
-
-			if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED )
-				continue;
-
-			//special case -> hero embarked a boat standing on a guarded tile -> we must allow to move away from that tile
-            if(cp->accessible == CGPathNode::VISITABLE && guardedSource && cp->theNodeBefore->land && ct->topVisitableId() == Obj::BOAT)
-				guardedSource = false;
-
-			int cost = gs->getMovementCost(hero, cp->coord, dp->coord, flying, movement);
-
-			//special case -> moving from src Subterranean gate to dest gate -> it's free
-			if(subterraneanEntry && destTopVisObjID == Obj::SUBTERRANEAN_GATE && cp->coord.z != dp->coord.z)
-				cost = 0;
-
-			int remains = movement - cost;
-
-			if(useEmbarkCost)
-			{
-				remains = hero->movementPointsAfterEmbark(movement, cost, useEmbarkCost - 1);
-				cost = movement - remains;
-			}
-
-			if(remains < 0)
-			{
-				//occurs rarely, when hero with low movepoints tries to leave the road
-				turnAtNextTile++;
-				int moveAtNextTile = maxMovePoints(cp);
-				cost = gs->getMovementCost(hero, cp->coord, dp->coord, flying, moveAtNextTile); //cost must be updated, movement points changed :(
-				remains = moveAtNextTile - cost;
-			}
-
-			if((dp->turns==0xff		//we haven't been here before
-				|| dp->turns > turnAtNextTile
-				|| (dp->turns >= turnAtNextTile  &&  dp->moveRemains < remains)) //this route is faster
-				&& (!guardedSource || destIsGuardian)) // Can step into tile of guard
-			{
-
-				assert(dp != cp->theNodeBefore); //two tiles can't point to each other
-				dp->moveRemains = remains;
-				dp->turns = turnAtNextTile;
-				dp->theNodeBefore = cp;
-
-				const bool guardedDst = gs->map->guardingCreaturePositions[dp->coord.x][dp->coord.y][dp->coord.z].valid()
-										&& dp->accessible == CGPathNode::BLOCKVIS;
-
-				if (dp->accessible == CGPathNode::ACCESSIBLE
-					|| (useEmbarkCost && allowEmbarkAndDisembark)
-					|| destTopVisObjID == Obj::SUBTERRANEAN_GATE
-					|| (guardedDst && !guardedSource)) // Can step into a hostile tile once.
-				{
-					mq.push_back(dp);
-				}
-			}
-		} //neighbours loop
-	} //queue loop
-
-	out.isValid = true;
-}
-
-CGPathNode *CPathfinder::getNode(const int3 &coord)
-{
-	return &out.nodes[coord.x][coord.y][coord.z];
-}
-
-bool CPathfinder::canMoveBetween(const int3 &a, const int3 &b) const
-{
-	return gs->checkForVisitableDir(a, b) && gs->checkForVisitableDir(b, a);
-}
-
-CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const TerrainTile *tinfo) const
-{
-	CGPathNode::EAccessibility ret = (tinfo->blocked ? CGPathNode::BLOCKED : CGPathNode::ACCESSIBLE);
-
-
-    if(tinfo->terType == ETerrainType::ROCK || !FoW[curPos.x][curPos.y][curPos.z])
-		return CGPathNode::BLOCKED;
-
-	if(tinfo->visitable)
-	{
-		if(tinfo->visitableObjects.front()->ID == Obj::SANCTUARY && tinfo->visitableObjects.back()->ID == Obj::HERO && tinfo->visitableObjects.back()->tempOwner != hero->tempOwner) //non-owned hero stands on Sanctuary
-		{
-			return CGPathNode::BLOCKED;
-		}
-		else
-		{
-			for(const CGObjectInstance *obj : tinfo->visitableObjects)
-			{
-				if(obj->passableFor(hero->tempOwner)) //special object instance specific passableness flag - overwrites other accessibility flags
-				{
-					ret = CGPathNode::ACCESSIBLE;
-				}
-				else if(obj->blockVisit)
-				{
-					return CGPathNode::BLOCKVIS;
-				}
-				else if(obj->ID != Obj::EVENT) //pathfinder should ignore placed events
-				{
-					ret =  CGPathNode::VISITABLE;
-				}
-			}
-		}
-	}
-	else if (gs->map->guardingCreaturePositions[curPos.x][curPos.y][curPos.z].valid()
-		&& !tinfo->blocked)
-	{
-		// Monster close by; blocked visit for battle.
-		return CGPathNode::BLOCKVIS;
-	}
-
-	return ret;
-}
-
-bool CPathfinder::goodForLandSeaTransition()
-{
-	if(cp->land != dp->land) //hero can traverse land<->sea only in special circumstances
-	{
-		if(cp->land) //from land to sea -> embark or assault hero on boat
-		{
-			if(dp->accessible == CGPathNode::ACCESSIBLE || destTopVisObjID < 0) //cannot enter empty water tile from land -> it has to be visitable
-				return false;
-			if(destTopVisObjID != Obj::HERO && destTopVisObjID != Obj::BOAT) //only boat or hero can be accessed from land
-				return false;
-			if(destTopVisObjID == Obj::BOAT)
-				useEmbarkCost = 1;
-		}
-		else //disembark
-		{
-			//can disembark only on coastal tiles
-			if(!dt->isCoastal())
-				return false;
-
-			//tile must be accessible -> exception: unblocked blockvis tiles -> clear but guarded by nearby monster coast
-			if( (dp->accessible != CGPathNode::ACCESSIBLE && (dp->accessible != CGPathNode::BLOCKVIS || dt->blocked))
-				|| dt->visitable)  //TODO: passableness problem -> town says it's passable (thus accessible) but we obviously can't disembark onto town gate
-				return false;;
-
-			useEmbarkCost = 2;
-		}
-	}
-
-	return true;
-}
-
-CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero) : CGameInfoCallback(_gs, boost::optional<PlayerColor>()), out(_out), hero(_hero), FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap)
-{
-	useSubterraneanGates = true;
-	allowEmbarkAndDisembark = true;
-}
-
-EVictoryLossCheckResult::EVictoryLossCheckResult() :
-	intValue(0)
-{
-}
-
-EVictoryLossCheckResult::EVictoryLossCheckResult(si32 intValue, std::string toSelf, std::string toOthers) :
-	messageToSelf(toSelf),
-	messageToOthers(toOthers),
-	intValue(intValue)
-{
-}
-
-bool EVictoryLossCheckResult::operator==(EVictoryLossCheckResult const & other) const
-{
-	return intValue == other.intValue;
-}
-
-bool EVictoryLossCheckResult::operator!=(EVictoryLossCheckResult const & other) const
-{
-	return intValue != other.intValue;
-}
-
-bool EVictoryLossCheckResult::victory() const
-{
-	return intValue == VICTORY;
-}
-
-bool EVictoryLossCheckResult::loss() const
-{
-	return intValue == DEFEAT;
-}
-
-EVictoryLossCheckResult EVictoryLossCheckResult::invert()
-{
-	return EVictoryLossCheckResult(-intValue, messageToOthers, messageToSelf);
-}
-
-EVictoryLossCheckResult EVictoryLossCheckResult::victory(std::string toSelf, std::string toOthers)
-{
-	return EVictoryLossCheckResult(VICTORY, toSelf, toOthers);
-}
-
-EVictoryLossCheckResult EVictoryLossCheckResult::defeat(std::string toSelf, std::string toOthers)
-{
-	return EVictoryLossCheckResult(DEFEAT, toSelf, toOthers);
-}
-
-std::ostream & operator<<(std::ostream & os, const EVictoryLossCheckResult & victoryLossCheckResult)
-{
-	os << victoryLossCheckResult.messageToSelf;
-	return os;
-}
-
-CRandomGenerator & CGameState::getRandomGenerator()
-{
-	return rand;
-}
+#include "StdInc.h"
+#include "CGameState.h"
+
+#include "mapping/CCampaignHandler.h"
+#include "mapObjects/CObjectClassesHandler.h"
+#include "CArtHandler.h"
+#include "CBuildingHandler.h"
+#include "CGeneralTextHandler.h"
+#include "CTownHandler.h"
+#include "CSpellHandler.h"
+#include "CHeroHandler.h"
+#include "mapObjects/CObjectHandler.h"
+#include "CCreatureHandler.h"
+#include "CModHandler.h"
+#include "VCMI_Lib.h"
+#include "Connection.h"
+#include "mapping/CMap.h"
+#include "mapping/CMapService.h"
+#include "StartInfo.h"
+#include "NetPacks.h"
+#include "registerTypes/RegisterTypes.h"
+#include "mapping/CMapInfo.h"
+#include "BattleState.h"
+#include "JsonNode.h"
+#include "filesystem/Filesystem.h"
+#include "GameConstants.h"
+#include "rmg/CMapGenerator.h"
+#include "CStopWatch.h"
+#include "mapping/CMapEditManager.h"
+
+class CGObjectInstance;
+
+#ifdef min
+#undef min
+#endif
+#ifdef max
+#undef max
+#endif
+
+/*
+ * CGameState.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
+ *
+ */
+
+template <typename T> class CApplyOnGS;
+
+class CBaseForGSApply
+{
+public:
+	virtual void applyOnGS(CGameState *gs, void *pack) const =0;
+	virtual ~CBaseForGSApply(){};
+	template<typename U> static CBaseForGSApply *getApplier(const U * t=nullptr)
+	{
+		return new CApplyOnGS<U>;
+	}
+};
+
+template <typename T> class CApplyOnGS : public CBaseForGSApply
+{
+public:
+	void applyOnGS(CGameState *gs, void *pack) const
+	{
+		T *ptr = static_cast<T*>(pack);
+
+		boost::unique_lock<boost::shared_mutex> lock(*gs->mx);
+		ptr->applyGs(gs);
+	}
+};
+
+static CApplier<CBaseForGSApply> *applierGs = nullptr;
+
+// class IObjectCaller
+// {
+// public:
+// 	virtual ~IObjectCaller(){};
+// 	virtual void preInit()=0;
+// 	virtual void postInit()=0;
+// };
+//
+// template <typename T>
+// class CObjectCaller : public IObjectCaller
+// {
+// public:
+// 	void preInit()
+// 	{
+// 		//T::preInit();
+// 	}
+// 	void postInit()
+// 	{
+// 		//T::postInit();
+// 	}
+// };
+
+// class CObjectCallersHandler
+// {
+// public:
+// 	std::vector<IObjectCaller*> apps;
+//
+// 	template<typename T> void registerType(const T * t=nullptr)
+// 	{
+// 		apps.push_back(new CObjectCaller<T>);
+// 	}
+//
+// 	CObjectCallersHandler()
+// 	{
+// 		registerTypesMapObjects(*this);
+// 	}
+//
+// 	~CObjectCallersHandler()
+// 	{
+// 		for (auto & elem : apps)
+// 			delete elem;
+// 	}
+//
+// 	void preInit()
+// 	{
+// // 		for (size_t i = 0; i < apps.size(); i++)
+// // 			apps[i]->preInit();
+// 	}
+//
+// 	void postInit()
+// 	{
+// 	//for (size_t i = 0; i < apps.size(); i++)
+// 	//apps[i]->postInit();
+// 	}
+// } *objCaller = nullptr;
+
+void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst) const
+{
+	int type = txt.first, ser = txt.second;
+
+	if(type == ART_NAMES)
+	{
+		dst = VLC->arth->artifacts[ser]->Name();
+	}
+	else if(type == CRE_PL_NAMES)
+	{
+		dst = VLC->creh->creatures[ser]->namePl;
+	}
+	else if(type == MINE_NAMES)
+	{
+		dst = VLC->generaltexth->mines[ser].first;
+	}
+	else if(type == MINE_EVNTS)
+	{
+		dst = VLC->generaltexth->mines[ser].second;
+	}
+	else if(type == SPELL_NAME)
+	{
+		dst = SpellID(ser).toSpell()->name;
+	}
+	else if(type == CRE_SING_NAMES)
+	{
+		dst = VLC->creh->creatures[ser]->nameSing;
+	}
+	else if(type == ART_DESCR)
+	{
+		dst = VLC->arth->artifacts[ser]->Description();
+	}
+	else if (type == ART_EVNTS)
+	{
+		dst = VLC->arth->artifacts[ser]->EventText();
+	}
+	else if (type == OBJ_NAMES)
+	{
+		dst = VLC->objtypeh->getObjectName(ser);
+	}
+	else
+	{
+		std::vector<std::string> *vec;
+		switch(type)
+		{
+		case GENERAL_TXT:
+			vec = &VLC->generaltexth->allTexts;
+			break;
+		case XTRAINFO_TXT:
+			vec = &VLC->generaltexth->xtrainfo;
+			break;
+		case RES_NAMES:
+			vec = &VLC->generaltexth->restypes;
+			break;
+		case ARRAY_TXT:
+			vec = &VLC->generaltexth->arraytxt;
+			break;
+		case CREGENS:
+			vec = &VLC->generaltexth->creGens;
+			break;
+		case CREGENS4:
+			vec = &VLC->generaltexth->creGens4;
+			break;
+		case ADVOB_TXT:
+			vec = &VLC->generaltexth->advobtxt;
+			break;
+		case SEC_SKILL_NAME:
+			vec = &VLC->generaltexth->skillName;
+			break;
+		case COLOR:
+			vec = &VLC->generaltexth->capColors;
+			break;
+		default:
+            logGlobal->errorStream() << "Failed string substitution because type is " << type;
+			dst = "#@#";
+			return;
+		}
+		if(vec->size() <= ser)
+		{
+            logGlobal->errorStream() << "Failed string substitution with type " << type << " because index " << ser << " is out of bounds!";
+			dst = "#!#";
+		}
+		else
+			dst = (*vec)[ser];
+	}
+}
+
+DLL_LINKAGE void MetaString::toString(std::string &dst) const
+{
+	size_t exSt = 0, loSt = 0, nums = 0;
+	dst.clear();
+
+	for(auto & elem : message)
+	{//TEXACT_STRING, TLOCAL_STRING, TNUMBER, TREPLACE_ESTRING, TREPLACE_LSTRING, TREPLACE_NUMBER
+		switch(elem)
+		{
+		case TEXACT_STRING:
+			dst += exactStrings[exSt++];
+			break;
+		case TLOCAL_STRING:
+			{
+				std::string hlp;
+				getLocalString(localStrings[loSt++], hlp);
+				dst += hlp;
+			}
+			break;
+		case TNUMBER:
+			dst += boost::lexical_cast<std::string>(numbers[nums++]);
+			break;
+		case TREPLACE_ESTRING:
+			boost::replace_first(dst, "%s", exactStrings[exSt++]);
+			break;
+		case TREPLACE_LSTRING:
+			{
+				std::string hlp;
+				getLocalString(localStrings[loSt++], hlp);
+				boost::replace_first(dst, "%s", hlp);
+			}
+			break;
+		case TREPLACE_NUMBER:
+			boost::replace_first(dst, "%d", boost::lexical_cast<std::string>(numbers[nums++]));
+			break;
+		case TREPLACE_PLUSNUMBER:
+			boost::replace_first(dst, "%+d", '+' + boost::lexical_cast<std::string>(numbers[nums++]));
+			break;
+		default:
+            logGlobal->errorStream() << "MetaString processing error!";
+			break;
+		}
+	}
+}
+
+DLL_LINKAGE std::string MetaString::toString() const
+{
+	std::string ret;
+	toString(ret);
+	return ret;
+}
+
+DLL_LINKAGE std::string MetaString::buildList () const
+///used to handle loot from creature bank
+{
+
+	size_t exSt = 0, loSt = 0, nums = 0;
+	std::string lista;
+	for (int i = 0; i < message.size(); ++i)
+	{
+		if (i > 0 && (message[i] == TEXACT_STRING || message[i] == TLOCAL_STRING))
+		{
+			if (exSt == exactStrings.size() - 1)
+				lista += VLC->generaltexth->allTexts[141]; //" and "
+			else
+				lista += ", ";
+		}
+		switch (message[i])
+		{
+			case TEXACT_STRING:
+				lista += exactStrings[exSt++];
+				break;
+			case TLOCAL_STRING:
+			{
+				std::string hlp;
+				getLocalString (localStrings[loSt++], hlp);
+				lista += hlp;
+			}
+				break;
+			case TNUMBER:
+				lista += boost::lexical_cast<std::string>(numbers[nums++]);
+				break;
+			case TREPLACE_ESTRING:
+				lista.replace (lista.find("%s"), 2, exactStrings[exSt++]);
+				break;
+			case TREPLACE_LSTRING:
+			{
+				std::string hlp;
+				getLocalString (localStrings[loSt++], hlp);
+				lista.replace (lista.find("%s"), 2, hlp);
+			}
+				break;
+			case TREPLACE_NUMBER:
+				lista.replace (lista.find("%d"), 2, boost::lexical_cast<std::string>(numbers[nums++]));
+				break;
+			default:
+                logGlobal->errorStream() << "MetaString processing error!";
+		}
+
+	}
+	return lista;
+}
+
+
+void  MetaString::addCreReplacement(CreatureID id, TQuantity count) //adds sing or plural name;
+{
+	if (!count)
+		addReplacement (CRE_PL_NAMES, id); //no creatures - just empty name (eg. defeat Angels)
+	else if (count == 1)
+		addReplacement (CRE_SING_NAMES, id);
+	else
+		addReplacement (CRE_PL_NAMES, id);
+}
+
+void MetaString::addReplacement(const CStackBasicDescriptor &stack)
+{
+	assert(stack.type); //valid type
+	addCreReplacement(stack.type->idNumber, stack.count);
+}
+
+static CGObjectInstance * createObject(Obj id, int subid, int3 pos, PlayerColor owner)
+{
+	CGObjectInstance * nobj;
+	switch(id)
+	{
+	case Obj::HERO:
+		{
+			auto handler = VLC->objtypeh->getHandlerFor(id, VLC->heroh->heroes[subid]->heroClass->id);
+			nobj = handler->create(handler->getTemplates().front());
+			break;
+		}
+	case Obj::TOWN:
+		nobj = new CGTownInstance;
+		break;
+	default: //rest of objects
+		nobj = new CGObjectInstance;
+		break;
+	}
+	nobj->ID = id;
+	nobj->subID = subid;
+	nobj->pos = pos;
+	nobj->tempOwner = owner;
+	if (id != Obj::HERO)
+		nobj->appearance = VLC->objtypeh->getHandlerFor(id, subid)->getTemplates().front();
+
+	return nobj;
+}
+
+CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, PlayerColor player, const CTown *town,
+	std::map<ui32, ConstTransitivePtr<CGHeroInstance> > &available, CRandomGenerator & rand, const CHeroClass * bannedClass /*= nullptr*/) const
+{
+	CGHeroInstance *ret = nullptr;
+
+	if(player>=PlayerColor::PLAYER_LIMIT)
+	{
+        logGlobal->errorStream() << "Cannot pick hero for " << town->faction->index << ". Wrong owner!";
+		return nullptr;
+	}
+
+	std::vector<CGHeroInstance *> pool;
+
+	if(native)
+	{
+		for(auto & elem : available)
+		{
+			if(pavailable.find(elem.first)->second & 1<<player.getNum()
+				&& elem.second->type->heroClass->faction == town->faction->index)
+			{
+				pool.push_back(elem.second); //get all available heroes
+			}
+		}
+		if(!pool.size())
+		{
+            logGlobal->errorStream() << "Cannot pick native hero for " << player << ". Picking any...";
+			return pickHeroFor(false, player, town, available, rand);
+		}
+		else
+		{
+			ret = *RandomGeneratorUtil::nextItem(pool, rand);
+		}
+	}
+	else
+	{
+		int sum=0, r;
+
+		for(auto & elem : available)
+		{
+			if (pavailable.find(elem.first)->second & (1<<player.getNum()) &&    // hero is available
+			    ( !bannedClass || elem.second->type->heroClass != bannedClass) ) // and his class is not same as other hero
+			{
+				pool.push_back(elem.second);
+				sum += elem.second->type->heroClass->selectionProbability[town->faction->index]; //total weight
+			}
+		}
+		if(!pool.size() || sum == 0)
+		{
+            logGlobal->errorStream() << "There are no heroes available for player " << player<<"!";
+			return nullptr;
+		}
+
+		r = rand.nextInt(sum - 1);
+		for (auto & elem : pool)
+		{
+			r -= elem->type->heroClass->selectionProbability[town->faction->index];
+			if(r < 0)
+			{
+				ret = elem;
+				break;
+			}
+		}
+		if(!ret)
+			ret = pool.back();
+	}
+
+	available.erase(ret->subID);
+	return ret;
+}
+
+void CGameState::CrossoverHeroesList::addHeroToBothLists(CGHeroInstance * hero)
+{
+	heroesFromPreviousScenario.push_back(hero);
+	heroesFromAnyPreviousScenarios.push_back(hero);
+}
+
+void CGameState::CrossoverHeroesList::removeHeroFromBothLists(CGHeroInstance * hero)
+{
+	heroesFromPreviousScenario -= hero;
+	heroesFromAnyPreviousScenarios -= hero;
+}
+
+CGameState::CampaignHeroReplacement::CampaignHeroReplacement(CGHeroInstance * hero, ObjectInstanceID heroPlaceholderId) : hero(hero), heroPlaceholderId(heroPlaceholderId)
+{
+
+}
+
+int CGameState::pickNextHeroType(PlayerColor owner)
+{
+	const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(owner);
+	if(ps.hero >= 0 && !isUsedHero(HeroTypeID(ps.hero))) //we haven't used selected hero
+	{
+		return ps.hero;
+	}
+
+	return pickUnusedHeroTypeRandomly(owner);
+}
+
+int CGameState::pickUnusedHeroTypeRandomly(PlayerColor owner)
+{
+	//list of available heroes for this faction and others
+	std::vector<HeroTypeID> factionHeroes, otherHeroes;
+
+	const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(owner);
+	for(HeroTypeID hid : getUnusedAllowedHeroes())
+	{
+		if(VLC->heroh->heroes[hid.getNum()]->heroClass->faction == ps.castle)
+			factionHeroes.push_back(hid);
+		else
+			otherHeroes.push_back(hid);
+	}
+
+	// select random hero native to "our" faction
+	if(!factionHeroes.empty())
+	{
+		return RandomGeneratorUtil::nextItem(factionHeroes, rand)->getNum();
+	}
+
+	logGlobal->warnStream() << "Cannot find free hero of appropriate faction for player " << owner << " - trying to get first available...";
+	if(!otherHeroes.empty())
+	{
+		return RandomGeneratorUtil::nextItem(otherHeroes, rand)->getNum();
+	}
+
+	logGlobal->errorStream() << "No free allowed heroes!";
+	auto notAllowedHeroesButStillBetterThanCrash = getUnusedAllowedHeroes(true);
+	if(notAllowedHeroesButStillBetterThanCrash.size())
+		return notAllowedHeroesButStillBetterThanCrash.begin()->getNum();
+
+	logGlobal->errorStream() << "No free heroes at all!";
+	assert(0); //current code can't handle this situation
+	return -1; // no available heroes at all
+}
+
+std::pair<Obj,int> CGameState::pickObject (CGObjectInstance *obj)
+{
+	switch(obj->ID)
+	{
+	case Obj::RANDOM_ART:
+		return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(rand, CArtifact::ART_TREASURE | CArtifact::ART_MINOR | CArtifact::ART_MAJOR | CArtifact::ART_RELIC));
+	case Obj::RANDOM_TREASURE_ART:
+		return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(rand, CArtifact::ART_TREASURE));
+	case Obj::RANDOM_MINOR_ART:
+		return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MINOR));
+	case Obj::RANDOM_MAJOR_ART:
+		return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MAJOR));
+	case Obj::RANDOM_RELIC_ART:
+		return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(rand, CArtifact::ART_RELIC));
+	case Obj::RANDOM_HERO:
+		return std::make_pair(Obj::HERO, pickNextHeroType(obj->tempOwner));
+	case Obj::RANDOM_MONSTER:
+		return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand));
+	case Obj::RANDOM_MONSTER_L1:
+		return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 1));
+	case Obj::RANDOM_MONSTER_L2:
+		return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 2));
+	case Obj::RANDOM_MONSTER_L3:
+		return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 3));
+	case Obj::RANDOM_MONSTER_L4:
+		return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 4));
+	case Obj::RANDOM_RESOURCE:
+		return std::make_pair(Obj::RESOURCE,rand.nextInt(6)); //now it's OH3 style, use %8 for mithril
+	case Obj::RANDOM_TOWN:
+		{
+			PlayerColor align = PlayerColor((static_cast<CGTownInstance*>(obj))->alignment);
+			si32 f; // can be negative (for random)
+			if(align >= PlayerColor::PLAYER_LIMIT)//same as owner / random
+			{
+				if(obj->tempOwner >= PlayerColor::PLAYER_LIMIT)
+					f = -1; //random
+				else
+					f = scenarioOps->getIthPlayersSettings(obj->tempOwner).castle;
+			}
+			else
+			{
+				f = scenarioOps->getIthPlayersSettings(align).castle;
+			}
+			if(f<0)
+			{
+				do
+				{
+					f = rand.nextInt(VLC->townh->factions.size() - 1);
+				}
+				while (VLC->townh->factions[f]->town == nullptr); // find playable faction
+			}
+			return std::make_pair(Obj::TOWN,f);
+		}
+	case Obj::RANDOM_MONSTER_L5:
+		return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 5));
+	case Obj::RANDOM_MONSTER_L6:
+		return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 6));
+	case Obj::RANDOM_MONSTER_L7:
+		return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 7));
+	case Obj::RANDOM_DWELLING:
+	case Obj::RANDOM_DWELLING_LVL:
+	case Obj::RANDOM_DWELLING_FACTION:
+		{
+			CGDwelling * dwl = static_cast<CGDwelling*>(obj);
+			int faction;
+
+			//if castle alignment available
+			if (auto info = dynamic_cast<CCreGenAsCastleInfo*>(dwl->info))
+			{
+				faction = rand.nextInt(VLC->townh->factions.size() - 1);
+				if (info->asCastle)
+				{
+					for(auto & elem : map->objects)
+					{
+						if(!elem)
+							continue;
+
+						if(elem->ID==Obj::RANDOM_TOWN
+							&& dynamic_cast<CGTownInstance*>(elem.get())->identifier == info->identifier)
+						{
+							randomizeObject(elem); //we have to randomize the castle first
+							faction = elem->subID;
+							break;
+						}
+						else if(elem->ID==Obj::TOWN
+							&& dynamic_cast<CGTownInstance*>(elem.get())->identifier == info->identifier)
+						{
+							faction = elem->subID;
+							break;
+						}
+					}
+				}
+				else
+				{
+					while(!(info->castles[0]&(1<<faction)))
+					{
+						if((faction>7) && (info->castles[1]&(1<<(faction-8))))
+							break;
+						faction = rand.nextInt(GameConstants::F_NUMBER - 1);
+					}
+				}
+			}
+			else // castle alignment fixed
+				faction = obj->subID;
+
+			int level;
+
+			//if level set to range
+			if (auto info = dynamic_cast<CCreGenLeveledInfo*>(dwl->info))
+			{
+				level = rand.nextInt(info->minLevel, info->maxLevel);
+			}
+			else // fixed level
+			{
+				level = obj->subID;
+			}
+
+			delete dwl->info;
+			dwl->info = nullptr;
+
+			std::pair<Obj, int> result(Obj::NO_OBJ, -1);
+			CreatureID cid = VLC->townh->factions[faction]->town->creatures[level][0];
+
+			//NOTE: this will pick last dwelling with this creature (Mantis #900)
+			//check for block map equality is better but more complex solution
+			auto testID = [&](Obj primaryID) -> void
+			{
+				auto dwellingIDs = VLC->objtypeh->knownSubObjects(primaryID);
+				for (si32 entry : dwellingIDs)
+				{
+					auto handler = dynamic_cast<const CDwellingInstanceConstructor*>(VLC->objtypeh->getHandlerFor(primaryID, entry).get());
+
+					if (handler->producesCreature(VLC->creh->creatures[cid]))
+						result = std::make_pair(primaryID, entry);
+				}
+			};
+
+			testID(Obj::CREATURE_GENERATOR1);
+			if (result.first == Obj::NO_OBJ)
+				testID(Obj::CREATURE_GENERATOR4);
+
+			if (result.first == Obj::NO_OBJ)
+			{
+				logGlobal->errorStream() << "Error: failed to find dwelling for "<< VLC->townh->factions[faction]->name << " of level " << int(level);
+				result = std::make_pair(Obj::CREATURE_GENERATOR1, *RandomGeneratorUtil::nextItem(VLC->objtypeh->knownSubObjects(Obj::CREATURE_GENERATOR1), rand));
+			}
+
+			return result;
+		}
+	}
+	return std::make_pair(Obj::NO_OBJ,-1);
+}
+
+void CGameState::randomizeObject(CGObjectInstance *cur)
+{
+	std::pair<Obj,int> ran = pickObject(cur);
+	if(ran.first == Obj::NO_OBJ || ran.second<0) //this is not a random object, or we couldn't find anything
+	{
+		if(cur->ID==Obj::TOWN)
+			cur->setType(cur->ID, cur->subID); // update def, if necessary
+		return;
+	}
+	else if(ran.first==Obj::HERO)//special code for hero
+	{
+		CGHeroInstance *h = dynamic_cast<CGHeroInstance *>(cur);
+		cur->setType(ran.first, ran.second);
+		map->heroesOnMap.push_back(h);
+		return;
+	}
+	else if(ran.first==Obj::TOWN)//special code for town
+	{
+		CGTownInstance *t = dynamic_cast<CGTownInstance*>(cur);
+		cur->setType(ran.first, ran.second);
+		map->towns.push_back(t);
+		return;
+	}
+	cur->setType(ran.first, ran.second);
+}
+
+int CGameState::getDate(Date::EDateType mode) const
+{
+	int temp;
+	switch (mode)
+	{
+	case Date::DAY:
+		return day;
+	case Date::DAY_OF_WEEK: //day of week
+		temp = (day)%7; // 1 - Monday, 7 - Sunday
+		return temp ? temp : 7;
+	case Date::WEEK:  //current week
+		temp = ((day-1)/7)+1;
+		if (!(temp%4))
+			return 4;
+		else
+			return (temp%4);
+	case Date::MONTH: //current month
+		return ((day-1)/28)+1;
+	case Date::DAY_OF_MONTH: //day of month
+		temp = (day)%28;
+		if (temp)
+			return temp;
+		else return 28;
+	}
+	return 0;
+}
+
+CGameState::CGameState()
+{
+	gs = this;
+	mx = new boost::shared_mutex();
+	applierGs = new CApplier<CBaseForGSApply>;
+	registerTypesClientPacks1(*applierGs);
+	registerTypesClientPacks2(*applierGs);
+	//objCaller = new CObjectCallersHandler;
+	globalEffects.setDescription("Global effects");
+	globalEffects.setNodeType(CBonusSystemNode::GLOBAL_EFFECTS);
+}
+
+CGameState::~CGameState()
+{
+	//delete mx;//TODO: crash on Linux (mutex must be unlocked before destruction)
+	map.dellNull();
+	curB.dellNull();
+	//delete scenarioOps; //TODO: fix for loading ind delete
+	//delete initialOpts;
+	delete applierGs;
+	//delete objCaller;
+
+	for(auto ptr : hpool.heroesPool) // clean hero pool
+		ptr.second.dellNull();
+}
+
+BattleInfo * CGameState::setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town)
+{
+	const TerrainTile &t = map->getTile(tile);
+	ETerrainType terrain = t.terType;
+	if(t.isCoastal() && !t.isWater())
+		terrain = ETerrainType::SAND;
+
+	BFieldType terType = battleGetBattlefieldType(tile);
+	if (heroes[0] && heroes[0]->boat && heroes[1] && heroes[1]->boat)
+		terType = BFieldType::SHIP_TO_SHIP;
+	return BattleInfo::setupBattle(tile, terrain, terType, armies, heroes, creatureBank, town);
+}
+
+void CGameState::init(StartInfo * si)
+{
+    logGlobal->infoStream() << "\tUsing random seed: "<< si->seedToBeUsed;
+	rand.setSeed(si->seedToBeUsed);
+	scenarioOps = CMemorySerializer::deepCopy(*si).release();
+	initialOpts = CMemorySerializer::deepCopy(*si).release();
+	si = nullptr;
+
+	switch(scenarioOps->mode)
+	{
+	case StartInfo::NEW_GAME:
+		initNewGame();
+		break;
+	case StartInfo::CAMPAIGN:
+		initCampaign();
+		break;
+	case StartInfo::DUEL:
+		initDuel();
+		return;
+	default:
+        logGlobal->errorStream() << "Wrong mode: " << (int)scenarioOps->mode;
+		return;
+	}
+	VLC->arth->initAllowedArtifactsList(map->allowedArtifact);
+    logGlobal->infoStream() << "Map loaded!";
+
+	checkMapChecksum();
+
+	day = 0;
+
+    logGlobal->debugStream() << "Initialization:";
+
+	initPlayerStates();
+	placeCampaignHeroes();
+	initGrailPosition();
+	initRandomFactionsForPlayers();
+	randomizeMapObjects();
+	placeStartingHeroes();
+	initStartingResources();
+	initHeroes();
+	initStartingBonus();
+	initTowns();
+	initMapObjects();
+	buildBonusSystemTree();
+	initVisitingAndGarrisonedHeroes();
+	initFogOfWar();
+
+    logGlobal->debugStream() << "\tChecking objectives";
+	map->checkForObjectives(); //needs to be run when all objects are properly placed
+
+	auto seedAfterInit = rand.nextInt();
+    logGlobal->infoStream() << "Seed after init is " << seedAfterInit << " (before was " << scenarioOps->seedToBeUsed << ")";
+	if(scenarioOps->seedPostInit > 0)
+	{
+		//RNG must be in the same state on all machines when initialization is done (otherwise we have desync)
+		assert(scenarioOps->seedPostInit == seedAfterInit);
+	}
+	else
+	{
+		scenarioOps->seedPostInit = seedAfterInit; //store the post init "seed"
+	}
+}
+
+void CGameState::initNewGame()
+{
+	if(scenarioOps->createRandomMap())
+	{
+		logGlobal->infoStream() << "Create random map.";
+		CStopWatch sw;
+
+		// Gen map
+		CMapGenerator mapGenerator(scenarioOps->mapGenOptions, scenarioOps->seedToBeUsed);
+		map = mapGenerator.generate().release();
+
+		// Update starting options
+		for(int i = 0; i < map->players.size(); ++i)
+		{
+			const auto & playerInfo = map->players[i];
+			if(playerInfo.canAnyonePlay())
+			{
+				PlayerSettings & playerSettings = scenarioOps->playerInfos[PlayerColor(i)];
+				playerSettings.compOnly = !playerInfo.canHumanPlay;
+				playerSettings.team = playerInfo.team;
+				playerSettings.castle = playerInfo.defaultCastle();
+				if(playerSettings.playerID == PlayerSettings::PLAYER_AI && playerSettings.name.empty())
+				{
+					playerSettings.name = VLC->generaltexth->allTexts[468];
+				}
+				playerSettings.color = PlayerColor(i);
+			}
+			else
+			{
+				scenarioOps->playerInfos.erase(PlayerColor(i));
+			}
+		}
+
+		logGlobal->infoStream() << boost::format("Generated random map in %i ms.") % sw.getDiff();
+	}
+	else
+	{
+		logGlobal->infoStream() << "Open map file: " << scenarioOps->mapname;
+		map = CMapService::loadMap(scenarioOps->mapname).release();
+	}
+}
+
+void CGameState::initCampaign()
+{
+	logGlobal->infoStream() << "Open campaign map file: " << scenarioOps->campState->currentMap;
+	auto campaign = scenarioOps->campState;
+	assert(vstd::contains(campaign->camp->mapPieces, *scenarioOps->campState->currentMap));
+
+	std::string scenarioName = scenarioOps->mapname.substr(0, scenarioOps->mapname.find('.'));
+	boost::to_lower(scenarioName);
+	scenarioName += ':' + boost::lexical_cast<std::string>(*campaign->currentMap);
+
+	std::string & mapContent = campaign->camp->mapPieces[*campaign->currentMap];
+	auto buffer = reinterpret_cast<const ui8 *>(mapContent.data());
+	map = CMapService::loadMap(buffer, mapContent.size(), scenarioName).release();
+}
+
+void CGameState::initDuel()
+{
+	DuelParameters dp;
+	try //CLoadFile likes throwing
+	{
+		if(boost::algorithm::ends_with(scenarioOps->mapname, ".json"))
+		{
+            logGlobal->infoStream() << "Loading duel settings from JSON file: " << scenarioOps->mapname;
+			dp = DuelParameters::fromJSON(scenarioOps->mapname);
+            logGlobal->infoStream() << "JSON file has been successfully read!";
+		}
+		else
+		{
+			CLoadFile lf(scenarioOps->mapname);
+			lf >> dp;
+		}
+	}
+	catch(...)
+	{
+        logGlobal->errorStream() << "Cannot load duel settings from " << scenarioOps->mapname;
+		throw;
+	}
+
+	const CArmedInstance *armies[2] = {nullptr};
+	const CGHeroInstance *heroes[2] = {nullptr};
+	CGTownInstance *town = nullptr;
+
+	for(int i = 0; i < 2; i++)
+	{
+		CArmedInstance *obj = nullptr;
+		if(dp.sides[i].heroId >= 0)
+		{
+			const DuelParameters::SideSettings &ss = dp.sides[i];
+			auto h = new CGHeroInstance();
+			armies[i] = heroes[i] = h;
+			obj = h;
+			h->subID = ss.heroId;
+			for(int i = 0; i < ss.heroPrimSkills.size(); i++)
+				h->pushPrimSkill(static_cast<PrimarySkill::PrimarySkill>(i), ss.heroPrimSkills[i]);
+
+			if(!ss.spells.empty())
+			{
+				h->putArtifact(ArtifactPosition::SPELLBOOK, CArtifactInstance::createNewArtifactInstance(0));
+				boost::copy(ss.spells, std::inserter(h->spells, h->spells.begin()));
+			}
+
+			for(auto &parka : ss.artifacts)
+			{
+				h->putArtifact(ArtifactPosition(parka.first), parka.second);
+			}
+
+			typedef const std::pair<si32, si8> &TSecSKill;
+			for(TSecSKill secSkill : ss.heroSecSkills)
+				h->setSecSkillLevel(SecondarySkill(secSkill.first), secSkill.second, 1);
+
+			h->initHero(HeroTypeID(h->subID));
+			obj->initObj();
+		}
+		else
+		{
+			auto c = new CGCreature();
+			armies[i] = obj = c;
+			//c->subID = 34;
+		}
+
+		obj->setOwner(PlayerColor(i));
+
+		for(int j = 0; j < ARRAY_COUNT(dp.sides[i].stacks); j++)
+		{
+			CreatureID cre = dp.sides[i].stacks[j].type;
+			TQuantity count = dp.sides[i].stacks[j].count;
+			if(count || obj->hasStackAtSlot(SlotID(j)))
+				obj->setCreature(SlotID(j), cre, count);
+		}
+
+		for(const DuelParameters::CusomCreature &cc : dp.creatures)
+		{
+			CCreature *c = VLC->creh->creatures[cc.id];
+			if(cc.attack >= 0)
+				c->getBonusLocalFirst(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK))->val = cc.attack;
+			if(cc.defense >= 0)
+				c->getBonusLocalFirst(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE))->val = cc.defense;
+			if(cc.speed >= 0)
+				c->getBonusLocalFirst(Selector::type(Bonus::STACKS_SPEED))->val = cc.speed;
+			if(cc.HP >= 0)
+				c->getBonusLocalFirst(Selector::type(Bonus::STACK_HEALTH))->val = cc.HP;
+			if(cc.dmg >= 0)
+			{
+				c->getBonusLocalFirst(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 1))->val = cc.dmg;
+				c->getBonusLocalFirst(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 2))->val = cc.dmg;
+			}
+			if(cc.shoots >= 0)
+				c->getBonusLocalFirst(Selector::type(Bonus::SHOTS))->val = cc.shoots;
+		}
+	}
+
+	curB = BattleInfo::setupBattle(int3(), dp.terType, dp.bfieldType, armies, heroes, false, town);
+	curB->obstacles = dp.obstacles;
+	curB->localInit();
+}
+
+void CGameState::checkMapChecksum()
+{
+	logGlobal->infoStream() << "\tOur checksum for the map: "<< map->checksum;
+	if(scenarioOps->mapfileChecksum)
+	{
+		logGlobal->infoStream() << "\tServer checksum for " << scenarioOps->mapname <<": "<< scenarioOps->mapfileChecksum;
+		if(map->checksum != scenarioOps->mapfileChecksum)
+		{
+			logGlobal->errorStream() << "Wrong map checksum!!!";
+			throw std::runtime_error("Wrong checksum");
+		}
+	}
+	else
+	{
+		scenarioOps->mapfileChecksum = map->checksum;
+	}
+}
+
+void CGameState::initGrailPosition()
+{
+	logGlobal->debugStream() << "\tPicking grail position";
+	//pick grail location
+	if(map->grailPos.x < 0 || map->grailRadious) //grail not set or set within a radius around some place
+	{
+		if(!map->grailRadious) //radius not given -> anywhere on map
+			map->grailRadious = map->width * 2;
+
+		std::vector<int3> allowedPos;
+		static const int BORDER_WIDTH = 9; // grail must be at least 9 tiles away from border
+
+		// add all not blocked tiles in range
+		for (int i = BORDER_WIDTH; i < map->width - BORDER_WIDTH ; i++)
+		{
+			for (int j = BORDER_WIDTH; j < map->height - BORDER_WIDTH; j++)
+			{
+				for (int k = 0; k < (map->twoLevel ? 2 : 1); k++)
+				{
+					const TerrainTile &t = map->getTile(int3(i, j, k));
+					if(!t.blocked
+						&& !t.visitable
+						&& t.terType != ETerrainType::WATER
+						&& t.terType != ETerrainType::ROCK
+						&& map->grailPos.dist2dSQ(int3(i, j, k)) <= (map->grailRadious * map->grailRadious))
+						allowedPos.push_back(int3(i,j,k));
+				}
+			}
+		}
+
+		//remove tiles with holes
+		for(auto & elem : map->objects)
+			if(elem && elem->ID == Obj::HOLE)
+				allowedPos -= elem->pos;
+
+		if(!allowedPos.empty())
+		{
+			map->grailPos = *RandomGeneratorUtil::nextItem(allowedPos, rand);
+		}
+		else
+		{
+			logGlobal->warnStream() << "Warning: Grail cannot be placed, no appropriate tile found!";
+		}
+	}
+}
+
+void CGameState::initRandomFactionsForPlayers()
+{
+	logGlobal->debugStream() << "\tPicking random factions for players";
+	for(auto & elem : scenarioOps->playerInfos)
+	{
+		if(elem.second.castle==-1)
+		{
+			auto randomID = rand.nextInt(map->players[elem.first.getNum()].allowedFactions.size() - 1);
+			auto iter = map->players[elem.first.getNum()].allowedFactions.begin();
+			std::advance(iter, randomID);
+
+			elem.second.castle = *iter;
+		}
+	}
+}
+
+void CGameState::randomizeMapObjects()
+{
+	logGlobal->debugStream() << "\tRandomizing objects";
+	for(CGObjectInstance *obj : map->objects)
+	{
+		if(!obj) continue;
+
+		randomizeObject(obj);
+		obj->hoverName = VLC->objtypeh->getObjectName(obj->ID);
+
+		//handle Favouring Winds - mark tiles under it
+		if(obj->ID == Obj::FAVORABLE_WINDS)
+		{
+			for (int i = 0; i < obj->getWidth() ; i++)
+			{
+				for (int j = 0; j < obj->getHeight() ; j++)
+				{
+					int3 pos = obj->pos - int3(i,j,0);
+					if(map->isInTheMap(pos)) map->getTile(pos).extTileFlags |= 128;
+				}
+			}
+		}
+	}
+}
+
+void CGameState::initPlayerStates()
+{
+	logGlobal->debugStream() << "\tCreating player entries in gs";
+	for(auto & elem : scenarioOps->playerInfos)
+	{
+		std::pair<PlayerColor, PlayerState> ins(elem.first,PlayerState());
+		ins.second.color=ins.first;
+		ins.second.human = elem.second.playerID;
+		ins.second.team = map->players[ins.first.getNum()].team;
+		teams[ins.second.team].id = ins.second.team;//init team
+		teams[ins.second.team].players.insert(ins.first);//add player to team
+		players.insert(ins);
+	}
+}
+
+void CGameState::placeCampaignHeroes()
+{
+	if (scenarioOps->campState)
+	{
+		// place bonus hero
+		auto campaignBonus = scenarioOps->campState->getBonusForCurrentMap();
+		bool campaignGiveHero = campaignBonus && campaignBonus.get().type == CScenarioTravel::STravelBonus::HERO;
+
+		if(campaignGiveHero)
+		{
+			auto playerColor = PlayerColor(campaignBonus->info1);
+			auto it = scenarioOps->playerInfos.find(playerColor);
+			if(it != scenarioOps->playerInfos.end())
+			{
+				auto heroTypeId = campaignBonus->info2;
+				if(heroTypeId == 0xffff) // random bonus hero
+				{
+					heroTypeId = pickUnusedHeroTypeRandomly(playerColor);
+				}
+
+				placeStartingHero(playerColor, HeroTypeID(heroTypeId), map->players[playerColor.getNum()].posOfMainTown);
+			}
+		}
+
+		// replace heroes placeholders
+		auto crossoverHeroes = getCrossoverHeroesFromPreviousScenarios();
+
+		if(!crossoverHeroes.heroesFromAnyPreviousScenarios.empty())
+		{
+			logGlobal->debugStream() << "\tGenerate list of hero placeholders";
+			auto campaignHeroReplacements = generateCampaignHeroesToReplace(crossoverHeroes);
+
+			logGlobal->debugStream() << "\tPrepare crossover heroes";
+			prepareCrossoverHeroes(campaignHeroReplacements, scenarioOps->campState->getCurrentScenario().travelOptions);
+
+			// remove same heroes on the map which will be added through crossover heroes
+			// INFO: we will remove heroes because later it may be possible that the API doesn't allow having heroes
+			// with the same hero type id
+			std::vector<CGHeroInstance *> removedHeroes;
+
+			for(auto & campaignHeroReplacement : campaignHeroReplacements)
+			{
+				auto hero = getUsedHero(HeroTypeID(campaignHeroReplacement.hero->subID));
+				if(hero)
+				{
+					removedHeroes.push_back(hero);
+					map->heroesOnMap -= hero;
+					map->objects[hero->id.getNum()] = nullptr;
+					map->removeBlockVisTiles(hero, true);
+				}
+			}
+
+			logGlobal->debugStream() << "\tReplace placeholders with heroes";
+			replaceHeroesPlaceholders(campaignHeroReplacements);
+
+			// remove hero placeholders on map
+			for(auto obj : map->objects)
+			{
+				if(obj && obj->ID == Obj::HERO_PLACEHOLDER)
+				{
+					auto heroPlaceholder = dynamic_cast<CGHeroPlaceholder *>(obj.get());
+					map->removeBlockVisTiles(heroPlaceholder, true);
+					map->objects[heroPlaceholder->id.getNum()] = nullptr;
+					delete heroPlaceholder;
+				}
+			}
+
+			// now add removed heroes again with unused type ID
+			for(auto hero : removedHeroes)
+			{
+				si32 heroTypeId = 0;
+				if(hero->ID == Obj::HERO)
+				{
+					heroTypeId = pickUnusedHeroTypeRandomly(hero->tempOwner);
+				}
+				else if(hero->ID == Obj::PRISON)
+				{
+					auto unusedHeroTypeIds = getUnusedAllowedHeroes();
+					if(!unusedHeroTypeIds.empty())
+					{
+						heroTypeId = (*RandomGeneratorUtil::nextItem(unusedHeroTypeIds, rand)).getNum();
+					}
+					else
+					{
+						logGlobal->errorStream() << "No free hero type ID found to replace prison.";
+						assert(0);
+					}
+				}
+				else
+				{
+					assert(0); // should not happen
+				}
+
+				hero->subID = heroTypeId;
+				hero->portrait = hero->subID;
+				map->getEditManager()->insertObject(hero, hero->pos);
+			}
+		}
+	}
+}
+
+void CGameState::placeStartingHero(PlayerColor playerColor, HeroTypeID heroTypeId, int3 townPos)
+{
+	townPos.x += 1;
+
+	CGHeroInstance * hero =  static_cast<CGHeroInstance*>(createObject(Obj::HERO, heroTypeId.getNum(), townPos, playerColor));
+	map->getEditManager()->insertObject(hero, townPos);
+}
+
+CGameState::CrossoverHeroesList CGameState::getCrossoverHeroesFromPreviousScenarios() const
+{
+	CrossoverHeroesList crossoverHeroes;
+
+	auto campaignState = scenarioOps->campState;
+	auto bonus = campaignState->getBonusForCurrentMap();
+	if (bonus && bonus->type == CScenarioTravel::STravelBonus::HEROES_FROM_PREVIOUS_SCENARIO)
+	{
+		crossoverHeroes.heroesFromAnyPreviousScenarios = crossoverHeroes.heroesFromPreviousScenario = campaignState->camp->scenarios[bonus->info2].crossoverHeroes;
+	}
+	else
+	{
+		if(!campaignState->mapsConquered.empty())
+		{
+			crossoverHeroes.heroesFromPreviousScenario = campaignState->camp->scenarios[campaignState->mapsConquered.back()].crossoverHeroes;
+
+			for(auto mapNr : campaignState->mapsConquered)
+			{
+				// create a list of deleted heroes
+				auto & scenario = campaignState->camp->scenarios[mapNr];
+				auto lostCrossoverHeroes = scenario.getLostCrossoverHeroes();
+
+				// remove heroes which didn't reached the end of the scenario, but were available at the start
+				for(auto hero : lostCrossoverHeroes)
+				{
+					crossoverHeroes.heroesFromAnyPreviousScenarios.erase(range::remove_if(crossoverHeroes.heroesFromAnyPreviousScenarios,
+						CGObjectInstanceBySubIdFinder(hero)), crossoverHeroes.heroesFromAnyPreviousScenarios.end());
+				}
+
+				// now add heroes which completed the scenario
+				for(auto hero : scenario.crossoverHeroes)
+				{
+					// add new heroes and replace old heroes with newer ones
+					auto it = range::find_if(crossoverHeroes.heroesFromAnyPreviousScenarios, CGObjectInstanceBySubIdFinder(hero));
+					if (it != crossoverHeroes.heroesFromAnyPreviousScenarios.end())
+					{
+						// replace old hero with newer one
+						crossoverHeroes.heroesFromAnyPreviousScenarios[it - crossoverHeroes.heroesFromAnyPreviousScenarios.begin()] = hero;
+					}
+					else
+					{
+						// add new hero
+						crossoverHeroes.heroesFromAnyPreviousScenarios.push_back(hero);
+					}
+				}
+			}
+		}
+	}
+
+	return crossoverHeroes;
+}
+
+void CGameState::prepareCrossoverHeroes(std::vector<CGameState::CampaignHeroReplacement> & campaignHeroReplacements, const CScenarioTravel & travelOptions) const
+{
+	// create heroes list for convenience iterating
+	std::vector<CGHeroInstance *> crossoverHeroes;
+	for(auto & campaignHeroReplacement : campaignHeroReplacements)
+	{
+		crossoverHeroes.push_back(campaignHeroReplacement.hero);
+	}
+
+	// TODO replace magic numbers with named constants
+	// TODO this logic (what should be kept) should be part of CScenarioTravel and be exposed via some clean set of methods
+	if(!(travelOptions.whatHeroKeeps & 1))
+	{
+		//trimming experience
+		for(CGHeroInstance * cgh : crossoverHeroes)
+		{
+			cgh->initExp();
+		}
+	}
+
+	if(!(travelOptions.whatHeroKeeps & 2))
+	{
+		//trimming prim skills
+		for(CGHeroInstance * cgh : crossoverHeroes)
+		{
+			for(int g=0; g<GameConstants::PRIMARY_SKILLS; ++g)
+			{
+				auto sel = Selector::type(Bonus::PRIMARY_SKILL)
+					.And(Selector::subtype(g))
+					.And(Selector::sourceType(Bonus::HERO_BASE_SKILL));
+
+				cgh->getBonusLocalFirst(sel)->val = cgh->type->heroClass->primarySkillInitial[g];
+			}
+		}
+	}
+
+	if(!(travelOptions.whatHeroKeeps & 4))
+	{
+		//trimming sec skills
+		for(CGHeroInstance * cgh : crossoverHeroes)
+		{
+			cgh->secSkills = cgh->type->secSkillsInit;
+			cgh->recreateSecondarySkillsBonuses();
+		}
+	}
+
+	if(!(travelOptions.whatHeroKeeps & 8))
+	{
+		for(CGHeroInstance * cgh : crossoverHeroes)
+		{
+			// Trimming spells
+			cgh->spells.clear();
+
+			// Spellbook will also be removed
+			if (cgh->hasSpellbook())
+				ArtifactLocation(cgh, ArtifactPosition(ArtifactPosition::SPELLBOOK)).removeArtifact();
+		}
+	}
+
+	if(!(travelOptions.whatHeroKeeps & 16))
+	{
+		//trimming artifacts
+		for(CGHeroInstance * hero : crossoverHeroes)
+		{
+			size_t totalArts = GameConstants::BACKPACK_START + hero->artifactsInBackpack.size();
+			for (size_t i = 0; i < totalArts; i++ )
+			{
+				auto artifactPosition = ArtifactPosition(i);
+				if(artifactPosition == ArtifactPosition::SPELLBOOK) continue; // do not handle spellbook this way
+
+				const ArtSlotInfo *info = hero->getSlot(artifactPosition);
+				if(!info)
+					continue;
+
+				// TODO: why would there be nullptr artifacts?
+				const CArtifactInstance *art = info->artifact;
+				if(!art)
+					continue;
+
+				int id  = art->artType->id;
+				assert( 8*18 > id );//number of arts that fits into h3m format
+				bool takeable = travelOptions.artifsKeptByHero[id / 8] & ( 1 << (id%8) );
+
+				ArtifactLocation al(hero, artifactPosition);
+				if(!takeable  &&  !al.getSlot()->locked)  //don't try removing locked artifacts -> it crashes #1719
+					al.removeArtifact();
+			}
+		}
+	}
+
+	//trimming creatures
+	for(CGHeroInstance * cgh : crossoverHeroes)
+	{
+		auto shouldSlotBeErased = [&](const std::pair<SlotID, CStackInstance *> & j) -> bool
+		{
+			CreatureID::ECreatureID crid = j.second->getCreatureID().toEnum();
+			return !(travelOptions.monstersKeptByHero[crid / 8] & (1 << (crid % 8)));
+		};
+
+		auto stacksCopy = cgh->stacks; //copy of the map, so we can iterate iover it and remove stacks
+		for(auto &slotPair : stacksCopy)
+			if(shouldSlotBeErased(slotPair))
+				cgh->eraseStack(slotPair.first);
+	}
+
+	// Removing short-term bonuses
+	for(CGHeroInstance * cgh : crossoverHeroes)
+	{
+		cgh->popBonuses(Selector::durationType(Bonus::ONE_DAY));
+		cgh->popBonuses(Selector::durationType(Bonus::ONE_WEEK));
+		cgh->popBonuses(Selector::durationType(Bonus::N_TURNS));
+		cgh->popBonuses(Selector::durationType(Bonus::N_DAYS));
+		cgh->popBonuses(Selector::durationType(Bonus::ONE_BATTLE));
+	}
+
+}
+
+void CGameState::placeStartingHeroes()
+{
+	logGlobal->debugStream() << "\tGiving starting hero";
+
+	for(auto & playerSettingPair : scenarioOps->playerInfos)
+	{
+		auto playerColor = playerSettingPair.first;
+		auto & playerInfo = map->players[playerColor.getNum()];
+		if(playerInfo.generateHeroAtMainTown && playerInfo.hasMainTown)
+		{
+			// Do not place a starting hero if the hero was already placed due to a campaign bonus
+			if(scenarioOps->campState)
+			{
+				if(auto campaignBonus = scenarioOps->campState->getBonusForCurrentMap())
+				{
+					if(campaignBonus->type == CScenarioTravel::STravelBonus::HERO && playerColor == PlayerColor(campaignBonus->info1)) continue;
+				}
+			}
+
+			int heroTypeId = pickNextHeroType(playerColor);
+			if(playerSettingPair.second.hero == -1) playerSettingPair.second.hero = heroTypeId;
+
+			placeStartingHero(playerColor, HeroTypeID(heroTypeId), playerInfo.posOfMainTown);
+		}
+	}
+}
+
+void CGameState::initStartingResources()
+{
+	logGlobal->debugStream() << "\tSetting up resources";
+	const JsonNode config(ResourceID("config/startres.json"));
+	const JsonVector &vector = config["difficulty"].Vector();
+	const JsonNode &level = vector[scenarioOps->difficulty];
+
+	TResources startresAI(level["ai"]);
+	TResources startresHuman(level["human"]);
+
+	for (auto & elem : players)
+	{
+		PlayerState &p = elem.second;
+
+		if (p.human)
+			p.resources = startresHuman;
+		else
+			p.resources = startresAI;
+	}
+
+	auto getHumanPlayerInfo = [&]() -> std::vector<const PlayerSettings *>
+	{
+		std::vector<const PlayerSettings *> ret;
+		for(auto it = scenarioOps->playerInfos.cbegin();
+			it != scenarioOps->playerInfos.cend(); ++it)
+		{
+			if(it->second.playerID != PlayerSettings::PLAYER_AI)
+				ret.push_back(&it->second);
+		}
+
+		return ret;
+	};
+
+	//give start resource bonus in case of campaign
+	if (scenarioOps->mode == StartInfo::CAMPAIGN)
+	{
+		auto chosenBonus = scenarioOps->campState->getBonusForCurrentMap();
+		if(chosenBonus && chosenBonus->type == CScenarioTravel::STravelBonus::RESOURCE)
+		{
+			std::vector<const PlayerSettings *> people = getHumanPlayerInfo(); //players we will give resource bonus
+			for(const PlayerSettings *ps : people)
+			{
+				std::vector<int> res; //resources we will give
+				switch (chosenBonus->info1)
+				{
+				case 0: case 1: case 2: case 3: case 4: case 5: case 6:
+					res.push_back(chosenBonus->info1);
+					break;
+				case 0xFD: //wood+ore
+					res.push_back(Res::WOOD); res.push_back(Res::ORE);
+					break;
+				case 0xFE:  //rare
+					res.push_back(Res::MERCURY); res.push_back(Res::SULFUR); res.push_back(Res::CRYSTAL); res.push_back(Res::GEMS);
+					break;
+				default:
+					assert(0);
+					break;
+				}
+				//increasing resource quantity
+				for (auto & re : res)
+				{
+					players[ps->color].resources[re] += chosenBonus->info2;
+				}
+			}
+		}
+	}
+}
+
+void CGameState::initHeroes()
+{
+	for(auto hero : map->heroesOnMap)  //heroes instances initialization
+	{
+		if (hero->getOwner() == PlayerColor::UNFLAGGABLE)
+		{
+			logGlobal->warnStream() << "Warning - hero with uninitialized owner!";
+			continue;
+		}
+
+		hero->initHero();
+		getPlayer(hero->getOwner())->heroes.push_back(hero);
+		map->allHeroes[hero->type->ID.getNum()] = hero;
+	}
+
+	for(auto obj : map->objects) //prisons
+	{
+		if(obj && obj->ID == Obj::PRISON)
+			map->allHeroes[obj->subID] = dynamic_cast<CGHeroInstance*>(obj.get());
+	}
+
+	std::set<HeroTypeID> heroesToCreate = getUnusedAllowedHeroes(); //ids of heroes to be created and put into the pool
+	for(auto ph : map->predefinedHeroes)
+	{
+		if(!vstd::contains(heroesToCreate, HeroTypeID(ph->subID)))
+			continue;
+		ph->initHero();
+		hpool.heroesPool[ph->subID] = ph;
+		hpool.pavailable[ph->subID] = 0xff;
+		heroesToCreate.erase(ph->type->ID);
+
+		map->allHeroes[ph->subID] = ph;
+	}
+
+	for(HeroTypeID htype : heroesToCreate) //all not used allowed heroes go with default state into the pool
+	{
+		auto  vhi = new CGHeroInstance();
+		vhi->initHero(htype);
+
+		int typeID = htype.getNum();
+		map->allHeroes[typeID] = vhi;
+		hpool.heroesPool[typeID] = vhi;
+		hpool.pavailable[typeID] = 0xff;
+	}
+
+	for(auto & elem : map->disposedHeroes)
+	{
+		hpool.pavailable[elem.heroId] = elem.players;
+	}
+
+	if (scenarioOps->mode == StartInfo::CAMPAIGN) //give campaign bonuses for specific / best hero
+	{
+		auto chosenBonus = scenarioOps->campState->getBonusForCurrentMap();
+		if (chosenBonus && chosenBonus->isBonusForHero() && chosenBonus->info1 != 0xFFFE) //exclude generated heroes
+		{
+			//find human player
+			PlayerColor humanPlayer=PlayerColor::NEUTRAL;
+			for (auto & elem : players)
+			{
+				if(elem.second.human)
+				{
+					humanPlayer = elem.first;
+					break;
+				}
+			}
+			assert(humanPlayer != PlayerColor::NEUTRAL);
+
+			std::vector<ConstTransitivePtr<CGHeroInstance> > & heroes = players[humanPlayer].heroes;
+
+			if (chosenBonus->info1 == 0xFFFD) //most powerful
+			{
+				int maxB = -1;
+				for (int b=0; b<heroes.size(); ++b)
+				{
+					if (maxB == -1 || heroes[b]->getTotalStrength() > heroes[maxB]->getTotalStrength())
+					{
+						maxB = b;
+					}
+				}
+				if(maxB < 0)
+					logGlobal->warnStream() << "Warning - cannot give bonus to hero cause there are no heroes!";
+				else
+					giveCampaignBonusToHero(heroes[maxB]);
+			}
+			else //specific hero
+			{
+				for (auto & heroe : heroes)
+				{
+					if (heroe->subID == chosenBonus->info1)
+					{
+						giveCampaignBonusToHero(heroe);
+						break;
+					}
+				}
+			}
+		}
+	}
+}
+
+void CGameState::giveCampaignBonusToHero(CGHeroInstance * hero)
+{
+	const boost::optional<CScenarioTravel::STravelBonus> & curBonus = scenarioOps->campState->getBonusForCurrentMap();
+	if(!curBonus)
+		return;
+
+	if(curBonus->isBonusForHero())
+	{
+		//apply bonus
+		switch (curBonus->type)
+		{
+		case CScenarioTravel::STravelBonus::SPELL:
+			hero->spells.insert(SpellID(curBonus->info2));
+			break;
+		case CScenarioTravel::STravelBonus::MONSTER:
+			{
+				for(int i=0; i<GameConstants::ARMY_SIZE; i++)
+				{
+					if(hero->slotEmpty(SlotID(i)))
+					{
+						hero->addToSlot(SlotID(i), CreatureID(curBonus->info2), curBonus->info3);
+						break;
+					}
+				}
+			}
+			break;
+		case CScenarioTravel::STravelBonus::ARTIFACT:
+			gs->giveHeroArtifact(hero, static_cast<ArtifactID>(curBonus->info2));
+			break;
+		case CScenarioTravel::STravelBonus::SPELL_SCROLL:
+			{
+				CArtifactInstance * scroll = CArtifactInstance::createScroll(SpellID(curBonus->info2).toSpell());
+				scroll->putAt(ArtifactLocation(hero, scroll->firstAvailableSlot(hero)));
+			}
+			break;
+		case CScenarioTravel::STravelBonus::PRIMARY_SKILL:
+			{
+				const ui8* ptr = reinterpret_cast<const ui8*>(&curBonus->info2);
+				for (int g=0; g<GameConstants::PRIMARY_SKILLS; ++g)
+				{
+					int val = ptr[g];
+					if (val == 0)
+					{
+						continue;
+					}
+					auto bb = new Bonus(Bonus::PERMANENT, Bonus::PRIMARY_SKILL, Bonus::CAMPAIGN_BONUS, val, *scenarioOps->campState->currentMap, g);
+					hero->addNewBonus(bb);
+				}
+			}
+			break;
+		case CScenarioTravel::STravelBonus::SECONDARY_SKILL:
+			hero->setSecSkillLevel(SecondarySkill(curBonus->info2), curBonus->info3, true);
+			break;
+		}
+	}
+}
+
+void CGameState::initFogOfWar()
+{
+	logGlobal->debugStream() << "\tFog of war"; //FIXME: should be initialized after all bonuses are set
+	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;
+
+		for(CGObjectInstance *obj : map->objects)
+		{
+			if(!obj || !vstd::contains(elem.second.players, obj->tempOwner)) continue; //not a flagged object
+
+			std::unordered_set<int3, ShashInt3> tiles;
+			obj->getSightTiles(tiles);
+			for(int3 tile : tiles)
+			{
+				elem.second.fogOfWarMap[tile.x][tile.y][tile.z] = 1;
+			}
+		}
+	}
+}
+
+void CGameState::initStartingBonus()
+{
+	logGlobal->debugStream() << "\tStarting bonuses";
+	for(auto & elem : players)
+	{
+		//starting bonus
+		if(scenarioOps->playerInfos[elem.first].bonus==PlayerSettings::RANDOM)
+			scenarioOps->playerInfos[elem.first].bonus = static_cast<PlayerSettings::Ebonus>(rand.nextInt(2));
+		switch(scenarioOps->playerInfos[elem.first].bonus)
+		{
+		case PlayerSettings::GOLD:
+			elem.second.resources[Res::GOLD] += rand.nextInt(500, 1000);
+			break;
+		case PlayerSettings::RESOURCE:
+			{
+				int res = VLC->townh->factions[scenarioOps->playerInfos[elem.first].castle]->town->primaryRes;
+				if(res == Res::WOOD_AND_ORE)
+				{
+					elem.second.resources[Res::WOOD] += rand.nextInt(5, 10);
+					elem.second.resources[Res::ORE] += rand.nextInt(5, 10);
+				}
+				else
+				{
+					elem.second.resources[res] += rand.nextInt(3, 6);
+				}
+				break;
+			}
+		case PlayerSettings::ARTIFACT:
+			{
+				if(!elem.second.heroes.size())
+				{
+					logGlobal->debugStream() << "Cannot give starting artifact - no heroes!";
+					break;
+				}
+				CArtifact *toGive;
+				toGive = VLC->arth->artifacts[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_TREASURE)];
+
+				CGHeroInstance *hero = elem.second.heroes[0];
+				giveHeroArtifact(hero, toGive->id);
+			}
+			break;
+		}
+	}
+}
+
+void CGameState::initTowns()
+{
+	logGlobal->debugStream() << "\tTowns";
+
+	//campaign bonuses for towns
+	if (scenarioOps->mode == StartInfo::CAMPAIGN)
+	{
+		auto chosenBonus = scenarioOps->campState->getBonusForCurrentMap();
+
+		if (chosenBonus && chosenBonus->type == CScenarioTravel::STravelBonus::BUILDING)
+		{
+			for (int g=0; g<map->towns.size(); ++g)
+			{
+				PlayerState * owner = getPlayer(map->towns[g]->getOwner());
+				if (owner)
+				{
+					PlayerInfo & pi = map->players[owner->color.getNum()];
+
+					if (owner->human && //human-owned
+						map->towns[g]->pos == pi.posOfMainTown + int3(2, 0, 0))
+					{
+						map->towns[g]->builtBuildings.insert(
+							CBuildingHandler::campToERMU(chosenBonus->info1, map->towns[g]->subID, map->towns[g]->builtBuildings));
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	CGTownInstance::universitySkills.clear();
+	for ( int i=0; i<4; i++)
+		CGTownInstance::universitySkills.push_back(14+i);//skills for university
+
+	for (auto & elem : map->towns)
+	{
+		CGTownInstance * vti =(elem);
+		if(!vti->town)
+		{
+			vti->town = VLC->townh->factions[vti->subID]->town;
+		}
+		if(vti->name.empty())
+		{
+			vti->name = *RandomGeneratorUtil::nextItem(vti->town->names, rand);
+		}
+
+		//init buildings
+		if(vstd::contains(vti->builtBuildings, BuildingID::DEFAULT)) //give standard set of buildings
+		{
+			vti->builtBuildings.erase(BuildingID::DEFAULT);
+			vti->builtBuildings.insert(BuildingID::VILLAGE_HALL);
+			vti->builtBuildings.insert(BuildingID::TAVERN);
+			vti->builtBuildings.insert(BuildingID::DWELL_FIRST);
+			if(rand.nextInt(1) == 1)
+			{
+				vti->builtBuildings.insert(BuildingID::DWELL_LVL_2);
+			}
+		}
+
+		//#1444 - remove entries that don't have buildings defined (like some unused extra town hall buildings)
+		vstd::erase_if(vti->builtBuildings, [vti](BuildingID bid){
+			return !vti->town->buildings.count(bid) || !vti->town->buildings.at(bid); });
+
+		if (vstd::contains(vti->builtBuildings, BuildingID::SHIPYARD) && vti->shipyardStatus()==IBoatGenerator::TILE_BLOCKED)
+			vti->builtBuildings.erase(BuildingID::SHIPYARD);//if we have harbor without water - erase it (this is H3 behaviour)
+
+		//init hordes
+		for (int i = 0; i<GameConstants::CREATURES_PER_TOWN; i++)
+			if (vstd::contains(vti->builtBuildings,(-31-i))) //if we have horde for this level
+			{
+				vti->builtBuildings.erase(BuildingID(-31-i));//remove old ID
+				if (vti->town->hordeLvl.at(0) == i)//if town first horde is this one
+				{
+					vti->builtBuildings.insert(BuildingID::HORDE_1);//add it
+					if (vstd::contains(vti->builtBuildings,(BuildingID::DWELL_UP_FIRST+i)))//if we have upgraded dwelling as well
+						vti->builtBuildings.insert(BuildingID::HORDE_1_UPGR);//add it as well
+				}
+				if (vti->town->hordeLvl.at(1) == i)//if town second horde is this one
+				{
+					vti->builtBuildings.insert(BuildingID::HORDE_2);
+					if (vstd::contains(vti->builtBuildings,(BuildingID::DWELL_UP_FIRST+i)))
+						vti->builtBuildings.insert(BuildingID::HORDE_2_UPGR);
+				}
+			}
+
+		//Early check for #1444-like problems
+		for(auto building : vti->builtBuildings)
+		{
+			assert(vti->town->buildings.at(building) != nullptr);
+			UNUSED(building);
+		}
+
+		//town events
+		for(CCastleEvent &ev : vti->events)
+		{
+			for (int i = 0; i<GameConstants::CREATURES_PER_TOWN; i++)
+				if (vstd::contains(ev.buildings,(-31-i))) //if we have horde for this level
+				{
+					ev.buildings.erase(BuildingID(-31-i));
+					if (vti->town->hordeLvl.at(0) == i)
+						ev.buildings.insert(BuildingID::HORDE_1);
+					if (vti->town->hordeLvl.at(1) == i)
+						ev.buildings.insert(BuildingID::HORDE_2);
+				}
+		}
+		//init spells
+		logGlobal->debugStream() << "\t\tTown init spells";
+		vti->spells.resize(GameConstants::SPELL_LEVELS);
+
+		for(ui32 z=0; z<vti->obligatorySpells.size();z++)
+		{
+			CSpell *s = vti->obligatorySpells[z].toSpell();
+			vti->spells[s->level-1].push_back(s->id);
+			vti->possibleSpells -= s->id;
+		}
+		logGlobal->debugStream() << "\t\tTown init spells2";
+		while(vti->possibleSpells.size())
+		{
+			ui32 total=0;
+			int sel = -1;
+
+			for(ui32 ps=0;ps<vti->possibleSpells.size();ps++)
+				total += vti->possibleSpells[ps].toSpell()->getProbability(vti->subID);
+
+			if (total == 0) // remaining spells have 0 probability
+				break;
+
+			auto r = rand.nextInt(total - 1);
+			for(ui32 ps=0; ps<vti->possibleSpells.size();ps++)
+			{
+				r -= vti->possibleSpells[ps].toSpell()->getProbability(vti->subID);
+				if(r<0)
+				{
+					sel = ps;
+					break;
+				}
+			}
+			if(sel<0)
+				sel=0;
+
+			CSpell *s = vti->possibleSpells[sel].toSpell();
+			vti->spells[s->level-1].push_back(s->id);
+			vti->possibleSpells -= s->id;
+		}
+		vti->possibleSpells.clear();
+		if(vti->getOwner() != PlayerColor::NEUTRAL)
+			getPlayer(vti->getOwner())->towns.push_back(vti);
+        logGlobal->debugStream() << "\t\tTown init spells3";
+
+	}
+}
+
+void CGameState::initMapObjects()
+{
+	logGlobal->debugStream() << "\tObject initialization";
+//	objCaller->preInit();
+	for(CGObjectInstance *obj : map->objects)
+	{
+		if(obj)
+			obj->initObj();
+	}
+	for(CGObjectInstance *obj : map->objects)
+	{
+		if(!obj)
+			continue;
+
+		switch (obj->ID)
+		{
+			case Obj::QUEST_GUARD:
+			case Obj::SEER_HUT:
+			{
+				auto q = static_cast<CGSeerHut*>(obj);
+				assert (q);
+				q->setObjToKill();
+			}
+		}
+	}
+	CGTeleport::postInit(); //pairing subterranean gates
+
+	map->calculateGuardingGreaturePositions(); //calculate once again when all the guards are placed and initialized
+}
+
+void CGameState::initVisitingAndGarrisonedHeroes()
+{
+	for(auto k=players.begin(); k!=players.end(); ++k)
+	{
+		if(k->first==PlayerColor::NEUTRAL)
+			continue;
+
+		//init visiting and garrisoned heroes
+		for(CGHeroInstance *h : k->second.heroes)
+		{
+			for(CGTownInstance *t : k->second.towns)
+			{
+				int3 vistile = t->pos; vistile.x--; //tile next to the entrance
+				if(vistile == h->pos || h->pos==t->pos)
+				{
+					t->setVisitingHero(h);
+					if(h->pos == t->pos) //visiting hero placed in the editor has same pos as the town - we need to correct it
+					{
+						map->removeBlockVisTiles(h);
+						h->pos.x -= 1;
+						map->addBlockVisTiles(h);
+					}
+					break;
+				}
+			}
+		}
+	}
+}
+
+BFieldType CGameState::battleGetBattlefieldType(int3 tile)
+{
+	if(tile==int3() && curB)
+		tile = curB->tile;
+	else if(tile==int3() && !curB)
+		return BFieldType::NONE;
+
+	const TerrainTile &t = map->getTile(tile);
+	//fight in mine -> subterranean
+	if(dynamic_cast<const CGMine *>(t.visitableObjects.front()))
+		return BFieldType::SUBTERRANEAN;
+
+	for(auto &obj : map->objects)
+	{
+		//look only for objects covering given tile
+		if( !obj || obj->pos.z != tile.z || !obj->coveringAt(tile.x, tile.y))
+			continue;
+
+		switch(obj->ID)
+		{
+		case Obj::CLOVER_FIELD:
+			return BFieldType::CLOVER_FIELD;
+		case Obj::CURSED_GROUND1: case Obj::CURSED_GROUND2:
+			return BFieldType::CURSED_GROUND;
+		case Obj::EVIL_FOG:
+			return BFieldType::EVIL_FOG;
+		case Obj::FAVORABLE_WINDS:
+			return BFieldType::FAVOURABLE_WINDS;
+		case Obj::FIERY_FIELDS:
+			return BFieldType::FIERY_FIELDS;
+		case Obj::HOLY_GROUNDS:
+			return BFieldType::HOLY_GROUND;
+		case Obj::LUCID_POOLS:
+			return BFieldType::LUCID_POOLS;
+		case Obj::MAGIC_CLOUDS:
+			return BFieldType::MAGIC_CLOUDS;
+		case Obj::MAGIC_PLAINS1: case Obj::MAGIC_PLAINS2:
+			return BFieldType::MAGIC_PLAINS;
+		case Obj::ROCKLANDS:
+			return BFieldType::ROCKLANDS;
+		}
+	}
+
+	if(!t.isWater() && t.isCoastal())
+		return BFieldType::SAND_SHORE;
+
+	switch(t.terType)
+	{
+	case ETerrainType::DIRT:
+		return BFieldType(rand.nextInt(3, 5));
+	case ETerrainType::SAND:
+		return BFieldType::SAND_MESAS; //TODO: coast support
+	case ETerrainType::GRASS:
+		return BFieldType(rand.nextInt(6, 7));
+	case ETerrainType::SNOW:
+		return BFieldType(rand.nextInt(10, 11));
+	case ETerrainType::SWAMP:
+		return BFieldType::SWAMP_TREES;
+	case ETerrainType::ROUGH:
+		return BFieldType::ROUGH;
+	case ETerrainType::SUBTERRANEAN:
+		return BFieldType::SUBTERRANEAN;
+	case ETerrainType::LAVA:
+		return BFieldType::LAVA;
+	case ETerrainType::WATER:
+		return BFieldType::SHIP;
+	case ETerrainType::ROCK:
+		return BFieldType::ROCKLANDS;
+	default:
+		return BFieldType::NONE;
+	}
+}
+
+UpgradeInfo CGameState::getUpgradeInfo(const CStackInstance &stack)
+{
+	UpgradeInfo ret;
+	const CCreature *base = stack.type;
+
+	const CGHeroInstance *h = stack.armyObj->ID == Obj::HERO ? static_cast<const CGHeroInstance*>(stack.armyObj) : nullptr;
+	const CGTownInstance *t = nullptr;
+
+	if(stack.armyObj->ID == Obj::TOWN)
+		t = static_cast<const CGTownInstance *>(stack.armyObj);
+	else if(h)
+	{	//hero specialty
+		TBonusListPtr lista = h->getBonuses(Selector::typeSubtype(Bonus::SPECIAL_UPGRADE, base->idNumber));
+		for(const Bonus *it : *lista)
+		{
+			auto nid = CreatureID(it->additionalInfo);
+			if (nid != base->idNumber) //in very specific case the upgrade is available by default (?)
+			{
+				ret.newID.push_back(nid);
+				ret.cost.push_back(VLC->creh->creatures[nid]->cost - base->cost);
+			}
+		}
+		t = h->visitedTown;
+	}
+	if(t)
+	{
+		for(const CGTownInstance::TCreaturesSet::value_type & dwelling : t->creatures)
+		{
+			if (vstd::contains(dwelling.second, base->idNumber)) //Dwelling with our creature
+			{
+				for(auto upgrID : dwelling.second)
+				{
+					if(vstd::contains(base->upgrades, upgrID)) //possible upgrade
+					{
+						ret.newID.push_back(upgrID);
+						ret.cost.push_back(VLC->creh->creatures[upgrID]->cost - base->cost);
+					}
+				}
+			}
+		}
+	}
+
+	//hero is visiting Hill Fort
+	if(h && map->getTile(h->visitablePos()).visitableObjects.front()->ID == Obj::HILL_FORT)
+	{
+		static const int costModifiers[] = {0, 25, 50, 75, 100}; //we get cheaper upgrades depending on level
+		const int costModifier = costModifiers[std::min<int>(std::max((int)base->level - 1, 0), ARRAY_COUNT(costModifiers) - 1)];
+
+		for(auto nid : base->upgrades)
+		{
+			ret.newID.push_back(nid);
+			ret.cost.push_back((VLC->creh->creatures[nid]->cost - base->cost) * costModifier / 100);
+		}
+	}
+
+	if(ret.newID.size())
+		ret.oldID = base->idNumber;
+
+	for (Res::ResourceSet &cost : ret.cost)
+		cost.positive(); //upgrade cost can't be negative, ignore missing resources
+
+	return ret;
+}
+
+PlayerRelations::PlayerRelations CGameState::getPlayerRelations( PlayerColor color1, PlayerColor color2 )
+{
+	if ( color1 == color2 )
+		return PlayerRelations::SAME_PLAYER;
+	if(color1 == PlayerColor::NEUTRAL || color2 == PlayerColor::NEUTRAL) //neutral player has no friends
+		return PlayerRelations::ENEMIES;
+
+	const TeamState * ts = getPlayerTeam(color1);
+	if (ts && vstd::contains(ts->players, color2))
+		return PlayerRelations::ALLIES;
+	return PlayerRelations::ENEMIES;
+}
+
+void CGameState::getNeighbours(const TerrainTile &srct, int3 tile, std::vector<int3> &vec, const boost::logic::tribool &onLand, bool limitCoastSailing)
+{
+	static const int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0),
+					int3(1,1,0),int3(-1,1,0),int3(1,-1,0),int3(-1,-1,0) };
+
+	//vec.reserve(8); //optimization
+	for (auto & dir : dirs)
+	{
+		const int3 hlp = tile + dir;
+		if(!map->isInTheMap(hlp))
+			continue;
+
+		const TerrainTile &hlpt = map->getTile(hlp);
+
+// 		//we cannot visit things from blocked tiles
+// 		if(srct.blocked && !srct.visitable && hlpt.visitable && srct.blockingObjects.front()->ID != HEROI_TYPE)
+// 		{
+// 			continue;
+// 		}
+
+        if(srct.terType == ETerrainType::WATER && limitCoastSailing && hlpt.terType == ETerrainType::WATER && dir.x && dir.y) //diagonal move through water
+		{
+			int3 hlp1 = tile,
+				hlp2 = tile;
+			hlp1.x += dir.x;
+			hlp2.y += dir.y;
+
+            if(map->getTile(hlp1).terType != ETerrainType::WATER || map->getTile(hlp2).terType != ETerrainType::WATER)
+				continue;
+		}
+
+        if((indeterminate(onLand)  ||  onLand == (hlpt.terType!=ETerrainType::WATER) )
+            && hlpt.terType != ETerrainType::ROCK)
+		{
+			vec.push_back(hlp);
+		}
+	}
+}
+
+int CGameState::getMovementCost(const CGHeroInstance *h, const int3 &src, const int3 &dest, bool flying, int remainingMovePoints, bool checkLast)
+{
+	if(src == dest) //same tile
+		return 0;
+
+	TerrainTile &s = map->getTile(src),
+		&d = map->getTile(dest);
+
+	//get basic cost
+	int ret = h->getTileCost(d,s);
+
+	if(d.blocked && flying)
+	{
+		bool freeFlying = h->getBonusesCount(Selector::typeSubtype(Bonus::FLYING_MOVEMENT, 1)) > 0;
+
+		if(!freeFlying)
+		{
+			ret *= 1.4; //40% penalty for movement over blocked tile
+		}
+	}
+    else if (d.terType == ETerrainType::WATER)
+	{
+		if(h->boat && s.hasFavourableWinds() && d.hasFavourableWinds()) //Favourable Winds
+			ret *= 0.666;
+		else if (!h->boat && h->getBonusesCount(Selector::typeSubtype(Bonus::WATER_WALKING, 1)) > 0)
+			ret *= 1.4; //40% penalty for water walking
+	}
+
+	if(src.x != dest.x  &&  src.y != dest.y) //it's diagonal move
+	{
+		int old = ret;
+		ret *= 1.414213;
+		//diagonal move costs too much but normal move is possible - allow diagonal move for remaining move points
+		if(ret > remainingMovePoints  &&  remainingMovePoints >= old)
+		{
+			return remainingMovePoints;
+		}
+	}
+
+
+	int left = remainingMovePoints-ret;
+	if(checkLast  &&  left > 0  &&  remainingMovePoints-ret < 250) //it might be the last tile - if no further move possible we take all move points
+	{
+		std::vector<int3> vec;
+		vec.reserve(8); //optimization
+        getNeighbours(d, dest, vec, s.terType != ETerrainType::WATER, true);
+		for(auto & elem : vec)
+		{
+			int fcost = getMovementCost(h,dest, elem, flying, left, false);
+			if(fcost <= left)
+			{
+				return ret;
+			}
+		}
+		ret = remainingMovePoints;
+	}
+	return ret;
+}
+
+void CGameState::apply(CPack *pack)
+{
+	ui16 typ = typeList.getTypeID(pack);
+	applierGs->apps[typ]->applyOnGS(this,pack);
+}
+
+void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int3 src, int movement)
+{
+	CPathfinder pathfinder(out, this, hero);
+	pathfinder.calculatePaths(src, movement);
+}
+
+/**
+ * Tells if the tile is guarded by a monster as well as the position
+ * of the monster that will attack on it.
+ *
+ * @return int3(-1, -1, -1) if the tile is unguarded, or the position of
+ * the monster guarding the tile.
+ */
+std::vector<CGObjectInstance*> CGameState::guardingCreatures (int3 pos) const
+{
+	std::vector<CGObjectInstance*> guards;
+	const int3 originalPos = pos;
+	if (!map->isInTheMap(pos))
+		return guards;
+
+	const TerrainTile &posTile = map->getTile(pos);
+	if (posTile.visitable)
+	{
+		for (CGObjectInstance* obj : posTile.visitableObjects)
+		{
+			if(obj->blockVisit)
+			{
+				if (obj->ID == Obj::MONSTER) // Monster
+					guards.push_back(obj);
+			}
+		}
+	}
+	pos -= int3(1, 1, 0); // Start with top left.
+	for (int dx = 0; dx < 3; dx++)
+	{
+		for (int dy = 0; dy < 3; dy++)
+		{
+			if (map->isInTheMap(pos))
+			{
+				const auto & tile = map->getTile(pos);
+                if (tile.visitable && (tile.isWater() == posTile.isWater()))
+				{
+					for (CGObjectInstance* obj : tile.visitableObjects)
+					{
+						if (obj->ID == Obj::MONSTER  &&  map->checkForVisitableDir(pos, &map->getTile(originalPos), originalPos)) // Monster being able to attack investigated tile
+						{
+							guards.push_back(obj);
+						}
+					}
+				}
+			}
+
+			pos.y++;
+		}
+		pos.y -= 3;
+		pos.x++;
+	}
+	return guards;
+
+}
+
+int3 CGameState::guardingCreaturePosition (int3 pos) const
+{
+	return gs->map->guardingCreaturePositions[pos.x][pos.y][pos.z];
+}
+
+bool CGameState::isVisible(int3 pos, PlayerColor player)
+{
+	if(player == PlayerColor::NEUTRAL)
+		return false;
+	return getPlayerTeam(player)->fogOfWarMap[pos.x][pos.y][pos.z];
+}
+
+bool CGameState::isVisible( const CGObjectInstance *obj, boost::optional<PlayerColor> player )
+{
+	if(!player)
+		return true;
+
+	if(*player == PlayerColor::NEUTRAL) //-> TODO ??? needed?
+		return false;
+	//object is visible when at least one blocked tile is visible
+	for(int fy=0; fy < obj->getHeight(); ++fy)
+	{
+		for(int fx=0; fx < obj->getWidth(); ++fx)
+		{
+			int3 pos = obj->pos + int3(-fx, -fy, 0);
+
+			if ( map->isInTheMap(pos) &&
+				 obj->coveringAt(pos.x, pos.y) &&
+				 isVisible(pos, *player))
+				return true;
+		}
+	}
+	return false;
+}
+
+bool CGameState::checkForVisitableDir(const int3 & src, const int3 & dst) const
+{
+	const TerrainTile * pom = &map->getTile(dst);
+	return map->checkForVisitableDir(src, pom, dst);
+}
+
+EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(PlayerColor player) const
+{
+	const std::string & messageWonSelf = VLC->generaltexth->allTexts[659];
+	const std::string & messageWonOther = VLC->generaltexth->allTexts[5];
+	const std::string & messageLostSelf = VLC->generaltexth->allTexts[7];
+	const std::string & messageLostOther = VLC->generaltexth->allTexts[8];
+
+	auto evaluateEvent = [=](const EventCondition & condition)
+	{
+		return this->checkForVictory(player, condition);
+	};
+
+	const PlayerState *p = CGameInfoCallback::getPlayer(player);
+
+	//cheater or tester, but has entered the code...
+	if (p->enteredWinningCheatCode)
+		return EVictoryLossCheckResult::victory(messageWonSelf, messageWonOther);
+
+	if (p->enteredLosingCheatCode)
+		return EVictoryLossCheckResult::defeat(messageLostSelf, messageLostOther);
+
+	for (const TriggeredEvent & event : map->triggeredEvents)
+	{
+		if ((event.trigger.test(evaluateEvent)))
+		{
+			if (event.effect.type == EventEffect::VICTORY)
+				return EVictoryLossCheckResult::victory(event.onFulfill, event.effect.toOtherMessage);
+
+			if (event.effect.type == EventEffect::DEFEAT)
+				return EVictoryLossCheckResult::defeat(event.onFulfill, event.effect.toOtherMessage);
+		}
+	}
+
+	if (checkForStandardLoss(player))
+	{
+		return EVictoryLossCheckResult::defeat(messageLostSelf, messageLostOther);
+	}
+	return EVictoryLossCheckResult();
+}
+
+bool CGameState::checkForVictory( PlayerColor player, const EventCondition & condition ) const
+{
+	const PlayerState *p = CGameInfoCallback::getPlayer(player);
+	switch (condition.condition)
+	{
+		case EventCondition::STANDARD_WIN:
+		{
+			return player == checkForStandardWin();
+		}
+		case EventCondition::HAVE_ARTIFACT: //check if any hero has winning artifact
+		{
+			for(auto & elem : p->heroes)
+				if(elem->hasArt(condition.objectType))
+					return true;
+			return false;
+		}
+		case EventCondition::HAVE_CREATURES:
+		{
+			//check if in players armies there is enough creatures
+			int total = 0; //creature counter
+			for(size_t i = 0; i < map->objects.size(); i++)
+			{
+				const CArmedInstance *ai = nullptr;
+				if(map->objects[i]
+					&& map->objects[i]->tempOwner == player //object controlled by player
+					&&  (ai = dynamic_cast<const CArmedInstance*>(map->objects[i].get()))) //contains army
+				{
+					for(auto & elem : ai->Slots()) //iterate through army
+						if(elem.second->type->idNumber == condition.objectType) //it's searched creature
+							total += elem.second->count;
+				}
+			}
+			return total >= condition.value;
+		}
+		case EventCondition::HAVE_RESOURCES:
+		{
+			return p->resources[condition.objectType] >= condition.value;
+		}
+		case EventCondition::HAVE_BUILDING:
+		{
+			if (condition.object) // specific town
+			{
+				const CGTownInstance *t = static_cast<const CGTownInstance *>(condition.object);
+				return (t->tempOwner == player && t->hasBuilt(BuildingID(condition.objectType)));
+			}
+			else // any town
+			{
+				for (const CGTownInstance * t : p->towns)
+				{
+					if (t->hasBuilt(BuildingID(condition.objectType)))
+						return true;
+				}
+				return false;
+			}
+		}
+		case EventCondition::DESTROY:
+		{
+			if (condition.object) // mode A - destroy specific object of this type
+			{
+				if (auto hero = dynamic_cast<const CGHeroInstance*>(condition.object))
+					return boost::range::find(gs->map->heroesOnMap, hero) == gs->map->heroesOnMap.end();
+				else
+					return getObj(condition.object->id) == nullptr;
+			}
+			else
+			{
+				for(auto & elem : map->objects) // mode B - destroy all objects of this type
+				{
+					if(elem && elem->ID == condition.objectType)
+						return false;
+				}
+				return true;
+			}
+		}
+		case EventCondition::CONTROL:
+		{
+			// list of players that need to control object to fulfull condition
+			// NOTE: cgameinfocallback specified explicitly in order to get const version
+			auto & team = CGameInfoCallback::getPlayerTeam(player)->players;
+
+			if (condition.object) // mode A - flag one specific object, like town
+			{
+				return team.count(condition.object->tempOwner) != 0;
+			}
+			else
+			{
+				for(auto & elem : map->objects) // mode B - flag all objects of this type
+				{
+					 //check not flagged objs
+					if ( elem && elem->ID == condition.objectType && team.count(elem->tempOwner) == 0 )
+						return false;
+				}
+				return true;
+			}
+		}
+		case EventCondition::TRANSPORT:
+		{
+			const CGTownInstance *t = static_cast<const CGTownInstance *>(condition.object);
+			if((t->visitingHero && t->visitingHero->hasArt(condition.objectType))
+				|| (t->garrisonHero && t->garrisonHero->hasArt(condition.objectType)))
+			{
+				return true;
+			}
+			return false;
+		}
+		case EventCondition::DAYS_PASSED:
+		{
+			return gs->day > condition.value;
+		}
+		case EventCondition::IS_HUMAN:
+		{
+			return p->human ? condition.value == 1 : condition.value == 0;
+		}
+		case EventCondition::DAYS_WITHOUT_TOWN:
+		{
+			if (p->daysWithoutCastle)
+				return p->daysWithoutCastle.get() >= condition.value;
+			else
+				return false;
+		}
+		case EventCondition::CONST_VALUE:
+		{
+			return condition.value; // just convert to bool
+		}
+	}
+	assert(0);
+	return false;
+}
+
+PlayerColor CGameState::checkForStandardWin() const
+{
+	//std victory condition is:
+	//all enemies lost
+	PlayerColor supposedWinner = PlayerColor::NEUTRAL;
+	TeamID winnerTeam = TeamID::NO_TEAM;
+	for(auto & elem : players)
+	{
+		if(elem.second.status == EPlayerStatus::INGAME && elem.first < PlayerColor::PLAYER_LIMIT)
+		{
+			if(supposedWinner == PlayerColor::NEUTRAL)
+			{
+				//first player remaining ingame - candidate for victory
+				supposedWinner = elem.second.color;
+				winnerTeam = elem.second.team;
+			}
+			else if(winnerTeam != elem.second.team)
+			{
+				//current candidate has enemy remaining in game -> no vicotry
+				return PlayerColor::NEUTRAL;
+			}
+		}
+	}
+
+	return supposedWinner;
+}
+
+bool CGameState::checkForStandardLoss( PlayerColor player ) const
+{
+	//std loss condition is: player lost all towns and heroes
+	const PlayerState &p = *CGameInfoCallback::getPlayer(player);
+	return !p.heroes.size() && !p.towns.size();
+}
+
+struct statsHLP
+{
+	typedef std::pair< PlayerColor, si64 > TStat;
+	//converts [<player's color, value>] to vec[place] -> platers
+	static std::vector< std::vector< PlayerColor > > getRank( std::vector<TStat> stats )
+	{
+		std::sort(stats.begin(), stats.end(), statsHLP());
+
+		//put first element
+		std::vector< std::vector<PlayerColor> > ret;
+		std::vector<PlayerColor> tmp;
+		tmp.push_back( stats[0].first );
+		ret.push_back( tmp );
+
+		//the rest of elements
+		for(int g=1; g<stats.size(); ++g)
+		{
+			if(stats[g].second == stats[g-1].second)
+			{
+				(ret.end()-1)->push_back( stats[g].first );
+			}
+			else
+			{
+				//create next occupied rank
+				std::vector<PlayerColor> tmp;
+				tmp.push_back(stats[g].first);
+				ret.push_back(tmp);
+			}
+		}
+
+		return ret;
+	}
+
+	bool operator()(const TStat & a, const TStat & b) const
+	{
+		return a.second > b.second;
+	}
+
+	static const CGHeroInstance * findBestHero(CGameState * gs, PlayerColor color)
+	{
+		std::vector<ConstTransitivePtr<CGHeroInstance> > &h = gs->players[color].heroes;
+		if(!h.size())
+			return nullptr;
+		//best hero will be that with highest exp
+		int best = 0;
+		for(int b=1; b<h.size(); ++b)
+		{
+			if(h[b]->exp > h[best]->exp)
+			{
+				best = b;
+			}
+		}
+		return h[best];
+	}
+
+	//calculates total number of artifacts that belong to given player
+	static int getNumberOfArts(const PlayerState * ps)
+	{
+		int ret = 0;
+		for(auto h : ps->heroes)
+		{
+			ret += h->artifactsInBackpack.size() + h->artifactsWorn.size();
+		}
+		return ret;
+	}
+
+	// get total strength of player army
+	static si64 getArmyStrength(const PlayerState * ps)
+	{
+		si64 str = 0;
+
+		for(auto h : ps->heroes)
+		{
+			if(!h->inTownGarrison)		//original h3 behavior
+				str += h->getArmyStrength();
+		}
+		return str;
+	}
+};
+
+void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
+{
+	auto playerInactive = [&](PlayerColor color)
+	{
+		return color == PlayerColor::NEUTRAL || players.at(color).status != EPlayerStatus::INGAME;
+	};
+
+#define FILL_FIELD(FIELD, VAL_GETTER) \
+	{ \
+		std::vector< std::pair< PlayerColor, si64 > > stats; \
+		for(auto g = players.begin(); g != players.end(); ++g) \
+		{ \
+			if(playerInactive(g->second.color)) \
+				continue; \
+			std::pair< PlayerColor, si64 > stat; \
+			stat.first = g->second.color; \
+			stat.second = VAL_GETTER; \
+			stats.push_back(stat); \
+		} \
+		tgi.FIELD = statsHLP::getRank(stats); \
+	}
+
+	for(auto & elem : players)
+	{
+		if(!playerInactive(elem.second.color))
+			tgi.playerColors.push_back(elem.second.color);
+	}
+
+	if(level >= 1) //num of towns & num of heroes
+	{
+		//num of towns
+		FILL_FIELD(numOfTowns, g->second.towns.size())
+		//num of heroes
+		FILL_FIELD(numOfHeroes, g->second.heroes.size())
+		//best hero's portrait
+		for(auto g = players.cbegin(); g != players.cend(); ++g)
+		{
+			if(playerInactive(g->second.color))
+				continue;
+			const CGHeroInstance * best = statsHLP::findBestHero(this, g->second.color);
+			InfoAboutHero iah;
+			iah.initFromHero(best, level >= 8);
+			iah.army.clear();
+			tgi.colorToBestHero[g->second.color] = iah;
+		}
+	}
+	if(level >= 2) //gold
+	{
+		FILL_FIELD(gold, g->second.resources[Res::GOLD])
+	}
+	if(level >= 2) //wood & ore
+	{
+		FILL_FIELD(woodOre, g->second.resources[Res::WOOD] + g->second.resources[Res::ORE])
+	}
+	if(level >= 3) //mercury, sulfur, crystal, gems
+	{
+		FILL_FIELD(mercSulfCrystGems, g->second.resources[Res::MERCURY] + g->second.resources[Res::SULFUR] + g->second.resources[Res::CRYSTAL] + g->second.resources[Res::GEMS])
+	}
+	if(level >= 4) //obelisks found
+	{
+		FILL_FIELD(obelisks, CGObelisk::visited[gs->getPlayerTeam(g->second.color)->id])
+	}
+	if(level >= 5) //artifacts
+	{
+		FILL_FIELD(artifacts, statsHLP::getNumberOfArts(&g->second))
+	}
+	if(level >= 6) //army strength
+	{
+		FILL_FIELD(army, statsHLP::getArmyStrength(&g->second))
+	}
+	if(level >= 7) //income
+	{
+		//TODO:obtainPlayersStats - income
+	}
+	if(level >= 8) //best hero's stats
+	{
+		//already set in  lvl 1 handling
+	}
+	if(level >= 9) //personality
+	{
+		for(auto g = players.cbegin(); g != players.cend(); ++g)
+		{
+			if(playerInactive(g->second.color)) //do nothing for neutral player
+				continue;
+			if(g->second.human)
+			{
+                tgi.personality[g->second.color] = EAiTactic::NONE;
+			}
+			else //AI
+			{
+                tgi.personality[g->second.color] = map->players[g->second.color.getNum()].aiTactic;
+			}
+
+		}
+	}
+	if(level >= 10) //best creature
+	{
+		//best creatures belonging to player (highest AI value)
+		for(auto g = players.cbegin(); g != players.cend(); ++g)
+		{
+			if(playerInactive(g->second.color)) //do nothing for neutral player
+				continue;
+			int bestCre = -1; //best creature's ID
+			for(auto & elem : g->second.heroes)
+			{
+				for(auto it = elem->Slots().begin(); it != elem->Slots().end(); ++it)
+				{
+					int toCmp = it->second->type->idNumber; //ID of creature we should compare with the best one
+					if(bestCre == -1 || VLC->creh->creatures[bestCre]->AIValue < VLC->creh->creatures[toCmp]->AIValue)
+					{
+						bestCre = toCmp;
+					}
+				}
+			}
+			tgi.bestCreature[g->second.color] = bestCre;
+		}
+	}
+
+#undef FILL_FIELD
+}
+
+std::map<ui32, ConstTransitivePtr<CGHeroInstance> > CGameState::unusedHeroesFromPool()
+{
+	std::map<ui32, ConstTransitivePtr<CGHeroInstance> > pool = hpool.heroesPool;
+	for ( auto i = players.cbegin() ; i != players.cend();i++)
+		for(auto j = i->second.availableHeroes.cbegin(); j != i->second.availableHeroes.cend(); j++)
+			if(*j)
+				pool.erase((**j).subID);
+
+	return pool;
+}
+
+void CGameState::buildBonusSystemTree()
+{
+	buildGlobalTeamPlayerTree();
+	attachArmedObjects();
+
+	for(CGTownInstance *t : map->towns)
+	{
+		t->deserializationFix();
+	}
+	// CStackInstance <-> CCreature, CStackInstance <-> CArmedInstance, CArtifactInstance <-> CArtifact
+	// are provided on initializing / deserializing
+}
+
+void CGameState::deserializationFix()
+{
+	buildGlobalTeamPlayerTree();
+	attachArmedObjects();
+}
+
+void CGameState::buildGlobalTeamPlayerTree()
+{
+	for(auto k=teams.begin(); k!=teams.end(); ++k)
+	{
+		TeamState *t = &k->second;
+		t->attachTo(&globalEffects);
+
+		for(PlayerColor teamMember : k->second.players)
+		{
+			PlayerState *p = getPlayer(teamMember);
+			assert(p);
+			p->attachTo(t);
+		}
+	}
+}
+
+void CGameState::attachArmedObjects()
+{
+	for(CGObjectInstance *obj : map->objects)
+	{
+		if(CArmedInstance *armed = dynamic_cast<CArmedInstance*>(obj))
+			armed->whatShouldBeAttached()->attachTo(armed->whereShouldBeAttached(this));
+	}
+}
+
+void CGameState::giveHeroArtifact(CGHeroInstance *h, ArtifactID aid)
+{
+	 CArtifact * const artifact = VLC->arth->artifacts[aid]; //pointer to constant object
+	 CArtifactInstance *ai = CArtifactInstance::createNewArtifactInstance(artifact);
+	 map->addNewArtifactInstance(ai);
+	 ai->putAt(ArtifactLocation(h, ai->firstAvailableSlot(h)));
+}
+
+std::set<HeroTypeID> CGameState::getUnusedAllowedHeroes(bool alsoIncludeNotAllowed /*= false*/) const
+{
+	std::set<HeroTypeID> ret;
+	for(int i = 0; i < map->allowedHeroes.size(); i++)
+		if(map->allowedHeroes[i] || alsoIncludeNotAllowed)
+			ret.insert(HeroTypeID(i));
+
+	for(auto hero : map->heroesOnMap)  //heroes instances initialization
+	{
+		if(hero->type)
+			ret -= hero->type->ID;
+		else
+			ret -= HeroTypeID(hero->subID);
+	}
+
+	for(auto obj : map->objects) //prisons
+		if(obj && obj->ID == Obj::PRISON)
+			ret -= HeroTypeID(obj->subID);
+
+	return ret;
+}
+
+std::vector<CGameState::CampaignHeroReplacement> CGameState::generateCampaignHeroesToReplace(CrossoverHeroesList & crossoverHeroes)
+{
+	std::vector<CampaignHeroReplacement> campaignHeroReplacements;
+
+	//selecting heroes by type
+	for(auto obj : map->objects)
+	{
+		if(obj && obj->ID == Obj::HERO_PLACEHOLDER)
+		{
+			auto heroPlaceholder = dynamic_cast<CGHeroPlaceholder *>(obj.get());
+			if(heroPlaceholder->subID != 0xFF) //select by type
+			{
+				auto it = range::find_if(crossoverHeroes.heroesFromAnyPreviousScenarios, [heroPlaceholder](CGHeroInstance * hero)
+				{
+					return hero->subID == heroPlaceholder->subID;
+				});
+
+				if(it != crossoverHeroes.heroesFromAnyPreviousScenarios.end())
+				{
+					auto hero = *it;
+					crossoverHeroes.removeHeroFromBothLists(hero);
+                    campaignHeroReplacements.push_back(CampaignHeroReplacement(CMemorySerializer::deepCopy(*hero).release(), heroPlaceholder->id));
+				}
+			}
+		}
+	}
+
+	//selecting heroes by power
+	range::sort(crossoverHeroes.heroesFromPreviousScenario, [](const CGHeroInstance * a, const CGHeroInstance * b)
+	{
+		return a->getHeroStrength() > b->getHeroStrength();
+	}); //sort, descending strength
+
+	// sort hero placeholders descending power
+	std::vector<CGHeroPlaceholder *> heroPlaceholders;
+	for(auto obj : map->objects)
+	{
+		if(obj && obj->ID == Obj::HERO_PLACEHOLDER)
+		{
+			auto heroPlaceholder = dynamic_cast<CGHeroPlaceholder *>(obj.get());
+			if(heroPlaceholder->subID == 0xFF) //select by power
+			{
+				heroPlaceholders.push_back(heroPlaceholder);
+			}
+		}
+	}
+	range::sort(heroPlaceholders, [](const CGHeroPlaceholder * a, const CGHeroPlaceholder * b)
+	{
+		return a->power > b->power;
+	});
+
+	for(int i = 0; i < heroPlaceholders.size(); ++i)
+	{
+		auto heroPlaceholder = heroPlaceholders[i];
+		if(crossoverHeroes.heroesFromPreviousScenario.size() > i)
+		{
+            auto hero = crossoverHeroes.heroesFromPreviousScenario[i];
+            campaignHeroReplacements.push_back(CampaignHeroReplacement(CMemorySerializer::deepCopy(*hero).release(), heroPlaceholder->id));
+		}
+	}
+
+	return campaignHeroReplacements;
+}
+
+void CGameState::replaceHeroesPlaceholders(const std::vector<CGameState::CampaignHeroReplacement> & campaignHeroReplacements)
+{
+	for(auto campaignHeroReplacement : campaignHeroReplacements)
+	{
+		auto heroPlaceholder = dynamic_cast<CGHeroPlaceholder*>(getObjInstance(campaignHeroReplacement.heroPlaceholderId));
+
+		CGHeroInstance *heroToPlace = campaignHeroReplacement.hero;
+		heroToPlace->id = campaignHeroReplacement.heroPlaceholderId;
+		heroToPlace->tempOwner = heroPlaceholder->tempOwner;
+		heroToPlace->pos = heroPlaceholder->pos;
+		heroToPlace->type = VLC->heroh->heroes[heroToPlace->subID];
+
+		for(auto &&i : heroToPlace->stacks)
+			i.second->type = VLC->creh->creatures[i.second->getCreatureID()];
+
+		auto fixArtifact = [&](CArtifactInstance * art)
+		{
+			art->artType = VLC->arth->artifacts[art->artType->id];
+			gs->map->artInstances.push_back(art);
+			art->id = ArtifactInstanceID(gs->map->artInstances.size() - 1);
+		};
+
+		for(auto &&i : heroToPlace->artifactsWorn)
+			fixArtifact(i.second.artifact);
+		for(auto &&i : heroToPlace->artifactsInBackpack)
+			fixArtifact(i.artifact);
+
+		map->heroesOnMap.push_back(heroToPlace);
+		map->objects[heroToPlace->id.getNum()] = heroToPlace;
+		map->addBlockVisTiles(heroToPlace);
+
+		scenarioOps->campState->getCurrentScenario().placedCrossoverHeroes.push_back(heroToPlace);
+	}
+}
+
+bool CGameState::isUsedHero(HeroTypeID hid) const
+{
+	return getUsedHero(hid);
+}
+
+CGHeroInstance * CGameState::getUsedHero(HeroTypeID hid) const
+{
+	for(auto hero : map->heroesOnMap)  //heroes instances initialization
+	{
+		if(hero->subID == hid.getNum())
+		{
+			return hero;
+		}
+	}
+
+	for(auto obj : map->objects) //prisons
+	{
+		if(obj && obj->ID == Obj::PRISON && obj->subID == hid.getNum())
+		{
+			return dynamic_cast<CGHeroInstance *>(obj.get());
+		}
+	}
+
+	return nullptr;
+}
+
+CGPathNode::CGPathNode()
+:coord(-1,-1,-1)
+{
+	accessible = NOT_SET;
+	land = 0;
+	moveRemains = 0;
+	turns = 255;
+	theNodeBefore = nullptr;
+}
+
+bool CGPathNode::reachable() const
+{
+	return turns < 255;
+}
+
+bool CPathsInfo::getPath( const int3 &dst, CGPath &out )
+{
+	assert(isValid);
+
+	out.nodes.clear();
+	const CGPathNode *curnode = &nodes[dst.x][dst.y][dst.z];
+	if(!curnode->theNodeBefore)
+		return false;
+
+
+	while(curnode)
+	{
+		CGPathNode cpn = *curnode;
+		curnode = curnode->theNodeBefore;
+		out.nodes.push_back(cpn);
+	}
+	return true;
+}
+
+CPathsInfo::CPathsInfo( const int3 &Sizes )
+:sizes(Sizes)
+{
+	hero = nullptr;
+	nodes = new CGPathNode**[sizes.x];
+	for(int i = 0; i < sizes.x; i++)
+	{
+		nodes[i] = new CGPathNode*[sizes.y];
+		for (int j = 0; j < sizes.y; j++)
+		{
+			nodes[i][j] = new CGPathNode[sizes.z];
+		}
+	}
+}
+
+CPathsInfo::~CPathsInfo()
+{
+	for(int i = 0; i < sizes.x; i++)
+	{
+		for (int j = 0; j < sizes.y; j++)
+		{
+			delete [] nodes[i][j];
+		}
+		delete [] nodes[i];
+	}
+	delete [] nodes;
+}
+
+int3 CGPath::startPos() const
+{
+	return nodes[nodes.size()-1].coord;
+}
+
+int3 CGPath::endPos() const
+{
+	return nodes[0].coord;
+}
+
+void CGPath::convert( ui8 mode )
+{
+	if(mode==0)
+	{
+		for(auto & elem : nodes)
+		{
+			elem.coord = CGHeroInstance::convertPosition(elem.coord,true);
+		}
+	}
+}
+
+PlayerState::PlayerState()
+ : color(-1), currentSelection(0xffffffff), enteredWinningCheatCode(0),
+   enteredLosingCheatCode(0), status(EPlayerStatus::INGAME)
+{
+	setNodeType(PLAYER);
+}
+
+std::string PlayerState::nodeName() const
+{
+	return "Player " + (color.getNum() < VLC->generaltexth->capColors.size() ? VLC->generaltexth->capColors[color.getNum()] : boost::lexical_cast<std::string>(color));
+}
+
+InfoAboutArmy::InfoAboutArmy():
+    owner(PlayerColor::NEUTRAL)
+{}
+
+InfoAboutArmy::InfoAboutArmy(const CArmedInstance *Army, bool detailed)
+{
+	initFromArmy(Army, detailed);
+}
+
+void InfoAboutArmy::initFromArmy(const CArmedInstance *Army, bool detailed)
+{
+	army = ArmyDescriptor(Army, detailed);
+	owner = Army->tempOwner;
+	name = Army->getHoverText();
+}
+
+void InfoAboutHero::assign(const InfoAboutHero & iah)
+{
+	InfoAboutArmy::operator = (iah);
+
+	details = (iah.details ? new Details(*iah.details) : nullptr);
+	hclass = iah.hclass;
+	portrait = iah.portrait;
+}
+
+InfoAboutHero::InfoAboutHero():
+    details(nullptr),
+    hclass(nullptr),
+    portrait(-1)
+{}
+
+InfoAboutHero::InfoAboutHero(const InfoAboutHero & iah):
+    InfoAboutArmy()
+{
+	assign(iah);
+}
+
+InfoAboutHero::InfoAboutHero(const CGHeroInstance *h, bool detailed)
+	: details(nullptr),
+	hclass(nullptr),
+	portrait(-1)
+{
+	initFromHero(h, detailed);
+}
+
+InfoAboutHero::~InfoAboutHero()
+{
+	delete details;
+}
+
+InfoAboutHero & InfoAboutHero::operator=(const InfoAboutHero & iah)
+{
+	assign(iah);
+	return *this;
+}
+
+void InfoAboutHero::initFromHero(const CGHeroInstance *h, bool detailed)
+{
+	if(!h)
+		return;
+
+	initFromArmy(h, detailed);
+
+	hclass = h->type->heroClass;
+	name = h->name;
+	portrait = h->portrait;
+
+	if(detailed)
+	{
+		//include details about hero
+		details = new Details;
+		details->luck = h->LuckVal();
+		details->morale = h->MoraleVal();
+		details->mana = h->mana;
+		details->primskills.resize(GameConstants::PRIMARY_SKILLS);
+
+		for (int i = 0; i < GameConstants::PRIMARY_SKILLS ; i++)
+		{
+			details->primskills[i] = h->getPrimSkillLevel(static_cast<PrimarySkill::PrimarySkill>(i));
+		}
+	}
+}
+
+InfoAboutTown::InfoAboutTown():
+    details(nullptr),
+    tType(nullptr),
+    built(0),
+    fortLevel(0)
+{
+
+}
+
+InfoAboutTown::InfoAboutTown(const CGTownInstance *t, bool detailed)
+{
+	initFromTown(t, detailed);
+}
+
+InfoAboutTown::~InfoAboutTown()
+{
+	delete details;
+}
+
+void InfoAboutTown::initFromTown(const CGTownInstance *t, bool detailed)
+{
+	initFromArmy(t, detailed);
+	army = ArmyDescriptor(t->getUpperArmy(), detailed);
+	built = t->builded;
+	fortLevel = t->fortLevel();
+	name = t->name;
+	tType = t->town;
+
+	if(detailed)
+	{
+		//include details about hero
+		details = new Details;
+		TResources income = t->dailyIncome();
+		details->goldIncome = income[Res::GOLD];
+		details->customRes = t->hasBuilt(BuildingID::RESOURCE_SILO);
+		details->hallLevel = t->hallLevel();
+		details->garrisonedHero = t->garrisonHero;
+	}
+}
+
+ArmyDescriptor::ArmyDescriptor(const CArmedInstance *army, bool detailed)
+    : isDetailed(detailed)
+{
+	for(auto & elem : army->Slots())
+	{
+		if(detailed)
+			(*this)[elem.first] = *elem.second;
+		else
+			(*this)[elem.first] = CStackBasicDescriptor(elem.second->type, elem.second->getQuantityID());
+	}
+}
+
+ArmyDescriptor::ArmyDescriptor()
+    : isDetailed(false)
+{
+
+}
+
+int ArmyDescriptor::getStrength() const
+{
+	ui64 ret = 0;
+	if(isDetailed)
+	{
+		for(auto & elem : *this)
+			ret += elem.second.type->AIValue * elem.second.count;
+	}
+	else
+	{
+		for(auto & elem : *this)
+			ret += elem.second.type->AIValue * CCreature::estimateCreatureCount(elem.second.count);
+	}
+	return ret;
+}
+
+DuelParameters::SideSettings::StackSettings::StackSettings()
+	: type(CreatureID::NONE), count(0)
+{
+}
+
+DuelParameters::SideSettings::StackSettings::StackSettings(CreatureID Type, si32 Count)
+	: type(Type), count(Count)
+{
+}
+
+DuelParameters::SideSettings::SideSettings()
+{
+	heroId = -1;
+}
+
+DuelParameters::DuelParameters():
+    terType(ETerrainType::DIRT),
+    bfieldType(BFieldType::ROCKLANDS)
+{
+}
+
+DuelParameters DuelParameters::fromJSON(const std::string &fname)
+{
+	DuelParameters ret;
+
+	const JsonNode duelData(ResourceID("DATA/" + fname, EResType::TEXT));
+	ret.terType = ETerrainType((int)duelData["terType"].Float());
+	ret.bfieldType = BFieldType((int)duelData["bfieldType"].Float());
+	for(const JsonNode &n : duelData["sides"].Vector())
+	{
+		SideSettings &ss = ret.sides[(int)n["side"].Float()];
+		int i = 0;
+		for(const JsonNode &stackNode : n["army"].Vector())
+		{
+			ss.stacks[i].type = CreatureID((si32)stackNode.Vector()[0].Float());
+			ss.stacks[i].count = stackNode.Vector()[1].Float();
+			i++;
+		}
+
+		if(n["heroid"].getType() == JsonNode::DATA_FLOAT)
+			ss.heroId = n["heroid"].Float();
+		else
+			ss.heroId = -1;
+
+		for(const JsonNode &entry : n["heroPrimSkills"].Vector())
+			ss.heroPrimSkills.push_back(entry.Float());
+
+		for(const JsonNode &skillNode : n["heroSecSkills"].Vector())
+		{
+			std::pair<si32, si8> secSkill;
+			secSkill.first = skillNode.Vector()[0].Float();
+			secSkill.second = skillNode.Vector()[1].Float();
+			ss.heroSecSkills.push_back(secSkill);
+		}
+
+		assert(ss.heroPrimSkills.empty() || ss.heroPrimSkills.size() == GameConstants::PRIMARY_SKILLS);
+
+		if(ss.heroId != -1)
+		{
+			const JsonNode & spells = n["spells"];
+			if(spells.getType() == JsonNode::DATA_STRING  &&  spells.String() == "all")
+			{
+				for(auto spell : VLC->spellh->objects)
+					if(spell->id <= SpellID::SUMMON_AIR_ELEMENTAL)
+						ss.spells.insert(spell->id);
+			}
+			else
+				for(const JsonNode &spell : n["spells"].Vector())
+					ss.spells.insert(SpellID(spell.Float()));
+		}
+	}
+
+	for(const JsonNode &n : duelData["obstacles"].Vector())
+	{
+		auto oi = make_shared<CObstacleInstance>();
+		if(n.getType() == JsonNode::DATA_VECTOR)
+		{
+			oi->ID = n.Vector()[0].Float();
+			oi->pos = n.Vector()[1].Float();
+		}
+		else
+		{
+			assert(n.getType() == JsonNode::DATA_FLOAT);
+			oi->ID = 21;
+			oi->pos = n.Float();
+		}
+		oi->uniqueID = ret.obstacles.size();
+		ret.obstacles.push_back(oi);
+	}
+
+	for(const JsonNode &n : duelData["creatures"].Vector())
+	{
+		CusomCreature cc;
+		cc.id = n["id"].Float();
+
+#define retrieve(name)								\
+	if(n[ #name ].getType() == JsonNode::DATA_FLOAT)\
+	cc.name = n[ #name ].Float();			\
+	else											\
+	cc.name = -1;
+
+		retrieve(attack);
+		retrieve(defense);
+		retrieve(HP);
+		retrieve(dmg);
+		retrieve(shoots);
+		retrieve(speed);
+		ret.creatures.push_back(cc);
+	}
+
+	return ret;
+}
+
+TeamState::TeamState()
+{
+	setNodeType(TEAM);
+}
+
+void CPathfinder::initializeGraph()
+{
+	CGPathNode ***graph = out.nodes;
+	for(size_t i=0; i < out.sizes.x; ++i)
+	{
+		for(size_t j=0; j < out.sizes.y; ++j)
+		{
+			for(size_t k=0; k < out.sizes.z; ++k)
+			{
+				curPos = int3(i,j,k);
+				const TerrainTile *tinfo = &gs->map->getTile(int3(i, j, k));
+				CGPathNode &node = graph[i][j][k];
+
+				node.accessible = evaluateAccessibility(tinfo);
+				node.turns = 0xff;
+				node.moveRemains = 0;
+				node.coord.x = i;
+				node.coord.y = j;
+				node.coord.z = k;
+                node.land = tinfo->terType != ETerrainType::WATER;
+				node.theNodeBefore = nullptr;
+			}
+		}
+	}
+}
+
+void CPathfinder::calculatePaths(int3 src /*= int3(-1,-1,-1)*/, int movement /*= -1*/)
+{
+	assert(hero);
+	assert(hero == getHero(hero->id));
+	if(src.x < 0)
+		src = hero->getPosition(false);
+	if(movement < 0)
+		movement = hero->movement;
+	bool flying = hero->hasBonusOfType(Bonus::FLYING_MOVEMENT);
+	int maxMovePointsLand = hero->maxMovePoints(true);
+	int maxMovePointsWater = hero->maxMovePoints(false);
+
+	auto maxMovePoints = [&](CGPathNode *cp) -> int
+	{
+		return cp->land ? maxMovePointsLand : maxMovePointsWater;
+	};
+
+	out.hero = hero;
+	out.hpos = src;
+
+	if(!gs->map->isInTheMap(src)/* || !gs->map->isInTheMap(dest)*/) //check input
+	{
+        logGlobal->errorStream() << "CGameState::calculatePaths: Hero outside the gs->map? How dare you...";
+		return;
+	}
+
+	initializeGraph();
+
+
+	//initial tile - set cost on 0 and add to the queue
+	CGPathNode &initialNode = *getNode(src);
+	initialNode.turns = 0;
+	initialNode.moveRemains = movement;
+	mq.push_back(&initialNode);
+
+	std::vector<int3> neighbours;
+	neighbours.reserve(16);
+	while(!mq.empty())
+	{
+		cp = mq.front();
+		mq.pop_front();
+
+		const int3 sourceGuardPosition = gs->map->guardingCreaturePositions[cp->coord.x][cp->coord.y][cp->coord.z];
+		bool guardedSource = (sourceGuardPosition != int3(-1, -1, -1) && cp->coord != src);
+		ct = &gs->map->getTile(cp->coord);
+
+		int movement = cp->moveRemains, turn = cp->turns;
+		if(!movement)
+		{
+			movement = maxMovePoints(cp);
+			turn++;
+		}
+
+
+		//add accessible neighbouring nodes to the queue
+		neighbours.clear();
+
+		//handling subterranean gate => it's exit is the only neighbour
+        bool subterraneanEntry = (ct->topVisitableId() == Obj::SUBTERRANEAN_GATE && useSubterraneanGates);
+		if(subterraneanEntry)
+		{
+			//try finding the exit gate
+			if(const CGObjectInstance *outGate = getObj(CGTeleport::getMatchingGate(ct->visitableObjects.back()->id), false))
+			{
+				const int3 outPos = outGate->visitablePos();
+				//gs->getNeighbours(*getTile(outPos), outPos, neighbours, boost::logic::indeterminate, !cp->land);
+				neighbours.push_back(outPos);
+			}
+			else
+			{
+				//gate with no exit (blocked) -> do nothing with this node
+				continue;
+			}
+		}
+
+		gs->getNeighbours(*ct, cp->coord, neighbours, boost::logic::indeterminate, !cp->land);
+
+		for(auto & neighbour : neighbours)
+		{
+			const int3 &n = neighbour; //current neighbor
+			dp = getNode(n);
+			dt = &gs->map->getTile(n);
+            destTopVisObjID = dt->topVisitableId();
+
+			useEmbarkCost = 0; //0 - usual movement; 1 - embark; 2 - disembark
+
+			int turnAtNextTile = turn;
+
+
+			const bool destIsGuardian = sourceGuardPosition == n;
+
+			if(!goodForLandSeaTransition())
+				continue;
+
+			if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED )
+				continue;
+
+			//special case -> hero embarked a boat standing on a guarded tile -> we must allow to move away from that tile
+            if(cp->accessible == CGPathNode::VISITABLE && guardedSource && cp->theNodeBefore->land && ct->topVisitableId() == Obj::BOAT)
+				guardedSource = false;
+
+			int cost = gs->getMovementCost(hero, cp->coord, dp->coord, flying, movement);
+
+			//special case -> moving from src Subterranean gate to dest gate -> it's free
+			if(subterraneanEntry && destTopVisObjID == Obj::SUBTERRANEAN_GATE && cp->coord.z != dp->coord.z)
+				cost = 0;
+
+			int remains = movement - cost;
+
+			if(useEmbarkCost)
+			{
+				remains = hero->movementPointsAfterEmbark(movement, cost, useEmbarkCost - 1);
+				cost = movement - remains;
+			}
+
+			if(remains < 0)
+			{
+				//occurs rarely, when hero with low movepoints tries to leave the road
+				turnAtNextTile++;
+				int moveAtNextTile = maxMovePoints(cp);
+				cost = gs->getMovementCost(hero, cp->coord, dp->coord, flying, moveAtNextTile); //cost must be updated, movement points changed :(
+				remains = moveAtNextTile - cost;
+			}
+
+			if((dp->turns==0xff		//we haven't been here before
+				|| dp->turns > turnAtNextTile
+				|| (dp->turns >= turnAtNextTile  &&  dp->moveRemains < remains)) //this route is faster
+				&& (!guardedSource || destIsGuardian)) // Can step into tile of guard
+			{
+
+				assert(dp != cp->theNodeBefore); //two tiles can't point to each other
+				dp->moveRemains = remains;
+				dp->turns = turnAtNextTile;
+				dp->theNodeBefore = cp;
+
+				const bool guardedDst = gs->map->guardingCreaturePositions[dp->coord.x][dp->coord.y][dp->coord.z].valid()
+										&& dp->accessible == CGPathNode::BLOCKVIS;
+
+				if (dp->accessible == CGPathNode::ACCESSIBLE
+					|| (useEmbarkCost && allowEmbarkAndDisembark)
+					|| destTopVisObjID == Obj::SUBTERRANEAN_GATE
+					|| (guardedDst && !guardedSource)) // Can step into a hostile tile once.
+				{
+					mq.push_back(dp);
+				}
+			}
+		} //neighbours loop
+	} //queue loop
+
+	out.isValid = true;
+}
+
+CGPathNode *CPathfinder::getNode(const int3 &coord)
+{
+	return &out.nodes[coord.x][coord.y][coord.z];
+}
+
+bool CPathfinder::canMoveBetween(const int3 &a, const int3 &b) const
+{
+	return gs->checkForVisitableDir(a, b) && gs->checkForVisitableDir(b, a);
+}
+
+CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const TerrainTile *tinfo) const
+{
+	CGPathNode::EAccessibility ret = (tinfo->blocked ? CGPathNode::BLOCKED : CGPathNode::ACCESSIBLE);
+
+
+    if(tinfo->terType == ETerrainType::ROCK || !FoW[curPos.x][curPos.y][curPos.z])
+		return CGPathNode::BLOCKED;
+
+	if(tinfo->visitable)
+	{
+		if(tinfo->visitableObjects.front()->ID == Obj::SANCTUARY && tinfo->visitableObjects.back()->ID == Obj::HERO && tinfo->visitableObjects.back()->tempOwner != hero->tempOwner) //non-owned hero stands on Sanctuary
+		{
+			return CGPathNode::BLOCKED;
+		}
+		else
+		{
+			for(const CGObjectInstance *obj : tinfo->visitableObjects)
+			{
+				if(obj->passableFor(hero->tempOwner)) //special object instance specific passableness flag - overwrites other accessibility flags
+				{
+					ret = CGPathNode::ACCESSIBLE;
+				}
+				else if(obj->blockVisit)
+				{
+					return CGPathNode::BLOCKVIS;
+				}
+				else if(obj->ID != Obj::EVENT) //pathfinder should ignore placed events
+				{
+					ret =  CGPathNode::VISITABLE;
+				}
+			}
+		}
+	}
+	else if (gs->map->guardingCreaturePositions[curPos.x][curPos.y][curPos.z].valid()
+		&& !tinfo->blocked)
+	{
+		// Monster close by; blocked visit for battle.
+		return CGPathNode::BLOCKVIS;
+	}
+
+	return ret;
+}
+
+bool CPathfinder::goodForLandSeaTransition()
+{
+	if(cp->land != dp->land) //hero can traverse land<->sea only in special circumstances
+	{
+		if(cp->land) //from land to sea -> embark or assault hero on boat
+		{
+			if(dp->accessible == CGPathNode::ACCESSIBLE || destTopVisObjID < 0) //cannot enter empty water tile from land -> it has to be visitable
+				return false;
+			if(destTopVisObjID != Obj::HERO && destTopVisObjID != Obj::BOAT) //only boat or hero can be accessed from land
+				return false;
+			if(destTopVisObjID == Obj::BOAT)
+				useEmbarkCost = 1;
+		}
+		else //disembark
+		{
+			//can disembark only on coastal tiles
+			if(!dt->isCoastal())
+				return false;
+
+			//tile must be accessible -> exception: unblocked blockvis tiles -> clear but guarded by nearby monster coast
+			if( (dp->accessible != CGPathNode::ACCESSIBLE && (dp->accessible != CGPathNode::BLOCKVIS || dt->blocked))
+				|| dt->visitable)  //TODO: passableness problem -> town says it's passable (thus accessible) but we obviously can't disembark onto town gate
+				return false;;
+
+			useEmbarkCost = 2;
+		}
+	}
+
+	return true;
+}
+
+CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero) : CGameInfoCallback(_gs, boost::optional<PlayerColor>()), out(_out), hero(_hero), FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap)
+{
+	useSubterraneanGates = true;
+	allowEmbarkAndDisembark = true;
+}
+
+EVictoryLossCheckResult::EVictoryLossCheckResult() :
+	intValue(0)
+{
+}
+
+EVictoryLossCheckResult::EVictoryLossCheckResult(si32 intValue, std::string toSelf, std::string toOthers) :
+	messageToSelf(toSelf),
+	messageToOthers(toOthers),
+	intValue(intValue)
+{
+}
+
+bool EVictoryLossCheckResult::operator==(EVictoryLossCheckResult const & other) const
+{
+	return intValue == other.intValue;
+}
+
+bool EVictoryLossCheckResult::operator!=(EVictoryLossCheckResult const & other) const
+{
+	return intValue != other.intValue;
+}
+
+bool EVictoryLossCheckResult::victory() const
+{
+	return intValue == VICTORY;
+}
+
+bool EVictoryLossCheckResult::loss() const
+{
+	return intValue == DEFEAT;
+}
+
+EVictoryLossCheckResult EVictoryLossCheckResult::invert()
+{
+	return EVictoryLossCheckResult(-intValue, messageToOthers, messageToSelf);
+}
+
+EVictoryLossCheckResult EVictoryLossCheckResult::victory(std::string toSelf, std::string toOthers)
+{
+	return EVictoryLossCheckResult(VICTORY, toSelf, toOthers);
+}
+
+EVictoryLossCheckResult EVictoryLossCheckResult::defeat(std::string toSelf, std::string toOthers)
+{
+	return EVictoryLossCheckResult(DEFEAT, toSelf, toOthers);
+}
+
+std::ostream & operator<<(std::ostream & os, const EVictoryLossCheckResult & victoryLossCheckResult)
+{
+	os << victoryLossCheckResult.messageToSelf;
+	return os;
+}
+
+CRandomGenerator & CGameState::getRandomGenerator()
+{
+	return rand;
+}

+ 2 - 2
lib/CGameState.h

@@ -14,7 +14,6 @@
 #include "IGameCallback.h"
 #include "ResourceSet.h"
 #include "int3.h"
-#include "CObjectHandler.h"
 #include "CRandomGenerator.h"
 
 /*
@@ -167,6 +166,7 @@ public:
 	ObjectInstanceID currentSelection; //id of hero/town, 0xffffffff if none
 	TeamID team;
 	TResources resources;
+	std::set<ObjectInstanceID> visitedObjects; // as a std::set, since most accesses here will be from visited status checks
 	std::vector<ConstTransitivePtr<CGHeroInstance> > heroes;
 	std::vector<ConstTransitivePtr<CGTownInstance> > towns;
 	std::vector<ConstTransitivePtr<CGHeroInstance> > availableHeroes; //heroes available in taverns
@@ -183,7 +183,7 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & color & human & currentSelection & team & resources & status;
-		h & heroes & towns & availableHeroes & dwellings;
+		h & heroes & towns & availableHeroes & dwellings & visitedObjects;
 		h & getBonusList(); //FIXME FIXME FIXME
 		h & status & daysWithoutCastle;
 		h & enteredLosingCheatCode & enteredWinningCheatCode;

+ 0 - 1
lib/CGeneralTextHandler.cpp

@@ -290,7 +290,6 @@ CGeneralTextHandler::CGeneralTextHandler()
 	readToVector("DATA/HEROSCRN.TXT", heroscrn);
 	readToVector("DATA/TENTCOLR.TXT", tentColors);
 	readToVector("DATA/SKILLLEV.TXT", levels);
-	readToVector("DATA/OBJNAMES.TXT", names);
 
 	localizedTexts = JsonNode(ResourceID("config/translate.json", EResType::TEXT));
 

+ 0 - 1
lib/CGeneralTextHandler.h

@@ -112,7 +112,6 @@ public:
 	std::vector<std::string> victoryConditions;
 
 	//objects
-	std::vector<std::string> names; //vector of objects; i-th object in vector has subnumber i
 	std::vector<std::string> creGens; //names of creatures' generators
 	std::vector<std::string> creGens4; //names of multiple creatures' generators
 	std::vector<std::string> advobtxt;

+ 30 - 19
lib/CHeroHandler.cpp

@@ -10,9 +10,11 @@
 #include "CCreatureHandler.h"
 #include "CModHandler.h"
 #include "CTownHandler.h"
-#include "CObjectHandler.h" //for hero specialty
+#include "mapObjects/CObjectHandler.h" //for hero specialty
 #include <math.h>
 
+#include "mapObjects/CObjectClassesHandler.h"
+
 /*
  * CHeroHandler.cpp, part of VCMI engine
  *
@@ -102,13 +104,12 @@ CHeroClass *CHeroClassHandler::loadFromJson(const JsonNode & node)
 
 	heroClass->imageBattleFemale = node["animation"]["battle"]["female"].String();
 	heroClass->imageBattleMale   = node["animation"]["battle"]["male"].String();
+	//MODS COMPATIBILITY FOR 0.96
 	heroClass->imageMapFemale    = node["animation"]["map"]["female"].String();
 	heroClass->imageMapMale      = node["animation"]["map"]["male"].String();
 
 	heroClass->name = node["name"].String();
 	heroClass->affinity = vstd::find_pos(affinityStr, node["affinity"].String());
-	if (heroClass->affinity >= 2) //FIXME: MODS COMPATIBILITY
-		heroClass->affinity = 0;
 
 	for(const std::string & pSkill : PrimarySkill::names)
 	{
@@ -122,15 +123,11 @@ CHeroClass *CHeroClassHandler::loadFromJson(const JsonNode & node)
 		heroClass->secSkillProbability.push_back(node["secondarySkills"][secSkill].Float());
 	}
 
-	//FIXME: MODS COMPATIBILITY
-	if (!node["commander"].isNull())
+	VLC->modh->identifiers.requestIdentifier ("creature", node["commander"],
+	[=](si32 commanderID)
 	{
-		VLC->modh->identifiers.requestIdentifier ("creature", node["commander"],
-		[=](si32 commanderID)
-		{
-			heroClass->commander = VLC->creh->creatures[commanderID];
-		});
-	}
+		heroClass->commander = VLC->creh->creatures[commanderID];
+	});
 
 	heroClass->defaultTavernChance = node["defaultTavern"].Float();
 	for(auto & tavern : node["tavern"].Struct())
@@ -200,6 +197,14 @@ void CHeroClassHandler::loadObject(std::string scope, std::string name, const Js
 
 	heroClasses.push_back(object);
 
+	VLC->modh->identifiers.requestIdentifier(scope, "object", "hero", [=](si32 index)
+	{
+		JsonNode classConf;
+		classConf["heroClass"].String() = name;
+		classConf.setMeta(scope);
+		VLC->objtypeh->loadSubObject(name, classConf, index, object->id);
+	});
+
 	VLC->modh->identifiers.registerObject(scope, "heroClass", name, object->id);
 }
 
@@ -211,6 +216,14 @@ void CHeroClassHandler::loadObject(std::string scope, std::string name, const Js
 	assert(heroClasses[index] == nullptr); // ensure that this id was not loaded before
 	heroClasses[index] = object;
 
+	VLC->modh->identifiers.requestIdentifier(scope, "object", "hero", [=](si32 index)
+	{
+		JsonNode classConf = data["mapObject"];
+		classConf["heroClass"].String() = name;
+		classConf.setMeta(scope);
+		VLC->objtypeh->loadSubObject(name, classConf, index, object->id);
+	});
+
 	VLC->modh->identifiers.registerObject(scope, "heroClass", name, object->id);
 }
 
@@ -231,16 +244,14 @@ void CHeroClassHandler::afterLoadFinalization()
 		}
 	}
 
-	ObjectTemplate base = VLC->dobjinfo->pickCandidates(Obj::HERO, 0).front();
 	for (CHeroClass * hc : heroClasses)
 	{
-		base.animationFile = hc->imageMapMale;
-		base.subid = hc->id;
-
-		// replace existing (if any) and add new template.
-		// Necessary for objects added via mods that don't have any templates in H3
-		VLC->dobjinfo->eraseAll(Obj::HERO, hc->id);
-		VLC->dobjinfo->registerTemplate(base);
+		if (!hc->imageMapMale.empty())
+		{
+			JsonNode templ;
+			templ["animation"].String() = hc->imageMapMale;
+			VLC->objtypeh->getHandlerFor(Obj::HERO, hc->id)->addTemplate(templ);
+		}
 	}
 }
 

+ 35 - 16
lib/CMakeLists.txt

@@ -6,20 +6,6 @@ include_directories(${Boost_INCLUDE_DIRS} ${SDL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}
 
 set(lib_SRCS
 		StdInc.cpp
-		IGameCallback.cpp
-		CGameInfoCallback.cpp
-		CGameState.cpp
-		CObjectHandler.cpp
-		Connection.cpp
-		NetPacksLib.cpp
-
-		registerTypes/RegisterTypes.cpp
-		registerTypes/TypesClientPacks1.cpp
-		registerTypes/TypesClientPacks2.cpp
-		registerTypes/TypesMapObjects1.cpp
-                registerTypes/TypesMapObjects2.cpp
-		registerTypes/TypesPregamePacks.cpp
-		registerTypes/TypesServerPacks.cpp
 
 		filesystem/AdapterLoaders.cpp
 		filesystem/CCompressedStream.cpp
@@ -33,6 +19,22 @@ set(lib_SRCS
 		filesystem/Filesystem.cpp
 		filesystem/ResourceID.cpp
 
+		mapObjects/CArmedInstance.cpp
+		mapObjects/CBank.cpp
+		mapObjects/CGHeroInstance.cpp
+		mapObjects/CGMarket.cpp
+		mapObjects/CGPandoraBox.cpp
+		mapObjects/CGTownInstance.cpp
+		mapObjects/CObjectClassesHandler.cpp
+		mapObjects/CObjectHandler.cpp
+		mapObjects/CommonConstructors.cpp
+		mapObjects/CQuest.cpp
+		mapObjects/CRewardableConstructor.cpp
+		mapObjects/CRewardableObject.cpp
+		mapObjects/JsonRandom.cpp
+		mapObjects/MiscObjects.cpp
+		mapObjects/ObjectTemplate.cpp
+
 		logging/CBasicLogConfigurator.cpp
 		logging/CLogger.cpp
 
@@ -63,7 +65,6 @@ set(lib_SRCS
 		CConsoleHandler.cpp
 		CCreatureHandler.cpp
 		CCreatureSet.cpp
-		CDefObjInfoHandler.cpp
 		CGameInterface.cpp
 		CGeneralTextHandler.cpp
 		CHeroHandler.cpp
@@ -81,6 +82,21 @@ set(lib_SRCS
 		ResourceSet.cpp
 		VCMI_Lib.cpp
 		VCMIDirs.cpp
+
+		IGameCallback.cpp
+		CGameInfoCallback.cpp
+		CGameState.cpp
+		Connection.cpp
+		NetPacksLib.cpp
+
+		registerTypes/RegisterTypes.cpp
+		registerTypes/TypesClientPacks1.cpp
+		registerTypes/TypesClientPacks2.cpp
+		registerTypes/TypesMapObjects1.cpp
+		registerTypes/TypesMapObjects2.cpp
+                registerTypes/TypesMapObjects3.cpp
+		registerTypes/TypesPregamePacks.cpp
+		registerTypes/TypesServerPacks.cpp
 )
 
 set(lib_HEADERS
@@ -89,6 +105,9 @@ set(lib_HEADERS
 		filesystem/CInputStream.h
 		filesystem/ISimpleResourceLoader.h
 
+		mapObjects/MapObjects.h
+
+		CSoundBase.h
 		AI_Base.h
 		CondSh.h
 		ConstTransitivePtr.h
@@ -118,5 +137,5 @@ set_target_properties(vcmi PROPERTIES ${PCH_PROPERTIES})
 cotire(vcmi)
 
 if (NOT APPLE) # Already inside vcmiclient bundle
-    install(TARGETS vcmi DESTINATION ${LIB_DIR})
+	install(TARGETS vcmi DESTINATION ${LIB_DIR})
 endif()

+ 37 - 10
lib/CModHandler.cpp

@@ -1,6 +1,6 @@
 #include "StdInc.h"
 #include "CModHandler.h"
-#include "CDefObjInfoHandler.h"
+#include "mapObjects/CObjectClassesHandler.h"
 #include "JsonNode.h"
 #include "filesystem/Filesystem.h"
 #include "filesystem/AdapterLoaders.h"
@@ -10,7 +10,7 @@
 #include "CArtHandler.h"
 #include "CTownHandler.h"
 #include "CHeroHandler.h"
-#include "CObjectHandler.h"
+#include "mapObjects/CObjectHandler.h"
 #include "StringConstants.h"
 #include "CStopWatch.h"
 #include "IHandlerBase.h"
@@ -26,6 +26,11 @@
  *
  */
 
+CIdentifierStorage::CIdentifierStorage():
+	state(LOADING)
+{
+}
+
 void CIdentifierStorage::checkIdentifier(std::string & ID)
 {
 	if (boost::algorithm::ends_with(ID, "."))
@@ -81,7 +86,10 @@ void CIdentifierStorage::requestIdentifier(ObjectCallback callback)
 
 	assert(!callback.localScope.empty());
 
-	scheduledRequests.push_back(callback);
+	if (state != FINISHED) // enqueue request if loading is still in progress
+		scheduledRequests.push_back(callback);
+	else // execute immediately for "late" requests
+		resolveIdentifier(callback);
 }
 
 void CIdentifierStorage::requestIdentifier(std::string scope, std::string type, std::string name, const std::function<void(si32)> & callback)
@@ -120,6 +128,19 @@ void CIdentifierStorage::tryRequestIdentifier(std::string type, const JsonNode &
 	requestIdentifier(ObjectCallback(name.meta, pair.first, type, pair.second, callback, true));
 }
 
+boost::optional<si32> CIdentifierStorage::getIdentifier(std::string scope, std::string type, std::string name, bool silent)
+{
+	auto pair = splitString(name, ':'); // remoteScope:name
+	auto idList = getPossibleIdentifiers(ObjectCallback(scope, pair.first, type, pair.second, std::function<void(si32)>(), silent));
+
+	if (idList.size() == 1)
+		return idList.front().id;
+	if (!silent)
+		logGlobal->errorStream() << "Failed to resolve identifier " << name << " of type " << type << " from mod " << scope;
+
+	return boost::optional<si32>();
+}
+
 boost::optional<si32> CIdentifierStorage::getIdentifier(std::string type, const JsonNode & name, bool silent)
 {
 	auto pair = splitString(name.String(), ':'); // remoteScope:name
@@ -128,7 +149,7 @@ boost::optional<si32> CIdentifierStorage::getIdentifier(std::string type, const
 	if (idList.size() == 1)
 		return idList.front().id;
 	if (!silent)
-		logGlobal->errorStream() << "Failed to resolve identifier " << name.String() << " from mod " << type;
+		logGlobal->errorStream() << "Failed to resolve identifier " << name.String() << " of type " << type << " from mod " << name.meta;
 
 	return boost::optional<si32>();
 }
@@ -142,7 +163,7 @@ boost::optional<si32> CIdentifierStorage::getIdentifier(const JsonNode & name, b
 	if (idList.size() == 1)
 		return idList.front().id;
 	if (!silent)
-		logGlobal->errorStream() << "Failed to resolve identifier " << name.String() << " from mod " << name.meta;
+		logGlobal->errorStream() << "Failed to resolve identifier " << name.String() << " of type " << pair2.first << " from mod " << name.meta;
 
 	return boost::optional<si32>();
 }
@@ -210,7 +231,9 @@ bool CIdentifierStorage::resolveIdentifier(const ObjectCallback & request)
 	}
 
 	if (request.optional && identifiers.empty()) // failed to resolve optinal ID
+	{
 		return true;
+	}
 
 	// error found. Try to generate some debug info
 	if (identifiers.size() == 0)
@@ -229,22 +252,25 @@ bool CIdentifierStorage::resolveIdentifier(const ObjectCallback & request)
 
 void CIdentifierStorage::finalize()
 {
+	state = FINALIZING;
 	bool errorsFound = false;
 
-	for(const ObjectCallback & request : scheduledRequests)
+	//Note: we may receive new requests during resolution phase -> end may change -> range for can't be used
+	for(auto it = scheduledRequests.begin(); it != scheduledRequests.end(); it++)
 	{
-		errorsFound |= !resolveIdentifier(request);
+		errorsFound |= !resolveIdentifier(*it);
 	}
 
 	if (errorsFound)
 	{
 		for(auto object : registeredObjects)
 		{
-			logGlobal->traceStream() << object.first << " -> " << object.second.id;
+			logGlobal->traceStream() << object.second.scope << " : " << object.first << " -> " << object.second.id;
 		}
 		logGlobal->errorStream() << "All known identifiers were dumped into log file";
 	}
 	assert(errorsFound == false);
+	state = FINISHED;
 }
 
 CContentHandler::ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, std::string objectName):
@@ -347,10 +373,11 @@ CContentHandler::CContentHandler()
 	handlers.insert(std::make_pair("artifacts", ContentTypeHandler(VLC->arth, "artifact")));
 	handlers.insert(std::make_pair("creatures", ContentTypeHandler(VLC->creh, "creature")));
 	handlers.insert(std::make_pair("factions", ContentTypeHandler(VLC->townh, "faction")));
+	handlers.insert(std::make_pair("objects", ContentTypeHandler(VLC->objtypeh, "object")));
 	handlers.insert(std::make_pair("heroes", ContentTypeHandler(VLC->heroh, "hero")));
-    handlers.insert(std::make_pair("spells", ContentTypeHandler(VLC->spellh, "spell")));
+	handlers.insert(std::make_pair("spells", ContentTypeHandler(VLC->spellh, "spell")));
 
-	//TODO: bonuses, something else?
+	//TODO: any other types of moddables?
 }
 
 bool CContentHandler::preloadModData(std::string modName, JsonNode modConfig, bool validate)

+ 12 - 1
lib/CModHandler.h

@@ -25,6 +25,13 @@ class IHandlerBase;
 /// if possible, objects ID's should be in format <type>.<name>, camelCase e.g. "creature.grandElf"
 class CIdentifierStorage
 {
+	enum ELoadingState
+	{
+		LOADING,
+		FINALIZING,
+		FINISHED
+	};
+
 	struct ObjectCallback // entry created on ID request
 	{
 		std::string localScope;  /// scope from which this ID was requested
@@ -52,6 +59,8 @@ class CIdentifierStorage
 	std::multimap<std::string, ObjectData > registeredObjects;
 	std::vector<ObjectCallback> scheduledRequests;
 
+	ELoadingState state;
+
 	/// Check if identifier can be valid (camelCase, point as separator)
 	void checkIdentifier(std::string & ID);
 
@@ -59,6 +68,7 @@ class CIdentifierStorage
 	bool resolveIdentifier(const ObjectCallback & callback);
 	std::vector<ObjectData> getPossibleIdentifiers(const ObjectCallback & callback);
 public:
+	CIdentifierStorage();
 	/// request identifier for specific object name.
 	/// Function callback will be called during ID resolution phase of loading
 	void requestIdentifier(std::string scope, std::string type, std::string name, const std::function<void(si32)> & callback);
@@ -70,6 +80,7 @@ public:
 	void tryRequestIdentifier(std::string type, const JsonNode & name, const std::function<void(si32)> & callback);
 
 	/// get identifier immediately. If identifier is not know and not silent call will result in error message
+	boost::optional<si32> getIdentifier(std::string scope, std::string type, std::string name, bool silent = false);
 	boost::optional<si32> getIdentifier(std::string type, const JsonNode & name, bool silent = false);
 	boost::optional<si32> getIdentifier(const JsonNode & name, bool silent = false);
 
@@ -81,7 +92,7 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & registeredObjects;
+		h & registeredObjects & state;
 	}
 };
 

+ 0 - 7777
lib/CObjectHandler.cpp

@@ -1,7777 +0,0 @@
-/*
- * CObjectHandler.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 "CObjectHandler.h"
-
-#include "CDefObjInfoHandler.h"
-#include "CGeneralTextHandler.h"
-#include "CDefObjInfoHandler.h"
-#include "CHeroHandler.h"
-#include "CSpellHandler.h"
-#include "CModHandler.h"
-#include "../client/CSoundBase.h"
-#include "CTownHandler.h"
-#include "CCreatureHandler.h"
-#include "VCMI_Lib.h"
-#include "IGameCallback.h"
-#include "CGameState.h"
-#include "NetPacks.h"
-#include "StartInfo.h"
-#include "mapping/CMap.h"
-#include <SDL_stdinc.h>
-#include "CBuildingHandler.h"
-#include "JsonNode.h"
-#include "filesystem/Filesystem.h"
-
-using namespace boost::assign;
-
-std::map<Obj, std::map<int, std::vector<ObjectInstanceID> > > CGTeleport::objs;
-std::vector<std::pair<ObjectInstanceID, ObjectInstanceID> > CGTeleport::gates;
-IGameCallback * IObjectInterface::cb = nullptr;
-std::map <PlayerColor, std::set <ui8> > CGKeys::playerKeyMap;
-std::map <si32, std::vector<ObjectInstanceID> > CGMagi::eyelist;
-ui8 CGObelisk::obeliskCount; //how many obelisks are on map
-std::map<TeamID, ui8> CGObelisk::visited; //map: team_id => how many obelisks has been visited
-
-std::vector<const CArtifact *> CGTownInstance::merchantArtifacts;
-std::vector<int> CGTownInstance::universitySkills;
-
-///helpers
-static void openWindow(const OpenWindow::EWindow type, const int id1, const int id2 = -1)
-{
-	OpenWindow ow;
-	ow.window = type;
-	ow.id1 = id1;
-	ow.id2 = id2;
-	IObjectInterface::cb->sendAndApply(&ow);
-}
-
-static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const ui16 soundID)
-{
-	InfoWindow iw;
-	iw.soundID = soundID;
-	iw.player = playerID;
-	iw.text.addTxt(MetaString::ADVOB_TXT,txtID);
-	IObjectInterface::cb->sendAndApply(&iw);
-}
-
-/*static void showInfoDialog(const ObjectInstanceID heroID, const ui32 txtID, const ui16 soundID)
-{
-	const PlayerColor playerID = IObjectInterface::cb->getOwner(heroID);
-	showInfoDialog(playerID,txtID,soundID);
-}*/
-
-static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 soundID)
-{
-	const PlayerColor playerID = h->getOwner();
-	showInfoDialog(playerID,txtID,soundID);
-}
-
-static std::string & visitedTxt(const bool visited)
-{
-	int id = visited ? 352 : 353;
-	return VLC->generaltexth->allTexts[id];
-}
-
-///IObjectInterface
-void IObjectInterface::onHeroVisit(const CGHeroInstance * h) const
-{}
-
-void IObjectInterface::onHeroLeave(const CGHeroInstance * h) const
-{}
-
-void IObjectInterface::newTurn () const
-{}
-
-IObjectInterface::~IObjectInterface()
-{}
-
-IObjectInterface::IObjectInterface()
-{}
-
-void IObjectInterface::initObj()
-{}
-
-void IObjectInterface::setProperty( ui8 what, ui32 val )
-{}
-
-bool IObjectInterface::wasVisited (PlayerColor player) const
-{
-	return false;
-}
-bool IObjectInterface::wasVisited (const CGHeroInstance * h) const
-{
-	return false;
-}
-
-void IObjectInterface::postInit()
-{}
-
-void IObjectInterface::preInit()
-{}
-
-void IObjectInterface::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
-{
-
-}
-
-void IObjectInterface::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
-{
-
-}
-
-void IObjectInterface::garrisonDialogClosed(const CGHeroInstance *hero) const
-{
-
-}
-
-void IObjectInterface::heroLevelUpDone(const CGHeroInstance *hero) const
-{
-
-}
-
-void CPlayersVisited::setPropertyDer( ui8 what, ui32 val )
-{
-	if(what == 10)
-		players.insert(PlayerColor(val));
-}
-
-bool CPlayersVisited::wasVisited( PlayerColor player ) const
-{
-	return vstd::contains(players,player);
-}
-
-bool CPlayersVisited::wasVisited( TeamID team ) const
-{
-	for(auto i : players)
-	{
-		if(cb->getPlayer(i)->team == team)
-			return true;
-	}
-	return false;
-}
-
-// Bank helper. Find the creature ID and their number, and store the
-// result in storage (either guards or reward creatures).
-static void readCreatures(const JsonNode &creature, std::vector< std::pair <CreatureID, ui32> > &storage)
-{
-	std::pair<CreatureID, si32> creInfo = std::make_pair(CreatureID::NONE, 0);
-
-	//TODO: replace numeric id's with mod-friendly string id's
-	creInfo.second = creature["number"].Float();
-	creInfo.first = CreatureID((si32)creature["id"].Float());
-	storage.push_back(creInfo);
-}
-
-// Bank helper. Process a bank level.
-static void readBankLevel(const JsonNode &level, BankConfig &bc)
-{
-	int idx;
-
-	bc.chance = level["chance"].Float();
-
-	for(const JsonNode &creature : level["guards"].Vector())
-	{
-		readCreatures(creature, bc.guards);
-	}
-
-	bc.upgradeChance = level["upgrade_chance"].Float();
-	bc.combatValue = level["combat_value"].Float();
-
-	bc.resources = Res::ResourceSet(level["reward_resources"]);
-
-	for(const JsonNode &creature : level["reward_creatures"].Vector())
-	{
-		readCreatures(creature, bc.creatures);
-	}
-
-	bc.artifacts.resize(4);
-	idx = 0;
-	for(const JsonNode &artifact : level["reward_artifacts"].Vector())
-	{
-		bc.artifacts[idx] = artifact.Float();
-		idx ++;
-	}
-
-	bc.value = level["value"].Float();
-	bc.rewardDifficulty = level["profitability"].Float();
-	bc.easiest = level["easiest"].Float();
-}
-
-CObjectHandler::CObjectHandler()
-{
-    logGlobal->traceStream() << "\t\tReading cregens ";
-
-	const JsonNode config(ResourceID("config/dwellings.json"));
-	for(const JsonNode &dwelling : config["dwellings"].Vector())
-	{
-		cregens[dwelling["dwelling"].Float()] = CreatureID((si32)dwelling["creature"].Float());
-	}
-    logGlobal->traceStream() << "\t\tDone loading cregens!";
-
-    logGlobal->traceStream() << "\t\tReading resources prices ";
-	const JsonNode config2(ResourceID("config/resources.json"));
-	for(const JsonNode &price : config2["resources_prices"].Vector())
-	{
-		resVals.push_back(price.Float());
-	}
-    logGlobal->traceStream() << "\t\tDone loading resource prices!";
-
-    logGlobal->traceStream() << "\t\tReading banks configs";
-	const JsonNode config3(ResourceID("config/bankconfig.json"));
-	int bank_num = 0;
-	for(const JsonNode &bank : config3["banks"].Vector())
-	{
-		creBanksNames[bank_num] = bank["name"].String();
-
-		int level_num = 0;
-		for(const JsonNode &level : bank["levels"].Vector())
-		{
-			banksInfo[bank_num].push_back(new BankConfig);
-			BankConfig &bc = *banksInfo[bank_num].back();
-			bc.level = level_num;
-
-			readBankLevel(level, bc);
-			level_num ++;
-		}
-
-		bank_num ++;
-	}
-    logGlobal->traceStream() << "\t\tDone loading banks configs";
-}
-
-CObjectHandler::~CObjectHandler()
-{
-	for(auto & mapEntry : banksInfo)
-	{
-		for(auto & vecEntry : mapEntry.second)
-		{
-			vecEntry.dellNull();
-		}
-	}
-}
-
-int CObjectHandler::bankObjToIndex (const CGObjectInstance * obj)
-{
-	switch (obj->ID) //find appriopriate key
-	{
-	case Obj::CREATURE_BANK:
-		return obj->subID;
-	case Obj::DERELICT_SHIP:
-		return 8;
-	case Obj::DRAGON_UTOPIA:
-		return 10;
-	case Obj::CRYPT:
-		return 9;
-	case Obj::SHIPWRECK:
-		return 7;
-	case Obj::PYRAMID:
-		return 21;
-	default:
-        logGlobal->warnStream() << "Unrecognized Bank indetifier!";
-		return 0;
-	}
-}
-PlayerColor CGObjectInstance::getOwner() const
-{
-	//if (state)
-	//	return state->owner;
-	//else
-		return tempOwner; //won't have owner
-}
-
-CGObjectInstance::CGObjectInstance():
-	pos(-1,-1,-1),
-	ID(Obj::NO_OBJ),
-	subID(-1),
-	tempOwner(PlayerColor::UNFLAGGABLE),
-	blockVisit(false)
-{
-}
-CGObjectInstance::~CGObjectInstance()
-{
-	//if (state)
-	//	delete state;
-	//state=nullptr;
-}
-
-const std::string & CGObjectInstance::getHoverText() const
-{
-	return hoverName;
-}
-void CGObjectInstance::setOwner(PlayerColor ow)
-{
-	//if (state)
-	//	state->owner = ow;
-	//else
-		tempOwner = ow;
-}
-int CGObjectInstance::getWidth() const//returns width of object graphic in tiles
-{
-	return appearance.getWidth();
-}
-int CGObjectInstance::getHeight() const //returns height of object graphic in tiles
-{
-	return appearance.getHeight();
-}
-bool CGObjectInstance::visitableAt(int x, int y) const //returns true if object is visitable at location (x, y) form left top tile of image (x, y in tiles)
-{
-	return appearance.isVisitableAt(pos.x - x, pos.y - y);
-}
-bool CGObjectInstance::blockingAt(int x, int y) const
-{
-	return appearance.isBlockedAt(pos.x - x, pos.y - y);
-}
-
-bool CGObjectInstance::coveringAt(int x, int y) const
-{
-	return appearance.isVisibleAt(pos.x - x, pos.y - y);
-}
-
-std::set<int3> CGObjectInstance::getBlockedPos() const
-{
-	std::set<int3> ret;
-	for(int w=0; w<getWidth(); ++w)
-	{
-		for(int h=0; h<getHeight(); ++h)
-		{
-			if (appearance.isBlockedAt(w, h))
-				ret.insert(int3(pos.x - w, pos.y - h, pos.z));
-		}
-	}
-	return ret;
-}
-
-std::set<int3> CGObjectInstance::getBlockedOffsets() const
-{
-	std::set<int3> ret;
-	for(int w=0; w<getWidth(); ++w)
-	{
-		for(int h=0; h<getHeight(); ++h)
-		{
-			if (appearance.isBlockedAt(w, h))
-				ret.insert(int3(-w, -h, 0));
-		}
-	}
-	return ret;
-}
-
-
-bool CGObjectInstance::operator<(const CGObjectInstance & cmp) const  //screen printing priority comparing
-{
-	if (appearance.printPriority != cmp.appearance.printPriority)
-		return appearance.printPriority > cmp.appearance.printPriority;
-
-	if(pos.y != cmp.pos.y)
-		return pos.y < cmp.pos.y;
-
-	if(cmp.ID==Obj::HERO && ID!=Obj::HERO)
-		return true;
-	if(cmp.ID!=Obj::HERO && ID==Obj::HERO)
-		return false;
-
-	if(!isVisitable() && cmp.isVisitable())
-		return true;
-	if(!cmp.isVisitable() && isVisitable())
-		return false;
-	if(this->pos.x<cmp.pos.x)
-		return true;
-	return false;
-}
-
-void CGObjectInstance::initObj()
-{
-	switch(ID)
-	{
-	case Obj::TAVERN:
-		blockVisit = true;
-		break;
-	}
-}
-
-void CGObjectInstance::setProperty( ui8 what, ui32 val )
-{
-	switch(what)
-	{
-	case ObjProperty::OWNER:
-		tempOwner = PlayerColor(val);
-		break;
-	case ObjProperty::BLOCKVIS:
-		blockVisit = val;
-		break;
-	case ObjProperty::ID:
-		ID = Obj(val);
-		break;
-	case ObjProperty::SUBID:
-		subID = val;
-		break;
-	}
-	setPropertyDer(what, val);
-}
-
-void CGObjectInstance::setPropertyDer( ui8 what, ui32 val )
-{}
-
-int3 CGObjectInstance::getSightCenter() const
-{
-	//return vistiable tile if possible
-	for(int i=0; i < 8; i++)
-		for(int j=0; j < 6; j++)
-			if(visitableAt(i,j))
-				return(pos + int3(i-7, j-5, 0));
-	return pos;
-}
-
-int CGObjectInstance::getSightRadious() const
-{
-	return 3;
-}
-void CGObjectInstance::getSightTiles(std::unordered_set<int3, ShashInt3> &tiles) const //returns reference to the set
-{
-	cb->getTilesInRange(tiles, getSightCenter(), getSightRadious(), tempOwner, 1);
-}
-void CGObjectInstance::hideTiles(PlayerColor ourplayer, int radius) const
-{
-	for (auto i = cb->gameState()->teams.begin(); i != cb->gameState()->teams.end(); i++)
-	{
-		if ( !vstd::contains(i->second.players, ourplayer ))//another team
-		{
-			for (auto & elem : i->second.players)
-				if ( cb->getPlayer(elem)->status == EPlayerStatus::INGAME )//seek for living player (if any)
-				{
-					FoWChange fw;
-					fw.mode = 0;
-					fw.player = elem;
-					cb->getTilesInRange (fw.tiles, pos, radius, (elem), -1);
-					cb->sendAndApply (&fw);
-					break;
-				}
-		}
-	}
-}
-int3 CGObjectInstance::getVisitableOffset() const
-{
-	for(int y = 0; y < appearance.getHeight(); y++)
-		for (int x = 0; x < appearance.getWidth(); x++)
-			if (appearance.isVisitableAt(x, y))
-				return int3(x,y,0);
-
-    logGlobal->warnStream() << "Warning: getVisitableOffset called on non-visitable obj!";
-	return int3(0,0,0);
-}
-
-void CGObjectInstance::getNameVis( std::string &hname ) const
-{
-	const CGHeroInstance *h = cb->getSelectedHero(cb->getCurrentPlayer());
-	hname = VLC->generaltexth->names[ID];
-	if(h)
-	{
-		const bool visited = h->hasBonusFrom(Bonus::OBJECT,ID);
-		hname + " " + visitedTxt(visited);
-	}
-}
-
-void CGObjectInstance::giveDummyBonus(ObjectInstanceID heroID, ui8 duration) const
-{
-	GiveBonus gbonus;
-	gbonus.bonus.type = Bonus::NONE;
-	gbonus.id = heroID.getNum();
-	gbonus.bonus.duration = duration;
-	gbonus.bonus.source = Bonus::OBJECT;
-	gbonus.bonus.sid = ID;
-	cb->giveHeroBonus(&gbonus);
-}
-
-void CGObjectInstance::onHeroVisit( const CGHeroInstance * h ) const
-{
-	switch(ID)
-	{
-	case Obj::HILL_FORT:
-		{
-			openWindow(OpenWindow::HILL_FORT_WINDOW,id.getNum(),h->id.getNum());
-		}
-		break;
-	case Obj::SANCTUARY:
-		{
-			//You enter the sanctuary and immediately feel as if a great weight has been lifted off your shoulders.  You feel safe here.
-			showInfoDialog(h,114,soundBase::GETPROTECTION);
-		}
-		break;
-	case Obj::TAVERN:
-		{
-			openWindow(OpenWindow::TAVERN_WINDOW,h->id.getNum(),id.getNum());
-		}
-		break;
-	}
-}
-
-ui8 CGObjectInstance::getPassableness() const
-{
-	return 0;
-}
-
-int3 CGObjectInstance::visitablePos() const
-{
-	return pos - getVisitableOffset();
-}
-
-bool CGObjectInstance::isVisitable() const
-{
-	return appearance.isVisitable();
-}
-
-bool CGObjectInstance::passableFor(PlayerColor color) const
-{
-	return getPassableness() & 1<<color.getNum();
-}
-
-CGObjectInstanceBySubIdFinder::CGObjectInstanceBySubIdFinder(CGObjectInstance * obj) : obj(obj)
-{
-
-}
-
-bool CGObjectInstanceBySubIdFinder::operator()(CGObjectInstance * obj) const
-{
-	return this->obj->subID == obj->subID;
-}
-
-static int lowestSpeed(const CGHeroInstance * chi)
-{
-	if(!chi->Slots().size())
-	{
-        logGlobal->errorStream() << "Error! Hero " << chi->id.getNum() << " ("<<chi->name<<") has no army!";
-		return 20;
-	}
-	auto i = chi->Slots().begin();
-	//TODO? should speed modifiers (eg from artifacts) affect hero movement?
-	int ret = (i++)->second->valOfBonuses(Bonus::STACKS_SPEED);
-	for (;i!=chi->Slots().end();i++)
-	{
-		ret = std::min(ret, i->second->valOfBonuses(Bonus::STACKS_SPEED));
-	}
-	return ret;
-}
-
-ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &from) const
-{
-	//base move cost
-	unsigned ret = 100;
-
-	//if there is road both on dest and src tiles - use road movement cost
-    if(dest.roadType != ERoadType::NO_ROAD && from.roadType != ERoadType::NO_ROAD)
-	{
-        int road = std::min(dest.roadType,from.roadType); //used road ID
-		switch(road)
-		{
-        case ERoadType::DIRT_ROAD:
-			ret = 75;
-			break;
-        case ERoadType::GRAVEL_ROAD:
-			ret = 65;
-			break;
-        case ERoadType::COBBLESTONE_ROAD:
-			ret = 50;
-			break;
-		default:
-            logGlobal->errorStream() << "Unknown road type: " << road << "... Something wrong!";
-			break;
-		}
-	}
-	else
-	{
-		//FIXME: in H3 presence of Nomad in army will remove terrain penalty for sand. Bonus not implemented in VCMI
-
-		// NOTE: in H3 neutral stacks will ignore terrain penalty only if placed as topmost stack(s) in hero army.
-		// This is clearly bug in H3 however intended behaviour is not clear.
-		// Current VCMI behaviour will ignore neutrals in calculations so army in VCMI
-		// will always have best penalty without any influence from player-defined stacks order
-
-		bool nativeArmy = true;
-		for(auto stack : stacks)
-		{
-			int nativeTerrain = VLC->townh->factions[stack.second->type->faction]->nativeTerrain;
-
-            if (nativeTerrain != -1 && nativeTerrain != from.terType)
-			{
-				nativeArmy = false;
-				break;
-			}
-		}
-		if (!nativeArmy)
-            ret = VLC->heroh->terrCosts[from.terType];
- 	}
-	return ret;
-}
-
-int3 CGHeroInstance::convertPosition(int3 src, bool toh3m) //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
-{
-	if (toh3m)
-	{
-		src.x+=1;
-		return src;
-	}
-	else
-	{
-		src.x-=1;
-		return src;
-	}
-}
-int3 CGHeroInstance::getPosition(bool h3m) const //h3m=true - returns position of hero object; h3m=false - returns position of hero 'manifestation'
-{
-	if (h3m)
-	{
-		return pos;
-	}
-	else
-	{
-		return convertPosition(pos,false);
-	}
-}
-
-bool CGHeroInstance::canWalkOnSea() const
-{
-	return hasBonusOfType(Bonus::FLYING_MOVEMENT) || hasBonusOfType(Bonus::WATER_WALKING);
-}
-
-ui8 CGHeroInstance::getSecSkillLevel(SecondarySkill skill) const
-{
-	for(auto & elem : secSkills)
-		if(elem.first == skill)
-			return elem.second;
-	return 0;
-}
-
-void CGHeroInstance::setSecSkillLevel(SecondarySkill which, int val, bool abs)
-{
-	if(getSecSkillLevel(which) == 0)
-	{
-		secSkills.push_back(std::pair<SecondarySkill,ui8>(which, val));
-		updateSkill(which, val);
-	}
-	else
-	{
-		for (auto & elem : secSkills)
-		{
-			if(elem.first == which)
-			{
-				if(abs)
-					elem.second = val;
-				else
-					elem.second += val;
-
-				if(elem.second > 3) //workaround to avoid crashes when same sec skill is given more than once
-				{
-                    logGlobal->warnStream() << "Warning: Skill " << which << " increased over limit! Decreasing to Expert.";
-					elem.second = 3;
-				}
-				updateSkill(which, elem.second); //when we know final value
-			}
-		}
-	}
-}
-
-bool CGHeroInstance::canLearnSkill() const
-{
-	return secSkills.size() < GameConstants::SKILL_PER_HERO;
-}
-
-int CGHeroInstance::maxMovePoints(bool onLand) const
-{
-	int base;
-
-	if(onLand)
-	{
-		// used function is f(x) = 66.6x + 1300, rounded to second digit, where x is lowest speed in army
-		static const int baseSpeed = 1300; // base speed from creature with 0 speed
-
-		int armySpeed = lowestSpeed(this) * 20 / 3;
-
-		base = armySpeed * 10 + baseSpeed; // separate *10 is intentional to receive same rounding as in h3
-		vstd::abetween(base, 1500, 2000); // base speed is limited by these values
-	}
-	else
-	{
-		base = 1500; //on water base movement is always 1500 (speed of army doesn't matter)
-	}
-
-	const Bonus::BonusType bt = onLand ? Bonus::LAND_MOVEMENT : Bonus::SEA_MOVEMENT;
-	const int bonus = valOfBonuses(Bonus::MOVEMENT) + valOfBonuses(bt);
-
-	const int subtype = onLand ? SecondarySkill::LOGISTICS : SecondarySkill::NAVIGATION;
-	const double modifier = valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, subtype) / 100.0;
-
-	return int(base* (1+modifier)) + bonus;
-}
-
-CGHeroInstance::CGHeroInstance()
- : IBoatGenerator(this)
-{
-	setNodeType(HERO);
-	ID = Obj::HERO;
-	tacticFormationEnabled = inTownGarrison = false;
-	mana = movement = portrait = level = -1;
-	isStanding = true;
-	moveDir = 4;
-	exp = 0xffffffff;
-	visitedTown = nullptr;
-	type = nullptr;
-	boat = nullptr;
-	commander = nullptr;
-	sex = 0xff;
-	secSkills.push_back(std::make_pair(SecondarySkill::DEFAULT, -1));
-}
-
-void CGHeroInstance::initHero(HeroTypeID SUBID)
-{
-	subID = SUBID.getNum();
-	initHero();
-}
-
-void CGHeroInstance::initHero()
-{
-	assert(validTypes(true));
-	if(!type)
-		type = VLC->heroh->heroes[subID];
-
-	if (ID == Obj::HERO)
-		appearance = VLC->dobjinfo->pickCandidates(Obj::HERO, type->heroClass->id).front();
-
-	if(!vstd::contains(spells, SpellID::PRESET)) //hero starts with a spell
-	{
-		for(auto spellID : type->spells)
-			spells.insert(spellID);
-	}
-	else //remove placeholder
-		spells -= SpellID::PRESET;
-
-	if(!getArt(ArtifactPosition::MACH4) && !getArt(ArtifactPosition::SPELLBOOK) && type->haveSpellBook) //no catapult means we haven't read pre-existent set -> use default rules for spellbook
-		putArtifact(ArtifactPosition::SPELLBOOK, CArtifactInstance::createNewArtifactInstance(0));
-
-	if(!getArt(ArtifactPosition::MACH4))
-		putArtifact(ArtifactPosition::MACH4, CArtifactInstance::createNewArtifactInstance(3)); //everyone has a catapult
-
-	if(portrait < 0 || portrait == 255)
-		portrait = type->imageIndex;
-	if(!hasBonus(Selector::sourceType(Bonus::HERO_BASE_SKILL)))
-	{
-		for(int g=0; g<GameConstants::PRIMARY_SKILLS; ++g)
-		{
-			pushPrimSkill(static_cast<PrimarySkill::PrimarySkill>(g), type->heroClass->primarySkillInitial[g]);
-		}
-	}
-	if(secSkills.size() == 1 && secSkills[0] == std::pair<SecondarySkill,ui8>(SecondarySkill::DEFAULT, -1)) //set secondary skills to default
-		secSkills = type->secSkillsInit;
-	if (!name.length())
-		name = type->name;
-
-	if (sex == 0xFF)//sex is default
-		sex = type->sex;
-
-	setFormation(false);
-	if (!stacksCount()) //standard army//initial army
-	{
-		initArmy();
-	}
-	assert(validTypes());
-
-	level = 1;
-	if(exp == 0xffffffff)
-	{
-		initExp();
-	}
-	else
-	{
-		levelUpAutomatically();
-	}
-
-	if (VLC->modh->modules.COMMANDERS && !commander)
-	{
-		commander = new CCommanderInstance(type->heroClass->commander->idNumber);
-		commander->setArmyObj (castToArmyObj()); //TODO: separate function for setting commanders
-		commander->giveStackExp (exp); //after our exp is set
-	}
-
-	if (mana < 0)
-		mana = manaLimit();
-}
-
-void CGHeroInstance::initArmy(IArmyDescriptor *dst /*= nullptr*/)
-{
-	if(!dst)
-		dst = this;
-
-	int howManyStacks = 0; //how many stacks will hero receives <1 - 3>
-	int pom = cb->gameState()->getRandomGenerator().nextInt(99);
-	int warMachinesGiven = 0;
-
-	if(pom < 9)
-		howManyStacks = 1;
-	else if(pom < 79)
-		howManyStacks = 2;
-	else
-		howManyStacks = 3;
-
-	vstd::amin(howManyStacks, type->initialArmy.size());
-
-	for(int stackNo=0; stackNo < howManyStacks; stackNo++)
-	{
-		auto & stack = type->initialArmy[stackNo];
-
-		int count = cb->gameState()->getRandomGenerator().nextInt(stack.minAmount, stack.maxAmount);
-
-		if(stack.creature >= CreatureID::CATAPULT &&
-		   stack.creature <= CreatureID::ARROW_TOWERS) //war machine
-		{
-			warMachinesGiven++;
-			if(dst != this)
-				continue;
-
-			int slot = -1;
-			ArtifactID aid = ArtifactID::NONE;
-			switch (stack.creature)
-			{
-			case CreatureID::CATAPULT:
-				slot = ArtifactPosition::MACH4;
-				aid = ArtifactID::CATAPULT;
-				break;
-			default:
-				aid = CArtHandler::creatureToMachineID(stack.creature);
-				slot = 9 + aid;
-				break;
-			}
-			auto convSlot = ArtifactPosition(slot);
-			if(!getArt(convSlot))
-				putArtifact(convSlot, CArtifactInstance::createNewArtifactInstance(aid));
-			else
-                logGlobal->warnStream() << "Hero " << name << " already has artifact at " << slot << ", omitting giving " << aid;
-		}
-		else
-			dst->setCreature(SlotID(stackNo-warMachinesGiven), stack.creature, count);
-	}
-}
-
-CGHeroInstance::~CGHeroInstance()
-{
-	commander.dellNull();
-}
-
-bool CGHeroInstance::needsLastStack() const
-{
-	return true;
-}
-
-void CGHeroInstance::onHeroVisit(const CGHeroInstance * h) const
-{
-	if(h == this) return; //exclude potential self-visiting
-
-	if (ID == Obj::HERO)
-	{
-		if( cb->gameState()->getPlayerRelations(tempOwner, h->tempOwner)) //our or ally hero
-		{
-			//exchange
-			cb->heroExchange(h->id, id);
-		}
-		else //battle
-		{
-			if(visitedTown) //we're in town
-				visitedTown->onHeroVisit(h); //town will handle attacking
-			else
-				cb->startBattleI(h,	this);
-		}
-	}
-	else if(ID == Obj::PRISON)
-	{
-		int txt_id;
-
-		if (cb->getHeroCount(h->tempOwner, false) < VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)//GameConstants::MAX_HEROES_PER_PLAYER) //free hero slot
-		{
-			cb->changeObjPos(id,pos+int3(1,0,0),0);
-			//update hero parameters
-			SetMovePoints smp;
-			smp.hid = id;
-			smp.val = maxMovePoints (true); //TODO: hota prison on water?
-			cb->setMovePoints (&smp);
-			cb->setManaPoints (id, manaLimit());
-
-			cb->setObjProperty(id, ObjProperty::ID, Obj::HERO); //set ID to 34
-			cb->giveHero(id,h->tempOwner); //recreates def and adds hero to player
-
-			txt_id = 102;
-		}
-		else //already 8 wandering heroes
-		{
-			txt_id = 103;
-		}
-
-		showInfoDialog(h,txt_id,soundBase::ROGUE);
-	}
-}
-
-const std::string & CGHeroInstance::getHoverText() const
-{
-	if(ID != Obj::PRISON)
-	{
-		hoverName = VLC->generaltexth->allTexts[15];
-		boost::algorithm::replace_first(hoverName,"%s",name);
-		boost::algorithm::replace_first(hoverName,"%s", type->heroClass->name);
-		return hoverName;
-	}
-	else
-		hoverName = VLC->generaltexth->names[ID];
-
-	return hoverName;
-}
-
-const std::string & CGHeroInstance::getBiography() const
-{
-	if (biography.length())
-		return biography;
-	return type->biography;
-}
-
-ui8 CGHeroInstance::maxlevelsToMagicSchool() const
-{
-	return type->heroClass->isMagicHero() ? 3 : 4;
-}
-ui8 CGHeroInstance::maxlevelsToWisdom() const
-{
-	return type->heroClass->isMagicHero() ? 3 : 6;
-}
-
-void CGHeroInstance::SecondarySkillsInfo::resetMagicSchoolCounter()
-{
-	magicSchoolCounter = 1;
-}
-void CGHeroInstance::SecondarySkillsInfo::resetWisdomCounter()
-{
-	wisdomCounter = 1;
-}
-
-void CGHeroInstance::initObj()
-{
-	blockVisit = true;
-	auto  hs = new HeroSpecial();
-	hs->setNodeType(CBonusSystemNode::SPECIALTY);
-	attachTo(hs); //do we ever need to detach it?
-
-	if(!type)
-		initHero(); //TODO: set up everything for prison before specialties are configured
-
-	skillsInfo.rand.setSeed(cb->gameState()->getRandomGenerator().nextInt());
-	skillsInfo.resetMagicSchoolCounter();
-	skillsInfo.resetWisdomCounter();
-
-	for(const auto &spec : type->spec) //TODO: unfity with bonus system
-	{
-		auto bonus = new Bonus();
-		bonus->val = spec.val;
-		bonus->sid = id.getNum(); //from the hero, specialty has no unique id
-		bonus->duration = Bonus::PERMANENT;
-		bonus->source = Bonus::HERO_SPECIAL;
-		switch (spec.type)
-		{
-			case 1:// creature specialty
-				{
-					hs->growsWithLevel = true;
-
-					const CCreature &specCreature = *VLC->creh->creatures[spec.additionalinfo]; //creature in which we have specialty
-
-					//int creLevel = specCreature.level;
-					//if(!creLevel)
-					//{
-					//	if(spec.additionalinfo == 146)
-					//		creLevel = 5; //treat ballista as 5-level
-					//	else
-					//	{
-					//        logGlobal->warnStream() << "Warning: unknown level of " << specCreature.namePl;
-					//		continue;
-					//	}
-					//}
-
-					//bonus->additionalInfo = spec.additionalinfo; //creature id, should not be used again - this works only with limiter
-					bonus->limiter.reset(new CCreatureTypeLimiter (specCreature, true)); //with upgrades
-					bonus->type = Bonus::PRIMARY_SKILL;
-					bonus->valType = Bonus::ADDITIVE_VALUE;
-
-					bonus->subtype = PrimarySkill::ATTACK;
-					hs->addNewBonus(bonus);
-
-					bonus = new Bonus(*bonus);
-					bonus->subtype = PrimarySkill::DEFENSE;
-					hs->addNewBonus(bonus);
-					//values will be calculated later
-
-					bonus = new Bonus(*bonus);
-					bonus->type = Bonus::STACKS_SPEED;
-					bonus->val = 1; //+1 speed
-					hs->addNewBonus(bonus);
-				}
-				break;
-			case 2://secondary skill
-				hs->growsWithLevel = true;
-				bonus->type = Bonus::SPECIAL_SECONDARY_SKILL; //needs to be recalculated with level, based on this value
-				bonus->valType = Bonus::BASE_NUMBER; // to receive nonzero value
-				bonus->subtype = spec.subtype; //skill id
-				bonus->val = spec.val; //value per level, in percent
-				hs->addNewBonus(bonus);
-				bonus = new Bonus(*bonus);
-
-				switch (spec.additionalinfo)
-				{
-					case 0: //normal
-						bonus->valType = Bonus::PERCENT_TO_BASE;
-						break;
-					case 1: //when it's navigation or there's no 'base' at all
-						bonus->valType = Bonus::PERCENT_TO_ALL;
-						break;
-				}
-				bonus->type = Bonus::SECONDARY_SKILL_PREMY; //value will be calculated later
-				hs->addNewBonus(bonus);
-				break;
-			case 3://spell damage bonus, level dependent but calculated elsewhere
-				bonus->type = Bonus::SPECIAL_SPELL_LEV;
-				bonus->subtype = spec.subtype;
-				hs->addNewBonus(bonus);
-				break;
-			case 4://creature stat boost
-				switch (spec.subtype)
-				{
-					case 1://attack
-						bonus->type = Bonus::PRIMARY_SKILL;
-						bonus->subtype = PrimarySkill::ATTACK;
-						break;
-					case 2://defense
-						bonus->type = Bonus::PRIMARY_SKILL;
-						bonus->subtype = PrimarySkill::DEFENSE;
-						break;
-					case 3:
-						bonus->type = Bonus::CREATURE_DAMAGE;
-						bonus->subtype = 0; //both min and max
-						break;
-					case 4://hp
-						bonus->type = Bonus::STACK_HEALTH;
-						break;
-					case 5:
-						bonus->type = Bonus::STACKS_SPEED;
-						break;
-					default:
-						continue;
-				}
-				bonus->additionalInfo = spec.additionalinfo; //creature id
-				bonus->valType = Bonus::ADDITIVE_VALUE;
-				bonus->limiter.reset(new CCreatureTypeLimiter (*VLC->creh->creatures[spec.additionalinfo], true));
-				hs->addNewBonus(bonus);
-				break;
-			case 5://spell damage bonus in percent
-				bonus->type = Bonus::SPECIFIC_SPELL_DAMAGE;
-				bonus->valType = Bonus::BASE_NUMBER; // current spell system is screwed
-				bonus->subtype = spec.subtype; //spell id
-				hs->addNewBonus(bonus);
-				break;
-			case 6://damage bonus for bless (Adela)
-				bonus->type = Bonus::SPECIAL_BLESS_DAMAGE;
-				bonus->subtype = spec.subtype; //spell id if you ever wanted to use it otherwise
-				bonus->additionalInfo = spec.additionalinfo; //damage factor
-				hs->addNewBonus(bonus);
-				break;
-			case 7://maxed mastery for spell
-				bonus->type = Bonus::MAXED_SPELL;
-				bonus->subtype = spec.subtype; //spell i
-				hs->addNewBonus(bonus);
-				break;
-			case 8://peculiar spells - enchantments
-				bonus->type = Bonus::SPECIAL_PECULIAR_ENCHANT;
-				bonus->subtype = spec.subtype; //spell id
-				bonus->additionalInfo = spec.additionalinfo;//0, 1 for Coronius
-				hs->addNewBonus(bonus);
-				break;
-			case 9://upgrade creatures
-			{
-				const auto &creatures = VLC->creh->creatures;
-				bonus->type = Bonus::SPECIAL_UPGRADE;
-				bonus->subtype = spec.subtype; //base id
-				bonus->additionalInfo = spec.additionalinfo; //target id
-				hs->addNewBonus(bonus);
-				bonus = new Bonus(*bonus);
-
-				for(auto cre_id : creatures[spec.subtype]->upgrades)
-				{
-					bonus->subtype = cre_id; //propagate for regular upgrades of base creature
-					hs->addNewBonus(bonus);
-					bonus = new Bonus(*bonus);
-				}
-				vstd::clear_pointer(bonus);
-				break;
-			}
-			case 10://resource generation
-				bonus->type = Bonus::GENERATE_RESOURCE;
-				bonus->subtype = spec.subtype;
-				hs->addNewBonus(bonus);
-				break;
-			case 11://starting skill with mastery (Adrienne)
-				setSecSkillLevel(SecondarySkill(spec.val), spec.additionalinfo, true);
-				break;
-			case 12://army speed
-				bonus->type = Bonus::STACKS_SPEED;
-				hs->addNewBonus(bonus);
-				break;
-			case 13://Dragon bonuses (Mutare)
-				bonus->type = Bonus::PRIMARY_SKILL;
-				bonus->valType = Bonus::ADDITIVE_VALUE;
-				switch (spec.subtype)
-				{
-					case 1:
-						bonus->subtype = PrimarySkill::ATTACK;
-						break;
-					case 2:
-						bonus->subtype = PrimarySkill::DEFENSE;
-						break;
-				}
-				bonus->limiter.reset(new HasAnotherBonusLimiter(Bonus::DRAGON_NATURE));
-				hs->addNewBonus(bonus);
-				break;
-			default:
-                logGlobal->warnStream() << "Unexpected hero specialty " << type;
-		}
-	}
-	specialty.push_back(hs); //will it work?
-
-	for (auto hs2 : type->specialty) //copy active (probably growing) bonuses from hero prootype to hero object
-	{
-		auto  hs = new HeroSpecial();
-		attachTo(hs); //do we ever need to detach it?
-
-		hs->setNodeType(CBonusSystemNode::SPECIALTY);
-		for (auto bonus : hs2.bonuses)
-		{
-			hs->addNewBonus (bonus);
-		}
-		hs->growsWithLevel = hs2.growsWithLevel;
-
-		specialty.push_back(hs); //will it work?
-	}
-
-	//initialize bonuses
-	recreateSecondarySkillsBonuses();
-	Updatespecialty();
-
-	mana = manaLimit(); //after all bonuses are taken into account, make sure this line is the last one
-	type->name = name;
-}
-void CGHeroInstance::Updatespecialty() //TODO: calculate special value of bonuses on-the-fly?
-{
-	for (auto hs : specialty)
-	{
-		if (hs->growsWithLevel)
-		{
-			//const auto &creatures = VLC->creh->creatures;
-
-			for(Bonus * b : hs->getBonusList())
-			{
-				switch (b->type)
-				{
-					case Bonus::SECONDARY_SKILL_PREMY:
-						b->val = (hs->valOfBonuses(Bonus::SPECIAL_SECONDARY_SKILL, b->subtype) * level);
-						break; //use only hero skills as bonuses to avoid feedback loop
-					case Bonus::PRIMARY_SKILL: //for creatures, that is
-					{
-						const CCreature * cre = nullptr;
-						int creLevel = 0;
-						if (auto creatureLimiter = std::dynamic_pointer_cast<CCreatureTypeLimiter>(b->limiter)) //TODO: more general eveluation of bonuses?
-						{
-							cre = creatureLimiter->creature;
-							creLevel = cre->level;
-							if (!creLevel)
-							{
-								creLevel = 5; //treat ballista as tier 5
-							}
-						}
-						else //no creature found, can't calculate value
-						{
-                            logGlobal->warnStream() << "Primary skill specialty growth supported only with creature type limiters";
-							break;
-						}
-
-						double primSkillModifier = (int)(level / creLevel) / 20.0;
-						int param;
-						switch (b->subtype)
-						{
-							case PrimarySkill::ATTACK:
-								param = cre->Attack();
-								break;
-							case PrimarySkill::DEFENSE:
-								param = cre->Defense();
-								break;
-							default:
-								continue;
-						}
-						b->val = ceil(param * (1 + primSkillModifier)) - param; //yep, overcomplicated but matches original
-						break;
-					}
-				}
-			}
-		}
-	}
-}
-
-void CGHeroInstance::recreateSecondarySkillsBonuses()
-{
-	auto secondarySkillsBonuses = getBonuses(Selector::sourceType(Bonus::SECONDARY_SKILL));
-	for(auto bonus : *secondarySkillsBonuses)
-		removeBonus(bonus);
-
-	for(auto skill_info : secSkills)
-		updateSkill(SecondarySkill(skill_info.first), skill_info.second);
-}
-
-void CGHeroInstance::updateSkill(SecondarySkill which, int val)
-{
-	if(which == SecondarySkill::LEADERSHIP || which == SecondarySkill::LUCK)
-	{ //luck-> VLC->generaltexth->arraytxt[73+luckSkill]; VLC->generaltexth->arraytxt[104+moraleSkill]
-		bool luck = which == SecondarySkill::LUCK;
-		Bonus::BonusType type[] = {Bonus::MORALE, Bonus::LUCK};
-
-		Bonus *b = getBonusLocalFirst(Selector::type(type[luck]).And(Selector::sourceType(Bonus::SECONDARY_SKILL)));
-		if(!b)
-		{
-			b = new Bonus(Bonus::PERMANENT, type[luck], Bonus::SECONDARY_SKILL, +val, which, which, Bonus::BASE_NUMBER);
-			addNewBonus(b);
-		}
-		else
-			b->val = +val;
-	}
-	else if(which == SecondarySkill::DIPLOMACY) //surrender discount: 20% per level
-	{
-
-		if(Bonus *b = getBonusLocalFirst(Selector::type(Bonus::SURRENDER_DISCOUNT).And(Selector::sourceType(Bonus::SECONDARY_SKILL))))
-			b->val = +val;
-		else
-			addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::SURRENDER_DISCOUNT, Bonus::SECONDARY_SKILL, val * 20, which));
-	}
-
-	int skillVal = 0;
-	switch (which)
-	{
-	case SecondarySkill::ARCHERY:
-		switch (val)
-		{
-		case 1:
-			skillVal = 10; break;
-		case 2:
-			skillVal = 25; break;
-		case 3:
-			skillVal = 50; break;
-		}
-		break;
-	case SecondarySkill::LOGISTICS:
-		skillVal = 10 * val; break;
-	case SecondarySkill::NAVIGATION:
-		skillVal = 50 * val; break;
-	case SecondarySkill::MYSTICISM:
-		skillVal = val; break;
-	case SecondarySkill::EAGLE_EYE:
-		skillVal = 30 + 10 * val; break;
-	case SecondarySkill::NECROMANCY:
-		skillVal = 10 * val; break;
-	case SecondarySkill::LEARNING:
-		skillVal = 5 * val; break;
-	case SecondarySkill::OFFENCE:
-		skillVal = 10 * val; break;
-	case SecondarySkill::ARMORER:
-		skillVal = 5 * val; break;
-	case SecondarySkill::INTELLIGENCE:
-		skillVal = 25 << (val-1); break;
-	case SecondarySkill::SORCERY:
-		skillVal = 5 * val; break;
-	case SecondarySkill::RESISTANCE:
-		skillVal = 5 << (val-1); break;
-	case SecondarySkill::FIRST_AID:
-		skillVal = 25 + 25*val; break;
-	case SecondarySkill::ESTATES:
-		skillVal = 125 << (val-1); break;
-	}
-
-
-	Bonus::ValueType skillValType = skillVal ? Bonus::BASE_NUMBER : Bonus::INDEPENDENT_MIN;
-	if(Bonus * b = getBonusList().getFirst(Selector::typeSubtype(Bonus::SECONDARY_SKILL_PREMY, which)
-										.And(Selector::sourceType(Bonus::SECONDARY_SKILL)))) //only local hero bonus
-	{
-		b->val = skillVal;
-		b->valType = skillValType;
-	}
-	else
-	{
-		auto bonus = new Bonus(Bonus::PERMANENT, Bonus::SECONDARY_SKILL_PREMY, Bonus::SECONDARY_SKILL, skillVal, id.getNum(), which, skillValType);
-		bonus->source = Bonus::SECONDARY_SKILL;
-		addNewBonus(bonus);
-	}
-
-}
-void CGHeroInstance::setPropertyDer( ui8 what, ui32 val )
-{
-	if(what == ObjProperty::PRIMARY_STACK_COUNT)
-		setStackCount(SlotID(0), val);
-}
-
-double CGHeroInstance::getFightingStrength() const
-{
-	return sqrt((1.0 + 0.05*getPrimSkillLevel(PrimarySkill::ATTACK)) * (1.0 + 0.05*getPrimSkillLevel(PrimarySkill::DEFENSE)));
-}
-
-double CGHeroInstance::getMagicStrength() const
-{
-	return sqrt((1.0 + 0.05*getPrimSkillLevel(PrimarySkill::KNOWLEDGE)) * (1.0 + 0.05*getPrimSkillLevel(PrimarySkill::SPELL_POWER)));
-}
-
-double CGHeroInstance::getHeroStrength() const
-{
-	return sqrt(pow(getFightingStrength(), 2.0) * pow(getMagicStrength(), 2.0));
-}
-
-ui64 CGHeroInstance::getTotalStrength() const
-{
-	double ret = getFightingStrength() * getArmyStrength();
-	return (ui64) ret;
-}
-
-TExpType CGHeroInstance::calculateXp(TExpType exp) const
-{
-	return exp * (100 + valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::LEARNING))/100.0;
-}
-
-ui8 CGHeroInstance::getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool) const
-{
-	si16 skill = -1; //skill level
-
-#define TRY_SCHOOL(schoolName, schoolMechanicsId, schoolOutId)	\
-	if(spell-> schoolName)									\
-	{															\
-		int thisSchool = std::max<int>(getSecSkillLevel( \
-			SecondarySkill(14 + (schoolMechanicsId))), \
-			valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 1 << (schoolMechanicsId))); \
-		if(thisSchool > skill)									\
-		{														\
-			skill = thisSchool;									\
-			if(outSelectedSchool)								\
-				*outSelectedSchool = schoolOutId;				\
-		}														\
-	}
-	TRY_SCHOOL(fire, 0, 1)
-	TRY_SCHOOL(air, 1, 0)
-	TRY_SCHOOL(water, 2, 2)
-	TRY_SCHOOL(earth, 3, 3)
-#undef TRY_SCHOOL
-
-
-
-	vstd::amax(skill, valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 0)); //any school bonus
-	vstd::amax(skill, valOfBonuses(Bonus::SPELL, spell->id.toEnum())); //given by artifact or other effect
-	if (hasBonusOfType(Bonus::MAXED_SPELL, spell->id))//hero specialty (Daremyth, Melodia)
-		skill = 3;
-	assert(skill >= 0 && skill <= 3);
-	return skill;
-}
-
-bool CGHeroInstance::canCastThisSpell(const CSpell * spell) const
-{
-	if(!getArt(ArtifactPosition::SPELLBOOK)) //if hero has no spellbook
-		return false;
-
-    if (spell->isSpecialSpell())
-    {
-        if (vstd::contains(spells, spell->id))
-        {//hero has this spell in spellbook
-            logGlobal->errorStream() << "Special spell in spellbook "<<spell->name;
-        }
-
-        if (hasBonusOfType(Bonus::SPELL, spell->id))
-            return true;
-
-        return false;
-    }
-    else
-    {
-        if(vstd::contains(spells, spell->id) //hero has this spell in spellbook
-            || (spell->air && hasBonusOfType(Bonus::AIR_SPELLS)) // this is air spell and hero can cast all air spells
-            || (spell->fire && hasBonusOfType(Bonus::FIRE_SPELLS)) // this is fire spell and hero can cast all fire spells
-            || (spell->water && hasBonusOfType(Bonus::WATER_SPELLS)) // this is water spell and hero can cast all water spells
-            || (spell->earth && hasBonusOfType(Bonus::EARTH_SPELLS)) // this is earth spell and hero can cast all earth spells
-            || hasBonusOfType(Bonus::SPELL, spell->id)
-            || hasBonusOfType(Bonus::SPELLS_OF_LEVEL, spell->level)
-            )
-            return true;
-
-        return false;
-    }
-}
-
-/**
- * Calculates what creatures and how many to be raised from a battle.
- * @param battleResult The results of the battle.
- * @return Returns a pair with the first value indicating the ID of the creature
- * type and second value the amount. Both values are returned as -1 if necromancy
- * could not be applied.
- */
-CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &battleResult) const
-{
-	const ui8 necromancyLevel = getSecSkillLevel(SecondarySkill::NECROMANCY);
-
-	// Hero knows necromancy or has Necromancer Cloak
-	if (necromancyLevel > 0 || hasBonusOfType(Bonus::IMPROVED_NECROMANCY))
-	{
-		double necromancySkill = valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::NECROMANCY)/100.0;
-		vstd::amin(necromancySkill, 1.0); //it's impossible to raise more creatures than all...
-		const std::map<ui32,si32> &casualties = battleResult.casualties[!battleResult.winner];
-		ui32 raisedUnits = 0;
-
-		// Figure out what to raise and how many.
-		const CreatureID creatureTypes[] = {CreatureID::SKELETON, CreatureID::WALKING_DEAD, CreatureID::WIGHTS, CreatureID::LICHES};
-		const bool improvedNecromancy = hasBonusOfType(Bonus::IMPROVED_NECROMANCY);
-		const CCreature *raisedUnitType = VLC->creh->creatures[creatureTypes[improvedNecromancy ? necromancyLevel : 0]];
-		const ui32 raisedUnitHP = raisedUnitType->valOfBonuses(Bonus::STACK_HEALTH);
-
-		//calculate creatures raised from each defeated stack
-		for (auto & casualtie : casualties)
-		{
-			// Get lost enemy hit points convertible to units.
-			CCreature * c = VLC->creh->creatures[casualtie.first];
-
-			const ui32 raisedHP = c->valOfBonuses(Bonus::STACK_HEALTH) * casualtie.second * necromancySkill;
-			raisedUnits += std::min<ui32>(raisedHP / raisedUnitHP, casualtie.second * necromancySkill); //limit to % of HP and % of original stack count
-		}
-
-		// Make room for new units.
-		SlotID slot = getSlotFor(raisedUnitType->idNumber);
-		if (slot == SlotID())
-		{
-			// If there's no room for unit, try it's upgraded version 2/3rds the size.
-			raisedUnitType = VLC->creh->creatures[*raisedUnitType->upgrades.begin()];
-			raisedUnits = (raisedUnits*2)/3;
-
-			slot = getSlotFor(raisedUnitType->idNumber);
-		}
-		if (raisedUnits <= 0)
-			raisedUnits = 1;
-
-		return CStackBasicDescriptor(raisedUnitType->idNumber, raisedUnits);
-	}
-
-	return CStackBasicDescriptor();
-}
-
-/**
- * Show the necromancy dialog with information about units raised.
- * @param raisedStack Pair where the first element represents ID of the raised creature
- * and the second element the amount.
- */
-void CGHeroInstance::showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const
-{
-	InfoWindow iw;
-	iw.soundID = soundBase::pickup01 + cb->gameState()->getRandomGenerator().nextInt(6);
-	iw.player = tempOwner;
-	iw.components.push_back(Component(raisedStack));
-
-	if (raisedStack.count > 1) // Practicing the dark arts of necromancy, ... (plural)
-	{
-		iw.text.addTxt(MetaString::GENERAL_TXT, 145);
-		iw.text.addReplacement(raisedStack.count);
-	}
-	else // Practicing the dark arts of necromancy, ... (singular)
-	{
-		iw.text.addTxt(MetaString::GENERAL_TXT, 146);
-	}
-	iw.text.addReplacement(raisedStack);
-
-	cb->showInfoDialog(&iw);
-}
-
-int3 CGHeroInstance::getSightCenter() const
-{
-	return getPosition(false);
-}
-
-int CGHeroInstance::getSightRadious() const
-{
-	return 5 + getSecSkillLevel(SecondarySkill::SCOUTING) + valOfBonuses(Bonus::SIGHT_RADIOUS); //default + scouting
-}
-
-si32 CGHeroInstance::manaRegain() const
-{
-	if (hasBonusOfType(Bonus::FULL_MANA_REGENERATION))
-		return manaLimit();
-
-	return 1 + valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 8) + valOfBonuses(Bonus::MANA_REGENERATION); //1 + Mysticism level
-}
-
-// /**
-//  * Places an artifact in hero's backpack. If it's a big artifact equips it
-//  * or discards it if it cannot be equipped.
-//  */
-// void CGHeroInstance::giveArtifact (ui32 aid) //use only for fixed artifacts
-// {
-// 	CArtifact * const artifact = VLC->arth->artifacts[aid]; //pointer to constant object
-// 	CArtifactInstance *ai = CArtifactInstance::createNewArtifactInstance(artifact);
-// 	ai->putAt(this, ai->firstAvailableSlot(this));
-// }
-
-int CGHeroInstance::getBoatType() const
-{
-	switch(type->heroClass->getAlignment())
-	{
-	case EAlignment::GOOD:
-		return 1;
-	case EAlignment::EVIL:
-		return 0;
-	case EAlignment::NEUTRAL:
-		return 2;
-	default:
-		throw std::runtime_error("Wrong alignment!");
-	}
-}
-
-void CGHeroInstance::getOutOffsets(std::vector<int3> &offsets) const
-{
-	static int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0), int3(1,1,0),int3(-1,1,0),int3(1,-1,0),int3(-1,-1,0) };
-	for (auto & dir : dirs)
-		offsets += dir;
-}
-
-int CGHeroInstance::getSpellCost(const CSpell *sp) const
-{
-	return sp->getCost(getSpellSchoolLevel(sp));
-}
-
-void CGHeroInstance::pushPrimSkill( PrimarySkill::PrimarySkill which, int val )
-{
-	assert(!hasBonus(Selector::typeSubtype(Bonus::PRIMARY_SKILL, which)
-						.And(Selector::sourceType(Bonus::HERO_BASE_SKILL))));
-	addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::PRIMARY_SKILL, Bonus::HERO_BASE_SKILL, val, id.getNum(), which));
-}
-
-EAlignment::EAlignment CGHeroInstance::getAlignment() const
-{
-	return type->heroClass->getAlignment();
-}
-
-void CGHeroInstance::initExp()
-{
-	exp = cb->gameState()->getRandomGenerator().nextInt(40, 89);
-}
-
-std::string CGHeroInstance::nodeName() const
-{
-	return "Hero " + name;
-}
-
-void CGHeroInstance::putArtifact(ArtifactPosition pos, CArtifactInstance *art)
-{
-	assert(!getArt(pos));
-	art->putAt(ArtifactLocation(this, pos));
-}
-
-void CGHeroInstance::putInBackpack(CArtifactInstance *art)
-{
-	putArtifact(art->firstBackpackSlot(this), art);
-}
-
-bool CGHeroInstance::hasSpellbook() const
-{
-	return getArt(ArtifactPosition::SPELLBOOK);
-}
-
-void CGHeroInstance::deserializationFix()
-{
-	artDeserializationFix(this);
-
-	for (auto hs : specialty)
-	{
-		attachTo (hs);
-	}
-}
-
-CBonusSystemNode * CGHeroInstance::whereShouldBeAttached(CGameState *gs)
-{
-	if(visitedTown)
-	{
-		if(inTownGarrison)
-			return visitedTown;
-		else
-			return &visitedTown->townAndVis;
-	}
-	else
-		return CArmedInstance::whereShouldBeAttached(gs);
-}
-
-int CGHeroInstance::movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark /*= false*/) const
-{
-	if(hasBonusOfType(Bonus::FREE_SHIP_BOARDING))
-		return (MPsBefore - basicCost) * static_cast<double>(maxMovePoints(disembark)) / maxMovePoints(!disembark);
-
-	return 0; //take all MPs otherwise
-}
-
-CGHeroInstance::ECanDig CGHeroInstance::diggingStatus() const
-{
-	if(movement < maxMovePoints(true))
-		return LACK_OF_MOVEMENT;
-    else if(cb->getTile(getPosition(false))->terType == ETerrainType::WATER)
-		return WRONG_TERRAIN;
-	else
-	{
-		const TerrainTile *t = cb->getTile(getPosition());
-		//TODO look for hole
-		//CGI->mh->getTerrainDescr(h->getPosition(false), hlp, false);
-		if(/*hlp.length() || */t->blockingObjects.size() > 1)
-			return TILE_OCCUPIED;
-		else
-			return CAN_DIG;
-	}
-}
-
-ArtBearer::ArtBearer CGHeroInstance::bearerType() const
-{
-	return ArtBearer::HERO;
-}
-
-std::vector<SecondarySkill> CGHeroInstance::getLevelUpProposedSecondarySkills() const
-{
-	std::vector<SecondarySkill> obligatorySkills; //hero is offered magic school or wisdom if possible
-	if (!skillsInfo.wisdomCounter)
-	{
-		if (cb->isAllowed(2, SecondarySkill::WISDOM) && !getSecSkillLevel(SecondarySkill::WISDOM))
-			obligatorySkills.push_back(SecondarySkill::WISDOM);
-	}
-	if (!skillsInfo.magicSchoolCounter)
-	{
-		std::vector<SecondarySkill> ss;
-		ss += SecondarySkill::FIRE_MAGIC, SecondarySkill::AIR_MAGIC, SecondarySkill::WATER_MAGIC, SecondarySkill::EARTH_MAGIC;
-
-		std::shuffle(ss.begin(), ss.end(), skillsInfo.rand.getStdGenerator());
-
-		for (auto skill : ss)
-		{
-			if (cb->isAllowed(2, skill) && !getSecSkillLevel(skill)) //only schools hero doesn't know yet
-			{
-				obligatorySkills.push_back(skill);
-				break; //only one
-			}
-		}
-	}
-
-	std::vector<SecondarySkill> skills;
-	//picking sec. skills for choice
-	std::set<SecondarySkill> basicAndAdv, expert, none;
-	for(int i=0;i<GameConstants::SKILL_QUANTITY;i++)
-		if (cb->isAllowed(2,i))
-			none.insert(SecondarySkill(i));
-
-	for(auto & elem : secSkills)
-	{
-		if(elem.second < SecSkillLevel::EXPERT)
-			basicAndAdv.insert(elem.first);
-		else
-			expert.insert(elem.first);
-		none.erase(elem.first);
-	}
-	for (auto s : obligatorySkills) //don't duplicate them
-	{
-		none.erase (s);
-		basicAndAdv.erase (s);
-		expert.erase (s);
-	}
-
-	//first offered skill:
-	// 1) give obligatory skill
-	// 2) give any other new skill
-	// 3) upgrade existing
-	if (canLearnSkill() && obligatorySkills.size() > 0)
-	{
-		skills.push_back (obligatorySkills[0]);
-	}
-	else if(none.size() && canLearnSkill()) //hero have free skill slot
-	{
-		skills.push_back(type->heroClass->chooseSecSkill(none, skillsInfo.rand)); //new skill
-		none.erase(skills.back());
-	}
-	else if(!basicAndAdv.empty())
-	{
-		skills.push_back(type->heroClass->chooseSecSkill(basicAndAdv, skillsInfo.rand)); //upgrade existing
-		basicAndAdv.erase(skills.back());
-	}
-
-	//second offered skill:
-	//1) upgrade existing
-	//2) give obligatory skill
-	//3) give any other new skill
-	if(!basicAndAdv.empty())
-	{
-		SecondarySkill s = type->heroClass->chooseSecSkill(basicAndAdv, skillsInfo.rand);//upgrade existing
-		skills.push_back(s);
-		basicAndAdv.erase(s);
-	}
-	else if (canLearnSkill() && obligatorySkills.size() > 1)
-	{
-		skills.push_back (obligatorySkills[1]);
-	}
-	else if(none.size() && canLearnSkill())
-	{
-		skills.push_back(type->heroClass->chooseSecSkill(none, skillsInfo.rand)); //give new skill
-		none.erase(skills.back());
-	}
-
-	return skills;
-}
-
-PrimarySkill::PrimarySkill CGHeroInstance::nextPrimarySkill() const
-{
-	assert(gainsLevel());
-	int randomValue = cb->gameState()->getRandomGenerator().nextInt(99), pom = 0, primarySkill = 0;
-	const auto & skillChances = (level > 9) ? type->heroClass->primarySkillLowLevel : type->heroClass->primarySkillHighLevel;
-
-	for(; primarySkill < GameConstants::PRIMARY_SKILLS; ++primarySkill)
-	{
-		pom += skillChances[primarySkill];
-		if(randomValue < pom)
-		{
-			break;
-		}
-	}
-
-	logGlobal->traceStream() << "The hero gets the primary skill " << primarySkill << " with a probability of " << randomValue << "%.";
-	return static_cast<PrimarySkill::PrimarySkill>(primarySkill);
-}
-
-boost::optional<SecondarySkill> CGHeroInstance::nextSecondarySkill() const
-{
-	assert(gainsLevel());
-
-	boost::optional<SecondarySkill> chosenSecondarySkill;
-	const auto proposedSecondarySkills = getLevelUpProposedSecondarySkills();
-	if(!proposedSecondarySkills.empty())
-	{
-		std::vector<SecondarySkill> learnedSecondarySkills;
-		for(auto secondarySkill : proposedSecondarySkills)
-		{
-			if(getSecSkillLevel(secondarySkill) > 0)
-			{
-				learnedSecondarySkills.push_back(secondarySkill);
-			}
-		}
-
-		auto & rand = cb->gameState()->getRandomGenerator();
-		if(learnedSecondarySkills.empty())
-		{
-			// there are only new skills to learn, so choose anyone of them
-			chosenSecondarySkill = *RandomGeneratorUtil::nextItem(proposedSecondarySkills, rand);
-		}
-		else
-		{
-			// preferably upgrade a already learned secondary skill
-			chosenSecondarySkill = *RandomGeneratorUtil::nextItem(learnedSecondarySkills, rand);
-		}
-	}
-	return chosenSecondarySkill;
-}
-
-void CGHeroInstance::setPrimarySkill(PrimarySkill::PrimarySkill primarySkill, si64 value, ui8 abs)
-{
-	if(primarySkill < PrimarySkill::EXPERIENCE)
-	{
-		Bonus * skill = getBonusLocalFirst(Selector::type(Bonus::PRIMARY_SKILL)
-											.And(Selector::subtype(primarySkill))
-											.And(Selector::sourceType(Bonus::HERO_BASE_SKILL)));
-		assert(skill);
-
-		if(abs)
-		{
-			skill->val = value;
-		}
-		else
-		{
-			skill->val += value;
-		}
-	}
-	else if(primarySkill == PrimarySkill::EXPERIENCE)
-	{
-		if(abs)
-		{
-			exp = value;
-		}
-		else
-		{
-			exp += value;
-		}
-	}
-}
-
-bool CGHeroInstance::gainsLevel() const
-{
-	return exp >= VLC->heroh->reqExp(level+1);
-}
-
-void CGHeroInstance::levelUp(std::vector<SecondarySkill> skills)
-{
-	++level;
-
-	//deterministic secondary skills
-	skillsInfo.magicSchoolCounter = (skillsInfo.magicSchoolCounter + 1) % maxlevelsToMagicSchool();
-	skillsInfo.wisdomCounter = (skillsInfo.wisdomCounter + 1) % maxlevelsToWisdom();
-	if(vstd::contains(skills, SecondarySkill::WISDOM))
-	{
-		skillsInfo.resetWisdomCounter();
-	}
-
-	SecondarySkill spellSchools[] = {
-		SecondarySkill::FIRE_MAGIC, SecondarySkill::AIR_MAGIC, SecondarySkill::WATER_MAGIC, SecondarySkill::EARTH_MAGIC};
-	for(auto skill : spellSchools)
-	{
-		if(vstd::contains(skills, skill))
-		{
-			skillsInfo.resetMagicSchoolCounter();
-			break;
-		}
-	}
-
-	//specialty
-	Updatespecialty();
-}
-
-void CGHeroInstance::levelUpAutomatically()
-{
-	while(gainsLevel())
-	{
-		const auto primarySkill = nextPrimarySkill();
-		setPrimarySkill(primarySkill, 1, false);
-
-		auto proposedSecondarySkills = getLevelUpProposedSecondarySkills();
-
-		const auto secondarySkill = nextSecondarySkill();
-		if(secondarySkill)
-		{
-			setSecSkillLevel(*secondarySkill, 1, false);
-		}
-
-		//TODO why has the secondary skills to be passed to the method?
-		levelUp(proposedSecondarySkills);
-	}
-}
-
-void CGDwelling::initObj()
-{
-	switch(ID)
-	{
-	case Obj::CREATURE_GENERATOR1:
-		{
-			CreatureID crid = VLC->objh->cregens[subID];
-			const CCreature *crs = VLC->creh->creatures[crid];
-
-			creatures.resize(1);
-			creatures[0].second.push_back(crid);
-			if (subID >= VLC->generaltexth->creGens.size()) //very messy workaround
-			{
-				auto & dwellingNames = VLC->townh->factions[crs->faction]->town->dwellingNames;
-				assert (dwellingNames.size() > crs->level - 1);
-				hoverName = dwellingNames[crs->level - 1];
-			}
-			else
-				hoverName = VLC->generaltexth->creGens[subID];
-			if(crs->level > 4)
-				putStack(SlotID(0), new CStackInstance(crs, (crs->growth) * 3));
-			if (getOwner() != PlayerColor::NEUTRAL)
-				cb->gameState()->players[getOwner()].dwellings.push_back (this);
-		}
-		break;
-
-	case Obj::CREATURE_GENERATOR4:
-		creatures.resize(4);
-		if(subID == 1) //Golem Factory
-		{
-			creatures[0].second.push_back(CreatureID::STONE_GOLEM);
-			creatures[1].second.push_back(CreatureID::IRON_GOLEM);
-			creatures[2].second.push_back(CreatureID::GOLD_GOLEM);
-			creatures[3].second.push_back(CreatureID::DIAMOND_GOLEM);
-			//guards
-			putStack(SlotID(0), new CStackInstance(CreatureID::GOLD_GOLEM, 9));
-			putStack(SlotID(1), new CStackInstance(CreatureID::DIAMOND_GOLEM, 6));
-		}
-		else if(subID == 0) // Elemental Conflux
-		{
-			creatures[0].second.push_back(CreatureID::AIR_ELEMENTAL);
-			creatures[1].second.push_back(CreatureID::FIRE_ELEMENTAL);
-			creatures[2].second.push_back(CreatureID::EARTH_ELEMENTAL);
-			creatures[3].second.push_back(CreatureID::WATER_ELEMENTAL);
-			//guards
-			putStack(SlotID(0), new CStackInstance(CreatureID::EARTH_ELEMENTAL, 12));
-		}
-		else
-		{
-			assert(0);
-		}
-		hoverName = VLC->generaltexth->creGens4[subID];
-		break;
-
-	case Obj::REFUGEE_CAMP:
-		//is handled within newturn func
-		break;
-
-	case Obj::WAR_MACHINE_FACTORY:
-		creatures.resize(3);
-		creatures[0].second.push_back(CreatureID::BALLISTA);
-		creatures[1].second.push_back(CreatureID::FIRST_AID_TENT);
-		creatures[2].second.push_back(CreatureID::AMMO_CART);
-		break;
-
-	default:
-		assert(0);
-		break;
-	}
-}
-
-void CGDwelling::setProperty(ui8 what, ui32 val)
-{
-	switch (what)
-	{
-		case ObjProperty::OWNER: //change owner
-			if (ID == Obj::CREATURE_GENERATOR1) //single generators
-			{
-				if (tempOwner != PlayerColor::NEUTRAL)
-				{
-					std::vector<ConstTransitivePtr<CGDwelling> >* dwellings = &cb->gameState()->players[tempOwner].dwellings;
-					dwellings->erase (std::find(dwellings->begin(), dwellings->end(), this));
-				}
-				if (PlayerColor(val) != PlayerColor::NEUTRAL) //can new owner be neutral?
-					cb->gameState()->players[PlayerColor(val)].dwellings.push_back (this);
-			}
-			break;
-		case ObjProperty::AVAILABLE_CREATURE:
-			creatures.resize(1);
-			creatures[0].second.resize(1);
-			creatures[0].second[0] = CreatureID(val);
-			break;
-	}
-	CGObjectInstance::setProperty(what,val);
-}
-void CGDwelling::onHeroVisit( const CGHeroInstance * h ) const
-{
-	if(ID == Obj::REFUGEE_CAMP && !creatures[0].first) //Refugee Camp, no available cres
-	{
-		InfoWindow iw;
-		iw.player = h->tempOwner;
-		iw.text.addTxt(MetaString::ADVOB_TXT, 44); //{%s} \n\n The camp is deserted.  Perhaps you should try next week.
-		iw.text.addReplacement(MetaString::OBJ_NAMES, ID);
-		cb->sendAndApply(&iw);
-		return;
-	}
-
-	PlayerRelations::PlayerRelations relations = cb->gameState()->getPlayerRelations( h->tempOwner, tempOwner );
-
-	if ( relations == PlayerRelations::ALLIES )
-		return;//do not allow recruiting or capturing
-
-	if( !relations  &&  stacksCount() > 0) //object is guarded, owned by enemy
-	{
-		BlockingDialog bd(true,false);
-		bd.player = h->tempOwner;
-		bd.text.addTxt(MetaString::GENERAL_TXT, 421); //Much to your dismay, the %s is guarded by %s %s. Do you wish to fight the guards?
-		bd.text.addReplacement(ID == Obj::CREATURE_GENERATOR1 ? MetaString::CREGENS : MetaString::CREGENS4, subID);
-		bd.text.addReplacement(MetaString::ARRAY_TXT, 176 + Slots().begin()->second->getQuantityID()*3);
-		bd.text.addReplacement(*Slots().begin()->second);
-		cb->showBlockingDialog(&bd);
-		return;
-	}
-
-	if(!relations  &&  ID != Obj::WAR_MACHINE_FACTORY)
-	{
-		cb->setOwner(this, h->tempOwner);
-	}
-
-	BlockingDialog bd (true,false);
-	bd.player = h->tempOwner;
-	if(ID == Obj::CREATURE_GENERATOR1 || ID == Obj::CREATURE_GENERATOR4)
-	{
-		bd.text.addTxt(MetaString::ADVOB_TXT, ID == Obj::CREATURE_GENERATOR1 ? 35 : 36); //{%s} Would you like to recruit %s? / {%s} Would you like to recruit %s, %s, %s, or %s?
-		bd.text.addReplacement(ID == Obj::CREATURE_GENERATOR1 ? MetaString::CREGENS : MetaString::CREGENS4, subID);
-		for(auto & elem : creatures)
-			bd.text.addReplacement(MetaString::CRE_PL_NAMES, elem.second[0]);
-	}
-	else if(ID == Obj::REFUGEE_CAMP)
-	{
-		bd.text.addTxt(MetaString::ADVOB_TXT, 35); //{%s} Would you like to recruit %s?
-		bd.text.addReplacement(MetaString::OBJ_NAMES, ID);
-		for(auto & elem : creatures)
-			bd.text.addReplacement(MetaString::CRE_PL_NAMES, elem.second[0]);
-	}
-	else if(ID == Obj::WAR_MACHINE_FACTORY)
-		bd.text.addTxt(MetaString::ADVOB_TXT, 157); //{War Machine Factory} Would you like to purchase War Machines?
-	else
-		throw std::runtime_error("Illegal dwelling!");
-
-	cb->showBlockingDialog(&bd);
-}
-
-void CGDwelling::newTurn() const
-{
-	if(cb->getDate(Date::DAY_OF_WEEK) != 1) //not first day of week
-		return;
-
-	//town growths and War Machines Factories are handled separately
-	if(ID == Obj::TOWN  ||  ID == Obj::WAR_MACHINE_FACTORY)
-		return;
-
-	if(ID == Obj::REFUGEE_CAMP) //if it's a refugee camp, we need to pick an available creature
-	{
-		cb->setObjProperty(id, ObjProperty::AVAILABLE_CREATURE, VLC->creh->pickRandomMonster(cb->gameState()->getRandomGenerator()));
-	}
-
-	bool change = false;
-
-	SetAvailableCreatures sac;
-	sac.creatures = creatures;
-	sac.tid = id;
-	for (size_t i = 0; i < creatures.size(); i++)
-	{
-		if(creatures[i].second.size())
-		{
-			CCreature *cre = VLC->creh->creatures[creatures[i].second[0]];
-			TQuantity amount = cre->growth * (1 + cre->valOfBonuses(Bonus::CREATURE_GROWTH_PERCENT)/100) + cre->valOfBonuses(Bonus::CREATURE_GROWTH);
-			if (VLC->modh->settings.DWELLINGS_ACCUMULATE_CREATURES && ID != Obj::REFUGEE_CAMP) //camp should not try to accumulate different kinds of creatures
-				sac.creatures[i].first += amount;
-			else
-				sac.creatures[i].first = amount;
-			change = true;
-		}
-	}
-
-	if(change)
-		cb->sendAndApply(&sac);
-}
-
-void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const
-{
-	CreatureID crid = creatures[0].second[0];
-	CCreature *crs = VLC->creh->creatures[crid];
-	TQuantity count = creatures[0].first;
-
-	if(crs->level == 1  &&  ID != Obj::REFUGEE_CAMP) //first level - creatures are for free
-	{
-		if(count) //there are available creatures
-		{
-			SlotID slot = h->getSlotFor(crid);
-			if(!slot.validSlot()) //no available slot
-			{
-				InfoWindow iw;
-				iw.player = h->tempOwner;
-				iw.text.addTxt(MetaString::GENERAL_TXT, 425);//The %s would join your hero, but there aren't enough provisions to support them.
-				iw.text.addReplacement(MetaString::CRE_PL_NAMES, crid);
-				cb->showInfoDialog(&iw);
-			}
-			else //give creatures
-			{
-				SetAvailableCreatures sac;
-				sac.tid = id;
-				sac.creatures = creatures;
-				sac.creatures[0].first = 0;
-
-
-				InfoWindow iw;
-				iw.player = h->tempOwner;
-				iw.text.addTxt(MetaString::GENERAL_TXT, 423); //%d %s join your army.
-				iw.text.addReplacement(count);
-				iw.text.addReplacement(MetaString::CRE_PL_NAMES, crid);
-
-				cb->showInfoDialog(&iw);
-				cb->sendAndApply(&sac);
-				cb->addToSlot(StackLocation(h, slot), crs, count);
-			}
-		}
-		else //there no creatures
-		{
-			InfoWindow iw;
-			iw.text.addTxt(MetaString::GENERAL_TXT, 422); //There are no %s here to recruit.
-			iw.text.addReplacement(MetaString::CRE_PL_NAMES, crid);
-			iw.player = h->tempOwner;
-			cb->sendAndApply(&iw);
-		}
-	}
-	else
-	{
-		if(ID == Obj::WAR_MACHINE_FACTORY) //pick available War Machines
-		{
-			//there is 1 war machine available to recruit if hero doesn't have one
-			SetAvailableCreatures sac;
-			sac.tid = id;
-			sac.creatures = creatures;
-			sac.creatures[0].first = !h->getArt(ArtifactPosition::MACH1); //ballista
-			sac.creatures[1].first = !h->getArt(ArtifactPosition::MACH3); //first aid tent
-			sac.creatures[2].first = !h->getArt(ArtifactPosition::MACH2); //ammo cart
-			cb->sendAndApply(&sac);
-		}
-
-		OpenWindow ow;
-		ow.id1 = id.getNum();
-		ow.id2 = h->id.getNum();
-		ow.window = (ID == Obj::CREATURE_GENERATOR1 || ID == Obj::REFUGEE_CAMP)
-			? OpenWindow::RECRUITMENT_FIRST
-			: OpenWindow::RECRUITMENT_ALL;
-		cb->sendAndApply(&ow);
-	}
-}
-
-void CGDwelling::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
-{
-	if (result.winner == 0)
-	{
-		onHeroVisit(hero);
-	}
-}
-
-void CGDwelling::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
-{
-	auto relations = cb->getPlayerRelations(getOwner(), hero->getOwner());
-	if(stacksCount() > 0  && relations == PlayerRelations::ENEMIES) //guards present
-	{
-		if(answer)
-			cb->startBattleI(hero, this);
-	}
-	else if(answer)
-	{
-		heroAcceptsCreatures(hero);
-	}
-}
-
-int CGTownInstance::getSightRadious() const //returns sight distance
-{
-	if (subID == ETownType::TOWER)
-	{
-		if (hasBuilt(BuildingID::GRAIL)) //skyship
-			return -1; //entire map
-		if (hasBuilt(BuildingID::LOOKOUT_TOWER)) //lookout tower
-			return 20;
-	}
-	return 5;
-}
-
-void CGTownInstance::setPropertyDer(ui8 what, ui32 val)
-{
-///this is freakin' overcomplicated solution
-	switch (what)
-	{
-	case ObjProperty::STRUCTURE_ADD_VISITING_HERO:
-			bonusingBuildings[val]->setProperty (ObjProperty::VISITORS, visitingHero->id.getNum());
-			break;
-		case ObjProperty::STRUCTURE_CLEAR_VISITORS:
-			bonusingBuildings[val]->setProperty (ObjProperty::STRUCTURE_CLEAR_VISITORS, 0);
-			break;
-		case ObjProperty::STRUCTURE_ADD_GARRISONED_HERO: //add garrisoned hero to visitors
-			bonusingBuildings[val]->setProperty (ObjProperty::VISITORS, garrisonHero->id.getNum());
-			break;
-		case ObjProperty::BONUS_VALUE_FIRST:
-			bonusValue.first = val;
-			break;
-		case ObjProperty::BONUS_VALUE_SECOND:
-			bonusValue.second = val;
-			break;
-	}
-}
-CGTownInstance::EFortLevel CGTownInstance::fortLevel() const //0 - none, 1 - fort, 2 - citadel, 3 - castle
-{
-	if (hasBuilt(BuildingID::CASTLE))
-		return CASTLE;
-	if (hasBuilt(BuildingID::CITADEL))
-		return CITADEL;
-	if (hasBuilt(BuildingID::FORT))
-		return FORT;
-	return NONE;
-}
-
-int CGTownInstance::hallLevel() const // -1 - none, 0 - village, 1 - town, 2 - city, 3 - capitol
-{
-	
-	if (hasBuilt(BuildingID::CAPITOL))
-		return 3;
-	if (hasBuilt(BuildingID::CITY_HALL))
-		return 2;
-	if (hasBuilt(BuildingID::TOWN_HALL))
-		return 1;
-	if (hasBuilt(BuildingID::VILLAGE_HALL))
-		return 0;
-	return -1;
-}
-int CGTownInstance::mageGuildLevel() const
-{
-	if (hasBuilt(BuildingID::MAGES_GUILD_5))
-		return 5;
-	if (hasBuilt(BuildingID::MAGES_GUILD_4))
-		return 4;
-	if (hasBuilt(BuildingID::MAGES_GUILD_3))
-		return 3;
-	if (hasBuilt(BuildingID::MAGES_GUILD_2))
-		return 2;
-	if (hasBuilt(BuildingID::MAGES_GUILD_1))
-		return 1;
-	return 0;
-}
-
-int CGTownInstance::getHordeLevel(const int & HID)  const//HID - 0 or 1; returns creature level or -1 if that horde structure is not present
-{
-	return town->hordeLvl.at(HID);
-}
-
-int CGTownInstance::creatureGrowth(const int & level) const
-{
-	return getGrowthInfo(level).totalGrowth();
-}
-
-GrowthInfo CGTownInstance::getGrowthInfo(int level) const
-{
-	GrowthInfo ret;
-
-	if (level<0 || level >=GameConstants::CREATURES_PER_TOWN)
-		return ret;
-	if (creatures[level].second.empty())
-		return ret; //no dwelling
-
-	const CCreature *creature = VLC->creh->creatures[creatures[level].second.back()];
-	const int base = creature->growth;
-	int castleBonus = 0;
-
-	ret.entries.push_back(GrowthInfo::Entry(VLC->generaltexth->allTexts[590], base));// \n\nBasic growth %d"
-
-	if (hasBuilt(BuildingID::CASTLE))
-		ret.entries.push_back(GrowthInfo::Entry(subID, BuildingID::CASTLE, castleBonus = base));
-	else if (hasBuilt(BuildingID::CITADEL))
-		ret.entries.push_back(GrowthInfo::Entry(subID, BuildingID::CITADEL, castleBonus = base / 2));
-
-	if(town->hordeLvl.at(0) == level)//horde 1
-		if(hasBuilt(BuildingID::HORDE_1))
-			ret.entries.push_back(GrowthInfo::Entry(subID, BuildingID::HORDE_1, creature->hordeGrowth));
-
-	if(town->hordeLvl.at(1) == level)//horde 2
-		if(hasBuilt(BuildingID::HORDE_2))
-			ret.entries.push_back(GrowthInfo::Entry(subID, BuildingID::HORDE_2, creature->hordeGrowth));
-
-	int dwellingBonus = 0;
-	if(const PlayerState *p = cb->getPlayer(tempOwner, false))
-	{
-		for(const CGDwelling *dwelling : p->dwellings)
-			if(vstd::contains(creatures[level].second, dwelling->creatures[0].second[0]))
-				dwellingBonus++;
-	}
-
-	if(dwellingBonus)
-		ret.entries.push_back(GrowthInfo::Entry(VLC->generaltexth->allTexts[591], dwellingBonus));// \nExternal dwellings %+d
-
-	//other *-of-legion-like bonuses (%d to growth cumulative with grail)
-	TBonusListPtr bonuses = getBonuses(Selector::type(Bonus::CREATURE_GROWTH).And(Selector::subtype(level)));
-	for(const Bonus *b : *bonuses)
-		ret.entries.push_back(GrowthInfo::Entry(b->Description() + " %+d", b->val));
-
-	//statue-of-legion-like bonus: % to base+castle
-	TBonusListPtr bonuses2 = getBonuses(Selector::type(Bonus::CREATURE_GROWTH_PERCENT));
-	for(const Bonus *b : *bonuses2)
-		ret.entries.push_back(GrowthInfo::Entry(b->Description() + " %+d", b->val * (base + castleBonus) / 100));
-
-	if(hasBuilt(BuildingID::GRAIL)) //grail - +50% to ALL (so far added) growth
-		ret.entries.push_back(GrowthInfo::Entry(subID, BuildingID::GRAIL, ret.totalGrowth() / 2));
-
-	return ret;
-}
-
-TResources CGTownInstance::dailyIncome() const
-{
-	TResources ret;
-
-	for (auto & p : town->buildings) 
-	{ 
-		BuildingID buildingUpgrade;
-
-		for (auto & p2 : town->buildings) 
-		{ 
-			if (p2.second->upgrade == p.first)
-			{
-				buildingUpgrade = p2.first;
-			}
-		}
-
-		if (!hasBuilt(buildingUpgrade)&&(hasBuilt(p.first)))
-		{
-			ret += p.second->produce;
-		}
-	
-	}
-
-	return ret;
-}
-bool CGTownInstance::hasFort() const
-{
-	return hasBuilt(BuildingID::FORT);
-}
-bool CGTownInstance::hasCapitol() const
-{
-	return hasBuilt(BuildingID::CAPITOL);
-}
-CGTownInstance::CGTownInstance()
-	:IShipyard(this), IMarket(this), town(nullptr), builded(0), destroyed(0), identifier(0), alignment(0xff)
-{
-
-}
-
-CGTownInstance::~CGTownInstance()
-{
-	for (auto & elem : bonusingBuildings)
-		delete elem;
-}
-
-int CGTownInstance::spellsAtLevel(int level, bool checkGuild) const
-{
-	if(checkGuild && mageGuildLevel() < level)
-		return 0;
-	int ret = 6 - level; //how many spells are available at this level
-
-	if (hasBuilt(BuildingID::LIBRARY, ETownType::TOWER))
-		ret++;
-
-	return ret;
-}
-
-bool CGTownInstance::needsLastStack() const
-{
-	if(garrisonHero)
-		return true;
-	else return false;
-}
-
-void CGTownInstance::onHeroVisit(const CGHeroInstance * h) const
-{
-	if( !cb->gameState()->getPlayerRelations( getOwner(), h->getOwner() ))//if this is enemy
-	{
-		if(armedGarrison() || visitingHero)
-		{
-			const CGHeroInstance *defendingHero = nullptr;
-			const CArmedInstance *defendingArmy = this;
-
-			if(visitingHero)
-				defendingHero = visitingHero;
-			else if(garrisonHero)
-				defendingHero = garrisonHero;
-
-			if(defendingHero)
-				defendingArmy = defendingHero;
-
-			bool outsideTown = (defendingHero == visitingHero && garrisonHero);
-
-			//TODO
-			//"borrowing" army from garrison to visiting hero
-
-			cb->startBattlePrimary(h, defendingArmy, getSightCenter(), h, defendingHero, false, (outsideTown ? nullptr : this));
-		}
-		else
-		{
-			cb->setOwner(this, h->tempOwner);
-			removeCapitols(h->getOwner());
-			cb->heroVisitCastle(this, h);
-		}
-	}
-	else if(h->visitablePos() == visitablePos())
-	{
-		if (h->commander && !h->commander->alive) //rise commander. TODO: interactive script
-		{
-			SetCommanderProperty scp;
-			scp.heroid = h->id;
-			scp.which = SetCommanderProperty::ALIVE;
-			scp.amount = 1;
-			cb->sendAndApply (&scp);
-		}
-		cb->heroVisitCastle(this, h);
-	}
-	else
-	{
-		logGlobal->errorStream() << h->name << " visits allied town of " << name << " from different pos?";
-	}
-}
-
-void CGTownInstance::onHeroLeave(const CGHeroInstance * h) const
-{
-	cb->stopHeroVisitCastle(this, h);
-}
-
-void CGTownInstance::initObj()
-///initialize town structures
-{
-	blockVisit = true;
-	hoverName = name + ", " + town->faction->name;
-
-	if (subID == ETownType::DUNGEON)
-		creatures.resize(GameConstants::CREATURES_PER_TOWN+1);//extra dwelling for Dungeon
-	else
-		creatures.resize(GameConstants::CREATURES_PER_TOWN);
-	for (int level = 0; level < GameConstants::CREATURES_PER_TOWN; level++)
-	{
-		BuildingID buildID = BuildingID(BuildingID::DWELL_FIRST).advance(level);
-		int upgradeNum = 0;
-
-		for (; town->buildings.count(buildID); upgradeNum++, buildID.advance(GameConstants::CREATURES_PER_TOWN))
-		{
-			if (hasBuilt(buildID) && town->creatures.at(level).size() > upgradeNum)
-				creatures[level].second.push_back(town->creatures[level][upgradeNum]);
-		}
-	}
-
-	switch (subID)
-	{ //add new visitable objects
-		case 0:
-			bonusingBuildings.push_back (new COPWBonus(BuildingID::STABLES, this));
-			break;
-		case 5:
-			bonusingBuildings.push_back (new COPWBonus(BuildingID::MANA_VORTEX, this));
-			//fallthrough
-		case 2: case 3: case 6:
-			bonusingBuildings.push_back (new CTownBonus(BuildingID::SPECIAL_4, this));
-			break;
-		case 7:
-			bonusingBuildings.push_back (new CTownBonus(BuildingID::SPECIAL_1, this));
-			break;
-	}
-	//add special bonuses from buildings
-
-	recreateBuildingsBonuses();
-}
-
-void CGTownInstance::newTurn() const
-{
-	if (cb->getDate(Date::DAY_OF_WEEK) == 1) //reset on new week
-	{
-		auto & rand = cb->gameState()->getRandomGenerator();
-
-		//give resources for Rampart, Mystic Pond
-		if (hasBuilt(BuildingID::MYSTIC_POND, ETownType::RAMPART)
-			&& cb->getDate(Date::DAY) != 1 && (tempOwner < PlayerColor::PLAYER_LIMIT))
-		{
-			int resID = rand.nextInt(2, 5); //bonus to random rare resource
-			resID = (resID==2)?1:resID;
-			int resVal = rand.nextInt(1, 4);//with size 1..4
-			cb->giveResource(tempOwner, static_cast<Res::ERes>(resID), resVal);
-			cb->setObjProperty (id, ObjProperty::BONUS_VALUE_FIRST, resID);
-			cb->setObjProperty (id, ObjProperty::BONUS_VALUE_SECOND, resVal);
-		}
-
-		if ( subID == ETownType::DUNGEON )
-			for (auto & elem : bonusingBuildings)
-		{
-			if ((elem)->ID == BuildingID::MANA_VORTEX)
-				cb->setObjProperty (id, ObjProperty::STRUCTURE_CLEAR_VISITORS, (elem)->id); //reset visitors for Mana Vortex
-		}
-
-		if (tempOwner == PlayerColor::NEUTRAL) //garrison growth for neutral towns
-			{
-				std::vector<SlotID> nativeCrits; //slots
-				for (auto & elem : Slots())
-				{
-					if (elem.second->type->faction == subID) //native
-					{
-						nativeCrits.push_back(elem.first); //collect matching slots
-					}
-				}
-				if (nativeCrits.size())
-				{
-					SlotID pos = *RandomGeneratorUtil::nextItem(nativeCrits, rand);
-					StackLocation sl(this, pos);
-
-					const CCreature *c = getCreature(pos);
-					if (rand.nextInt(99) < 90 || c->upgrades.empty()) //increase number if no upgrade available
-					{
-						cb->changeStackCount(sl, c->growth);
-					}
-					else //upgrade
-					{
-						cb->changeStackType(sl, VLC->creh->creatures[*c->upgrades.begin()]);
-					}
-				}
-				if ((stacksCount() < GameConstants::ARMY_SIZE && rand.nextInt(99) < 25) || Slots().empty()) //add new stack
-				{
-					int i = rand.nextInt(std::min(GameConstants::CREATURES_PER_TOWN, cb->getDate(Date::MONTH) << 1) - 1);
-					if (!town->creatures[i].empty())
-					{
-						CreatureID c = town->creatures[i][0];
-						SlotID n;
-
-						TQuantity count = creatureGrowth(i);
-						if (!count) // no dwelling
-							count = VLC->creh->creatures[c]->growth;
-
-						{//no lower tiers or above current month
-
-							if ((n = getSlotFor(c)).validSlot())
-							{
-								StackLocation sl(this, n);
-								if (slotEmpty(n))
-									cb->insertNewStack(sl, VLC->creh->creatures[c], count);
-								else //add to existing
-									cb->changeStackCount(sl, count);
-							}
-						}
-					}
-				}
-			}
-	}
-}
-
-int3 CGTownInstance::getSightCenter() const
-{
-	return pos - int3(2,0,0);
-}
-
-ui8 CGTownInstance::getPassableness() const
-{
-	if (!armedGarrison())//empty castle - anyone can visit
-		return GameConstants::ALL_PLAYERS;
-	if ( tempOwner == PlayerColor::NEUTRAL )//neutral guarded - no one can visit
-		return 0;
-
-	ui8 mask = 0;
-	TeamState * ts = cb->gameState()->getPlayerTeam(tempOwner);
-	for(PlayerColor it : ts->players)
-		mask |= 1<<it.getNum();//allies - add to possible visitors
-
-	return mask;
-}
-
-void CGTownInstance::getOutOffsets( std::vector<int3> &offsets ) const
-{
-	offsets += int3(-1,2,0), int3(-3,2,0);
-}
-
-void CGTownInstance::removeCapitols (PlayerColor owner) const
-{
-	if (hasCapitol()) // search if there's an older capitol
-	{
-		PlayerState* state = cb->gameState()->getPlayer (owner); //get all towns owned by player
-		for (auto i = state->towns.cbegin(); i < state->towns.cend(); ++i)
-		{
-			if (*i != this && (*i)->hasCapitol())
-			{
-				RazeStructures rs;
-				rs.tid = id;
-				rs.bid.insert(BuildingID::CAPITOL);
-				rs.destroyed = destroyed;
-				cb->sendAndApply(&rs);
-				return;
-			}
-		}
-	}
-}
-
-int CGTownInstance::getBoatType() const
-{
-	switch (town->faction->alignment)
-	{
-	case EAlignment::EVIL : return 0;
-	case EAlignment::GOOD : return 1;
-	case EAlignment::NEUTRAL : return 2;
-	}
-	assert(0);
-	return -1;
-}
-
-int CGTownInstance::getMarketEfficiency() const
-{
-	if (!hasBuilt(BuildingID::MARKETPLACE))
-		return 0;
-
-	const PlayerState *p = cb->getPlayer(tempOwner);
-	assert(p);
-
-	int marketCount = 0;
-	for(const CGTownInstance *t : p->towns)
-		if(t->hasBuilt(BuildingID::MARKETPLACE))
-			marketCount++;
-
-	return marketCount;
-}
-
-bool CGTownInstance::allowsTrade(EMarketMode::EMarketMode mode) const
-{
-	switch(mode)
-	{
-	case EMarketMode::RESOURCE_RESOURCE:
-	case EMarketMode::RESOURCE_PLAYER:
-		return hasBuilt(BuildingID::MARKETPLACE);
-
-	case EMarketMode::ARTIFACT_RESOURCE:
-	case EMarketMode::RESOURCE_ARTIFACT:
-		return hasBuilt(BuildingID::ARTIFACT_MERCHANT, ETownType::TOWER)
-		    || hasBuilt(BuildingID::ARTIFACT_MERCHANT, ETownType::DUNGEON)
-		    || hasBuilt(BuildingID::ARTIFACT_MERCHANT, ETownType::CONFLUX);
-
-	case EMarketMode::CREATURE_RESOURCE:
-		return hasBuilt(BuildingID::FREELANCERS_GUILD, ETownType::STRONGHOLD);
-
-	case EMarketMode::CREATURE_UNDEAD:
-		return hasBuilt(BuildingID::SKELETON_TRANSFORMER, ETownType::NECROPOLIS);
-
-	case EMarketMode::RESOURCE_SKILL:
-		return hasBuilt(BuildingID::MAGIC_UNIVERSITY, ETownType::CONFLUX);
-	default:
-		assert(0);
-		return false;
-	}
-}
-
-std::vector<int> CGTownInstance::availableItemsIds(EMarketMode::EMarketMode mode) const
-{
-	if(mode == EMarketMode::RESOURCE_ARTIFACT)
-	{
-		std::vector<int> ret;
-		for(const CArtifact *a : merchantArtifacts)
-			if(a)
-				ret.push_back(a->id);
-			else
-				ret.push_back(-1);
-		return ret;
-	}
-	else if ( mode == EMarketMode::RESOURCE_SKILL )
-	{
-		return universitySkills;
-	}
-	else
-		return IMarket::availableItemsIds(mode);
-}
-
-void CGTownInstance::updateAppearance()
-{
-	if (!hasFort())
-		appearance.animationFile = town->clientInfo.advMapVillage;
-	else if(hasCapitol())
-		appearance.animationFile = town->clientInfo.advMapCapitol;
-	else
-		appearance.animationFile = town->clientInfo.advMapCastle;
-}
-
-std::string CGTownInstance::nodeName() const
-{
-	return "Town (" + (town ? town->faction->name : "unknown") + ") of " +  name;
-}
-
-void CGTownInstance::deserializationFix()
-{
-	attachTo(&townAndVis);
-
-	//Hero is already handled by CGameState::attachArmedObjects
-
-// 	if(visitingHero)
-// 		visitingHero->attachTo(&townAndVis);
-// 	if(garrisonHero)
-// 		garrisonHero->attachTo(this);
-}
-
-void CGTownInstance::updateMoraleBonusFromArmy()
-{
-	Bonus *b = getBonusList().getFirst(Selector::sourceType(Bonus::ARMY).And(Selector::type(Bonus::MORALE)));
-	if(!b)
-	{
-		b = new Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, 0, -1);
-		addNewBonus(b);
-	}
-
-	if (garrisonHero)
-		b->val = 0;
-	else
-		CArmedInstance::updateMoraleBonusFromArmy();
-}
-
-void CGTownInstance::recreateBuildingsBonuses()
-{
-	static TPropagatorPtr playerProp(new CPropagatorNodeType(PLAYER));
-
-	BonusList bl;
-	getExportedBonusList().getBonuses(bl, Selector::sourceType(Bonus::TOWN_STRUCTURE));
-	for(Bonus *b : bl)
-		removeBonus(b);
-
-	//tricky! -> checks tavern only if no bratherhood of sword or not a castle
-	if(subID != ETownType::CASTLE || !addBonusIfBuilt(BuildingID::BROTHERHOOD, Bonus::MORALE, +2))
-		addBonusIfBuilt(BuildingID::TAVERN, Bonus::MORALE, +1);
-
-	if(subID == ETownType::CASTLE) //castle
-	{
-		addBonusIfBuilt(BuildingID::LIGHTHOUSE, Bonus::SEA_MOVEMENT, +500, playerProp);
-		addBonusIfBuilt(BuildingID::GRAIL,      Bonus::MORALE, +2, playerProp); //colossus
-	}
-	else if(subID == ETownType::RAMPART) //rampart
-	{
-		addBonusIfBuilt(BuildingID::FOUNTAIN_OF_FORTUNE, Bonus::LUCK, +2); //fountain of fortune
-		addBonusIfBuilt(BuildingID::GRAIL, Bonus::LUCK, +2, playerProp); //guardian spirit
-	}
-	else if(subID == ETownType::TOWER) //tower
-	{
-		addBonusIfBuilt(BuildingID::GRAIL, Bonus::PRIMARY_SKILL, +15, PrimarySkill::KNOWLEDGE); //grail
-	}
-	else if(subID == ETownType::INFERNO) //Inferno
-	{
-		addBonusIfBuilt(BuildingID::STORMCLOUDS, Bonus::PRIMARY_SKILL, +2, PrimarySkill::SPELL_POWER); //Brimstone Clouds
-	}
-	else if(subID == ETownType::NECROPOLIS) //necropolis
-	{
-		addBonusIfBuilt(BuildingID::COVER_OF_DARKNESS,    Bonus::DARKNESS, +20);
-		addBonusIfBuilt(BuildingID::NECROMANCY_AMPLIFIER, Bonus::SECONDARY_SKILL_PREMY, +10, playerProp, SecondarySkill::NECROMANCY); //necromancy amplifier
-		addBonusIfBuilt(BuildingID::GRAIL, Bonus::SECONDARY_SKILL_PREMY, +20, playerProp, SecondarySkill::NECROMANCY); //Soul prison
-	}
-	else if(subID == ETownType::DUNGEON) //Dungeon
-	{
-		addBonusIfBuilt(BuildingID::GRAIL, Bonus::PRIMARY_SKILL, +12, PrimarySkill::SPELL_POWER); //grail
-	}
-	else if(subID == ETownType::STRONGHOLD) //Stronghold
-	{
-		addBonusIfBuilt(BuildingID::GRAIL, Bonus::PRIMARY_SKILL, +20, PrimarySkill::ATTACK); //grail
-	}
-	else if(subID == ETownType::FORTRESS) //Fortress
-	{
-		addBonusIfBuilt(BuildingID::GLYPHS_OF_FEAR, Bonus::PRIMARY_SKILL, +2, PrimarySkill::DEFENSE); //Glyphs of Fear
-		addBonusIfBuilt(BuildingID::BLOOD_OBELISK,  Bonus::PRIMARY_SKILL, +2, PrimarySkill::ATTACK); //Blood Obelisk
-		addBonusIfBuilt(BuildingID::GRAIL, Bonus::PRIMARY_SKILL, +10, PrimarySkill::ATTACK); //grail
-		addBonusIfBuilt(BuildingID::GRAIL, Bonus::PRIMARY_SKILL, +10, PrimarySkill::DEFENSE); //grail
-	}
-	else if(subID == ETownType::CONFLUX)
-	{
-
-	}
-}
-
-bool CGTownInstance::addBonusIfBuilt(BuildingID building, Bonus::BonusType type, int val, int subtype /*= -1*/)
-{
-	static auto emptyPropagator = TPropagatorPtr();
-	return addBonusIfBuilt(building, type, val, emptyPropagator, subtype);
-}
-
-bool CGTownInstance::addBonusIfBuilt(BuildingID building, Bonus::BonusType type, int val, TPropagatorPtr & prop, int subtype /*= -1*/)
-{
-	if(hasBuilt(building))
-	{
-		std::ostringstream descr;
-		descr << town->buildings.at(building)->Name() << " ";
-		if(val > 0)
-			descr << "+";
-		else if(val < 0)
-			descr << "-";
-		descr << val;
-
-		Bonus *b = new Bonus(Bonus::PERMANENT, type, Bonus::TOWN_STRUCTURE, val, building, descr.str(), subtype);
-		if(prop)
-			b->addPropagator(prop);
-		addNewBonus(b);
-		return true;
-	}
-
-	return false;
-}
-
-void CGTownInstance::setVisitingHero(CGHeroInstance *h)
-{
-	assert(!!visitingHero == !h);
-	if(h)
-	{
-		PlayerState *p = cb->gameState()->getPlayer(h->tempOwner);
-		assert(p);
-		h->detachFrom(p);
-		h->attachTo(&townAndVis);
-		visitingHero = h;
-		h->visitedTown = this;
-		h->inTownGarrison = false;
-	}
-	else
-	{
-		PlayerState *p = cb->gameState()->getPlayer(visitingHero->tempOwner);
-		visitingHero->visitedTown = nullptr;
-		visitingHero->detachFrom(&townAndVis);
-		visitingHero->attachTo(p);
-		visitingHero = nullptr;
-	}
-}
-
-void CGTownInstance::setGarrisonedHero(CGHeroInstance *h)
-{
-	assert(!!garrisonHero == !h);
-	if(h)
-	{
-		PlayerState *p = cb->gameState()->getPlayer(h->tempOwner);
-		assert(p);
-		h->detachFrom(p);
-		h->attachTo(this);
-		garrisonHero = h;
-		h->visitedTown = this;
-		h->inTownGarrison = true;
-	}
-	else
-	{
-		PlayerState *p = cb->gameState()->getPlayer(garrisonHero->tempOwner);
-		garrisonHero->visitedTown = nullptr;
-		garrisonHero->inTownGarrison = false;
-		garrisonHero->detachFrom(this);
-		garrisonHero->attachTo(p);
-		garrisonHero = nullptr;
-	}
-	updateMoraleBonusFromArmy(); //avoid giving morale bonus for same army twice
-}
-
-bool CGTownInstance::armedGarrison() const
-{
-	return stacksCount() || garrisonHero;
-}
-
-int CGTownInstance::getTownLevel() const
-{
-	// count all buildings that are not upgrades
-	return boost::range::count_if(builtBuildings, [&](const BuildingID & build)
-	{
-		return town->buildings.at(build) && town->buildings.at(build)->upgrade == -1;
-	});
-}
-
-CBonusSystemNode * CGTownInstance::whatShouldBeAttached()
-{
-	return &townAndVis;
-}
-
-const CArmedInstance * CGTownInstance::getUpperArmy() const
-{
-	if(garrisonHero)
-		return garrisonHero;
-	return this;
-}
-
-bool CGTownInstance::hasBuilt(BuildingID buildingID, int townID) const
-{
-	if (townID == town->faction->index || townID == ETownType::ANY)
-		return hasBuilt(buildingID);
-	return false;
-}
-
-bool CGTownInstance::hasBuilt(BuildingID buildingID) const
-{
-	return vstd::contains(builtBuildings, buildingID);
-}
-
-void CGTownInstance::addHeroToStructureVisitors( const CGHeroInstance *h, si32 structureInstanceID ) const
-{
-	if(visitingHero == h)
-		cb->setObjProperty(id, ObjProperty::STRUCTURE_ADD_VISITING_HERO, structureInstanceID); //add to visitors
-	else if(garrisonHero == h)
-		cb->setObjProperty(id, ObjProperty::STRUCTURE_ADD_GARRISONED_HERO, structureInstanceID); //then it must be garrisoned hero
-	else
-	{
-		//should never ever happen
-        logGlobal->errorStream() << "Cannot add hero " << h->name << " to visitors of structure #" << structureInstanceID;
-		assert(0);
-	}
-}
-
-void CGTownInstance::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
-{
-	if(result.winner == 0)
-	{
-		removeCapitols(hero->getOwner());
-		cb->setOwner (this, hero->tempOwner); //give control after checkout is done
-		FoWChange fw;
-		fw.player = hero->tempOwner;
-		fw.mode = 1;
-		getSightTiles (fw.tiles); //update visibility for castle structures
-		cb->sendAndApply (&fw);
-	}
-}
-
-bool CGVisitableOPH::wasVisited (const CGHeroInstance * h) const
-{
-	return vstd::contains(visitors, h->id);
-}
-
-void CGVisitableOPH::onHeroVisit( const CGHeroInstance * h ) const
-{
-	if(!vstd::contains(visitors, h->id))
-	{
-		onNAHeroVisit (h, false);
-		switch(ID)
-		{
-		case Obj::TREE_OF_KNOWLEDGE:
-		case Obj::ARENA:
-		case Obj::LIBRARY_OF_ENLIGHTENMENT:
-		case Obj::SCHOOL_OF_MAGIC:
-		case Obj::SCHOOL_OF_WAR:
-			break;
-		default:
-			cb->setObjProperty(id, ObjProperty::VISITORS, h->id.getNum()); //add to the visitors
-			break;
-		}
-	}
-	else
-	{
-		onNAHeroVisit(h, true);
-	}
-}
-
-void CGVisitableOPH::initObj()
-{
-	if(ID==Obj::TREE_OF_KNOWLEDGE)
-	{
-		switch (cb->gameState()->getRandomGenerator().nextInt(2))
-		{
-		case 1:
-			treePrice[Res::GOLD] = 2000;
-			break;
-		case 2:
-			treePrice[Res::GEMS] = 10;
-			break;
-		default:
-			break;
-		}
-	}
-}
-
-void CGVisitableOPH::treeSelected (const CGHeroInstance * h, ui32 result) const
-{
-	if(result) //player agreed to give res for exp
-	{
-		si64 expToGive = VLC->heroh->reqExp(h->level+1) - VLC->heroh->reqExp(h->level);;
-		cb->giveResources (h->getOwner(), -treePrice);
-		cb->changePrimSkill (h, PrimarySkill::EXPERIENCE, expToGive);
-		cb->setObjProperty (id, ObjProperty::VISITORS, h->id.getNum()); //add to the visitors
-	}
-}
-void CGVisitableOPH::onNAHeroVisit (const CGHeroInstance * h, bool alreadyVisited) const
-{
-	Component::EComponentType c_id = Component::PRIM_SKILL; //most used here
-	int subid=0, ot=0, sound = 0;
-	TExpType val=1;
-	switch(ID)
-	{
-	case Obj::ARENA:
-		sound = soundBase::NOMAD;
-		ot = 0;
-		break;
-	case Obj::MERCENARY_CAMP:
-		sound = soundBase::NOMAD;
-		subid=PrimarySkill::ATTACK;
-		ot=80;
-		break;
-	case Obj::MARLETTO_TOWER:
-		sound = soundBase::NOMAD;
-		subid=PrimarySkill::DEFENSE;
-		ot=39;
-		break;
-	case Obj::STAR_AXIS:
-		sound = soundBase::gazebo;
-		subid=PrimarySkill::SPELL_POWER;
-		ot=100;
-		break;
-	case Obj::GARDEN_OF_REVELATION:
-		sound = soundBase::GETPROTECTION;
-		subid=PrimarySkill::KNOWLEDGE;
-		ot=59;
-		break;
-	case Obj::LEARNING_STONE:
-		sound = soundBase::gazebo;
-		c_id=Component::EXPERIENCE;
-		ot=143;
-		val=1000;
-		break;
-	case Obj::TREE_OF_KNOWLEDGE:
-		sound = soundBase::gazebo;
-		c_id = Component::EXPERIENCE;
-		subid = 1;
-		ot = 147;
-		val = 1;
-		break;
-	case Obj::LIBRARY_OF_ENLIGHTENMENT:
-		sound = soundBase::gazebo;
-		ot = 66;
-		break;
-	case Obj::SCHOOL_OF_MAGIC:
-		sound = soundBase::faerie;
-		ot = 71;
-		break;
-	case Obj::SCHOOL_OF_WAR:
-		c_id=Component::PRIM_SKILL;
-		sound = soundBase::MILITARY;
-		ot = 158;
-		break;
-	}
-	if (!alreadyVisited)
-	{
-		switch (ID)
-		{
-		case Obj::ARENA:
-			{
-				BlockingDialog sd(false,true);
-				sd.soundID = sound;
-				sd.text.addTxt(MetaString::ADVOB_TXT,ot);
-				sd.components.push_back(Component(c_id, PrimarySkill::ATTACK, 2, 0));
-				sd.components.push_back(Component(c_id, PrimarySkill::DEFENSE, 2, 0));
-				sd.player = h->getOwner();
-				cb->showBlockingDialog(&sd);
-				return;
-			}
-		case Obj::MERCENARY_CAMP:
-		case Obj::MARLETTO_TOWER:
-		case Obj::STAR_AXIS:
-		case Obj::GARDEN_OF_REVELATION:
-			{
-				cb->changePrimSkill (h, static_cast<PrimarySkill::PrimarySkill>(subid), val);
-				InfoWindow iw;
-				iw.soundID = sound;
-				iw.components.push_back(Component(c_id, subid, val, 0));
-				iw.text.addTxt(MetaString::ADVOB_TXT,ot);
-				iw.player = h->getOwner();
-				cb->showInfoDialog(&iw);
-				break;
-			}
-		case Obj::LEARNING_STONE: //give exp
-			{
-				val = h->calculateXp(val);
-				InfoWindow iw;
-				iw.soundID = sound;
-				iw.components.push_back (Component(c_id,subid,val,0));
-				iw.player = h->getOwner();
-				iw.text.addTxt(MetaString::ADVOB_TXT,ot);
-				cb->showInfoDialog(&iw);
-				cb->changePrimSkill(h, PrimarySkill::EXPERIENCE, val);
-				break;
-			}
-		case Obj::TREE_OF_KNOWLEDGE:
-			{
-				val = VLC->heroh->reqExp (h->level + val) - VLC->heroh->reqExp(h->level);
-				if(!treePrice.nonZero())
-				{
-					cb->setObjProperty (id, ObjProperty::VISITORS, h->id.getNum()); //add to the visitors
-					InfoWindow iw;
-					iw.soundID = sound;
-					iw.components.push_back (Component(c_id,subid,1,0));
-					iw.player = h->getOwner();
-					iw.text.addTxt (MetaString::ADVOB_TXT,148);
-					cb->showInfoDialog (&iw);
-					cb->changePrimSkill (h, PrimarySkill::EXPERIENCE, val);
-					break;
-				}
-				else
-				{
-					if(treePrice[Res::GOLD] > 0)
-						ot = 149;
-					else
-						ot = 151;
-
-					if(!cb->getPlayer(h->tempOwner)->resources.canAfford(treePrice)) //not enough resources
-					{
-						ot++;
-						showInfoDialog(h,ot,sound);
-						return;
-					}
-
-					BlockingDialog sd (true, false);
-					sd.soundID = sound;
-					sd.player = h->getOwner();
-					sd.text.addTxt (MetaString::ADVOB_TXT,ot);
-					sd.addResourceComponents (treePrice);
-					cb->showBlockingDialog (&sd);
-				}
-				break;
-			}
-		case Obj::LIBRARY_OF_ENLIGHTENMENT:
-			{
-				int txt_id = 66;
-				if(h->level  <  10 - 2*h->getSecSkillLevel(SecondarySkill::DIPLOMACY)) //not enough level
-				{
-					txt_id += 2;
-				}
-				else
-				{
-					cb->setObjProperty(id, ObjProperty::VISITORS, h->id.getNum()); //add to the visitors
-					cb->changePrimSkill (h, PrimarySkill::ATTACK, 2);
-					cb->changePrimSkill (h, PrimarySkill::DEFENSE, 2);
-					cb->changePrimSkill (h, PrimarySkill::KNOWLEDGE, 2);
-					cb->changePrimSkill (h, PrimarySkill::SPELL_POWER, 2);
-				}
-				showInfoDialog(h,txt_id,sound);
-				break;
-			}
-		case Obj::SCHOOL_OF_MAGIC:
-		case Obj::SCHOOL_OF_WAR:
-			{
-				int skill = (ID==Obj::SCHOOL_OF_MAGIC ? 2 : 0);
-				if (cb->getResource (h->getOwner(), Res::GOLD) < 1000) //not enough resources
-				{
-					showInfoDialog (h->getOwner(), ot+2, sound);
-				}
-				else
-				{
-					BlockingDialog sd(true,true);
-					sd.soundID = sound;
-					sd.player = h->getOwner();
-					sd.text.addTxt(MetaString::ADVOB_TXT,ot);
-					sd.components.push_back(Component(c_id, skill, +1, 0));
-					sd.components.push_back(Component(c_id, skill+1, +1, 0));
-					cb->showBlockingDialog(&sd);
-				}
-			}
-			break;
-		}
-	}
-	else
-	{
-		ot++;
-		showInfoDialog (h->getOwner(),ot,sound);
-	}
-}
-
-const std::string & CGVisitableOPH::getHoverText() const
-{
-	int pom = -1;
-	switch(ID)
-	{
-	case Obj::ARENA:
-		pom = -1;
-		break;
-	case Obj::MERCENARY_CAMP:
-		pom = 8;
-		break;
-	case Obj::MARLETTO_TOWER:
-		pom = 7;
-		break;
-	case Obj::STAR_AXIS:
-		pom = 11;
-		break;
-	case Obj::GARDEN_OF_REVELATION:
-		pom = 4;
-		break;
-	case Obj::LEARNING_STONE:
-		pom = 5;
-		break;
-	case Obj::TREE_OF_KNOWLEDGE:
-		pom = 18;
-		break;
-	case Obj::LIBRARY_OF_ENLIGHTENMENT:
-		break;
-	case Obj::SCHOOL_OF_MAGIC:
-		pom = 9;
-		break;
-	case Obj::SCHOOL_OF_WAR:
-		pom = 10;
-		break;
-	default:
-		throw std::runtime_error("Wrong CGVisitableOPH object ID!\n");
-	}
-	hoverName = VLC->generaltexth->names[ID];
-	if(pom >= 0)
-		hoverName += ("\n" + VLC->generaltexth->xtrainfo[pom]);
-	const CGHeroInstance *h = cb->getSelectedHero (cb->getCurrentPlayer());
-	if(h)
-	{
-		hoverName += "\n\n";
-		bool visited = vstd::contains (visitors, h->id);
-		hoverName += visitedTxt (visited);
-	}
-	return hoverName;
-}
-
-void CGVisitableOPH::arenaSelected(const CGHeroInstance * h, int primSkill ) const
-{
-	cb->setObjProperty(id, ObjProperty::VISITORS, h->id.getNum()); //add to the visitors
-	cb->changePrimSkill(h, static_cast<PrimarySkill::PrimarySkill>(primSkill-1), 2);
-}
-
-void CGVisitableOPH::setPropertyDer( ui8 what, ui32 val )
-{
-	if(what == ObjProperty::VISITORS)
-		visitors.insert(ObjectInstanceID(val));
-}
-
-void CGVisitableOPH::schoolSelected(const CGHeroInstance * h, ui32 which) const
-{
-	if(!which) //player refused to pay
-		return;
-
-	int base = (ID == Obj::SCHOOL_OF_MAGIC  ?  2  :  0);
-	cb->setObjProperty (id, ObjProperty::VISITORS, h->id.getNum()); //add to the visitors
-	cb->giveResource (h->getOwner(),Res::GOLD,-1000); //take 1000 gold
-	cb->changePrimSkill (h, static_cast<PrimarySkill::PrimarySkill>(base + which-1), +1); //give appropriate skill
-}
-
-void CGVisitableOPH::blockingDialogAnswered(const CGHeroInstance *h, ui32 answer) const
-{
-	switch (ID)
-	{
-	case Obj::ARENA:
-		arenaSelected(h, answer);
-		break;
-
-	case Obj::TREE_OF_KNOWLEDGE:
-		treeSelected(h, answer);
-		break;
-
-	case Obj::SCHOOL_OF_MAGIC:
-	case Obj::SCHOOL_OF_WAR:
-		schoolSelected(h, answer);
-		break;
-
-	default:
-		assert(0);
-		break;
-	}
-}
-
-COPWBonus::COPWBonus (BuildingID index, CGTownInstance *TOWN)
-{
-	ID = index;
-	town = TOWN;
-	id = town->bonusingBuildings.size();
-}
-void COPWBonus::setProperty(ui8 what, ui32 val)
-{
-	switch (what)
-	{
-		case ObjProperty::VISITORS:
-			visitors.insert(val);
-			break;
-		case ObjProperty::STRUCTURE_CLEAR_VISITORS:
-			visitors.clear();
-			break;
-	}
-}
-void COPWBonus::onHeroVisit (const CGHeroInstance * h) const
-{
-	ObjectInstanceID heroID = h->id;
-	if (town->hasBuilt(ID))
-	{
-		InfoWindow iw;
-		iw.player = h->tempOwner;
-		switch (town->subID)
-		{
-			case ETownType::CASTLE: //Stables
-				if (!h->hasBonusFrom(Bonus::OBJECT, Obj::STABLES)) //does not stack with advMap Stables
-				{
-					GiveBonus gb;
-					gb.bonus = Bonus(Bonus::ONE_WEEK, Bonus::LAND_MOVEMENT, Bonus::OBJECT, 600, 94, VLC->generaltexth->arraytxt[100]);
-					gb.id = heroID.getNum();
-					cb->giveHeroBonus(&gb);
-					iw.text << VLC->generaltexth->allTexts[580];
-					cb->showInfoDialog(&iw);
-				}
-				break;
-			case ETownType::DUNGEON: //Mana Vortex
-				if (visitors.empty() && h->mana <= h->manaLimit() * 2)
-				{
-					cb->setManaPoints (heroID, 2 * h->manaLimit());
-					//TODO: investigate line below
-					//cb->setObjProperty (town->id, ObjProperty::VISITED, true);
-					iw.text << VLC->generaltexth->allTexts[579];
-					cb->showInfoDialog(&iw);
-					town->addHeroToStructureVisitors(h, id);
-				}
-				break;
-		}
-	}
-}
-CTownBonus::CTownBonus (BuildingID index, CGTownInstance *TOWN)
-{
-	ID = index;
-	town = TOWN;
-	id = town->bonusingBuildings.size();
-}
-void CTownBonus::setProperty (ui8 what, ui32 val)
-{
-	if(what == ObjProperty::VISITORS)
-		visitors.insert(ObjectInstanceID(val));
-}
-void CTownBonus::onHeroVisit (const CGHeroInstance * h) const
-{
-	ObjectInstanceID heroID = h->id;
-	if (town->hasBuilt(ID) && visitors.find(heroID) == visitors.end())
-	{
-		InfoWindow iw;
-		PrimarySkill::PrimarySkill what = PrimarySkill::ATTACK;
-		int val=0, mid=0;
-		switch (ID)
-		{
-			case BuildingID::SPECIAL_4:
-				switch(town->subID)
-				{
-					case ETownType::TOWER: //wall
-						what = PrimarySkill::KNOWLEDGE;
-						val = 1;
-						mid = 581;
-						iw.components.push_back (Component(Component::PRIM_SKILL, 3, 1, 0));
-						break;
-					case ETownType::INFERNO: //order of fire
-						what = PrimarySkill::SPELL_POWER;
-						val = 1;
-						mid = 582;
-						iw.components.push_back (Component(Component::PRIM_SKILL, 2, 1, 0));
-						break;
-					case ETownType::STRONGHOLD://hall of Valhalla
-						what = PrimarySkill::ATTACK;
-						val = 1;
-						mid = 584;
-						iw.components.push_back (Component(Component::PRIM_SKILL, 0, 1, 0));
-						break;
-					case ETownType::DUNGEON://academy of battle scholars
-						what = PrimarySkill::EXPERIENCE;
-						val = h->calculateXp(1000);
-						mid = 583;
-						iw.components.push_back (Component(Component::EXPERIENCE, 0, val, 0));
-						break;
-				}
-				break;
-			case BuildingID::SPECIAL_1:
-				switch(town->subID)
-				{
-					case ETownType::FORTRESS: //cage of warlords
-						what = PrimarySkill::DEFENSE;
-						val = 1;
-						mid = 585;
-						iw.components.push_back (Component(Component::PRIM_SKILL, 1, 1, 0));
-						break;
-				}
-				break;
-		}
-		assert(mid);
-		iw.player = cb->getOwner(heroID);
-		iw.text << VLC->generaltexth->allTexts[mid];
-		cb->showInfoDialog(&iw);
-		cb->changePrimSkill (cb->getHero(heroID), what, val);
-		town->addHeroToStructureVisitors(h, id);
-	}
-}
-const std::string & CGCreature::getHoverText() const
-{
-	if(stacks.empty())
-	{
-		static const std::string errorValue("!!!INVALID_STACK!!!");
-
-		//should not happen...
-		logGlobal->errorStream() << "Invalid stack at tile " << pos << ": subID=" << subID << "; id=" << id;
-		return errorValue; // references to temporary are illegal - use pre-constructed string
-	}
-
-	MetaString ms;
-	int pom = stacks.begin()->second->getQuantityID();
-	pom = 172 + 3*pom;
-	ms.addTxt(MetaString::ARRAY_TXT,pom);
-	ms << " " ;
-	ms.addTxt(MetaString::CRE_PL_NAMES,subID);
-	ms.toString(hoverName);
-
-	if(const CGHeroInstance *selHero = cb->getSelectedHero(cb->getCurrentPlayer()))
-	{
-		const JsonNode & texts = VLC->generaltexth->localizedTexts["adventureMap"]["monsterThreat"];
-
-		hoverName += texts["title"].String();
-		int choice;
-		double ratio = ((double)getArmyStrength() / selHero->getTotalStrength());
-		     if (ratio < 0.1)  choice = 0;
-		else if (ratio < 0.25) choice = 1;
-		else if (ratio < 0.6)  choice = 2;
-		else if (ratio < 0.9)  choice = 3;
-		else if (ratio < 1.1)  choice = 4;
-		else if (ratio < 1.3)  choice = 5;
-		else if (ratio < 1.8)  choice = 6;
-		else if (ratio < 2.5)  choice = 7;
-		else if (ratio < 4)    choice = 8;
-		else if (ratio < 8)    choice = 9;
-		else if (ratio < 20)   choice = 10;
-		else                   choice = 11;
-		hoverName += texts["levels"].Vector()[choice].String();
-	}
-	return hoverName;
-}
-void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
-{
-	int action = takenAction(h);
-	switch( action ) //decide what we do...
-	{
-	case FIGHT:
-		fight(h);
-		break;
-	case FLEE: //flee
-		{
-			flee(h);
-			break;
-		}
-	case JOIN_FOR_FREE: //join for free
-		{
-			BlockingDialog ynd(true,false);
-			ynd.player = h->tempOwner;
-			ynd.text.addTxt(MetaString::ADVOB_TXT, 86);
-			ynd.text.addReplacement(MetaString::CRE_PL_NAMES, subID);
-			cb->showBlockingDialog(&ynd);
-			break;
-		}
-	default: //join for gold
-		{
-			assert(action > 0);
-
-			//ask if player agrees to pay gold
-			BlockingDialog ynd(true,false);
-			ynd.player = h->tempOwner;
-			std::string tmp = VLC->generaltexth->advobtxt[90];
-			boost::algorithm::replace_first(tmp,"%d",boost::lexical_cast<std::string>(getStackCount(SlotID(0))));
-			boost::algorithm::replace_first(tmp,"%d",boost::lexical_cast<std::string>(action));
-			boost::algorithm::replace_first(tmp,"%s",VLC->creh->creatures[subID]->namePl);
-			ynd.text << tmp;
-			cb->showBlockingDialog(&ynd);
-			break;
-		}
-	}
-
-}
-
-void CGCreature::initObj()
-{
-	blockVisit = true;
-	switch(character)
-	{
-	case 0:
-		character = -4;
-		break;
-	case 1:
-		character = cb->gameState()->getRandomGenerator().nextInt(1, 7);
-		break;
-	case 2:
-		character = cb->gameState()->getRandomGenerator().nextInt(1, 10);
-		break;
-	case 3:
-		character = cb->gameState()->getRandomGenerator().nextInt(4, 10);
-		break;
-	case 4:
-		character = 10;
-		break;
-	}
-
-	stacks[SlotID(0)]->setType(CreatureID(subID));
-	TQuantity &amount = stacks[SlotID(0)]->count;
-	CCreature &c = *VLC->creh->creatures[subID];
-	if(amount == 0)
-	{
-		amount = cb->gameState()->getRandomGenerator().nextInt(c.ammMin, c.ammMax);
-
-		if(amount == 0) //armies with 0 creatures are illegal
-		{
-            logGlobal->warnStream() << "Problem: stack " << nodeName() << " cannot have 0 creatures. Check properties of " << c.nodeName();
-			amount = 1;
-		}
-	}
-	formation.randomFormation = cb->gameState()->getRandomGenerator().nextInt();
-
-	temppower = stacks[SlotID(0)]->count * 1000;
-	refusedJoining = false;
-}
-
-void CGCreature::newTurn() const
-{//Works only for stacks of single type of size up to 2 millions
-	if (stacks.begin()->second->count < VLC->modh->settings.CREEP_SIZE && cb->getDate(Date::DAY_OF_WEEK) == 1 && cb->getDate(Date::DAY) > 1)
-	{
-		ui32 power = temppower * (100 +  VLC->modh->settings.WEEKLY_GROWTH)/100;
-		cb->setObjProperty(id, ObjProperty::MONSTER_COUNT, std::min (power/1000 , (ui32)VLC->modh->settings.CREEP_SIZE)); //set new amount
-		cb->setObjProperty(id, ObjProperty::MONSTER_POWER, power); //increase temppower
-	}
-	if (VLC->modh->modules.STACK_EXP)
-		cb->setObjProperty(id, ObjProperty::MONSTER_EXP, VLC->modh->settings.NEUTRAL_STACK_EXP); //for testing purpose
-}
-void CGCreature::setPropertyDer(ui8 what, ui32 val)
-{
-	switch (what)
-	{
-		case ObjProperty::MONSTER_COUNT:
-			stacks[SlotID(0)]->count = val;
-			break;
-		case ObjProperty::MONSTER_POWER:
-			temppower = val;
-			break;
-		case ObjProperty::MONSTER_EXP:
-			giveStackExp(val);
-			break;
-		case ObjProperty::MONSTER_RESTORE_TYPE:
-			formation.basicType = val;
-			break;
-		case ObjProperty::MONSTER_REFUSED_JOIN:
-			refusedJoining = val;
-			break;
-	}
-}
-
-int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
-{
-	//calculate relative strength of hero and creatures armies
-	double relStrength = double(h->getTotalStrength()) / getArmyStrength();
-
-	int powerFactor;
-	if(relStrength >= 7)
-		powerFactor = 11;
-
-	else if(relStrength >= 1)
-		powerFactor = (int)(2*(relStrength-1));
-
-	else if(relStrength >= 0.5)
-		powerFactor = -1;
-
-	else if(relStrength >= 0.333)
-		powerFactor = -2;
-	else
-		powerFactor = -3;
-
-	std::set<CreatureID> myKindCres; //what creatures are the same kind as we
-	const CCreature * myCreature = VLC->creh->creatures[subID];
-	myKindCres.insert(myCreature->idNumber); //we
-	myKindCres.insert(myCreature->upgrades.begin(), myCreature->upgrades.end()); //our upgrades
-
-	for(ConstTransitivePtr<CCreature> &crea : VLC->creh->creatures)
-	{
-		if(vstd::contains(crea->upgrades, myCreature->idNumber)) //it's our base creatures
-			myKindCres.insert(crea->idNumber);
-	}
-
-	int count = 0, //how many creatures of similar kind has hero
-		totalCount = 0;
-
-	for (auto & elem : h->Slots())
-	{
-		if(vstd::contains(myKindCres,elem.second->type->idNumber))
-			count += elem.second->count;
-		totalCount += elem.second->count;
-	}
-
-	int sympathy = 0; // 0 if hero have no similar creatures
-	if(count)
-		sympathy++; // 1 - if hero have at least 1 similar creature
-	if(count*2 > totalCount)
-		sympathy++; // 2 - hero have similar creatures more that 50%
-
-	int charisma = powerFactor + h->getSecSkillLevel(SecondarySkill::DIPLOMACY) + sympathy;
-
-	if(charisma < character) //creatures will fight
-		return -2;
-
-	if (allowJoin)
-	{
-		if(h->getSecSkillLevel(SecondarySkill::DIPLOMACY) + sympathy + 1 >= character)
-			return 0; //join for free
-
-		else if(h->getSecSkillLevel(SecondarySkill::DIPLOMACY) * 2  +  sympathy  +  1 >= character)
-			return VLC->creh->creatures[subID]->cost[6] * getStackCount(SlotID(0)); //join for gold
-	}
-
-	//we are still here - creatures have not joined hero, flee or fight
-
-	if (charisma > character)
-		return -1; //flee
-	else
-		return -2; //fight
-}
-
-void CGCreature::fleeDecision(const CGHeroInstance *h, ui32 pursue) const
-{
-	if(refusedJoining)
-		cb->setObjProperty(id, ObjProperty::MONSTER_REFUSED_JOIN, false);
-
-	if(pursue)
-	{
-		fight(h);
-	}
-	else
-	{
-		cb->removeObject(this);
-	}
-}
-
-void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) const
-{
-	if(!accept)
-	{
-		if(takenAction(h,false) == -1) //they flee
-		{
-			cb->setObjProperty(id, ObjProperty::MONSTER_REFUSED_JOIN, true);
-			flee(h);
-		}
-		else //they fight
-		{
-			showInfoDialog(h,87,0);//Insulted by your refusal of their offer, the monsters attack!
-			fight(h);
-		}
-	}
-	else //accepted
-	{
-		if (cb->getResource(h->tempOwner, Res::GOLD) < cost) //player don't have enough gold!
-		{
-			InfoWindow iw;
-			iw.player = h->tempOwner;
-			iw.text << std::pair<ui8,ui32>(1,29);  //You don't have enough gold
-			cb->showInfoDialog(&iw);
-
-			//act as if player refused
-			joinDecision(h,cost,false);
-			return;
-		}
-
-		//take gold
-		if(cost)
-			cb->giveResource(h->tempOwner,Res::GOLD,-cost);
-
-		cb->tryJoiningArmy(this, h, true, true);
-	}
-}
-
-void CGCreature::fight( const CGHeroInstance *h ) const
-{
-	//split stacks
-	//TODO: multiple creature types in a stack?
-	int basicType = stacks.begin()->second->type->idNumber;
-	cb->setObjProperty(id, ObjProperty::MONSTER_RESTORE_TYPE, basicType); //store info about creature stack
-
-	double relativePower = static_cast<double>(h->getTotalStrength()) / getArmyStrength();
-	int stacksCount;
-	//TODO: number depends on tile type
-	if (relativePower < 0.5)
-	{
-		stacksCount = 7;
-	}
-	else if (relativePower < 0.67)
-	{
-		stacksCount = 7;
-	}
-	else if (relativePower < 1)
-	{
-		stacksCount = 6;
-	}
-	else if (relativePower < 1.5)
-	{
-		stacksCount = 5;
-	}
-	else if (relativePower < 2)
-	{
-		stacksCount = 4;
-	}
-	else
-	{
-		stacksCount = 3;
-	}
-	SlotID sourceSlot = stacks.begin()->first;
-	SlotID destSlot;
-	for (int stacksLeft = stacksCount; stacksLeft > 1; --stacksLeft)
-	{
-		int stackSize = stacks.begin()->second->count / stacksLeft;
-		if (stackSize)
-		{
-			if ((destSlot = getFreeSlot()).validSlot())
-				cb->moveStack(StackLocation(this, sourceSlot), StackLocation(this, destSlot), stackSize);
-			else
-			{
-                logGlobal->warnStream() <<"Warning! Not enough empty slots to split stack!";
-				break;
-			}
-		}
-		else break;
-	}
-	if (stacksCount > 1)
-	{
-		if (formation.randomFormation % 100 < 50) //upgrade
-		{
-			SlotID slotId = SlotID(stacks.size() / 2);
-			const auto & upgrades = getStack(slotId).type->upgrades;
-			if(!upgrades.empty())
-			{
-				auto it = RandomGeneratorUtil::nextItem(upgrades, cb->gameState()->getRandomGenerator());
-				cb->changeStackType(StackLocation(this, slotId), VLC->creh->creatures[*it]);
-			}
-		}
-	}
-
-	cb->startBattleI(h, this);
-
-}
-
-void CGCreature::flee( const CGHeroInstance * h ) const
-{
-	BlockingDialog ynd(true,false);
-	ynd.player = h->tempOwner;
-	ynd.text.addTxt(MetaString::ADVOB_TXT,91);
-	ynd.text.addReplacement(MetaString::CRE_PL_NAMES, subID);
-	cb->showBlockingDialog(&ynd);
-}
-
-void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
-{
-
-	if(result.winner==0)
-	{
-		cb->removeObject(this);
-	}
-	else
-	{
-		//int killedAmount=0;
-		//for(std::set<std::pair<ui32,si32> >::iterator i=result->casualties[1].begin(); i!=result->casualties[1].end(); i++)
-		//	if(i->first == subID)
-		//		killedAmount += i->second;
-		//cb->setAmount(id, slots.find(0)->second.second - killedAmount);
-
-		/*
-		MetaString ms;
-		int pom = slots.find(0)->second.getQuantityID();
-		pom = 174 + 3*pom + 1;
-		ms << std::pair<ui8,ui32>(6,pom) << " " << std::pair<ui8,ui32>(7,subID);
-		cb->setHoverName(id,&ms);
-		cb->setObjProperty(id, 11, slots.begin()->second.count * 1000);
-		*/
-
-		//merge stacks into one
-		TSlots::const_iterator i;
-		CCreature * cre = VLC->creh->creatures[formation.basicType];
-		for (i = stacks.begin(); i != stacks.end(); i++)
-		{
-			if (cre->isMyUpgrade(i->second->type))
-			{
-				cb->changeStackType (StackLocation(this, i->first), cre); //un-upgrade creatures
-			}
-		}
-
-		//first stack has to be at slot 0 -> if original one got killed, move there first remaining stack
-		if(!hasStackAtSlot(SlotID(0)))
-			cb->moveStack(StackLocation(this, stacks.begin()->first), StackLocation(this, SlotID(0)), stacks.begin()->second->count);
-
-		while (stacks.size() > 1) //hopefully that's enough
-		{
-			// TODO it's either overcomplicated (if we assume there'll be only one stack) or buggy (if we allow multiple stacks... but that'll also cause troubles elsewhere)
-			i = stacks.end();
-			i--;
-			SlotID slot = getSlotFor(i->second->type);
-			if (slot == i->first) //no reason to move stack to its own slot
-				break;
-			else
-				cb->moveStack (StackLocation(this, i->first), StackLocation(this, slot), i->second->count);
-		}
-
-		cb->setObjProperty(id, ObjProperty::MONSTER_POWER, stacks.begin()->second->count * 1000); //remember casualties
-	}
-}
-
-void CGCreature::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
-{
-	auto action = takenAction(hero);
-	if(!refusedJoining && action >= JOIN_FOR_FREE) //higher means price
-		joinDecision(hero, action, answer);
-	else if(action != FIGHT)
-		fleeDecision(hero, answer);
-	else
-		assert(0);
-}
-
-void CGMine::onHeroVisit( const CGHeroInstance * h ) const
-{
-	int relations = cb->gameState()->getPlayerRelations(h->tempOwner, tempOwner);
-
-	if(relations == 2) //we're visiting our mine
-	{
-		cb->showGarrisonDialog(id,h->id,true);
-		return;
-	}
-	else if (relations == 1)//ally
-		return;
-
-	if(stacksCount()) //Mine is guarded
-	{
-		BlockingDialog ynd(true,false);
-		ynd.player = h->tempOwner;
-		ynd.text.addTxt(MetaString::ADVOB_TXT, subID == 7 ? 84 : 187);
-		cb->showBlockingDialog(&ynd);
-		return;
-	}
-
-	flagMine(h->tempOwner);
-
-}
-
-void CGMine::newTurn() const
-{
-	if(cb->getDate() == 1)
-		return;
-
-	if (tempOwner == PlayerColor::NEUTRAL)
-		return;
-
-	cb->giveResource(tempOwner, producedResource, producedQuantity);
-}
-
-void CGMine::initObj()
-{
-	if(subID >= 7) //Abandoned Mine
-	{
-		//set guardians
-		int howManyTroglodytes = cb->gameState()->getRandomGenerator().nextInt(100, 199);
-		auto troglodytes = new CStackInstance(CreatureID::TROGLODYTES, howManyTroglodytes);
-		putStack(SlotID(0), troglodytes);
-
-		//after map reading tempOwner placeholds bitmask for allowed resources
-		std::vector<Res::ERes> possibleResources;
-		for (int i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++)
-			if(tempOwner.getNum() & 1<<i) //NOTE: reuse of tempOwner
-				possibleResources.push_back(static_cast<Res::ERes>(i));
-
-		assert(!possibleResources.empty());
-		producedResource = *RandomGeneratorUtil::nextItem(possibleResources, cb->gameState()->getRandomGenerator());
-		tempOwner = PlayerColor::NEUTRAL;
-		hoverName = VLC->generaltexth->mines[7].first + "\n" + VLC->generaltexth->allTexts[202] + " " + troglodytes->getQuantityTXT(false) + " " + troglodytes->type->namePl;
-	}
-	else
-	{
-		producedResource = static_cast<Res::ERes>(subID);
-
-		MetaString ms;
-		ms << std::pair<ui8,ui32>(9,producedResource);
-		if(tempOwner >= PlayerColor::PLAYER_LIMIT)
-			tempOwner = PlayerColor::NEUTRAL;
-		else
-			ms << " (" << std::pair<ui8,ui32>(6,23+tempOwner.getNum()) << ")";
-		ms.toString(hoverName);
-	}
-
-	producedQuantity = defaultResProduction();
-}
-
-void CGMine::flagMine(PlayerColor player) const
-{
-	assert(tempOwner != player);
-	cb->setOwner(this, player); //not ours? flag it!
-
-	MetaString ms;
-	ms << std::pair<ui8,ui32>(9,subID) << "\n(" << std::pair<ui8,ui32>(6,23+player.getNum()) << ")";
-	if(subID == 7)
-	{
-		ms << "(%s)";
-		ms.addReplacement(MetaString::RES_NAMES, producedResource);
-	}
-	cb->setHoverName(this,&ms);
-
-	InfoWindow iw;
-	iw.soundID = soundBase::FLAGMINE;
-	iw.text.addTxt(MetaString::MINE_EVNTS,producedResource); //not use subID, abandoned mines uses default mine texts
-	iw.player = player;
-	iw.components.push_back(Component(Component::RESOURCE,producedResource,producedQuantity,-1));
-	cb->showInfoDialog(&iw);
-}
-
-ui32 CGMine::defaultResProduction()
-{
-	switch(producedResource)
-	{
-	case Res::WOOD:
-	case Res::ORE:
-		return 2;
-	case Res::GOLD:
-		return 1000;
-	default:
-		return 1;
-	}
-}
-
-void CGMine::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
-{
-	if(result.winner == 0) //attacker won
-	{
-		if(subID == 7)
-		{
-			showInfoDialog(hero->tempOwner, 85, 0);
-		}
-		flagMine(hero->tempOwner);
-	}
-}
-
-void CGMine::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
-{
-	if(answer)
-		cb->startBattleI(hero, this);
-}
-
-void CGResource::initObj()
-{
-	blockVisit = true;
-	hoverName = VLC->generaltexth->restypes[subID];
-
-	if(!amount)
-	{
-		switch(subID)
-		{
-		case 6:
-			amount = cb->gameState()->getRandomGenerator().nextInt(500, 1000);
-			break;
-		case 0: case 2:
-			amount = cb->gameState()->getRandomGenerator().nextInt(6, 10);
-			break;
-		default:
-			amount = cb->gameState()->getRandomGenerator().nextInt(3, 5);
-			break;
-		}
-	}
-}
-
-void CGResource::onHeroVisit( const CGHeroInstance * h ) const
-{
-	if(stacksCount())
-	{
-		if(message.size())
-		{
-			BlockingDialog ynd(true,false);
-			ynd.player = h->getOwner();
-			ynd.text << message;
-			cb->showBlockingDialog(&ynd);
-		}
-		else
-		{
-			blockingDialogAnswered(h, true); //behave as if player accepted battle
-		}
-	}
-	else
-	{
-		if(message.length())
-		{
-			InfoWindow iw;
-			iw.player = h->tempOwner;
-			iw.text << message;
-			cb->showInfoDialog(&iw);
-		}
-		collectRes(h->getOwner());
-	}
-}
-
-void CGResource::collectRes( PlayerColor player ) const
-{
-	cb->giveResource(player, static_cast<Res::ERes>(subID), amount);
-	ShowInInfobox sii;
-	sii.player = player;
-	sii.c = Component(Component::RESOURCE,subID,amount,0);
-	sii.text.addTxt(MetaString::ADVOB_TXT,113);
-	sii.text.addReplacement(MetaString::RES_NAMES, subID);
-	cb->showCompInfo(&sii);
-	cb->removeObject(this);
-}
-
-void CGResource::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
-{
-	if(result.winner == 0) //attacker won
-		collectRes(hero->getOwner());
-}
-
-void CGResource::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
-{
-	if(answer)
-		cb->startBattleI(hero, this);
-}
-
-void CGVisitableOPW::newTurn() const
-{
-	if (cb->getDate(Date::DAY_OF_WEEK) == 1) //first day of week = 1
-	{
-		cb->setObjProperty(id, ObjProperty::VISITED, false);
-		MetaString ms; //set text to "not visited"
-		ms << std::pair<ui8,ui32>(3,ID) << " " << std::pair<ui8,ui32>(1,353);
-		cb->setHoverName(this,&ms);
-	}
-}
-bool CGVisitableOPW::wasVisited(PlayerColor player) const
-{
-	return visited; //TODO: other players should see object as unvisited
-}
-
-void CGVisitableOPW::onHeroVisit( const CGHeroInstance * h ) const
-{
-	int mid=0, sound = 0;
-	switch (ID)
-	{
-	case Obj::MYSTICAL_GARDEN:
-		sound = soundBase::experience;
-		mid = 92;
-		break;
-	case Obj::WINDMILL:
-		sound = soundBase::GENIE;
-		mid = 170;
-		break;
-	case Obj::WATER_WHEEL:
-		sound = soundBase::GENIE;
-		mid = 164;
-		break;
-	default:
-		assert(0);
-	}
-	if (visited)
-	{
-		if (ID!=Obj::WINDMILL)
-			mid++;
-		else
-			mid--;
-		showInfoDialog(h,mid,sound);
-	}
-	else
-	{
-		Component::EComponentType type = Component::RESOURCE;
-		Res::ERes sub=Res::WOOD;
-		int val=0;
-		auto & rand = cb->gameState()->getRandomGenerator();
-
-		switch (ID)
-		{
-		case Obj::MYSTICAL_GARDEN:
-			if(rand.nextInt(1) == 1)
-			{
-				sub = Res::GEMS;
-				val = 5;
-			}
-			else
-			{
-				sub = Res::GOLD;
-				val = 500;
-			}
-			break;
-		case Obj::WINDMILL:
-			mid = 170;
-			sub = static_cast<Res::ERes>(rand.nextInt(1, 5));
-			val = rand.nextInt(3, 6);
-			break;
-		case Obj::WATER_WHEEL:
-			mid = 164;
-			sub = Res::GOLD;
-			if(cb->getDate(Date::DAY)<8)
-				val = 500;
-			else
-				val = 1000;
-		}
-		cb->giveResource(h->tempOwner, sub, val);
-		InfoWindow iw;
-		iw.soundID = sound;
-		iw.player = h->tempOwner;
-		iw.components.push_back(Component(type,sub,val,0));
-		iw.text.addTxt(MetaString::ADVOB_TXT,mid);
-		cb->showInfoDialog(&iw);
-		cb->setObjProperty(id, ObjProperty::VISITED, true);
-		MetaString ms; //set text to "visited"
-		ms.addTxt(MetaString::OBJ_NAMES,ID); ms << " "; ms.addTxt(MetaString::GENERAL_TXT,352);
-		cb->setHoverName(this,&ms);
-	}
-}
-
-void CGVisitableOPW::setPropertyDer( ui8 what, ui32 val )
-{
-	if(what == ObjProperty::VISITED)
-		visited = val;
-}
-
-void CGTeleport::onHeroVisit( const CGHeroInstance * h ) const
-{
-	ObjectInstanceID destinationid;
-	switch(ID)
-	{
-	case Obj::MONOLITH_ONE_WAY_ENTRANCE: //one way - find corresponding exit monolith
-	{
-		if(vstd::contains(objs,Obj::MONOLITH_ONE_WAY_EXIT) && vstd::contains(objs[Obj::MONOLITH_ONE_WAY_EXIT],subID) && objs[Obj::MONOLITH_ONE_WAY_EXIT][subID].size())
-		{
-			destinationid = *RandomGeneratorUtil::nextItem(objs[Obj::MONOLITH_ONE_WAY_EXIT][subID], cb->gameState()->getRandomGenerator());
-		}
-		else
-		{
-            logGlobal->warnStream() << "Cannot find corresponding exit monolith for "<< id;
-		}
-		break;
-	}
-	case Obj::MONOLITH_TWO_WAY://two way monolith - pick any other one
-	case Obj::WHIRLPOOL: //Whirlpool
-		if(vstd::contains(objs,ID) && vstd::contains(objs[ID],subID) && objs[ID][subID].size()>1)
-		{
-			//choose another exit
-			do
-			{
-				destinationid = *RandomGeneratorUtil::nextItem(objs[ID][subID], cb->gameState()->getRandomGenerator());
-			} while(destinationid == id);
-
-			if (ID == Obj::WHIRLPOOL)
-			{
-				if (!h->hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION))
-				{
-					if (h->Slots().size() > 1 || h->Slots().begin()->second->count > 1)
-					{ //we can't remove last unit
-						SlotID targetstack = h->Slots().begin()->first; //slot numbers may vary
-						for(auto i = h->Slots().rbegin(); i != h->Slots().rend(); i++)
-						{
-							if (h->getPower(targetstack) > h->getPower(i->first))
-							{
-								targetstack = (i->first);
-							}
-						}
-
-						TQuantity countToTake = h->getStackCount(targetstack) * 0.5;
-						vstd::amax(countToTake, 1);
-
-
-						InfoWindow iw;
-						iw.player = h->tempOwner;
-						iw.text.addTxt (MetaString::ADVOB_TXT, 168);
-						iw.components.push_back (Component(CStackBasicDescriptor(h->getCreature(targetstack), countToTake)));
-						cb->showInfoDialog(&iw);
-						cb->changeStackCount(StackLocation(h, targetstack), -countToTake);
-					}
-				}
-			}
-		}
-		else
-            logGlobal->warnStream() << "Cannot find corresponding exit monolith for "<< id;
-		break;
-	case Obj::SUBTERRANEAN_GATE: //find nearest subterranean gate on the other level
-		{
-			destinationid = getMatchingGate(id);
-			if(destinationid == ObjectInstanceID()) //no exit
-			{
-				showInfoDialog(h,153,0);//Just inside the entrance you find a large pile of rubble blocking the tunnel. You leave discouraged.
-			}
-			break;
-		}
-	}
-	if(destinationid == ObjectInstanceID())
-	{
-        logGlobal->warnStream() << "Cannot find exit... (obj at " << pos << ") :( ";
-		return;
-	}
-	if (ID == Obj::WHIRLPOOL)
-	{
-		std::set<int3> tiles = cb->getObj(destinationid)->getBlockedPos();
-		auto & tile = *RandomGeneratorUtil::nextItem(tiles, cb->gameState()->getRandomGenerator());
-		cb->moveHero(h->id, tile + int3(1,0,0), true);
-	}
-	else
-		cb->moveHero (h->id,CGHeroInstance::convertPosition(cb->getObj(destinationid)->pos,true) - getVisitableOffset(), true);
-}
-
-void CGTeleport::initObj()
-{
-	int si = subID;
-	switch (ID)
-	{
-	case Obj::SUBTERRANEAN_GATE://ignore subterranean gates subid
-	case Obj::WHIRLPOOL:
-		{
-			si = 0;
-			break;
-		}
-	default:
-		break;
-	}
-	objs[ID][si].push_back(id);
-}
-
-void CGTeleport::postInit() //matches subterranean gates into pairs
-{
-	//split on underground and surface gates
-	std::vector<const CGObjectInstance *> gatesSplit[2]; //surface and underground gates
-	for(auto & elem : objs[Obj::SUBTERRANEAN_GATE][0])
-	{
-		const CGObjectInstance *hlp = cb->getObj(elem);
-		gatesSplit[hlp->pos.z].push_back(hlp);
-	}
-
-	//sort by position
-	std::sort(gatesSplit[0].begin(), gatesSplit[0].end(), [](const CGObjectInstance * a, const CGObjectInstance * b)
-	{
-		return a->pos < b->pos;
-	});
-
-	for(size_t i = 0; i < gatesSplit[0].size(); i++)
-	{
-		const CGObjectInstance *cur = gatesSplit[0][i];
-
-		//find nearest underground exit
-		std::pair<int, si32> best(-1, std::numeric_limits<si32>::max()); //pair<pos_in_vector, distance^2>
-		for(int j = 0; j < gatesSplit[1].size(); j++)
-		{
-			const CGObjectInstance *checked = gatesSplit[1][j];
-			if(!checked)
-				continue;
-			si32 hlp = checked->pos.dist2dSQ(cur->pos);
-			if(hlp < best.second)
-			{
-				best.first = j;
-				best.second = hlp;
-			}
-		}
-
-		if(best.first >= 0) //found pair
-		{
-			gates.push_back(std::make_pair(cur->id, gatesSplit[1][best.first]->id));
-			gatesSplit[1][best.first] = nullptr;
-		}
-		else
-			gates.push_back(std::make_pair(cur->id, ObjectInstanceID()));
-	}
-	objs.erase(Obj::SUBTERRANEAN_GATE);
-}
-
-ObjectInstanceID CGTeleport::getMatchingGate(ObjectInstanceID id)
-{
-	for(auto & gate : gates)
-	{
-		if(gate.first == id)
-			return gate.second;
-		if(gate.second == id)
-			return gate.first;
-	}
-
-	return ObjectInstanceID();
-}
-
-void CGArtifact::initObj()
-{
-	blockVisit = true;
-	if(ID == Obj::ARTIFACT)
-	{
-		hoverName = VLC->arth->artifacts[subID]->Name();
-		if(!storedArtifact->artType)
-			storedArtifact->setType(VLC->arth->artifacts[subID]);
-	}
-	if(ID == Obj::SPELL_SCROLL)
-		subID = 1;
-
-	assert(storedArtifact->artType);
-	assert(storedArtifact->getParentNodes().size());
-
-	//assert(storedArtifact->artType->id == subID); //this does not stop desync
-}
-
-void CGArtifact::onHeroVisit( const CGHeroInstance * h ) const
-{
-	if(!stacksCount())
-	{
-		InfoWindow iw;
-		iw.player = h->tempOwner;
-		switch(ID)
-		{
-		case Obj::ARTIFACT:
-			{
-				iw.soundID = soundBase::treasure; //play sound only for non-scroll arts
-				iw.components.push_back(Component(Component::ARTIFACT,subID,0,0));
-				if(message.length())
-					iw.text <<  message;
-				else
-				{
-					if (VLC->arth->artifacts[subID]->EventText().size())
-						iw.text << std::pair<ui8, ui32> (MetaString::ART_EVNTS, subID);
-					else //fix for mod artifacts with no event text
-					{
-						iw.text.addTxt (MetaString::ADVOB_TXT, 183); //% has found treasure
-						iw.text.addReplacement (h->name);
-					}
-
-				}
-			}
-			break;
-		case Obj::SPELL_SCROLL:
-			{
-				int spellID = storedArtifact->getGivenSpellID();
-				iw.components.push_back (Component(Component::SPELL, spellID,0,0));
-				iw.text.addTxt (MetaString::ADVOB_TXT,135);
-				iw.text.addReplacement(MetaString::SPELL_NAME, spellID);
-			}
-			break;
-		}
-		cb->showInfoDialog(&iw);
-		pick(h);
-	}
-	else
-	{
-		if(message.size())
-		{
-			BlockingDialog ynd(true,false);
-			ynd.player = h->getOwner();
-			ynd.text << message;
-			cb->showBlockingDialog(&ynd);
-		}
-		else
-		{
-			blockingDialogAnswered(h, true);
-		}
-	}
-}
-
-void CGArtifact::pick(const CGHeroInstance * h) const
-{
-	cb->giveHeroArtifact(h, storedArtifact, ArtifactPosition::FIRST_AVAILABLE);
-	cb->removeObject(this);
-}
-
-void CGArtifact::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
-{
-	if(result.winner == 0) //attacker won
-		pick(hero);
-}
-
-void CGArtifact::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
-{
-	if(answer)
-		cb->startBattleI(hero, this);
-}
-
-void CGPickable::initObj()
-{
-	blockVisit = true;
-	switch(ID)
-	{
-	case Obj::CAMPFIRE:
-		val2 = cb->gameState()->getRandomGenerator().nextInt(4, 6);
-		val1 = val2 * 100;
-		type = cb->gameState()->getRandomGenerator().nextInt(5); // given resource
-		break;
-	case Obj::FLOTSAM:
-		switch(type = cb->gameState()->getRandomGenerator().nextInt(3))
-		{
-		case 0:
-			val1 = val2 = 0;
-			break;
-		case 1:
-			val1 = 5;
-			val2 = 0;
-			break;
-		case 2:
-			val1 = 5;
-			val2 = 200;
-			break;
-		case 3:
-			val1 = 10;
-			val2 = 500;
-			break;
-		}
-		break;
-	case Obj::SEA_CHEST:
-		{
-			int hlp = cb->gameState()->getRandomGenerator().nextInt(99);
-			if(hlp < 20)
-			{
-				val1 = 0;
-				type = 0;
-			}
-			else if(hlp < 90)
-			{
-				val1 = 1500;
-				type = 2;
-			}
-			else
-			{
-				val1 = 1000;
-				val2 = VLC->arth->pickRandomArtifact(cb->gameState()->getRandomGenerator(), CArtifact::ART_TREASURE);
-				type = 1;
-			}
-		}
-		break;
-	case Obj::SHIPWRECK_SURVIVOR:
-		{
-			int hlp = cb->gameState()->getRandomGenerator().nextInt(99);
-			if(hlp < 55)
-				val1 = VLC->arth->pickRandomArtifact(cb->gameState()->getRandomGenerator(), CArtifact::ART_TREASURE);
-			else if(hlp < 75)
-				val1 = VLC->arth->pickRandomArtifact(cb->gameState()->getRandomGenerator(), CArtifact::ART_MINOR);
-			else if(hlp < 95)
-				val1 = VLC->arth->pickRandomArtifact(cb->gameState()->getRandomGenerator(), CArtifact::ART_MAJOR);
-			else
-				val1 = VLC->arth->pickRandomArtifact(cb->gameState()->getRandomGenerator(), CArtifact::ART_RELIC);
-		}
-		break;
-	case Obj::TREASURE_CHEST:
-		{
-			int hlp = cb->gameState()->getRandomGenerator().nextInt(99);
-			if(hlp >= 95)
-			{
-				type = 1;
-				val1 = VLC->arth->pickRandomArtifact(cb->gameState()->getRandomGenerator(), CArtifact::ART_TREASURE);
-				return;
-			}
-			else if (hlp >= 65)
-			{
-				val1 = 2000;
-			}
-			else if(hlp >= 33)
-			{
-				val1 = 1500;
-			}
-			else
-			{
-				val1 = 1000;
-			}
-
-			val2 = val1 - 500;
-			type = 0;
-			break;
-		}
-	}
-}
-
-void CGPickable::onHeroVisit( const CGHeroInstance * h ) const
-{
-	switch(ID)
-	{
-	case Obj::CAMPFIRE:
-		{
-			cb->giveResource(h->tempOwner,static_cast<Res::ERes>(type),val2); //non-gold resource
-			cb->giveResource(h->tempOwner,Res::GOLD,val1);//gold
-			InfoWindow iw;
-			iw.soundID = soundBase::experience;
-			iw.player = h->tempOwner;
-			iw.components.push_back(Component(Component::RESOURCE,Res::GOLD,val1,0));
-			iw.components.push_back(Component(Component::RESOURCE,type,val2,0));
-			iw.text.addTxt(MetaString::ADVOB_TXT,23);
-			cb->showInfoDialog(&iw);
-			break;
-		}
-	case Obj::FLOTSAM:
-		{
-			cb->giveResource(h->tempOwner,Res::WOOD,val1); //wood
-			cb->giveResource(h->tempOwner,Res::GOLD,val2);//gold
-			InfoWindow iw;
-			iw.soundID = soundBase::GENIE;
-			iw.player = h->tempOwner;
-			if(val1)
-				iw.components.push_back(Component(Component::RESOURCE,Res::WOOD,val1,0));
-			if(val2)
-				iw.components.push_back(Component(Component::RESOURCE,Res::GOLD,val2,0));
-
-			iw.text.addTxt(MetaString::ADVOB_TXT, 51+type);
-			cb->showInfoDialog(&iw);
-			break;
-		}
-	case Obj::SEA_CHEST:
-		{
-			InfoWindow iw;
-			iw.soundID = soundBase::chest;
-			iw.player = h->tempOwner;
-			iw.text.addTxt(MetaString::ADVOB_TXT, 116 + type);
-
-			if(val1) //there is gold
-			{
-				iw.components.push_back(Component(Component::RESOURCE,Res::GOLD,val1,0));
-				cb->giveResource(h->tempOwner,Res::GOLD,val1);
-			}
-			if(type == 1) //art
-			{
-				//TODO: what if no space in backpack?
-				iw.components.push_back(Component(Component::ARTIFACT, val2, 1, 0));
-				iw.text.addReplacement(MetaString::ART_NAMES, val2);
-				cb->giveHeroNewArtifact(h, VLC->arth->artifacts[val2],ArtifactPosition::FIRST_AVAILABLE);
-			}
-			cb->showInfoDialog(&iw);
-			break;
-		}
-	case Obj::SHIPWRECK_SURVIVOR:
-		{
-			InfoWindow iw;
-			iw.soundID = soundBase::experience;
-			iw.player = h->tempOwner;
-			iw.components.push_back(Component(Component::ARTIFACT,val1,1,0));
-			iw.text.addTxt(MetaString::ADVOB_TXT, 125);
-			iw.text.addReplacement(MetaString::ART_NAMES, val1);
-			cb->giveHeroNewArtifact(h, VLC->arth->artifacts[val1],ArtifactPosition::FIRST_AVAILABLE);
-			cb->showInfoDialog(&iw);
-			break;
-		}
-	case Obj::TREASURE_CHEST:
-		{
-			if (subID) //not OH3 treasure chest
-			{
-                logGlobal->warnStream() << "Not supported WoG treasure chest!";
-				return;
-			}
-
-			if(type) //there is an artifact
-			{
-				cb->giveHeroNewArtifact(h, VLC->arth->artifacts[val1],ArtifactPosition::FIRST_AVAILABLE);
-				InfoWindow iw;
-				iw.soundID = soundBase::treasure;
-				iw.player = h->tempOwner;
-				iw.components.push_back(Component(Component::ARTIFACT,val1,1,0));
-				iw.text.addTxt(MetaString::ADVOB_TXT,145);
-				iw.text.addReplacement(MetaString::ART_NAMES, val1);
-				cb->showInfoDialog(&iw);
-				break;
-			}
-			else
-			{
-				BlockingDialog sd(false,true);
-				sd.player = h->tempOwner;
-				sd.text.addTxt(MetaString::ADVOB_TXT,146);
-				sd.components.push_back(Component(Component::RESOURCE,Res::GOLD,val1,0));
-				TExpType expVal = h->calculateXp(val2);
-				sd.components.push_back(Component(Component::EXPERIENCE,0,expVal, 0));
-				sd.soundID = soundBase::chest;
-				cb->showBlockingDialog(&sd);
-				return;
-			}
-		}
-	}
-	cb->removeObject(this);
-}
-
-void CGPickable::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
-{
-	switch(answer)
-	{
-	case 1: //player pick gold
-		cb->giveResource(hero->tempOwner, Res::GOLD, val1);
-		break;
-	case 2: //player pick exp
-		cb->changePrimSkill(hero, PrimarySkill::EXPERIENCE, hero->calculateXp(val2));
-		break;
-	default:
-		throw std::runtime_error("Unhandled treasure choice");
-	}
-	cb->removeObject(this);
-}
-
-bool CQuest::checkQuest (const CGHeroInstance * h) const
-{
-	switch (missionType)
-	{
-		case MISSION_NONE:
-			return true;
-		case MISSION_LEVEL:
-			if (m13489val <= h->level)
-				return true;
-			return false;
-		case MISSION_PRIMARY_STAT:
-			for (int i = 0; i < GameConstants::PRIMARY_SKILLS; ++i)
-			{
-				if (h->getPrimSkillLevel(static_cast<PrimarySkill::PrimarySkill>(i)) < m2stats[i])
-					return false;
-			}
-			return true;
-		case MISSION_KILL_HERO:
-		case MISSION_KILL_CREATURE:
-			if (!h->cb->getObjByQuestIdentifier(m13489val))
-				return true;
-			return false;
-		case MISSION_ART:
-			for (auto & elem : m5arts)
-			{
-				if (h->hasArt(elem))
-					continue;
-				return false; //if the artifact was not found
-			}
-			return true;
-		case MISSION_ARMY:
-			{
-				std::vector<CStackBasicDescriptor>::const_iterator cre;
-				TSlots::const_iterator it;
-				ui32 count;
-				for (cre = m6creatures.begin(); cre != m6creatures.end(); ++cre)
-				{
-					for (count = 0, it = h->Slots().begin(); it !=  h->Slots().end(); ++it)
-					{
-						if (it->second->type == cre->type)
-							count += it->second->count;
-					}
-					if (count < cre->count) //not enough creatures of this kind
-						return false;
-				}
-			}
-			return true;
-		case MISSION_RESOURCES:
-			for (Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, +1)) //including Mithril ?
-			{	//Quest has no direct access to callback
-				if (h->cb->getResource (h->tempOwner, i) < m7resources[i])
-					return false;
-			}
-			return true;
-		case MISSION_HERO:
-			if (m13489val == h->type->ID.getNum())
-				return true;
-			return false;
-		case MISSION_PLAYER:
-			if (m13489val == h->getOwner().getNum())
-				return true;
-			return false;
-		default:
-			return false;
-	}
-}
-void CQuest::getVisitText (MetaString &iwText, std::vector<Component> &components, bool isCustom, bool firstVisit, const CGHeroInstance * h) const
-{
-	std::string text;
-	bool failRequirements = (h ? !checkQuest(h) : true);
-
-	if (firstVisit)
-	{
-		isCustom = isCustomFirst;
-		iwText << (text = firstVisitText);
-	}
-	else if (failRequirements)
-	{
-		isCustom = isCustomNext;
-		iwText << (text = nextVisitText);
-	}
-	switch (missionType)
-	{
-		case MISSION_LEVEL:
-			components.push_back(Component (Component::EXPERIENCE, 0, m13489val, 0));
-			if (!isCustom)
-				iwText.addReplacement(m13489val);
-			break;
-		case MISSION_PRIMARY_STAT:
-		{
-			MetaString loot;
-			for (int i = 0; i < 4; ++i)
-			{
-				if (m2stats[i])
-				{
-					components.push_back(Component (Component::PRIM_SKILL, i, m2stats[i], 0));
-					loot << "%d %s";
-					loot.addReplacement(m2stats[i]);
-					loot.addReplacement(VLC->generaltexth->primarySkillNames[i]);
-				}
-			}
-			if (!isCustom)
-				iwText.addReplacement(loot.buildList());
-		}
-			break;
-		case MISSION_KILL_HERO:
-			components.push_back(Component(Component::HERO_PORTRAIT, heroPortrait, 0, 0));
-			if (!isCustom)
-				addReplacements(iwText, text);
-			break;
-		case MISSION_HERO:
-            //FIXME: portrait may not match hero, if custom portrait was set in map editor
-			components.push_back(Component (Component::HERO_PORTRAIT, VLC->heroh->heroes[m13489val]->imageIndex, 0, 0));
-			if (!isCustom)
-				iwText.addReplacement(VLC->heroh->heroes[m13489val]->name);
-			break;
-		case MISSION_KILL_CREATURE:
-			{
-				components.push_back(Component(stackToKill));
-				if (!isCustom)
-				{
-					addReplacements(iwText, text);
-				}
-			}
-			break;
-		case MISSION_ART:
-		{
-			MetaString loot;
-			for (auto & elem : m5arts)
-			{
-				components.push_back(Component (Component::ARTIFACT, elem, 0, 0));
-				loot << "%s";
-				loot.addReplacement(MetaString::ART_NAMES, elem);
-			}
-			if (!isCustom)
-				iwText.addReplacement(loot.buildList());
-		}
-			break;
-		case MISSION_ARMY:
-		{
-			MetaString loot;
-			for (auto & elem : m6creatures)
-			{
-				components.push_back(Component(elem));
-				loot << "%s";
-				loot.addReplacement(elem);
-			}
-			if (!isCustom)
-				iwText.addReplacement(loot.buildList());
-		}
-			break;
-		case MISSION_RESOURCES:
-		{
-			MetaString loot;
-			for (int i = 0; i < 7; ++i)
-			{
-				if (m7resources[i])
-				{
-					components.push_back(Component (Component::RESOURCE, i, m7resources[i], 0));
-					loot << "%d %s";
-					loot.addReplacement(m7resources[i]);
-					loot.addReplacement(MetaString::RES_NAMES, i);
-				}
-			}
-			if (!isCustom)
-				iwText.addReplacement(loot.buildList());
-		}
-			break;
-		case MISSION_PLAYER:
-			components.push_back(Component (Component::FLAG, m13489val, 0, 0));
-			if (!isCustom)
-				iwText.addReplacement(VLC->generaltexth->colors[m13489val]);
-			break;
-	}
-}
-void CQuest::getRolloverText (MetaString &ms, bool onHover) const
-{
-	if (onHover)
-		ms << "\n\n";
-
-	ms << VLC->generaltexth->quests[missionType-1][onHover ? 3 : 4][textOption];
-
-	switch (missionType)
-	{
-		case MISSION_LEVEL:
-			ms.addReplacement(m13489val);
-			break;
-		case MISSION_PRIMARY_STAT:
-			{
-				MetaString loot;
-				for (int i = 0; i < 4; ++i)
-				{
-					if (m2stats[i])
-					{
-						loot << "%d %s";
-						loot.addReplacement(m2stats[i]);
-						loot.addReplacement(VLC->generaltexth->primarySkillNames[i]);
-					}
-				}
-				ms.addReplacement(loot.buildList());
-			}
-			break;
-		case MISSION_KILL_HERO:
-			ms.addReplacement(heroName);
-			break;
-		case MISSION_KILL_CREATURE:
-			ms.addReplacement(stackToKill);
-			break;
-		case MISSION_ART:
-			{
-				MetaString loot;
-				for (auto & elem : m5arts)
-				{
-					loot << "%s";
-					loot.addReplacement(MetaString::ART_NAMES, elem);
-				}
-				ms.addReplacement(loot.buildList());
-			}
-			break;
-		case MISSION_ARMY:
-			{
-				MetaString loot;
-				for (auto & elem : m6creatures)
-				{
-					loot << "%s";
-					loot.addReplacement(elem);
-				}
-				ms.addReplacement(loot.buildList());
-			}
-			break;
-		case MISSION_RESOURCES:
-			{
-				MetaString loot;
-				for (int i = 0; i < 7; ++i)
-				{
-					if (m7resources[i])
-					{
-						loot << "%d %s";
-						loot.addReplacement(m7resources[i]);
-						loot.addReplacement(MetaString::RES_NAMES, i);
-					}
-				}
-				ms.addReplacement(loot.buildList());
-			}
-			break;
-		case MISSION_HERO:
-			ms.addReplacement(VLC->heroh->heroes[m13489val]->name);
-			break;
-		case MISSION_PLAYER:
-			ms.addReplacement(VLC->generaltexth->colors[m13489val]);
-			break;
-		default:
-			break;
-	}
-}
-
-void CQuest::getCompletionText (MetaString &iwText, std::vector<Component> &components, bool isCustom, const CGHeroInstance * h) const
-{
-	iwText << completedText;
-	switch (missionType)
-	{
-		case CQuest::MISSION_LEVEL:
-			if (!isCustomComplete)
-				iwText.addReplacement(m13489val);
-			break;
-		case CQuest::MISSION_PRIMARY_STAT:
-			if (vstd::contains (completedText,'%')) //there's one case when there's nothing to replace
-			{
-				MetaString loot;
-				for (int i = 0; i < 4; ++i)
-				{
-					if (m2stats[i])
-					{
-						loot << "%d %s";
-						loot.addReplacement(m2stats[i]);
-						loot.addReplacement(VLC->generaltexth->primarySkillNames[i]);
-					}
-				}
-				if (!isCustomComplete)
-					iwText.addReplacement(loot.buildList());
-			}
-			break;
-		case CQuest::MISSION_ART:
-		{
-			MetaString loot;
-			for (auto & elem : m5arts)
-			{
-				loot << "%s";
-				loot.addReplacement(MetaString::ART_NAMES, elem);
-			}
-			if (!isCustomComplete)
-				iwText.addReplacement(loot.buildList());
-		}
-			break;
-		case CQuest::MISSION_ARMY:
-		{
-			MetaString loot;
-			for (auto & elem : m6creatures)
-			{
-				loot << "%s";
-				loot.addReplacement(elem);
-			}
-			if (!isCustomComplete)
-				iwText.addReplacement(loot.buildList());
-		}
-			break;
-		case CQuest::MISSION_RESOURCES:
-		{
-			MetaString loot;
-			for (int i = 0; i < 7; ++i)
-			{
-				if (m7resources[i])
-				{
-					loot << "%d %s";
-					loot.addReplacement(m7resources[i]);
-					loot.addReplacement(MetaString::RES_NAMES, i);
-				}
-			}
-			if (!isCustomComplete)
-				iwText.addReplacement(loot.buildList());
-		}
-			break;
-		case MISSION_KILL_HERO:
-		case MISSION_KILL_CREATURE:
-			if (!isCustomComplete)
-				addReplacements(iwText, completedText);
-			break;
-		case MISSION_HERO:
-			if (!isCustomComplete)
-				iwText.addReplacement(VLC->heroh->heroes[m13489val]->name);
-			break;
-		case MISSION_PLAYER:
-			if (!isCustomComplete)
-				iwText.addReplacement(VLC->generaltexth->colors[m13489val]);
-			break;
-	}
-}
-void CGSeerHut::setObjToKill()
-{
-	if (quest->missionType == CQuest::MISSION_KILL_CREATURE)
-	{
-		quest->stackToKill = getCreatureToKill(false)->getStack(SlotID(0)); //FIXME: stacks tend to disappear (desync?) on server :?
-		assert(quest->stackToKill.type);
-		quest->stackToKill.count = 0; //no count in info window
-		quest->stackDirection = checkDirection();
-	}
-	else if (quest->missionType == CQuest::MISSION_KILL_HERO)
-	{
-		quest->heroName = getHeroToKill(false)->name;
-		quest->heroPortrait = getHeroToKill(false)->portrait;
-	}
-}
-
-void CGSeerHut::init()
-{
-	seerName = *RandomGeneratorUtil::nextItem(VLC->generaltexth->seerNames, cb->gameState()->getRandomGenerator());
-	quest->textOption = cb->gameState()->getRandomGenerator().nextInt(2);
-}
-
-void CGSeerHut::initObj()
-{
-	init();
-
-	quest->progress = CQuest::NOT_ACTIVE;
-	if (quest->missionType)
-	{
-		if (!quest->isCustomFirst)
-			quest->firstVisitText = VLC->generaltexth->quests[quest->missionType-1][0][quest->textOption];
-		if (!quest->isCustomNext)
-			quest->nextVisitText = VLC->generaltexth->quests[quest->missionType-1][1][quest->textOption];
-		if (!quest->isCustomComplete)
-			quest->completedText = VLC->generaltexth->quests[quest->missionType-1][2][quest->textOption];
-	}
-	else
-	{
-		quest->progress = CQuest::COMPLETE;
-		quest->firstVisitText = VLC->generaltexth->seerEmpty[quest->textOption];
-	}
-
-}
-
-void CGSeerHut::getRolloverText (MetaString &text, bool onHover) const
-{
-	quest->getRolloverText (text, onHover);//TODO: simplify?
-	if (!onHover)
-		text.addReplacement(seerName);
-}
-
-const std::string & CGSeerHut::getHoverText() const
-{
-	switch (ID)
-	{
-	case Obj::SEER_HUT:
-		if (quest->progress != CQuest::NOT_ACTIVE)
-		{
-			hoverName = VLC->generaltexth->allTexts[347];
-			boost::algorithm::replace_first(hoverName,"%s", seerName);
-		}
-		else //just seer hut
-			hoverName = VLC->generaltexth->names[ID];
-		break;
-	case Obj::QUEST_GUARD:
-		hoverName = VLC->generaltexth->names[ID];
-		break;
-	default:
-        logGlobal->debugStream() << "unrecognized quest object";
-	}
-	if (quest->progress & quest->missionType) //rollover when the quest is active
-	{
-		MetaString ms;
-		getRolloverText (ms, true);
-		hoverName += ms.toString();
-	}
-	return hoverName;
-}
-
-void CQuest::addReplacements(MetaString &out, const std::string &base) const
-{
-	switch(missionType)
-	{
-	case MISSION_KILL_CREATURE:
-		out.addReplacement(stackToKill);
-		if (std::count(base.begin(), base.end(), '%') == 2) //say where is placed monster
-		{
-			out.addReplacement(VLC->generaltexth->arraytxt[147+stackDirection]);
-		}
-		break;
-	case MISSION_KILL_HERO:
-		out.addReplacement(heroName);
-		break;
-	}
-}
-
-bool IQuestObject::checkQuest(const CGHeroInstance* h) const
-{
-	return quest->checkQuest(h);
-}
-
-void IQuestObject::getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h) const
-{
-	quest->getVisitText (text,components, isCustom, FirstVisit, h);
-}
-
-void CGSeerHut::getCompletionText(MetaString &text, std::vector<Component> &components, bool isCustom, const CGHeroInstance * h) const
-{
-	quest->getCompletionText (text, components, isCustom, h);
-	switch (rewardType)
-	{
-		case EXPERIENCE: components.push_back(Component (Component::EXPERIENCE, 0, h->calculateXp(rVal), 0));
-			break;
-		case MANA_POINTS: components.push_back(Component (Component::PRIM_SKILL, 5, rVal, 0));
-			break;
-		case MORALE_BONUS: components.push_back(Component (Component::MORALE, 0, rVal, 0));
-			break;
-		case LUCK_BONUS: components.push_back(Component (Component::LUCK, 0, rVal, 0));
-			break;
-		case RESOURCES: components.push_back(Component (Component::RESOURCE, rID, rVal, 0));
-			break;
-		case PRIMARY_SKILL: components.push_back(Component (Component::PRIM_SKILL, rID, rVal, 0));
-			break;
-		case SECONDARY_SKILL: components.push_back(Component (Component::SEC_SKILL, rID, rVal, 0));
-			break;
-		case ARTIFACT: components.push_back(Component (Component::ARTIFACT, rID, 0, 0));
-			break;
-		case SPELL: components.push_back(Component (Component::SPELL, rID, 0, 0));
-			break;
-		case CREATURE: components.push_back(Component (Component::CREATURE, rID, rVal, 0));
-			break;
-	}
-}
-
-void CGSeerHut::setPropertyDer (ui8 what, ui32 val)
-{
-	switch (what)
-	{
-		case 10:
-			quest->progress = static_cast<CQuest::Eprogress>(val);
-			break;
-	}
-}
-void CGSeerHut::newTurn() const
-{
-	if (quest->lastDay >= 0 && quest->lastDay < cb->getDate()-1) //time is up
-	{
-		cb->setObjProperty (id, 10, CQuest::COMPLETE);
-	}
-
-}
-void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
-{
-	InfoWindow iw;
-	iw.player = h->getOwner();
-	if (quest->progress < CQuest::COMPLETE)
-	{
-		bool firstVisit = !quest->progress;
-		bool failRequirements = !checkQuest(h);
-		bool isCustom=false;
-
-		if (firstVisit)
-		{
-			isCustom = quest->isCustomFirst;
-			cb->setObjProperty (id, 10, CQuest::IN_PROGRESS);
-
-			AddQuest aq;
-			aq.quest = QuestInfo (quest, this, visitablePos());
-			aq.player = h->tempOwner;
-			cb->sendAndApply (&aq); //TODO: merge with setObjProperty?
-		}
-		else if (failRequirements)
-		{
-			isCustom = quest->isCustomNext;
-		}
-
-		if (firstVisit || failRequirements)
-		{
-			getVisitText (iw.text, iw.components, isCustom, firstVisit, h);
-
-			cb->showInfoDialog(&iw);
-		}
-		if (!failRequirements) // propose completion, also on first visit
-		{
-			BlockingDialog bd (true, false);
-			bd.player = h->getOwner();
-			bd.soundID = soundBase::QUEST;
-
-			getCompletionText (bd.text, bd.components, isCustom, h);
-
-			cb->showBlockingDialog (&bd);
-			return;
-		}
-	}
-	else
-	{
-		iw.text << VLC->generaltexth->seerEmpty[quest->textOption];
-		if (ID == Obj::SEER_HUT)
-			iw.text.addReplacement(seerName);
-		cb->showInfoDialog(&iw);
-	}
-}
-int CGSeerHut::checkDirection() const
-{
-	int3 cord = getCreatureToKill()->pos;
-	if ((double)cord.x/(double)cb->getMapSize().x < 0.34) //north
-	{
-		if ((double)cord.y/(double)cb->getMapSize().y < 0.34) //northwest
-			return 8;
-		else if ((double)cord.y/(double)cb->getMapSize().y < 0.67) //north
-			return 1;
-		else //northeast
-			return 2;
-	}
-	else if ((double)cord.x/(double)cb->getMapSize().x < 0.67) //horizontal
-	{
-		if ((double)cord.y/(double)cb->getMapSize().y < 0.34) //west
-			return 7;
-		else if ((double)cord.y/(double)cb->getMapSize().y < 0.67) //central
-			return 9;
-		else //east
-			return 3;
-	}
-	else //south
-	{
-		if ((double)cord.y/(double)cb->getMapSize().y < 0.34) //southwest
-			return 6;
-		else if ((double)cord.y/(double)cb->getMapSize().y < 0.67) //south
-			return 5;
-		else //southeast
-			return 4;
-	}
-}
-void CGSeerHut::finishQuest(const CGHeroInstance * h, ui32 accept) const
-{
-	if (accept)
-	{
-		switch (quest->missionType)
-		{
-			case CQuest::MISSION_ART:
-				for (auto & elem : quest->m5arts)
-				{
-					cb->removeArtifact(ArtifactLocation(h, h->getArtPos(elem, false)));
-				}
-				break;
-			case CQuest::MISSION_ARMY:
-					cb->takeCreatures(h->id, quest->m6creatures);
-				break;
-			case CQuest::MISSION_RESOURCES:
-				for (int i = 0; i < 7; ++i)
-				{
-					cb->giveResource(h->getOwner(), static_cast<Res::ERes>(i), -quest->m7resources[i]);
-				}
-				break;
-			default:
-				break;
-		}
-		cb->setObjProperty (id, 10, CQuest::COMPLETE); //mission complete
-		completeQuest(h); //make sure to remove QuestGuard at the very end
-	}
-}
-void CGSeerHut::completeQuest (const CGHeroInstance * h) const //reward
-{
-	switch (rewardType)
-	{
-		case EXPERIENCE:
-		{
-			TExpType expVal = h->calculateXp(rVal);
-			cb->changePrimSkill(h, PrimarySkill::EXPERIENCE, expVal, false);
-			break;
-		}
-		case MANA_POINTS:
-		{
-			cb->setManaPoints(h->id, h->mana+rVal);
-			break;
-		}
-		case MORALE_BONUS: case LUCK_BONUS:
-		{
-			Bonus hb(Bonus::ONE_WEEK, (rewardType == 3 ? Bonus::MORALE : Bonus::LUCK),
-				Bonus::OBJECT, rVal, h->id.getNum(), "", -1);
-			GiveBonus gb;
-			gb.id = h->id.getNum();
-			gb.bonus = hb;
-			cb->giveHeroBonus(&gb);
-		}
-			break;
-		case RESOURCES:
-			cb->giveResource(h->getOwner(), static_cast<Res::ERes>(rID), rVal);
-			break;
-		case PRIMARY_SKILL:
-			cb->changePrimSkill(h, static_cast<PrimarySkill::PrimarySkill>(rID), rVal, false);
-			break;
-		case SECONDARY_SKILL:
-			cb->changeSecSkill(h, SecondarySkill(rID), rVal, false);
-			break;
-		case ARTIFACT:
-			cb->giveHeroNewArtifact(h, VLC->arth->artifacts[rID],ArtifactPosition::FIRST_AVAILABLE);
-			break;
-		case SPELL:
-		{
-			std::set<SpellID> spell;
-			spell.insert (SpellID(rID));
-			cb->changeSpells(h, true, spell);
-		}
-			break;
-		case CREATURE:
-			{
-				CCreatureSet creatures;
-				creatures.setCreature(SlotID(0), CreatureID(rID), rVal);
-				cb->giveCreatures(this, h, creatures, false);
-			}
-			break;
-		default:
-			break;
-	}
-}
-
-const CGHeroInstance * CGSeerHut::getHeroToKill(bool allowNull) const
-{
-	const CGObjectInstance *o = cb->getObjByQuestIdentifier(quest->m13489val);
-	if(allowNull && !o)
-		return nullptr;
-	assert(o && (o->ID == Obj::HERO  ||  o->ID == Obj::PRISON));
-	return static_cast<const CGHeroInstance*>(o);
-}
-
-const CGCreature * CGSeerHut::getCreatureToKill(bool allowNull) const
-{
-	const CGObjectInstance *o = cb->getObjByQuestIdentifier(quest->m13489val);
-	if(allowNull && !o)
-		return nullptr;
-	assert(o && o->ID == Obj::MONSTER);
-	return static_cast<const CGCreature*>(o);
-}
-
-void CGSeerHut::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
-{
-	finishQuest(hero, answer);
-}
-
-void CGQuestGuard::init()
-{
-	blockVisit = true;
-	quest->textOption = cb->gameState()->getRandomGenerator().nextInt(3, 5);
-}
-void CGQuestGuard::completeQuest(const CGHeroInstance *h) const
-{
-	cb->removeObject(this);
-}
-void CGWitchHut::initObj()
-{
-	ability = *RandomGeneratorUtil::nextItem(allowedAbilities, cb->gameState()->getRandomGenerator());
-}
-
-void CGWitchHut::onHeroVisit( const CGHeroInstance * h ) const
-{
-	InfoWindow iw;
-	iw.soundID = soundBase::gazebo;
-	iw.player = h->getOwner();
-	if(!wasVisited(h->tempOwner))
-		cb->setObjProperty(id, 10, h->tempOwner.getNum());
-	ui32 txt_id;
-	if(h->getSecSkillLevel(SecondarySkill(ability))) //you already know this skill
-	{
-		txt_id =172;
-	}
-	else if(!h->canLearnSkill()) //already all skills slots used
-	{
-		txt_id = 173;
-	}
-	else //give sec skill
-	{
-		iw.components.push_back(Component(Component::SEC_SKILL, ability, 1, 0));
-		txt_id = 171;
-		cb->changeSecSkill(h, SecondarySkill(ability), 1, true);
-	}
-
-	iw.text.addTxt(MetaString::ADVOB_TXT,txt_id);
-	iw.text.addReplacement(MetaString::SEC_SKILL_NAME, ability);
-	cb->showInfoDialog(&iw);
-}
-
-const std::string & CGWitchHut::getHoverText() const
-{
-	hoverName = VLC->generaltexth->names[ID];
-	if(wasVisited(cb->getLocalPlayer()))
-	{
-		hoverName += "\n" + VLC->generaltexth->allTexts[356]; // + (learn %s)
-		boost::algorithm::replace_first(hoverName,"%s",VLC->generaltexth->skillName[ability]);
-		const CGHeroInstance *h = cb->getSelectedHero(cb->getCurrentPlayer());
-		if(h && h->getSecSkillLevel(SecondarySkill(ability))) //hero knows that ability
-			hoverName += "\n\n" + VLC->generaltexth->allTexts[357]; // (Already learned)
-	}
-	return hoverName;
-}
-
-bool CGBonusingObject::wasVisited (const CGHeroInstance * h) const
-{
-	return h->hasBonusFrom(Bonus::OBJECT, ID);
-}
-
-void CGBonusingObject::onHeroVisit( const CGHeroInstance * h ) const
-{
-	bool visited = h->hasBonusFrom(Bonus::OBJECT,ID);
-	int messageID=0;
-	int bonusMove = 0;
-	ui32 descr_id = 0;
-	InfoWindow iw;
-	iw.player = h->tempOwner;
-	GiveBonus gbonus;
-	gbonus.id = h->id.getNum();
-	gbonus.bonus.duration = Bonus::ONE_BATTLE;
-	gbonus.bonus.source = Bonus::OBJECT;
-	gbonus.bonus.sid = ID;
-
-	bool second = false;
-	Bonus secondBonus;
-
-	switch(ID)
-	{
-	case Obj::BUOY:
-		messageID = 21;
-		iw.soundID = soundBase::MORALE;
-		gbonus.bonus.type = Bonus::MORALE;
-		gbonus.bonus.val = +1;
-		descr_id = 94;
-		break;
-	case Obj::SWAN_POND:
-		messageID = 29;
-		iw.soundID = soundBase::LUCK;
-		gbonus.bonus.type = Bonus::LUCK;
-		gbonus.bonus.val = 2;
-		descr_id = 67;
-		bonusMove = -h->movement;
-		break;
-	case Obj::FAERIE_RING:
-		messageID = 49;
-		iw.soundID = soundBase::LUCK;
-		gbonus.bonus.type = Bonus::LUCK;
-		gbonus.bonus.val = 1;
-		descr_id = 71;
-		break;
-	case Obj::FOUNTAIN_OF_FORTUNE:
-		messageID = 55;
-		iw.soundID = soundBase::LUCK;
-		gbonus.bonus.type = Bonus::LUCK;
-		gbonus.bonus.val = cb->gameState()->getRandomGenerator().nextInt(-1, 3);
-		descr_id = 69;
-		gbonus.bdescr.addReplacement((gbonus.bonus.val<0 ? "-" : "+") + boost::lexical_cast<std::string>(gbonus.bonus.val));
-		break;
-	case Obj::IDOL_OF_FORTUNE:
-		messageID = 62;
-		iw.soundID = soundBase::experience;
-
-		gbonus.bonus.val = 1;
-		descr_id = 68;
-		if(cb->getDate(Date::DAY_OF_WEEK) == 7) //7th day of week
-		{
-			gbonus.bonus.type = Bonus::MORALE;
-			second = true;
-			secondBonus = gbonus.bonus;
-			secondBonus.type = Bonus::LUCK;
-		}
-		else
-		{
-			gbonus.bonus.type = (cb->getDate(Date::DAY_OF_WEEK)%2) ? Bonus::LUCK : Bonus::MORALE;
-		}
-		break;
-	case Obj::MERMAID:
-		messageID = 83;
-		iw.soundID = soundBase::LUCK;
-		gbonus.bonus.type = Bonus::LUCK;
-		gbonus.bonus.val = 1;
-		descr_id = 72;
-		break;
-	case Obj::RALLY_FLAG:
-		iw.soundID = soundBase::MORALE;
-		messageID = 111;
-		gbonus.bonus.type = Bonus::MORALE;
-		gbonus.bonus.val = 1;
-		descr_id = 102;
-
-		second = true;
-		secondBonus = gbonus.bonus;
-		secondBonus.type = Bonus::LUCK;
-
-		bonusMove = 400;
-		break;
-	case Obj::OASIS:
-		iw.soundID = soundBase::MORALE;
-		messageID = 95;
-		gbonus.bonus.type = Bonus::MORALE;
-		gbonus.bonus.val = 1;
-		descr_id = 95;
-		bonusMove = 800;
-		break;
-	case Obj::TEMPLE:
-		messageID = 140;
-		iw.soundID = soundBase::temple;
-		gbonus.bonus.type = Bonus::MORALE;
-		if(cb->getDate(Date::DAY_OF_WEEK)==7) //sunday
-		{
-			gbonus.bonus.val = 2;
-			descr_id = 97;
-		}
-		else
-		{
-			gbonus.bonus.val = 1;
-			descr_id = 96;
-		}
-		break;
-	case Obj::WATERING_HOLE:
-		iw.soundID = soundBase::MORALE;
-		messageID = 166;
-		gbonus.bonus.type = Bonus::MORALE;
-		gbonus.bonus.val = 1;
-		descr_id = 100;
-		bonusMove = 400;
-		break;
-	case Obj::FOUNTAIN_OF_YOUTH:
-		iw.soundID = soundBase::MORALE;
-		messageID = 57;
-		gbonus.bonus.type = Bonus::MORALE;
-		gbonus.bonus.val = 1;
-		descr_id = 103;
-		bonusMove = 400;
-		break;
-	case Obj::STABLES:
-		iw.soundID = soundBase::STORE;
-		bool someUpgradeDone = false;
-
-		for (auto i = h->Slots().begin(); i != h->Slots().end(); ++i)
-		{
-			if(i->second->type->idNumber == CreatureID::CAVALIER)
-			{
-				cb->changeStackType(StackLocation(h, i->first), VLC->creh->creatures[CreatureID::CHAMPION]);
-				someUpgradeDone = true;
-			}
-		}
-		if (someUpgradeDone)
-		{
-			messageID = 138;
-			iw.components.push_back(Component(Component::CREATURE,11,0,1));
-		}
-		else
-			messageID = 137;
-
-		gbonus.bonus.type = Bonus::LAND_MOVEMENT;
-		gbonus.bonus.val = 600;
-		bonusMove = 600;
-		gbonus.bonus.duration = Bonus::ONE_WEEK;
-		//gbonus.bdescr <<  std::pair<ui8,ui32>(6, 100);
-		break;
-	}
-	if (descr_id != 0)
-		gbonus.bdescr.addTxt(MetaString::ARRAY_TXT,descr_id);
-	assert(messageID);
-	if(visited)
-	{
-		if(ID==Obj::RALLY_FLAG || ID==Obj::OASIS || ID==Obj::MERMAID || ID==Obj::STABLES)
-			messageID--;
-		else
-			messageID++;
-	}
-	else
-	{
-		//TODO: fix if second bonus val != main bonus val
-		if(gbonus.bonus.type == Bonus::MORALE   ||   secondBonus.type == Bonus::MORALE)
-			iw.components.push_back(Component(Component::MORALE,0,gbonus.bonus.val,0));
-		if(gbonus.bonus.type == Bonus::LUCK   ||   secondBonus.type == Bonus::LUCK)
-			iw.components.push_back(Component(Component::LUCK,0,gbonus.bonus.val,0));
-		cb->giveHeroBonus(&gbonus);
-		if(second)
-		{
-			gbonus.bonus = secondBonus;
-			cb->giveHeroBonus(&gbonus);
-		}
-		if(bonusMove) //swan pond - take all move points, stables - give move point this day
-		{
-			SetMovePoints smp;
-			smp.hid = h->id;
-			smp.val = h->movement + bonusMove;
-			cb->setMovePoints(&smp);
-		}
-	}
-	iw.text.addTxt(MetaString::ADVOB_TXT,messageID);
-	cb->showInfoDialog(&iw);
-}
-
-const std::string & CGBonusingObject::getHoverText() const
-{
-	const CGHeroInstance *h = cb->getSelectedHero(cb->getCurrentPlayer());
-	hoverName = VLC->generaltexth->names[ID];
-	if(h)
-	{
-		bool visited = h->hasBonusFrom(Bonus::OBJECT,ID);
-		hoverName += " " + visitedTxt(visited);
-	}
-	return hoverName;
-}
-
-void CGBonusingObject::initObj()
-{
-	if(ID == Obj::BUOY || ID == Obj::MERMAID)
-	{
-		blockVisit = true;
-	}
-}
-
-void CGMagicSpring::setPropertyDer(ui8 what, ui32 val)
-{
-	CGVisitableOPW::setPropertyDer (what, val); //set visitable if applicable
-	if (what == ObjProperty::LEFT_VISITED)
-	{
-		if (visitedTile == RIGHT)
-			visited = true; //both field were used, object is not available this week
-		else
-			visitedTile = LEFT;
-	}
-	else if (what == ObjProperty::RIGHT_VISITED)
-	{
-		if (visitedTile == LEFT)
-			visited = true;
-		else
-			visitedTile = RIGHT;
-	}
-	else if (what == ObjProperty::LEFTRIGHT_CLEAR)
-		visitedTile = CLEAR;
-}
-std::vector<int3> CGMagicSpring::getVisitableOffsets() const
-{
-	std::vector <int3> visitableTiles;
-
-	for(int y = 0; y < 6; y++)
-		for (int x = 0; x < 8; x++) //starting from left
-			if (appearance.isVisitableAt(x, y))
-				visitableTiles.push_back (int3(x, y , 0));
-
-	return visitableTiles;
-}
-
-int3 CGMagicSpring::getVisitableOffset() const
-{
-	//FIXME: this also should stop AI from passing through already visited spring, is that ok?
-	auto visitableTiles = getVisitableOffsets();
-
-	if (visitableTiles.size() < 2)
-	{
-		logGlobal->warnStream() << "Warning: Magic Spring should have at least two visitable offsets!";
-		return int3(-1,-1,-1);
-	}
-    if (visited)
-		return int3(-1,-1,-1);
-	else
-	{
-		if (visitedTile == RIGHT)
-			return visitableTiles[0]; //visit the other one now
-		else if (visitedTile == LEFT)
-			return visitableTiles[1];
-		else
-			return visitableTiles[0]; //only left one?
-	}
-}
-
-void CGMagicSpring::onHeroVisit(const CGHeroInstance * h) const
-{
-	int messageID;
-
-	if (!visited)
-	{
-		if (h->mana > h->manaLimit())
-			messageID = 76;
-		else
-		{
-			messageID = 74;
-			cb->setManaPoints (h->id, 2 * h->manaLimit());//TODO: mark left or right tile visited
-			if (visitedTile) //visitng the second tile
-				cb->setObjProperty (id, ObjProperty::VISITED, true);
-			else
-			{
-				auto visitableTiles = getVisitableOffsets();
-				assert (visitableTiles.size() >= 2);
-				if (h->getPosition() == pos - visitableTiles[0])
-					cb->setObjProperty (id, ObjProperty::LEFT_VISITED, true);
-				else if (h->getPosition() == pos - visitableTiles[1])
-					cb->setObjProperty (id, ObjProperty::RIGHT_VISITED, true);
-				else
-					logGlobal->warnStream() << "Warning: hero is not on any Magic Spring visitable offsets!";
-			}
-		}
-	}
-	else
-		messageID = 75;
-	showInfoDialog(h,messageID,soundBase::GENIE);
-}
-void CGMagicSpring::newTurn() const
-{
-	CGVisitableOPW::newTurn();
-	if (cb->getDate(Date::DAY_OF_WEEK) == 1)
-	{
-		cb->setObjProperty(id, ObjProperty::LEFTRIGHT_CLEAR, false);
-	}
-}
-
-
-const std::string & CGMagicSpring::getHoverText() const
-{
-	//TODO: change hover text depending on hovered tile
-	hoverName = VLC->generaltexth->names[ID] + " " + visitedTxt(visited);
-	return hoverName;
-}
-
-void CGMagicWell::onHeroVisit( const CGHeroInstance * h ) const
-{
-	int message;
-
-	if(h->hasBonusFrom(Bonus::OBJECT,ID)) //has already visited Well today
-	{
-		message = 78;//"A second drink at the well in one day will not help you."
-	}
-	else if(h->mana < h->manaLimit())
-	{
-		giveDummyBonus(h->id);
-		cb->setManaPoints(h->id,h->manaLimit());
-		message = 77;
-	}
-	else
-	{
-		message = 79;
-	}
-	showInfoDialog(h,message,soundBase::faerie);
-}
-
-const std::string & CGMagicWell::getHoverText() const
-{
-	getNameVis(hoverName);
-	return hoverName;
-}
-
-void CGPandoraBox::initObj()
-{
-	blockVisit = (ID==Obj::PANDORAS_BOX); //block only if it's really pandora's box (events also derive from that class)
-	hasGuardians = stacks.size();
-}
-
-void CGPandoraBox::onHeroVisit(const CGHeroInstance * h) const
-{
-		BlockingDialog bd (true, false);
-		bd.player = h->getOwner();
-		bd.soundID = soundBase::QUEST;
-		bd.text.addTxt (MetaString::ADVOB_TXT, 14);
-		cb->showBlockingDialog (&bd);
-}
-
-void CGPandoraBox::giveContentsUpToExp(const CGHeroInstance *h) const
-{
-	cb->removeAfterVisit(this);
-
-	InfoWindow iw;
-	iw.player = h->getOwner();
-
-	bool changesPrimSkill = false;
-	for (auto & elem : primskills)
-	{
-		if(elem)
-		{
-			changesPrimSkill = true;
-			break;
-		}
-	}
-
-	if(gainedExp || changesPrimSkill || abilities.size())
-	{
-		TExpType expVal = h->calculateXp(gainedExp);
-		//getText(iw,afterBattle,175,h); //wtf?
-		iw.text.addTxt(MetaString::ADVOB_TXT, 175); //%s learns something
-		iw.text.addReplacement(h->name);
-
-		if(expVal)
-			iw.components.push_back(Component(Component::EXPERIENCE,0,expVal,0));
-		for(int i=0; i<primskills.size(); i++)
-			if(primskills[i])
-				iw.components.push_back(Component(Component::PRIM_SKILL,i,primskills[i],0));
-
-		for(int i=0; i<abilities.size(); i++)
-			iw.components.push_back(Component(Component::SEC_SKILL,abilities[i],abilityLevels[i],0));
-
-		cb->showInfoDialog(&iw);
-
-		//give sec skills
-		for(int i=0; i<abilities.size(); i++)
-		{
-			int curLev = h->getSecSkillLevel(abilities[i]);
-
-			if( (curLev  &&  curLev < abilityLevels[i])	|| (h->canLearnSkill() ))
-			{
-				cb->changeSecSkill(h,abilities[i],abilityLevels[i],true);
-			}
-		}
-
-		//give prim skills
-		for(int i=0; i<primskills.size(); i++)
-			if(primskills[i])
-				cb->changePrimSkill(h,static_cast<PrimarySkill::PrimarySkill>(i),primskills[i],false);
-
-		assert(!cb->isVisitCoveredByAnotherQuery(this, h));
-
-		//give exp
-		if(expVal)
-			cb->changePrimSkill(h, PrimarySkill::EXPERIENCE, expVal, false);
-	}
-
-	if(!cb->isVisitCoveredByAnotherQuery(this, h))
-		giveContentsAfterExp(h);
-	//Otherwise continuation occurs via post-level-up callback.
-}
-
-void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const
-{
-	bool hadGuardians = hasGuardians; //copy, because flag will be emptied after issuing first post-battle message
-
-	std::string msg = message; //in case box is removed in the meantime
-	InfoWindow iw;
-	iw.player = h->getOwner();
-
-	if(spells.size())
-	{
-		std::set<SpellID> spellsToGive;
-		iw.components.clear();
-		if (spells.size() > 1)
-		{
-			iw.text.addTxt(MetaString::ADVOB_TXT, 188); //%s learns spells
-		}
-		else
-		{
-			iw.text.addTxt(MetaString::ADVOB_TXT, 184); //%s learns a spell
-		}
-		iw.text.addReplacement(h->name);
-		std::vector<ConstTransitivePtr<CSpell> > * sp = &VLC->spellh->objects;
-		for(auto i=spells.cbegin(); i != spells.cend(); i++)
-		{
-			if ((*sp)[*i]->level <= h->getSecSkillLevel(SecondarySkill::WISDOM) + 2) //enough wisdom
-			{
-				iw.components.push_back(Component(Component::SPELL,*i,0,0));
-				spellsToGive.insert(*i);
-			}
-		}
-		if(!spellsToGive.empty())
-		{
-			cb->changeSpells(h,true,spellsToGive);
-			cb->showInfoDialog(&iw);
-		}
-	}
-
-	if(manaDiff)
-	{
-		getText(iw,hadGuardians,manaDiff,176,177,h);
-		iw.components.push_back(Component(Component::PRIM_SKILL,5,manaDiff,0));
-		cb->showInfoDialog(&iw);
-		cb->setManaPoints(h->id, h->mana + manaDiff);
-	}
-
-	if(moraleDiff)
-	{
-		getText(iw,hadGuardians,moraleDiff,178,179,h);
-		iw.components.push_back(Component(Component::MORALE,0,moraleDiff,0));
-		cb->showInfoDialog(&iw);
-		GiveBonus gb;
-		gb.bonus = Bonus(Bonus::ONE_BATTLE,Bonus::MORALE,Bonus::OBJECT,moraleDiff,id.getNum(),"");
-		gb.id = h->id.getNum();
-		cb->giveHeroBonus(&gb);
-	}
-
-	if(luckDiff)
-	{
-		getText(iw,hadGuardians,luckDiff,180,181,h);
-		iw.components.push_back(Component(Component::LUCK,0,luckDiff,0));
-		cb->showInfoDialog(&iw);
-		GiveBonus gb;
-		gb.bonus = Bonus(Bonus::ONE_BATTLE,Bonus::LUCK,Bonus::OBJECT,luckDiff,id.getNum(),"");
-		gb.id = h->id.getNum();
-		cb->giveHeroBonus(&gb);
-	}
-
-	iw.components.clear();
-	iw.text.clear();
-	for(int i=0; i<resources.size(); i++)
-	{
-		if(resources[i] < 0)
-			iw.components.push_back(Component(Component::RESOURCE,i,resources[i],0));
-	}
-	if(iw.components.size())
-	{
-		getText(iw,hadGuardians,182,h);
-		cb->showInfoDialog(&iw);
-	}
-
-	iw.components.clear();
-	iw.text.clear();
-	for(int i=0; i<resources.size(); i++)
-	{
-		if(resources[i] > 0)
-			iw.components.push_back(Component(Component::RESOURCE,i,resources[i],0));
-	}
-	if(iw.components.size())
-	{
-		getText(iw,hadGuardians,183,h);
-		cb->showInfoDialog(&iw);
-	}
-
-	iw.components.clear();
-	// 	getText(iw,afterBattle,183,h);
-	iw.text.addTxt(MetaString::ADVOB_TXT, 183); //% has found treasure
-	iw.text.addReplacement(h->name);
-	for(auto & elem : artifacts)
-	{
-		iw.components.push_back(Component(Component::ARTIFACT,elem,0,0));
-		if(iw.components.size() >= 14)
-		{
-			cb->showInfoDialog(&iw);
-			iw.components.clear();
-			iw.text.addTxt(MetaString::ADVOB_TXT, 183); //% has found treasure - once more?
-			iw.text.addReplacement(h->name);
-		}
-	}
-	if(iw.components.size())
-	{
-		cb->showInfoDialog(&iw);
-	}
-
-	for(int i=0; i<resources.size(); i++)
-		if(resources[i])
-			cb->giveResource(h->getOwner(),static_cast<Res::ERes>(i),resources[i]);
-
-	for(auto & elem : artifacts)
-		cb->giveHeroNewArtifact(h, VLC->arth->artifacts[elem],ArtifactPosition::FIRST_AVAILABLE);
-
-	iw.components.clear();
-	iw.text.clear();
-
-	if (creatures.Slots().size())
-	{ //this part is taken straight from creature bank
-		MetaString loot;
-		for(auto & elem : creatures.Slots())
-		{ //build list of joined creatures
-			iw.components.push_back(Component(*elem.second));
-			loot << "%s";
-			loot.addReplacement(*elem.second);
-		}
-
-		if (creatures.Slots().size() == 1 && creatures.Slots().begin()->second->count == 1)
-			iw.text.addTxt(MetaString::ADVOB_TXT, 185);
-		else
-			iw.text.addTxt(MetaString::ADVOB_TXT, 186);
-
-		iw.text.addReplacement(loot.buildList());
-		iw.text.addReplacement(h->name);
-
-		cb->showInfoDialog(&iw);
-		cb->giveCreatures(this, h, creatures, true);
-	}
-	if(!hasGuardians && msg.size())
-	{
-		iw.text << msg;
-		cb->showInfoDialog(&iw);
-	}
-}
-
-void CGPandoraBox::getText( InfoWindow &iw, bool &afterBattle, int text, const CGHeroInstance * h ) const
-{
-	if(afterBattle || !message.size())
-	{
-		iw.text.addTxt(MetaString::ADVOB_TXT,text);//%s has lost treasure.
-		iw.text.addReplacement(h->name);
-	}
-	else
-	{
-		iw.text << message;
-		afterBattle = true;
-	}
-}
-
-void CGPandoraBox::getText( InfoWindow &iw, bool &afterBattle, int val, int negative, int positive, const CGHeroInstance * h ) const
-{
-	iw.components.clear();
-	iw.text.clear();
-	if(afterBattle || !message.size())
-	{
-		iw.text.addTxt(MetaString::ADVOB_TXT,val < 0 ? negative : positive); //%s's luck takes a turn for the worse / %s's luck increases
-		iw.text.addReplacement(h->name);
-	}
-	else
-	{
-		iw.text << message;
-		afterBattle = true;
-	}
-}
-
-void CGPandoraBox::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
-{
-	if(result.winner)
-		return;
-
-	giveContentsUpToExp(hero);
-}
-
-void CGPandoraBox::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
-{
-	if (answer)
-	{
-		if (stacksCount() > 0) //if pandora's box is protected by army
-		{
-			showInfoDialog(hero,16,0);
-			cb->startBattleI(hero, this); //grants things after battle
-		}
-		else if (message.size() == 0 && resources.size() == 0
-			&& primskills.size() == 0 && abilities.size() == 0
-			&& abilityLevels.size() == 0 &&  artifacts.size() == 0
-			&& spells.size() == 0 && creatures.Slots().size() > 0
-			&& gainedExp == 0 && manaDiff == 0 && moraleDiff == 0 && luckDiff == 0) //if it gives nothing without battle
-		{
-			showInfoDialog(hero,15,0);
-			cb->removeObject(this);
-		}
-		else //if it gives something without battle
-		{
-			giveContentsUpToExp(hero);
-		}
-	}
-}
-
-void CGPandoraBox::heroLevelUpDone(const CGHeroInstance *hero) const
-{
-	giveContentsAfterExp(hero);
-}
-
-void CGEvent::onHeroVisit( const CGHeroInstance * h ) const
-{
-	if(!(availableFor & (1 << h->tempOwner.getNum())))
-		return;
-	if(cb->getPlayerSettings(h->tempOwner)->playerID)
-	{
-		if(humanActivate)
-			activated(h);
-	}
-	else if(computerActivate)
-		activated(h);
-}
-
-void CGEvent::activated( const CGHeroInstance * h ) const
-{
-	if(stacksCount() > 0)
-	{
-		InfoWindow iw;
-		iw.player = h->tempOwner;
-		if(message.size())
-			iw.text << message;
-		else
-			iw.text.addTxt(MetaString::ADVOB_TXT, 16);
-		cb->showInfoDialog(&iw);
-		cb->startBattleI(h, this);
-	}
-	else
-	{
-		giveContentsUpToExp(h);
-	}
-}
-
-void CGObservatory::onHeroVisit( const CGHeroInstance * h ) const
-{
-	InfoWindow iw;
-	iw.player = h->tempOwner;
-	switch (ID)
-	{
-	case Obj::REDWOOD_OBSERVATORY:
-	case Obj::PILLAR_OF_FIRE:
-	{
-		iw.soundID = soundBase::LIGHTHOUSE;
-		iw.text.addTxt(MetaString::ADVOB_TXT,98 + (ID==Obj::PILLAR_OF_FIRE));
-
-		FoWChange fw;
-		fw.player = h->tempOwner;
-		fw.mode = 1;
-		cb->getTilesInRange (fw.tiles, pos, 20, h->tempOwner, 1);
-		cb->sendAndApply (&fw);
-		break;
-	}
-	case Obj::COVER_OF_DARKNESS:
-	{
-		iw.text.addTxt (MetaString::ADVOB_TXT, 31);
-		hideTiles(h->tempOwner, 20);
-		break;
-	}
-	}
-	cb->showInfoDialog(&iw);
-}
-
-void CGShrine::onHeroVisit( const CGHeroInstance * h ) const
-{
-	if(spell == SpellID::NONE)
-	{
-        logGlobal->errorStream() << "Not initialized shrine visited!";
-		return;
-	}
-
-	if(!wasVisited(h->tempOwner))
-		cb->setObjProperty(id, 10, h->tempOwner.getNum());
-
-	InfoWindow iw;
-	iw.soundID = soundBase::temple;
-	iw.player = h->getOwner();
-	iw.text.addTxt(MetaString::ADVOB_TXT,127 + ID - 88);
-	iw.text.addTxt(MetaString::SPELL_NAME,spell);
-	iw.text << ".";
-
-	if(!h->getArt(ArtifactPosition::SPELLBOOK))
-	{
-		iw.text.addTxt(MetaString::ADVOB_TXT,131);
-	}
-	else if(ID == Obj::SHRINE_OF_MAGIC_THOUGHT  && !h->getSecSkillLevel(SecondarySkill::WISDOM)) //it's third level spell and hero doesn't have wisdom
-	{
-		iw.text.addTxt(MetaString::ADVOB_TXT,130);
-	}
-	else if(vstd::contains(h->spells,spell))//hero already knows the spell
-	{
-		iw.text.addTxt(MetaString::ADVOB_TXT,174);
-	}
-	else //give spell
-	{
-		std::set<SpellID> spells;
-		spells.insert(spell);
-		cb->changeSpells(h, true, spells);
-
-		iw.components.push_back(Component(Component::SPELL,spell,0,0));
-	}
-
-	cb->showInfoDialog(&iw);
-}
-
-void CGShrine::initObj()
-{
-	if(spell == SpellID::NONE) //spell not set
-	{
-		int level = ID-87;
-		std::vector<SpellID> possibilities;
-		cb->getAllowedSpells (possibilities, level);
-
-		if(possibilities.empty())
-		{
-            logGlobal->errorStream() << "Error: cannot init shrine, no allowed spells!";
-			return;
-		}
-
-		spell = *RandomGeneratorUtil::nextItem(possibilities, cb->gameState()->getRandomGenerator());
-	}
-}
-
-const std::string & CGShrine::getHoverText() const
-{
-	hoverName = VLC->generaltexth->names[ID];
-	if(wasVisited(cb->getCurrentPlayer())) //TODO: use local player, not current
-	{
-		hoverName += "\n" + VLC->generaltexth->allTexts[355]; // + (learn %s)
-		boost::algorithm::replace_first(hoverName,"%s", spell.toSpell()->name);
-		const CGHeroInstance *h = cb->getSelectedHero(cb->getCurrentPlayer());
-		if(h && vstd::contains(h->spells,spell)) //hero knows that ability
-			hoverName += "\n\n" + VLC->generaltexth->allTexts[354]; // (Already learned)
-	}
-	return hoverName;
-}
-
-void CGSignBottle::initObj()
-{
-	//if no text is set than we pick random from the predefined ones
-	if(message.empty())
-	{
-		message = *RandomGeneratorUtil::nextItem(VLC->generaltexth->randsign, cb->gameState()->getRandomGenerator());
-	}
-
-	if(ID == Obj::OCEAN_BOTTLE)
-	{
-		blockVisit = true;
-	}
-}
-
-void CGSignBottle::onHeroVisit( const CGHeroInstance * h ) const
-{
-	InfoWindow iw;
-	iw.soundID = soundBase::STORE;
-	iw.player = h->getOwner();
-	iw.text << message;
-	cb->showInfoDialog(&iw);
-
-	if(ID == Obj::OCEAN_BOTTLE)
-		cb->removeObject(this);
-}
-
-//TODO: remove
-//void CGScholar::giveAnyBonus( const CGHeroInstance * h ) const
-//{
-//
-//}
-
-void CGScholar::onHeroVisit( const CGHeroInstance * h ) const
-{
-
-	EBonusType type = bonusType;
-	int bid = bonusID;
-	//check if the bonus if applicable, if not - give primary skill (always possible)
-	int ssl = h->getSecSkillLevel(SecondarySkill(bid)); //current sec skill level, used if bonusType == 1
-	if((type == SECONDARY_SKILL
-			&& ((ssl == 3)  ||  (!ssl  &&  !h->canLearnSkill()))) ////hero already has expert level in the skill or (don't know skill and doesn't have free slot)
-		|| (type == SPELL  &&  (!h->getArt(ArtifactPosition::SPELLBOOK) || vstd::contains(h->spells, (ui32) bid)
-		|| ( SpellID(bid).toSpell()->level > h->getSecSkillLevel(SecondarySkill::WISDOM) + 2)
-		))) //hero doesn't have a spellbook or already knows the spell or doesn't have Wisdom
-	{
-		type = PRIM_SKILL;
-		bid = cb->gameState()->getRandomGenerator().nextInt(GameConstants::PRIMARY_SKILLS - 1);
-	}
-
-	InfoWindow iw;
-	iw.soundID = soundBase::gazebo;
-	iw.player = h->getOwner();
-	iw.text.addTxt(MetaString::ADVOB_TXT,115);
-
-	switch (type)
-	{
-	case PRIM_SKILL:
-		cb->changePrimSkill(h,static_cast<PrimarySkill::PrimarySkill>(bid),+1);
-		iw.components.push_back(Component(Component::PRIM_SKILL,bid,+1,0));
-		break;
-	case SECONDARY_SKILL:
-		cb->changeSecSkill(h,SecondarySkill(bid),+1);
-		iw.components.push_back(Component(Component::SEC_SKILL,bid,ssl+1,0));
-		break;
-	case SPELL:
-		{
-			std::set<SpellID> hlp;
-			hlp.insert(SpellID(bid));
-			cb->changeSpells(h,true,hlp);
-			iw.components.push_back(Component(Component::SPELL,bid,0,0));
-		}
-		break;
-	default:
-		logGlobal->errorStream() << "Error: wrong bonus type (" << (int)type << ") for Scholar!\n";
-		return;
-	}
-
-	cb->showInfoDialog(&iw);
-	cb->removeObject(this);
-}
-
-void CGScholar::initObj()
-{
-	blockVisit = true;
-	if(bonusType == RANDOM)
-	{
-		bonusType = static_cast<EBonusType>(cb->gameState()->getRandomGenerator().nextInt(2));
-		switch(bonusType)
-		{
-		case PRIM_SKILL:
-			bonusID = cb->gameState()->getRandomGenerator().nextInt(GameConstants::PRIMARY_SKILLS -1);
-			break;
-		case SECONDARY_SKILL:
-			bonusID = cb->gameState()->getRandomGenerator().nextInt(GameConstants::SKILL_QUANTITY -1);
-			break;
-		case SPELL:
-			std::vector<SpellID> possibilities;
-			for (int i = 1; i < 6; ++i)
-				cb->getAllowedSpells (possibilities, i);
-			bonusID = *RandomGeneratorUtil::nextItem(possibilities, cb->gameState()->getRandomGenerator());
-			break;
-		}
-	}
-}
-
-void CGGarrison::onHeroVisit (const CGHeroInstance *h) const
-{
-	int ally = cb->gameState()->getPlayerRelations(h->tempOwner, tempOwner);
-	if (!ally && stacksCount() > 0) {
-		//TODO: Find a way to apply magic garrison effects in battle.
-		cb->startBattleI(h, this);
-		return;
-	}
-
-	//New owner.
-	if (!ally)
-		cb->setOwner(this, h->tempOwner);
-
-	cb->showGarrisonDialog(id, h->id, removableUnits);
-}
-
-ui8 CGGarrison::getPassableness() const
-{
-	if ( !stacksCount() )//empty - anyone can visit
-		return GameConstants::ALL_PLAYERS;
-	if ( tempOwner == PlayerColor::NEUTRAL )//neutral guarded - no one can visit
-		return 0;
-
-	ui8 mask = 0;
-	TeamState * ts = cb->gameState()->getPlayerTeam(tempOwner);
-	for(PlayerColor it : ts->players)
-		mask |= 1<<it.getNum(); //allies - add to possible visitors
-
-	return mask;
-}
-
-void CGGarrison::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
-{
-	if (result.winner == 0)
-		onHeroVisit(hero);
-}
-
-void CGOnceVisitable::onHeroVisit( const CGHeroInstance * h ) const
-{
-	int sound = soundBase::sound_todo;
-	int txtid;
-
-	switch(ID)
-	{
-	case Obj::CORPSE:
-		txtid = 37;
-		sound = soundBase::MYSTERY;
-		break;
-	case Obj::LEAN_TO:
-		sound = soundBase::GENIE;
-		txtid = 64;
-		break;
-	case Obj::WAGON:
-		sound = soundBase::GENIE;
-		txtid = 154;
-		break;
-	case Obj::WARRIORS_TOMB:
-		{
-			//ask if player wants to search the Tomb
-			BlockingDialog bd(true, false);
-			bd.soundID = soundBase::GRAVEYARD;
-			bd.player = h->getOwner();
-			bd.text.addTxt(MetaString::ADVOB_TXT,161);
-			cb->showBlockingDialog(&bd);
-			return;
-		}
-	default:
-        logGlobal->errorStream() << "Error: Unknown object (" << ID <<") treated as CGOnceVisitable!";
-		return;
-	}
-
-	InfoWindow iw;
-	iw.soundID = sound;
-	iw.player = h->getOwner();
-
-	if(players.size()) //we have been already visited...
-	{
-		txtid++;
-		if(ID == Obj::WAGON) //wagon has extra text (for finding art) we need to omit
-			txtid++;
-		iw.text.addTxt(MetaString::ADVOB_TXT, txtid);
-	}
-	else //first visit - give bonus!
-	{
-		switch(artOrRes)
-		{
-		case 0: // first visit but empty
-			if (ID == Obj::CORPSE)
-				++txtid;
-			else
-				txtid+=2;
-			iw.text.addTxt(MetaString::ADVOB_TXT, txtid);
-			break;
-		case 1: //art
-			iw.components.push_back(Component(Component::ARTIFACT,bonusType,0,0));
-			cb->giveHeroNewArtifact(h, VLC->arth->artifacts[bonusType],ArtifactPosition::FIRST_AVAILABLE);
-			iw.text.addTxt(MetaString::ADVOB_TXT, txtid);
-			if (ID == Obj::CORPSE)
-			{
-				iw.text << "%s";
-				iw.text.addReplacement(MetaString::ART_NAMES, bonusType);
-			}
-			break;
-		case 2: //res
-			iw.text.addTxt(MetaString::ADVOB_TXT, txtid);
-			iw.components.push_back (Component(Component::RESOURCE, bonusType, bonusVal, 0));
-			cb->giveResource(h->getOwner(), static_cast<Res::ERes>(bonusType), bonusVal);
-			break;
-		}
-		if(ID == Obj::WAGON  &&  artOrRes == 1)
-		{
-			iw.text.localStrings.back().second++;
-			iw.text.addReplacement(MetaString::ART_NAMES, bonusType);
-		}
-	}
-
-	cb->showInfoDialog(&iw);
-	cb->setObjProperty(id, 10, h->getOwner().getNum());
-}
-
-const std::string & CGOnceVisitable::getHoverText() const
-{
-	const bool visited = wasVisited(cb->getCurrentPlayer());
-	hoverName = VLC->generaltexth->names[ID] + " " + visitedTxt(visited);
-	return hoverName;
-}
-
-void CGOnceVisitable::initObj()
-{
-	switch(ID)
-	{
-	case Obj::CORPSE:
-		{
-			blockVisit = true;
-			int hlp = cb->gameState()->getRandomGenerator().nextInt(99);
-			if(hlp < 20)
-			{
-				artOrRes = 1;
-				bonusType = VLC->arth->pickRandomArtifact(cb->gameState()->getRandomGenerator(), CArtifact::ART_TREASURE | CArtifact::ART_MINOR | CArtifact::ART_MAJOR);
-			}
-			else
-			{
-				artOrRes = 0;
-			}
-		}
-		break;
-
-	case Obj::LEAN_TO:
-		{
-			artOrRes = 2;
-			bonusType = cb->gameState()->getRandomGenerator().nextInt(5); //any basic resource without gold
-			bonusVal = cb->gameState()->getRandomGenerator().nextInt(1, 4);
-		}
-		break;
-
-	case Obj::WARRIORS_TOMB:
-		{
-			artOrRes = 1;
-
-			int hlp = cb->gameState()->getRandomGenerator().nextInt(99);
-			if(hlp < 30)
-				bonusType = VLC->arth->pickRandomArtifact(cb->gameState()->getRandomGenerator(), CArtifact::ART_TREASURE);
-			else if(hlp < 80)
-				bonusType = VLC->arth->pickRandomArtifact(cb->gameState()->getRandomGenerator(), CArtifact::ART_MINOR);
-			else if(hlp < 95)
-				bonusType = VLC->arth->pickRandomArtifact(cb->gameState()->getRandomGenerator(), CArtifact::ART_MAJOR);
-			else
-				bonusType = VLC->arth->pickRandomArtifact(cb->gameState()->getRandomGenerator(), CArtifact::ART_RELIC);
-		}
-		break;
-
-	case Obj::WAGON:
-		{
-			int hlp = cb->gameState()->getRandomGenerator().nextInt(99);
-
-			if(hlp < 10)
-			{
-				artOrRes = 0; // nothing... :(
-			}
-			else if(hlp < 50) //minor or treasure art
-			{
-				artOrRes = 1;
-				bonusType = VLC->arth->pickRandomArtifact(cb->gameState()->getRandomGenerator(), CArtifact::ART_TREASURE | CArtifact::ART_MINOR);
-			}
-			else //2 - 5 of non-gold resource
-			{
-				artOrRes = 2;
-				bonusType = cb->gameState()->getRandomGenerator().nextInt(5);
-				bonusVal = cb->gameState()->getRandomGenerator().nextInt(2, 5);
-			}
-		}
-		break;
-	}
-}
-
-void CGOnceVisitable::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
-{
-	//must have been Tomb
-	if(answer)
-	{
-		InfoWindow iw;
-		iw.player = hero->getOwner();
-		iw.components.push_back(Component(Component::MORALE,0,-3,0));
-
-		if(players.size()) //we've been already visited, player found nothing
-		{
-			iw.text.addTxt(MetaString::ADVOB_TXT,163);
-		}
-		else //first visit - give artifact
-		{
-			iw.text.addTxt(MetaString::ADVOB_TXT,162);
-			iw.components.push_back(Component(Component::ARTIFACT,bonusType,0,0));
-			iw.text.addReplacement(MetaString::ART_NAMES, bonusType);
-
-			cb->giveHeroNewArtifact(hero, VLC->arth->artifacts[bonusType],ArtifactPosition::FIRST_AVAILABLE);
-		}
-
-		if(!hero->hasBonusFrom(Bonus::OBJECT,ID)) //we don't have modifier from this object yet
-		{
-			//ruin morale
-			GiveBonus gb;
-			gb.id = hero->id.getNum();
-			gb.bonus = Bonus(Bonus::ONE_BATTLE,Bonus::MORALE,Bonus::OBJECT,-3,id.getNum(),"");
-			gb.bdescr.addTxt(MetaString::ARRAY_TXT,104); //Warrior Tomb Visited -3
-			cb->giveHeroBonus(&gb);
-		}
-		cb->showInfoDialog(&iw);
-		cb->setObjProperty(id, 10, hero->getOwner().getNum());
-	}
-}
-
-void CBank::initObj()
-{
-	index = VLC->objh->bankObjToIndex(this);
-	bc = nullptr;
-	daycounter = 0;
-	multiplier = 1;
-}
-const std::string & CBank::getHoverText() const
-{
-	bool visited = (bc == nullptr);
-	hoverName = VLC->objh->creBanksNames[index] + " " + visitedTxt(visited);
-	return hoverName;
-}
-void CBank::reset(ui16 var1) //prevents desync
-{
-	ui8 chance = 0;
-	for (auto & elem : VLC->objh->banksInfo[index])
-	{
-		if (var1 < (chance += elem->chance))
-		{
- 			bc = elem;
-			break;
-		}
-	}
-	artifacts.clear();
-}
-
-void CBank::initialize() const
-{
-	cb->setObjProperty(id, ObjProperty::BANK_RESET, cb->gameState()->getRandomGenerator().nextInt()); //synchronous reset
-
-	for (ui8 i = 0; i <= 3; i++)
-	{
-		for (ui8 n = 0; n < bc->artifacts[i]; n++)
-		{
-			CArtifact::EartClass artClass;
-			switch(i)
-			{
-			case 0: artClass = CArtifact::ART_TREASURE; break;
-			case 1: artClass = CArtifact::ART_MINOR; break;
-			case 2: artClass = CArtifact::ART_MAJOR; break;
-			case 3: artClass = CArtifact::ART_RELIC; break;
-			default: assert(0); continue;
-			}
-
-			int artID = VLC->arth->pickRandomArtifact(cb->gameState()->getRandomGenerator(), artClass);
-			cb->setObjProperty(id, ObjProperty::BANK_ADD_ARTIFACT, artID);
-		}
-	}
-
-	cb->setObjProperty(id, ObjProperty::BANK_INIT_ARMY, cb->gameState()->getRandomGenerator().nextInt()); //get army
-}
-void CBank::setPropertyDer (ui8 what, ui32 val)
-/// random values are passed as arguments and processed identically on all clients
-{
-	switch (what)
-	{
-		case ObjProperty::BANK_DAYCOUNTER: //daycounter
-			if (val == 0)
-				daycounter = 1; //yes, 1
-			else
-				daycounter++;
-			break;
-		case ObjProperty::BANK_MULTIPLIER: //multiplier, in percent
-			multiplier = val / 100.0;
-			break;
-		case 13: //bank preset
-			bc = VLC->objh->banksInfo[index][val];
-			break;
-		case ObjProperty::BANK_RESET:
-			reset (val%100);
-			break;
-		case ObjProperty::BANK_CLEAR_CONFIG:
-			bc = nullptr;
-			break;
-		case ObjProperty::BANK_CLEAR_ARTIFACTS: //remove rewards from Derelict Ship
-			artifacts.clear();
-			break;
-		case ObjProperty::BANK_INIT_ARMY: //set ArmedInstance army
-			{
-				int upgraded = 0;
-				if (val%100 < bc->upgradeChance) //once again anti-desync
-					upgraded = 1;
-				switch (bc->guards.size())
-				{
-					case 1:
-						for	(int i = 0; i < 4; ++i)
-							setCreature (SlotID(i), bc->guards[0].first, bc->guards[0].second  / 5 );
-						setCreature (SlotID(4), CreatureID(bc->guards[0].first + upgraded), bc->guards[0].second  / 5 );
-						break;
-					case 4:
-					{
-						if (bc->guards.back().second) //all stacks are present
-						{
-							for (auto & elem : bc->guards)
-							{
-								setCreature (SlotID(stacksCount()), elem.first, elem.second);
-							}
-						}
-						else if (bc->guards[2].second)//Wraiths are present, split two stacks in Crypt
-						{
-							setCreature (SlotID(0), bc->guards[0].first, bc->guards[0].second  / 2 );
-							setCreature (SlotID(1), bc->guards[1].first, bc->guards[1].second / 2);
-							setCreature (SlotID(2), CreatureID(bc->guards[2].first + upgraded), bc->guards[2].second);
-							setCreature (SlotID(3), bc->guards[1].first, bc->guards[1].second / 2 );
-							setCreature (SlotID(4), bc->guards[0].first, bc->guards[0].second - (bc->guards[0].second  / 2) );
-
-						}
-						else //split both stacks
-						{
-							for	(int i = 0; i < 3; ++i) //skellies
-								setCreature (SlotID(2*i), bc->guards[0].first, bc->guards[0].second  / 3);
-							for	(int i = 0; i < 2; ++i) //zombies
-								setCreature (SlotID(2*i+1), bc->guards[1].first, bc->guards[1].second  / 2);
-						}
-					}
-						break;
-					default:
-                        logGlobal->warnStream() << "Error: Unexpected army data: " << bc->guards.size() <<" items found";
-						return;
-				}
-			}
-			break;
-		case ObjProperty::BANK_ADD_ARTIFACT: //add Artifact
-		{
-			artifacts.push_back (val);
-			break;
-		}
-	}
-}
-
-void CBank::newTurn() const
-{
-	if (bc == nullptr)
-	{
-		if (cb->getDate() == 1)
-			initialize(); //initialize on first day
-		else if (daycounter >= 28 && (subID < 13 || subID > 16)) //no reset for Emissaries
-		{
-			initialize();
-			cb->setObjProperty (id, ObjProperty::BANK_DAYCOUNTER, 0); //daycounter 0
-			if (ID == Obj::DERELICT_SHIP && cb->getDate() > 1)
-			{
-				cb->setObjProperty (id, ObjProperty::BANK_MULTIPLIER, 0);//ugly hack to make derelict ships usable only once
-				cb->setObjProperty (id, ObjProperty::BANK_CLEAR_ARTIFACTS, 0);
-			}
-		}
-		else
-			cb->setObjProperty (id, ObjProperty::BANK_DAYCOUNTER, 1); //daycounter++
-	}
-}
-bool CBank::wasVisited (PlayerColor player) const
-{
-	return !bc;
-}
-
-void CBank::onHeroVisit (const CGHeroInstance * h) const
-{
-	if (bc)
-	{
-		int banktext = 0;
-		switch (ID)
-		{
-		case Obj::CREATURE_BANK:
-			banktext = 32;
-			break;
-		case Obj::DERELICT_SHIP:
-			banktext = 41;
-			break;
-		case Obj::DRAGON_UTOPIA:
-			banktext = 47;
-			break;
-		case Obj::CRYPT:
-			banktext = 119;
-			break;
-		case Obj::SHIPWRECK:
-			banktext = 122;
-			break;
-		}
-		BlockingDialog bd (true, false);
-		bd.player = h->getOwner();
-		bd.soundID = soundBase::ROGUE;
-		bd.text.addTxt(MetaString::ADVOB_TXT,banktext);
-		if (ID == Obj::CREATURE_BANK)
-			bd.text.addReplacement(VLC->objh->creBanksNames[index]);
-		cb->showBlockingDialog (&bd);
-	}
-	else
-	{
-		InfoWindow iw;
-		iw.soundID = soundBase::GRAVEYARD;
-		iw.player = h->getOwner();
-		if (ID == Obj::CRYPT) //morale penalty for empty Crypt
-		{
-			GiveBonus gbonus;
-			gbonus.id = h->id.getNum();
-			gbonus.bonus.duration = Bonus::ONE_BATTLE;
-			gbonus.bonus.source = Bonus::OBJECT;
-			gbonus.bonus.sid = ID;
-			gbonus.bdescr << "\n" << VLC->generaltexth->arraytxt[98];
-			gbonus.bonus.type = Bonus::MORALE;
-			gbonus.bonus.val = -1;
-			cb->giveHeroBonus(&gbonus);
-			iw.text << VLC->generaltexth->advobtxt[120];
-			iw.components.push_back (Component (Component::MORALE, 0 , -1, 0));
-		}
-		else
-		{
-			iw.text << VLC->generaltexth->advobtxt[33];
-			iw.text.addReplacement(VLC->objh->creBanksNames[index]);
-		}
-		cb->showInfoDialog(&iw);
-	}
-}
-
-void CBank::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
-{
-	if (result.winner == 0)
-	{
-		int textID = -1;
-		InfoWindow iw;
-		iw.player = hero->getOwner();
-		MetaString loot;
-
-		switch (ID)
-		{
-		case Obj::CREATURE_BANK: case Obj::DRAGON_UTOPIA:
-			textID = 34;
-			break;
-		case Obj::DERELICT_SHIP:
-			if (multiplier)
-				textID = 43;
-			else
-			{
-				GiveBonus gbonus;
-				gbonus.id = hero->id.getNum();
-				gbonus.bonus.duration = Bonus::ONE_BATTLE;
-				gbonus.bonus.source = Bonus::OBJECT;
-				gbonus.bonus.sid = ID;
-				gbonus.bdescr << "\n" << VLC->generaltexth->arraytxt[101];
-				gbonus.bonus.type = Bonus::MORALE;
-				gbonus.bonus.val = -1;
-				cb->giveHeroBonus(&gbonus);
-				textID = 42;
-				iw.components.push_back (Component (Component::MORALE, 0 , -1, 0));
-			}
-			break;
-		case Obj::CRYPT:
-			if (bc->resources.size() != 0)
-				textID = 121;
-			else
-			{
-				iw.components.push_back (Component (Component::MORALE, 0 , -1, 0));
-				GiveBonus gbonus;
-				gbonus.id = hero->id.getNum();
-				gbonus.bonus.duration = Bonus::ONE_BATTLE;
-				gbonus.bonus.source = Bonus::OBJECT;
-				gbonus.bonus.sid = ID;
-				gbonus.bdescr << "\n" << VLC->generaltexth->arraytxt[ID];
-				gbonus.bonus.type = Bonus::MORALE;
-				gbonus.bonus.val = -1;
-				cb->giveHeroBonus(&gbonus);
-				textID = 120;
-				iw.components.push_back (Component (Component::MORALE, 0 , -1, 0));
-			}
-			break;
-		case Obj::SHIPWRECK:
-			if (bc->resources.size())
-				textID = 124;
-			else
-				textID = 123;
-			break;
-		}
-
-		//grant resources
-		if (textID != 42) //empty derelict ship gives no cash
-		{
-			for (int it = 0; it < bc->resources.size(); it++)
-			{
-				if (bc->resources[it] != 0)
-				{
-					iw.components.push_back (Component (Component::RESOURCE, it, bc->resources[it], 0));
-					loot << "%d %s";
-					loot.addReplacement(iw.components.back().val);
-					loot.addReplacement(MetaString::RES_NAMES, iw.components.back().subtype);
-					cb->giveResource (hero->getOwner(), static_cast<Res::ERes>(it), bc->resources[it]);
-				}
-			}
-		}
-		//grant artifacts
-		for (auto & elem : artifacts)
-		{
-			iw.components.push_back (Component (Component::ARTIFACT, elem, 0, 0));
-			loot << "%s";
-			loot.addReplacement(MetaString::ART_NAMES, elem);
-			cb->giveHeroNewArtifact (hero, VLC->arth->artifacts[elem], ArtifactPosition::FIRST_AVAILABLE);
-		}
-		//display loot
-		if (!iw.components.empty())
-		{
-			iw.text.addTxt (MetaString::ADVOB_TXT, textID);
-			if (textID == 34)
-			{
-				iw.text.addReplacement(MetaString::CRE_PL_NAMES, result.casualties[1].begin()->first);
-				iw.text.addReplacement(loot.buildList());
-			}
-			cb->showInfoDialog(&iw);
-		}
-		loot.clear();
-		iw.components.clear();
-		iw.text.clear();
-
-		//grant creatures
-		CCreatureSet ourArmy;
-		for (auto it = bc->creatures.cbegin(); it != bc->creatures.cend(); it++)
-		{
-			SlotID slot = ourArmy.getSlotFor(it->first);
-			ourArmy.addToSlot(slot, it->first, it->second);
-		}
-		for (auto & elem : ourArmy.Slots())
-		{
-			iw.components.push_back(Component(*elem.second));
-			loot << "%s";
-			loot.addReplacement(*elem.second);
-		}
-
-		if (ourArmy.Slots().size())
-		{
-			if (ourArmy.Slots().size() == 1 && ourArmy.Slots().begin()->second->count == 1)
-				iw.text.addTxt (MetaString::ADVOB_TXT, 185);
-			else
-				iw.text.addTxt (MetaString::ADVOB_TXT, 186);
-
-			iw.text.addReplacement(loot.buildList());
-			iw.text.addReplacement(hero->name);
-			cb->showInfoDialog(&iw);
-			cb->giveCreatures(this, hero, ourArmy, false);
-		}
-		cb->setObjProperty (id, ObjProperty::BANK_CLEAR_CONFIG, 0); //bc = nullptr
-	}
-}
-
-void CBank::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
-{
-	if (answer)
-	{
-		cb->startBattleI(hero, this, true);
-	}
-}
-
-void CGPyramid::initObj()
-{
-	std::vector<SpellID> available;
-	cb->getAllowedSpells (available, 5);
-	if (available.size())
-	{
-		bc = VLC->objh->banksInfo[21].front(); //TODO: remove hardcoded value?
-		spell = *RandomGeneratorUtil::nextItem(available, cb->gameState()->getRandomGenerator());
-	}
-	else
-	{
-        logGlobal->errorStream() <<"No spells available for Pyramid! Object set to empty.";
-	}
-	setPropertyDer(ObjProperty::BANK_INIT_ARMY, cb->gameState()->getRandomGenerator().nextInt()); //set guards at game start
-}
-const std::string & CGPyramid::getHoverText() const
-{
-	hoverName = VLC->objh->creBanksNames[21]+ " " + visitedTxt((bc==nullptr));
-	return hoverName;
-}
-void CGPyramid::onHeroVisit (const CGHeroInstance * h) const
-{
-	if (bc)
-	{
-		BlockingDialog bd (true, false);
-		bd.player = h->getOwner();
-		bd.soundID = soundBase::MYSTERY;
-		bd.text << VLC->generaltexth->advobtxt[105];
-		cb->showBlockingDialog(&bd);
-	}
-	else
-	{
-		InfoWindow iw;
-		iw.player = h->getOwner();
-		iw.text << VLC->generaltexth->advobtxt[107];
-		iw.components.push_back (Component (Component::LUCK, 0 , -2, 0));
-		GiveBonus gb;
-		gb.bonus = Bonus(Bonus::ONE_BATTLE,Bonus::LUCK,Bonus::OBJECT,-2,id.getNum(),VLC->generaltexth->arraytxt[70]);
-		gb.id = h->id.getNum();
-		cb->giveHeroBonus(&gb);
-		cb->showInfoDialog(&iw);
-	}
-}
-
-void CGPyramid::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
-{
-	if (result.winner == 0)
-	{
-		InfoWindow iw;
-		iw.player = hero->getOwner();
-		iw.text.addTxt (MetaString::ADVOB_TXT, 106);
-		iw.text.addTxt (MetaString::SPELL_NAME, spell);
-		if (!hero->getArt(ArtifactPosition::SPELLBOOK))
-			iw.text.addTxt (MetaString::ADVOB_TXT, 109); //no spellbook
-		else if (hero->getSecSkillLevel(SecondarySkill::WISDOM) < 3)
-			iw.text.addTxt (MetaString::ADVOB_TXT, 108); //no expert Wisdom
-		else
-		{
-			std::set<SpellID> spells;
-			spells.insert (SpellID(spell));
-			cb->changeSpells (hero, true, spells);
-			iw.components.push_back(Component (Component::SPELL, spell, 0, 0));
-		}
-		cb->showInfoDialog(&iw);
-		cb->setObjProperty (id, ObjProperty::BANK_CLEAR_CONFIG, 0);
-	}
-}
-
-void CGKeys::setPropertyDer (ui8 what, ui32 val) //101-108 - enable key for player 1-8
-{
-	if (what >= 101 && what <= (100 + PlayerColor::PLAYER_LIMIT_I))
-	{
-		PlayerColor player(what-101);
-		playerKeyMap[player].insert((ui8)val);
-	}
-	else
-		logGlobal->errorStream() << boost::format("Unexpected properties requested to set: what=%d, val=%d") % (int)what % val;
-}
-
-bool CGKeys::wasMyColorVisited (PlayerColor player) const
-{
-	if (vstd::contains(playerKeyMap[player], subID)) //creates set if it's not there
-		return true;
-	else
-		return false;
-}
-
-const std::string& CGKeys::getHoverText() const
-{
-	bool visited = wasMyColorVisited (cb->getLocalPlayer());
-	hoverName = getName() + "\n" + visitedTxt(visited);
-	return hoverName;
-}
-
-
-const std::string CGKeys::getName() const
-{
-	std::string name;
-	name = VLC->generaltexth->tentColors[subID] + " " + VLC->generaltexth->names[ID];
-	return name;
-}
-
-bool CGKeymasterTent::wasVisited (PlayerColor player) const
-{
-	return wasMyColorVisited (player);
-}
-
-void CGKeymasterTent::onHeroVisit( const CGHeroInstance * h ) const
-{
-	int txt_id;
-	if (!wasMyColorVisited (h->getOwner()) )
-	{
-		cb->setObjProperty(id, h->tempOwner.getNum()+101, subID);
-		txt_id=19;
-	}
-	else
-		txt_id=20;
-    showInfoDialog(h,txt_id,soundBase::CAVEHEAD);
-}
-
-void CGBorderGuard::initObj()
-{
-	//ui32 m13489val = subID; //store color as quest info
-	blockVisit = true;
-}
-
-void CGBorderGuard::getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h) const
-{
-	text << std::pair<ui8,ui32>(11,18);
-}
-
-void CGBorderGuard::getRolloverText (MetaString &text, bool onHover) const
-{
-	if (!onHover)
-		text << VLC->generaltexth->tentColors[subID] << " " << VLC->generaltexth->names[Obj::KEYMASTER];
-}
-
-bool CGBorderGuard::checkQuest (const CGHeroInstance * h) const
-{
-	return wasMyColorVisited (h->tempOwner);
-}
-
-void CGBorderGuard::onHeroVisit( const CGHeroInstance * h ) const
-{
-	if (wasMyColorVisited (h->getOwner()) )
-	{
-		BlockingDialog bd (true, false);
-		bd.player = h->getOwner();
-		bd.soundID = soundBase::QUEST;
-		bd.text.addTxt (MetaString::ADVOB_TXT, 17);
-		cb->showBlockingDialog (&bd);
-	}
-	else
-	{
-		showInfoDialog(h,18,soundBase::CAVEHEAD);
-
-		AddQuest aq;
-		aq.quest = QuestInfo (quest, this, visitablePos());
-		aq.player = h->tempOwner;
-		cb->sendAndApply (&aq);
-		//TODO: add this quest only once OR check for multiple instances later
-	}
-}
-
-void CGBorderGuard::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
-{
-	if (answer)
-		cb->removeObject(this);
-}
-
-void CGBorderGate::onHeroVisit( const CGHeroInstance * h ) const //TODO: passability
-{
-	if (!wasMyColorVisited (h->getOwner()) )
-	{
-		showInfoDialog(h,18,0);
-
-		AddQuest aq;
-		aq.quest = QuestInfo (quest, this, visitablePos());
-		aq.player = h->tempOwner;
-		cb->sendAndApply (&aq);
-	}
-}
-
-ui8 CGBorderGate::getPassableness() const
-{
-	ui8 ret = 0;
-	for (int i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++)
-		ret |= wasMyColorVisited(PlayerColor(i))<<i;
-	return ret;
-}
-
-void CGMagi::initObj()
-{
-	if (ID == Obj::EYE_OF_MAGI)
-	{
-		blockVisit = true;
-		eyelist[subID].push_back(id);
-	}
-}
-void CGMagi::onHeroVisit(const CGHeroInstance * h) const
-{
-	if (ID == Obj::HUT_OF_MAGI)
-	{
-		showInfoDialog(h, 61, soundBase::LIGHTHOUSE);
-
-		if (!eyelist[subID].empty())
-		{
-			CenterView cv;
-			cv.player = h->tempOwner;
-			cv.focusTime = 2000;
-
-			FoWChange fw;
-			fw.player = h->tempOwner;
-			fw.mode = 1;
-
-			for(auto it : eyelist[subID])
-			{
-				const CGObjectInstance *eye = cb->getObj(it);
-
-				cb->getTilesInRange (fw.tiles, eye->pos, 10, h->tempOwner, 1);
-				cb->sendAndApply(&fw);
-				cv.pos = eye->pos;
-
-				cb->sendAndApply(&cv);
-			}
-			cv.pos = h->getPosition(false);
-			cb->sendAndApply(&cv);
-		}
-	}
-	else if (ID == Obj::EYE_OF_MAGI)
-	{
-		showInfoDialog(h,48,soundBase::invalid);
-	}
-
-}
-void CGBoat::initObj()
-{
-	hero = nullptr;
-}
-
-void CGSirens::initObj()
-{
-	blockVisit = true;
-}
-
-const std::string & CGSirens::getHoverText() const
-{
-	getNameVis(hoverName);
-	return hoverName;
-}
-
-void CGSirens::onHeroVisit( const CGHeroInstance * h ) const
-{
-	InfoWindow iw;
-	iw.soundID = soundBase::DANGER;
-	iw.player = h->tempOwner;
-	if(h->hasBonusFrom(Bonus::OBJECT,ID)) //has already visited Sirens
-	{
-		iw.text.addTxt(MetaString::ADVOB_TXT,133);
-	}
-	else
-	{
-		giveDummyBonus(h->id, Bonus::ONE_BATTLE);
-		TExpType xp = 0;
-
-		for (auto i = h->Slots().begin(); i != h->Slots().end(); i++)
-		{
-			TQuantity drown = i->second->count * 0.3;
-			if(drown)
-			{
-				cb->changeStackCount(StackLocation(h, i->first), -drown);
-				xp += drown * i->second->type->valOfBonuses(Bonus::STACK_HEALTH);
-			}
-		}
-
-		if(xp)
-		{
-			xp = h->calculateXp(xp);
-			iw.text.addTxt(MetaString::ADVOB_TXT,132);
-			iw.text.addReplacement(xp);
-			cb->changePrimSkill(h, PrimarySkill::EXPERIENCE, xp, false);
-		}
-		else
-		{
-			iw.text.addTxt(MetaString::ADVOB_TXT,134);
-		}
-	}
-	cb->showInfoDialog(&iw);
-
-}
-
-//bool IShipyard::validLocation() const
-//{
-//	std::vector<int3> offsets;
-//	getOutOffsets(offsets);
-//
-//	TerrainTile *tile;
-//	for(int i = 0; i < offsets.size(); i++)
-//		if((tile = IObjectInterface::cb->getTile(o->pos + offsets[i]))  &&  tile->terType == TerrainTile::water) //tile is in the map and is water
-//			return true;
-//	return false;
-//}
-
-int3 IBoatGenerator::bestLocation() const
-{
-	std::vector<int3> offsets;
-	getOutOffsets(offsets);
-
-	for (auto & offset : offsets)
-	{
-		if (const TerrainTile *tile = IObjectInterface::cb->getTile(o->pos + offset, false)) //tile is in the map
-		{
-            if (tile->terType == ETerrainType::WATER  &&  (!tile->blocked || tile->blockingObjects.front()->ID == 8)) //and is water and is not blocked or is blocked by boat
-				return o->pos + offset;
-		}
-	}
-	return int3 (-1,-1,-1);
-}
-
-IBoatGenerator::EGeneratorState IBoatGenerator::shipyardStatus() const
-{
-	int3 tile = bestLocation();
-	const TerrainTile *t = IObjectInterface::cb->getTile(tile);
-	if(!t)
-		return TILE_BLOCKED; //no available water
-	else if(!t->blockingObjects.size())
-		return GOOD; //OK
-	else if(t->blockingObjects.front()->ID == Obj::BOAT)
-		return BOAT_ALREADY_BUILT; //blocked with boat
-	else
-		return TILE_BLOCKED; //blocked
-}
-
-int IBoatGenerator::getBoatType() const
-{
-	//We make good ships by default
-	return 1;
-}
-
-
-IBoatGenerator::IBoatGenerator(const CGObjectInstance *O)
-: o(O)
-{
-}
-
-void IBoatGenerator::getProblemText(MetaString &out, const CGHeroInstance *visitor) const
-{
-	switch(shipyardStatus())
-	{
-	case BOAT_ALREADY_BUILT:
-		out.addTxt(MetaString::GENERAL_TXT, 51);
-		break;
-	case TILE_BLOCKED:
-		if(visitor)
-		{
-			out.addTxt(MetaString::GENERAL_TXT, 134);
-			out.addReplacement(visitor->name);
-		}
-		else
-			out.addTxt(MetaString::ADVOB_TXT, 189);
-		break;
-	case NO_WATER:
-        logGlobal->errorStream() << "Shipyard without water!!! " << o->pos << "\t" << o->id;
-		return;
-	}
-}
-
-void IShipyard::getBoatCost( std::vector<si32> &cost ) const
-{
-	cost.resize(GameConstants::RESOURCE_QUANTITY);
-	cost[Res::WOOD] = 10;
-	cost[Res::GOLD] = 1000;
-}
-
-IShipyard::IShipyard(const CGObjectInstance *O)
-	: IBoatGenerator(O)
-{
-}
-
-IShipyard * IShipyard::castFrom( CGObjectInstance *obj )
-{
-	if(!obj)
-		return nullptr;
-
-	if(obj->ID == Obj::TOWN)
-	{
-		return static_cast<CGTownInstance*>(obj);
-	}
-	else if(obj->ID == Obj::SHIPYARD)
-	{
-		return static_cast<CGShipyard*>(obj);
-	}
-	else
-	{
-		return nullptr;
-	}
-}
-
-const IShipyard * IShipyard::castFrom( const CGObjectInstance *obj )
-{
-	return castFrom(const_cast<CGObjectInstance*>(obj));
-}
-
-CGShipyard::CGShipyard()
-	:IShipyard(this)
-{
-}
-
-void CGShipyard::getOutOffsets( std::vector<int3> &offsets ) const
-{
-	// H J L K I
-	// A x S x B
-	// C E G F D
-	offsets += int3(-3,0,0), int3(1,0,0), //AB
-		int3(-3,1,0), int3(1,1,0), int3(-2,1,0), int3(0,1,0), int3(-1,1,0), //CDEFG
-		int3(-3,-1,0), int3(1,-1,0), int3(-2,-1,0), int3(0,-1,0), int3(-1,-1,0); //HIJKL
-}
-
-void CGShipyard::onHeroVisit( const CGHeroInstance * h ) const
-{
-	if(!cb->gameState()->getPlayerRelations(tempOwner, h->tempOwner))
-		cb->setOwner(this, h->tempOwner);
-
-	auto s = shipyardStatus();
-	if(s != IBoatGenerator::GOOD)
-	{
-		InfoWindow iw;
-		iw.player = tempOwner;
-		getProblemText(iw.text, h);
-		cb->showInfoDialog(&iw);
-	}
-	else
-	{
-		openWindow(OpenWindow::SHIPYARD_WINDOW,id.getNum(),h->id.getNum());
-	}
-}
-
-void CCartographer::onHeroVisit( const CGHeroInstance * h ) const
-{
-	if (!wasVisited (h->getOwner()) ) //if hero has not visited yet this cartographer
-	{
-		if (cb->getResource(h->tempOwner, Res::GOLD) >= 1000) //if he can afford a map
-		{
-			//ask if he wants to buy one
-			int text=0;
-			switch (subID)
-			{
-				case 0:
-					text = 25;
-					break;
-				case 1:
-					text = 26;
-					break;
-				case 2:
-					text = 27;
-					break;
-				default:
-                    logGlobal->warnStream() << "Unrecognized subtype of cartographer";
-			}
-			assert(text);
-			BlockingDialog bd (true, false);
-			bd.player = h->getOwner();
-			bd.soundID = soundBase::LIGHTHOUSE;
-			bd.text.addTxt (MetaString::ADVOB_TXT, text);
-			cb->showBlockingDialog (&bd);
-		}
-		else //if he cannot afford
-		{
-			showInfoDialog(h,28,soundBase::CAVEHEAD);
-		}
-	}
-	else //if he already visited carographer
-	{
-		showInfoDialog(h,24,soundBase::CAVEHEAD);
-	}
-}
-
-void CCartographer::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
-{
-	if (answer) //if hero wants to buy map
-	{
-		cb->giveResource (hero->tempOwner, Res::GOLD, -1000);
-		FoWChange fw;
-		fw.mode = 1;
-		fw.player = hero->tempOwner;
-
-		//subIDs of different types of cartographers:
-		//water = 0; land = 1; underground = 2;
-		cb->getAllTiles (fw.tiles, hero->tempOwner, subID - 1, !subID + 1); //reveal appropriate tiles
-		cb->sendAndApply (&fw);
-		cb->setObjProperty (id, 10, hero->tempOwner.getNum());
-	}
-}
-
-void CGDenOfthieves::onHeroVisit (const CGHeroInstance * h) const
-{
-	cb->showThievesGuildWindow(h->tempOwner, id);
-}
-
-void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const
-{
-	InfoWindow iw;
-	iw.player = h->tempOwner;
-	TeamState *ts = cb->gameState()->getPlayerTeam(h->tempOwner);
-	assert(ts);
-	TeamID team = ts->id;
-
-	if(!wasVisited(team))
-	{
-		iw.text.addTxt(MetaString::ADVOB_TXT, 96);
-		cb->sendAndApply(&iw);
-
-		cb->setObjProperty(id, 20, h->tempOwner.getNum()); //increment general visited obelisks counter
-
-		openWindow(OpenWindow::PUZZLE_MAP, h->tempOwner.getNum());
-
-		cb->setObjProperty(id, 10, h->tempOwner.getNum()); //mark that particular obelisk as visited
-	}
-	else
-	{
-		iw.text.addTxt(MetaString::ADVOB_TXT, 97);
-		cb->sendAndApply(&iw);
-	}
-
-}
-
-void CGObelisk::initObj()
-{
-	obeliskCount++;
-}
-
-const std::string & CGObelisk::getHoverText() const
-{
-	bool visited = wasVisited(cb->getLocalPlayer());
-	hoverName = VLC->generaltexth->names[ID] + " " + visitedTxt(visited);
-	return hoverName;
-}
-
-void CGObelisk::setPropertyDer( ui8 what, ui32 val )
-{
-	CPlayersVisited::setPropertyDer(what, val);
-	switch(what)
-	{
-	case 20:
-		assert(val < PlayerColor::PLAYER_LIMIT_I);
-		visited[TeamID(val)]++;
-
-		if(visited[TeamID(val)] > obeliskCount)
-		{
-            logGlobal->errorStream() << "Error: Visited " << visited[TeamID(val)] << "\t\t" << obeliskCount;
-			assert(0);
-		}
-
-		break;
-	}
-}
-
-void CGLighthouse::onHeroVisit( const CGHeroInstance * h ) const
-{
-	if(h->tempOwner != tempOwner)
-	{
-		PlayerColor oldOwner = tempOwner;
-		cb->setOwner(this,h->tempOwner); //not ours? flag it!
-		showInfoDialog(h,69,soundBase::LIGHTHOUSE);
-		giveBonusTo(h->tempOwner);
-
-		if(oldOwner < PlayerColor::PLAYER_LIMIT) //remove bonus from old owner
-		{
-			RemoveBonus rb(RemoveBonus::PLAYER);
-			rb.whoID = oldOwner.getNum();
-			rb.source = Bonus::OBJECT;
-			rb.id = id.getNum();
-			cb->sendAndApply(&rb);
-		}
-	}
-}
-
-void CGLighthouse::initObj()
-{
-	if(tempOwner < PlayerColor::PLAYER_LIMIT)
-	{
-		giveBonusTo(tempOwner);
-	}
-}
-
-const std::string & CGLighthouse::getHoverText() const
-{
-	hoverName = VLC->generaltexth->names[ID];
-	//TODO: owned by %s player
-	return hoverName;
-}
-
-void CGLighthouse::giveBonusTo( PlayerColor player ) const
-{
-	GiveBonus gb(GiveBonus::PLAYER);
-	gb.bonus.type = Bonus::SEA_MOVEMENT;
-	gb.bonus.val = 500;
-	gb.id = player.getNum();
-	gb.bonus.duration = Bonus::PERMANENT;
-	gb.bonus.source = Bonus::OBJECT;
-	gb.bonus.sid = id.getNum();
-	cb->sendAndApply(&gb);
-}
-
-void CArmedInstance::randomizeArmy(int type)
-{
-	for (auto & elem : stacks)
-	{
-		int & randID = elem.second->idRand;
-		if(randID >= 0)
-		{
-			int level = randID / 2;
-			bool upgrade = randID % 2;
-			elem.second->setType(VLC->townh->factions[type]->town->creatures[level][upgrade]);
-
-			randID = -1;
-		}
-		assert(elem.second->valid(false));
-		assert(elem.second->armyObj == this);
-	}
-	return;
-}
-
-CArmedInstance::CArmedInstance()
-{
-	battle = nullptr;
-}
-
-//int CArmedInstance::valOfGlobalBonuses(CSelector selector) const
-//{
-////	if (tempOwner != NEUTRAL_PLAYER)
-//	return cb->gameState()->players[tempOwner].valOfBonuses(selector);
-//}
-
-void CArmedInstance::updateMoraleBonusFromArmy()
-{
-	if(!validTypes(false)) //object not randomized, don't bother
-		return;
-
-	Bonus *b = getBonusList().getFirst(Selector::sourceType(Bonus::ARMY).And(Selector::type(Bonus::MORALE)));
-	if(!b)
-	{
-		b = new Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, 0, -1);
-		addNewBonus(b);
-	}
-
-	//number of alignments and presence of undead
-	std::set<TFaction> factions;
-	bool hasUndead = false;
-
-	for(auto slot : Slots())
-	{
-		const CStackInstance * inst = slot.second;
-		const CCreature * creature  = VLC->creh->creatures[inst->getCreatureID()];
-
-		factions.insert(creature->faction);
-		// Check for undead flag instead of faction (undead mummies are neutral)
-		hasUndead |= inst->hasBonusOfType(Bonus::UNDEAD);
-	}
-
-	size_t factionsInArmy = factions.size(); //town garrison seems to take both sets into account
-
-	// Take Angelic Alliance troop-mixing freedom of non-evil units into account.
-	if (hasBonusOfType(Bonus::NONEVIL_ALIGNMENT_MIX))
-	{
-		size_t mixableFactions = 0;
-
-		for(TFaction f : factions)
-		{
-			if (VLC->townh->factions[f]->alignment != EAlignment::EVIL)
-				mixableFactions++;
-		}
-		if (mixableFactions > 0)
-			factionsInArmy -= mixableFactions - 1;
-	}
-
-	if(factionsInArmy == 1)
-	{
-		b->val = +1;
-		b->description = VLC->generaltexth->arraytxt[115]; //All troops of one alignment +1
-	}
-	else if (!factions.empty()) // no bonus from empty garrison
-	{
-	 	b->val = 2 - factionsInArmy;
-		b->description = boost::str(boost::format(VLC->generaltexth->arraytxt[114]) % factionsInArmy % b->val); //Troops of %d alignments %d
-	}
-	boost::algorithm::trim(b->description);
-
-	//-1 modifier for any Undead unit in army
-	const ui8 UNDEAD_MODIFIER_ID = -2;
-	Bonus *undeadModifier = getBonusList().getFirst(Selector::source(Bonus::ARMY, UNDEAD_MODIFIER_ID));
- 	if(hasUndead)
-	{
-		if(!undeadModifier)
-			addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, -1, UNDEAD_MODIFIER_ID, VLC->generaltexth->arraytxt[116]));
-	}
-	else if(undeadModifier)
-		removeBonus(undeadModifier);
-
-}
-
-void CArmedInstance::armyChanged()
-{
-	updateMoraleBonusFromArmy();
-}
-
-CBonusSystemNode * CArmedInstance::whereShouldBeAttached(CGameState *gs)
-{
-	if(tempOwner < PlayerColor::PLAYER_LIMIT)
-		return gs->getPlayer(tempOwner);
-	else
-		return &gs->globalEffects;
-}
-
-CBonusSystemNode * CArmedInstance::whatShouldBeAttached()
-{
-	return this;
-}
-
-bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode::EMarketMode mode) const
-{
-	switch(mode)
-	{
-	case EMarketMode::RESOURCE_RESOURCE:
-		{
-			double effectiveness = std::min((getMarketEfficiency() + 1.0) / 20.0, 0.5);
-
-			double r = VLC->objh->resVals[id1], //value of given resource
-				g = VLC->objh->resVals[id2] / effectiveness; //value of wanted resource
-
-			if(r>g) //if given resource is more expensive than wanted
-			{
-				val2 = ceil(r / g);
-				val1 = 1;
-			}
-			else //if wanted resource is more expensive
-			{
-				val1 = (g / r) + 0.5;
-				val2 = 1;
-			}
-		}
-		break;
-	case EMarketMode::CREATURE_RESOURCE:
-		{
-			const double effectivenessArray[] = {0.0, 0.3, 0.45, 0.50, 0.65, 0.7, 0.85, 0.9, 1.0};
-			double effectiveness = effectivenessArray[std::min(getMarketEfficiency(), 8)];
-
-			double r = VLC->creh->creatures[id1]->cost[6], //value of given creature in gold
-				g = VLC->objh->resVals[id2] / effectiveness; //value of wanted resource
-
-			if(r>g) //if given resource is more expensive than wanted
-			{
-				val2 = ceil(r / g);
-				val1 = 1;
-			}
-			else //if wanted resource is more expensive
-			{
-				val1 = (g / r) + 0.5;
-				val2 = 1;
-			}
-		}
-		break;
-	case EMarketMode::RESOURCE_PLAYER:
-		val1 = 1;
-		val2 = 1;
-		break;
-	case EMarketMode::RESOURCE_ARTIFACT:
-		{
-			double effectiveness = std::min((getMarketEfficiency() + 3.0) / 20.0, 0.6);
-			double r = VLC->objh->resVals[id1], //value of offered resource
-				g = VLC->arth->artifacts[id2]->price / effectiveness; //value of bought artifact in gold
-
-			if(id1 != 6) //non-gold prices are doubled
-				r /= 2;
-
-			val1 = std::max(1, (int)((g / r) + 0.5)); //don't sell arts for less than 1 resource
-			val2 = 1;
-		}
-		break;
-	case EMarketMode::ARTIFACT_RESOURCE:
-		{
-			double effectiveness = std::min((getMarketEfficiency() + 3.0) / 20.0, 0.6);
-			double r = VLC->arth->artifacts[id1]->price * effectiveness,
-				g = VLC->objh->resVals[id2];
-
-// 			if(id2 != 6) //non-gold prices are doubled
-// 				r /= 2;
-
-			val1 = 1;
-			val2 = std::max(1, (int)((r / g) + 0.5)); //at least one resource is given in return
-		}
-		break;
-	case EMarketMode::CREATURE_EXP:
-		{
-			val1 = 1;
-			val2 = (VLC->creh->creatures[id1]->AIValue / 40) * 5;
-		}
-		break;
-	case EMarketMode::ARTIFACT_EXP:
-		{
-			val1 = 1;
-
-			int givenClass = VLC->arth->artifacts[id1]->getArtClassSerial();
-			if(givenClass < 0 || givenClass > 3)
-			{
-				val2 = 0;
-				return false;
-			}
-
-			static const int expPerClass[] = {1000, 1500, 3000, 6000};
-			val2 = expPerClass[givenClass];
-		}
-		break;
-	default:
-		assert(0);
-		return false;
-	}
-
-	return true;
-}
-
-bool IMarket::allowsTrade(EMarketMode::EMarketMode mode) const
-{
-	return false;
-}
-
-int IMarket::availableUnits(EMarketMode::EMarketMode mode, int marketItemSerial) const
-{
-	switch(mode)
-	{
-	case EMarketMode::RESOURCE_RESOURCE:
-	case EMarketMode::ARTIFACT_RESOURCE:
-	case EMarketMode::CREATURE_RESOURCE:
-			return -1;
-	default:
-			return 1;
-	}
-}
-
-std::vector<int> IMarket::availableItemsIds(EMarketMode::EMarketMode mode) const
-{
-	std::vector<int> ret;
-	switch(mode)
-	{
-	case EMarketMode::RESOURCE_RESOURCE:
-	case EMarketMode::ARTIFACT_RESOURCE:
-	case EMarketMode::CREATURE_RESOURCE:
-		for (int i = 0; i < 7; i++)
-			ret.push_back(i);
-	}
-	return ret;
-}
-
-const IMarket * IMarket::castFrom(const CGObjectInstance *obj, bool verbose /*= true*/)
-{
-	switch(obj->ID)
-	{
-	case Obj::TOWN:
-		return static_cast<const CGTownInstance*>(obj);
-	case Obj::ALTAR_OF_SACRIFICE:
-	case Obj::BLACK_MARKET:
-	case Obj::TRADING_POST:
-	case Obj::TRADING_POST_SNOW:
-	case Obj::FREELANCERS_GUILD:
-		return static_cast<const CGMarket*>(obj);
-	case Obj::UNIVERSITY:
-		return static_cast<const CGUniversity*>(obj);
-	default:
-		if(verbose)
-            logGlobal->errorStream() << "Cannot cast to IMarket object with ID " << obj->ID;
-		return nullptr;
-	}
-}
-
-IMarket::IMarket(const CGObjectInstance *O)
-	:o(O)
-{
-
-}
-
-std::vector<EMarketMode::EMarketMode> IMarket::availableModes() const
-{
-	std::vector<EMarketMode::EMarketMode> ret;
-	for (int i = 0; i < EMarketMode::MARTKET_AFTER_LAST_PLACEHOLDER; i++)
-		if(allowsTrade((EMarketMode::EMarketMode)i))
-			ret.push_back((EMarketMode::EMarketMode)i);
-
-	return ret;
-}
-
-void CGMarket::onHeroVisit(const CGHeroInstance * h) const
-{
-	openWindow(OpenWindow::MARKET_WINDOW,id.getNum(),h->id.getNum());
-}
-
-int CGMarket::getMarketEfficiency() const
-{
-	return 5;
-}
-
-bool CGMarket::allowsTrade(EMarketMode::EMarketMode mode) const
-{
-	switch(mode)
-	{
-	case EMarketMode::RESOURCE_RESOURCE:
-	case EMarketMode::RESOURCE_PLAYER:
-		switch(ID)
-		{
-		case Obj::TRADING_POST:
-		case Obj::TRADING_POST_SNOW:
-			return true;
-		default:
-			return false;
-		}
-	case EMarketMode::CREATURE_RESOURCE:
-		return ID == Obj::FREELANCERS_GUILD;
-	//case ARTIFACT_RESOURCE:
-	case EMarketMode::RESOURCE_ARTIFACT:
-		return ID == Obj::BLACK_MARKET;
-	case EMarketMode::ARTIFACT_EXP:
-	case EMarketMode::CREATURE_EXP:
-		return ID == Obj::ALTAR_OF_SACRIFICE; //TODO? check here for alignment of visiting hero? - would not be coherent with other checks here
-	case EMarketMode::RESOURCE_SKILL:
-		return ID == Obj::UNIVERSITY;
-	default:
-		return false;
-	}
-}
-
-int CGMarket::availableUnits(EMarketMode::EMarketMode mode, int marketItemSerial) const
-{
-	return -1;
-}
-
-std::vector<int> CGMarket::availableItemsIds(EMarketMode::EMarketMode mode) const
-{
-	switch(mode)
-	{
-	case EMarketMode::RESOURCE_RESOURCE:
-	case EMarketMode::RESOURCE_PLAYER:
-		return IMarket::availableItemsIds(mode);
-	default:
-		return std::vector<int>();
-	}
-}
-
-CGMarket::CGMarket()
-	:IMarket(this)
-{
-}
-
-std::vector<int> CGBlackMarket::availableItemsIds(EMarketMode::EMarketMode mode) const
-{
-	switch(mode)
-	{
-	case EMarketMode::ARTIFACT_RESOURCE:
-		return IMarket::availableItemsIds(mode);
-	case EMarketMode::RESOURCE_ARTIFACT:
-		{
-			std::vector<int> ret;
-			for(const CArtifact *a : artifacts)
-				if(a)
-					ret.push_back(a->id);
-				else
-					ret.push_back(-1);
-			return ret;
-		}
-	default:
-		return std::vector<int>();
-	}
-}
-
-void CGBlackMarket::newTurn() const
-{
-	if(cb->getDate(Date::DAY_OF_MONTH) != 1) //new month
-		return;
-
-	SetAvailableArtifacts saa;
-	saa.id = id.getNum();
-	cb->pickAllowedArtsSet(saa.arts);
-	cb->sendAndApply(&saa);
-}
-
-void CGUniversity::initObj()
-{
-	std::vector<int> toChoose;
-	for(int i = 0; i < GameConstants::SKILL_QUANTITY; ++i)
-	{
-		if(cb->isAllowed(2, i))
-		{
-			toChoose.push_back(i);
-		}
-	}
-	if(toChoose.size() < 4)
-	{
-        logGlobal->warnStream()<<"Warning: less then 4 available skills was found by University initializer!";
-		return;
-	}
-
-	// get 4 skills
-	for(int i = 0; i < 4; ++i)
-	{
-		// move randomly one skill to selected and remove from list
-		auto it = RandomGeneratorUtil::nextItem(toChoose, cb->gameState()->getRandomGenerator());
-		skills.push_back(*it);
-		toChoose.erase(it);
-	}
-}
-
-std::vector<int> CGUniversity::availableItemsIds(EMarketMode::EMarketMode mode) const
-{
-	switch (mode)
-	{
-		case EMarketMode::RESOURCE_SKILL:
-			return skills;
-
-		default:
-			return std::vector <int> ();
-	}
-}
-
-void CGUniversity::onHeroVisit(const CGHeroInstance * h) const
-{
-	openWindow(OpenWindow::UNIVERSITY_WINDOW,id.getNum(),h->id.getNum());
-}
-
-GrowthInfo::Entry::Entry(const std::string &format, int _count)
-	: count(_count)
-{
-	description = boost::str(boost::format(format) % count);
-}
-
-GrowthInfo::Entry::Entry(int subID, BuildingID building, int _count)
-	: count(_count)
-{
-	description = boost::str(boost::format("%s %+d") % VLC->townh->factions[subID]->town->buildings.at(building)->Name() % count);
-}
-
-CTownAndVisitingHero::CTownAndVisitingHero()
-{
-	setNodeType(TOWN_AND_VISITOR);
-}
-
-int GrowthInfo::totalGrowth() const
-{
-	int ret = 0;
-	for(const Entry &entry : entries)
-		ret += entry.count;
-
-	return ret;
-}

+ 0 - 1533
lib/CObjectHandler.h

@@ -1,1533 +0,0 @@
-#pragma once
-
-#include "../lib/CCreatureSet.h"
-#include "../lib/CTownHandler.h"
-#include "../lib/CDefObjInfoHandler.h"
-#include "CArtHandler.h"
-#include "../lib/ConstTransitivePtr.h"
-#include "int3.h"
-#include "GameConstants.h"
-#include "ResourceSet.h"
-#include "CRandomGenerator.h"
-
-/*
- * CObjectHandler.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
- *
- */
-
-class CGameState;
-class CArtifactInstance;
-struct MetaString;
-struct BattleInfo;
-struct QuestInfo;
-class IGameCallback;
-struct BattleResult;
-class CGObjectInstance;
-class CScript;
-class CObjectScript;
-class CGHeroInstance;
-class CTown;
-class CHero;
-class CBuilding;
-class CSpell;
-class CGTownInstance;
-class CGTownBuilding;
-class CArtifact;
-class CSpecObjInfo;
-class CCastleEvent;
-struct TerrainTile;
-struct InfoWindow;
-struct Component;
-struct BankConfig;
-struct UpdateHerospecialty;
-struct NewArtifact;
-class CGBoat;
-class CArtifactSet;
-class CCommanderInstance;
-
-class DLL_LINKAGE CQuest
-{
-public:
-	enum Emission {MISSION_NONE = 0, MISSION_LEVEL = 1, MISSION_PRIMARY_STAT = 2, MISSION_KILL_HERO = 3, MISSION_KILL_CREATURE = 4,
-		MISSION_ART = 5, MISSION_ARMY = 6, MISSION_RESOURCES = 7, MISSION_HERO = 8, MISSION_PLAYER = 9, MISSION_KEYMASTER = 10};
-	enum Eprogress {NOT_ACTIVE, IN_PROGRESS, COMPLETE};
-
-	si32 qid; //unique quest id for serialization / identification
-
-	Emission missionType;
-	Eprogress progress;
-	si32 lastDay; //after this day (first day is 0) mission cannot be completed; if -1 - no limit
-
-	ui32 m13489val;
-	std::vector<ui32> m2stats;
-	std::vector<ui16> m5arts; //artifacts id
-	std::vector<CStackBasicDescriptor> m6creatures; //pair[cre id, cre count], CreatureSet info irrelevant
-	std::vector<ui32> m7resources; //TODO: use resourceset?
-
-	//following field are used only for kill creature/hero missions, the original objects became inaccessible after their removal, so we need to store info needed for messages / hover text
-	ui8 textOption;
-	CStackBasicDescriptor stackToKill;
-	ui8 stackDirection;
-	std::string heroName; //backup of hero name
-	si32 heroPortrait;
-
-	std::string firstVisitText, nextVisitText, completedText;
-	bool isCustomFirst, isCustomNext, isCustomComplete;
-
-	CQuest(){missionType = MISSION_NONE;}; //default constructor
-	virtual ~CQuest(){};
-
-	virtual bool checkQuest (const CGHeroInstance * h) const; //determines whether the quest is complete or not
-	virtual void getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h = nullptr) const;
-	virtual void getCompletionText (MetaString &text, std::vector<Component> &components, bool isCustom, const CGHeroInstance * h = nullptr) const;
-	virtual void getRolloverText (MetaString &text, bool onHover) const; //hover or quest log entry
-	virtual void completeQuest (const CGHeroInstance * h) const {};
-	virtual void addReplacements(MetaString &out, const std::string &base) const;
-
-	bool operator== (const CQuest & quest) const
-	{
-		return (quest.qid == qid);
-	}
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & qid & missionType & progress & lastDay & m13489val & m2stats & m5arts & m6creatures & m7resources
-			& textOption & stackToKill & stackDirection & heroName & heroPortrait
-			& firstVisitText & nextVisitText & completedText & isCustomFirst & isCustomNext & isCustomComplete;
-	}
-};
-
-class DLL_LINKAGE IObjectInterface
-{
-public:
-	static IGameCallback *cb;
-
-	IObjectInterface();
-	virtual ~IObjectInterface();
-
-	virtual void onHeroVisit(const CGHeroInstance * h) const;
-	virtual void onHeroLeave(const CGHeroInstance * h) const;
-	virtual void newTurn() const;
-	virtual void initObj(); //synchr
-	virtual void setProperty(ui8 what, ui32 val);//synchr
-	
-	//Called when queries created DURING HERO VISIT are resolved
-	//First parameter is always hero that visited object and triggered the query
-	virtual void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const;
-	virtual void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const;
-	virtual void garrisonDialogClosed(const CGHeroInstance *hero) const;
-	virtual void heroLevelUpDone(const CGHeroInstance *hero) const;
-
-//unified interface, AI helpers
-	virtual bool wasVisited (PlayerColor player) const;
-	virtual bool wasVisited (const CGHeroInstance * h) const;
-
-	static void preInit(); //called before objs receive their initObj
-	static void postInit();//called after objs receive their initObj
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		logGlobal->errorStream() << "IObjectInterface serialized, unexpected, should not happen!";
-	}
-};
-
-class DLL_LINKAGE IBoatGenerator
-{
-public:
-	const CGObjectInstance *o;
-
-	IBoatGenerator(const CGObjectInstance *O);
-	virtual ~IBoatGenerator() {}
-
-	virtual int getBoatType() const; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral
-	virtual void getOutOffsets(std::vector<int3> &offsets) const =0; //offsets to obj pos when we boat can be placed
-	int3 bestLocation() const; //returns location when the boat should be placed
-
-	enum EGeneratorState {GOOD, BOAT_ALREADY_BUILT, TILE_BLOCKED, NO_WATER};
-	EGeneratorState shipyardStatus() const; //0 - can buid, 1 - there is already a boat at dest tile, 2 - dest tile is blocked, 3 - no water
-	void getProblemText(MetaString &out, const CGHeroInstance *visitor = nullptr) const;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & o;
-	}
-};
-
-class DLL_LINKAGE IShipyard : public IBoatGenerator
-{
-public:
-	IShipyard(const CGObjectInstance *O);
-	virtual ~IShipyard() {}
-
-	virtual void getBoatCost(std::vector<si32> &cost) const;
-
-	static const IShipyard *castFrom(const CGObjectInstance *obj);
-	static IShipyard *castFrom(CGObjectInstance *obj);
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<IBoatGenerator&>(*this);
-	}
-};
-
-class DLL_LINKAGE IMarket
-{
-public:
-	const CGObjectInstance *o;
-
-	IMarket(const CGObjectInstance *O);
-	virtual ~IMarket() {}
-
-	virtual int getMarketEfficiency() const =0;
-	virtual bool allowsTrade(EMarketMode::EMarketMode mode) const;
-	virtual int availableUnits(EMarketMode::EMarketMode mode, int marketItemSerial) const; //-1 if unlimited
-	virtual std::vector<int> availableItemsIds(EMarketMode::EMarketMode mode) const;
-
-	bool getOffer(int id1, int id2, int &val1, int &val2, EMarketMode::EMarketMode mode) const; //val1 - how many units of id1 player has to give to receive val2 units
-	std::vector<EMarketMode::EMarketMode> availableModes() const;
-
-	static const IMarket *castFrom(const CGObjectInstance *obj, bool verbose = true);
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & o;
-	}
-};
-
-class DLL_LINKAGE CGObjectInstance : public IObjectInterface
-{
-public:
-	mutable std::string hoverName;
-	int3 pos; //h3m pos
-	Obj ID;
-	si32 subID; //normal subID (this one from OH3 maps ;])
-	ObjectInstanceID id;//number of object in map's vector
-	ObjectTemplate appearance;
-
-	PlayerColor tempOwner;
-	bool blockVisit; //if non-zero then blocks the tile but is visitable from neighbouring tile
-
-	virtual ui8 getPassableness() const; //bitmap - if the bit is set the corresponding player can pass through the visitable tiles of object, even if it's blockvis; if not set - default properties from definfo are used
-	virtual int3 getSightCenter() const; //"center" tile from which the sight distance is calculated
-	virtual int getSightRadious() const; //sight distance (should be used if player-owned structure)
-	bool passableFor(PlayerColor color) const;
-	void getSightTiles(std::unordered_set<int3, ShashInt3> &tiles) const; //returns reference to the set
-	PlayerColor getOwner() const;
-	void setOwner(PlayerColor ow);
-	int getWidth() const; //returns width of object graphic in tiles
-	int getHeight() const; //returns height of object graphic in tiles
-	virtual bool visitableAt(int x, int y) const; //returns true if object is visitable at location (x, y) (h3m pos)
-	virtual int3 getVisitableOffset() const; //returns (x,y,0) offset to first visitable tile from bottom right obj tile (0,0,0) (h3m pos)
-	int3 visitablePos() const;
-	bool blockingAt(int x, int y) const; //returns true if object is blocking location (x, y) (h3m pos)
-	bool coveringAt(int x, int y) const; //returns true if object covers with picture location (x, y) (h3m pos)
-	std::set<int3> getBlockedPos() const; //returns set of positions blocked by this object
-	std::set<int3> getBlockedOffsets() const; //returns set of relative positions blocked by this object
-	bool isVisitable() const; //returns true if object is visitable
-	bool operator<(const CGObjectInstance & cmp) const;  //screen printing priority comparing
-	void hideTiles(PlayerColor ourplayer, int radius) const;
-	CGObjectInstance();
-	virtual ~CGObjectInstance();
-	//CGObjectInstance(const CGObjectInstance & right);
-	//CGObjectInstance& operator=(const CGObjectInstance & right);
-	virtual const std::string & getHoverText() const;
-
-	///IObjectInterface
-	void initObj() override;
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void setProperty(ui8 what, ui32 val) override;//synchr
-
-	friend class CGameHandler;
-
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & hoverName & pos & ID & subID & id & tempOwner & blockVisit & appearance;
-		//definfo is handled by map serializer
-	}
-protected:
-	virtual void setPropertyDer(ui8 what, ui32 val);//synchr
-
-	void getNameVis(std::string &hname) const;
-	void giveDummyBonus(ObjectInstanceID heroID, ui8 duration = Bonus::ONE_DAY) const;
-};
-
-/// function object which can be used to find an object with an specific sub ID
-class CGObjectInstanceBySubIdFinder
-{
-public:
-	CGObjectInstanceBySubIdFinder(CGObjectInstance * obj);
-	bool operator()(CGObjectInstance * obj) const;
-
-private:
-	CGObjectInstance * obj;
-};
-
-class CGHeroPlaceholder : public CGObjectInstance
-{
-public:
-	//subID stores id of hero type. If it's 0xff then following field is used
-	ui8 power;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-		h & power;
-	}
-};
-
-class DLL_LINKAGE CPlayersVisited: public CGObjectInstance
-{
-public:
-	std::set<PlayerColor> players; //players that visited this object
-
-	bool wasVisited(PlayerColor player) const;
-	bool wasVisited(TeamID team) const;
-	void setPropertyDer(ui8 what, ui32 val) override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-		h & players;
-	}
-};
-
-class DLL_LINKAGE CArmedInstance: public CGObjectInstance, public CBonusSystemNode, public CCreatureSet
-{
-public:
-	BattleInfo *battle; //set to the current battle, if engaged
-
-	void randomizeArmy(int type);
-	virtual void updateMoraleBonusFromArmy();
-
-	void armyChanged() override;
-
-	//////////////////////////////////////////////////////////////////////////
-//	int valOfGlobalBonuses(CSelector selector) const; //used only for castle interface								???
-	virtual CBonusSystemNode *whereShouldBeAttached(CGameState *gs);
-	virtual CBonusSystemNode *whatShouldBeAttached();
-	//////////////////////////////////////////////////////////////////////////
-
-	CArmedInstance();
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-		h & static_cast<CBonusSystemNode&>(*this);
-		h & static_cast<CCreatureSet&>(*this);
-	}
-};
-
-class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet
-{
-public:
-	enum ECanDig
-	{
-		CAN_DIG, LACK_OF_MOVEMENT, WRONG_TERRAIN, TILE_OCCUPIED
-	};
-	//////////////////////////////////////////////////////////////////////////
-
-	ui8 moveDir; //format:	123
-					//		8 4
-					//		765
-	mutable ui8 isStanding, tacticFormationEnabled;
-
-	//////////////////////////////////////////////////////////////////////////
-
-	ConstTransitivePtr<CHero> type;
-	TExpType exp; //experience points
-	ui32 level; //current level of hero
-	std::string name; //may be custom
-	std::string biography; //if custom
-	si32 portrait; //may be custom
-	si32 mana; // remaining spell points
-	std::vector<std::pair<SecondarySkill,ui8> > secSkills; //first - ID of skill, second - level of skill (1 - basic, 2 - adv., 3 - expert); if hero has ability (-1, -1) it meansthat it should have default secondary abilities
-	ui32 movement; //remaining movement points
-	ui8 sex;
-	bool inTownGarrison; // if hero is in town garrison
-	ConstTransitivePtr<CGTownInstance> visitedTown; //set if hero is visiting town or in the town garrison
-	ConstTransitivePtr<CCommanderInstance> commander;
-	const CGBoat *boat; //set to CGBoat when sailing
-
-
-	//std::vector<const CArtifact*> artifacts; //hero's artifacts from bag
-	//std::map<ui16, const CArtifact*> artifWorn; //map<position,artifact_id>; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5
-	std::set<SpellID> spells; //known spells (spell IDs)
-
-
-	struct DLL_LINKAGE Patrol
-	{
-		Patrol(){patrolling=false;patrolRadious=-1;};
-		bool patrolling;
-		ui32 patrolRadious;
-		template <typename Handler> void serialize(Handler &h, const int version)
-		{
-			h & patrolling & patrolRadious;
-		}
-	} patrol;
-
-	struct DLL_LINKAGE HeroSpecial : CBonusSystemNode
-	{
-		bool growsWithLevel;
-
-		HeroSpecial(){growsWithLevel = false;};
-
-		template <typename Handler> void serialize(Handler &h, const int version)
-		{
-			h & static_cast<CBonusSystemNode&>(*this);
-			h & growsWithLevel;
-		}
-	};
-
-	std::vector<HeroSpecial*> specialty;
-
-	struct DLL_LINKAGE SecondarySkillsInfo
-	{
-		//skills are determined, initialized at map start
-		//FIXME remove mutable
-		mutable CRandomGenerator rand;
-		ui8 magicSchoolCounter;
-		ui8 wisdomCounter;
-
-		void resetMagicSchoolCounter();
-		void resetWisdomCounter();
-
-		template <typename Handler> void serialize(Handler &h, const int version)
-		{
-			h & magicSchoolCounter & wisdomCounter & rand;
-		}
-	} skillsInfo;
-
-	int3 getSightCenter() const; //"center" tile from which the sight distance is calculated
-	int getSightRadious() const; //sight distance (should be used if player-owned structure)
-	//////////////////////////////////////////////////////////////////////////
-
-	int getBoatType() const; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral
-	void getOutOffsets(std::vector<int3> &offsets) const; //offsets to obj pos when we boat can be placed
-
-	//////////////////////////////////////////////////////////////////////////
-
-	bool hasSpellbook() const;
-	EAlignment::EAlignment getAlignment() const;
-	const std::string &getBiography() const;
-	bool needsLastStack()const;
-	ui32 getTileCost(const TerrainTile &dest, const TerrainTile &from) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling
-	ui32 getLowestCreatureSpeed() const;
-	int3 getPosition(bool h3m = false) const; //h3m=true - returns position of hero object; h3m=false - returns position of hero 'manifestation'
-	si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day
-	bool canWalkOnSea() const;
-	int getCurrentLuck(int stack=-1, bool town=false) const;
-	int getSpellCost(const CSpell *sp) const; //do not use during battles -> bonuses from army would be ignored
-
-	// ----- primary and secondary skill, experience, level handling -----
-
-	/// Returns true if hero has lower level than should upon his experience.
-	bool gainsLevel() const;
-
-	/// Returns the next primary skill on level up. Can only be called if hero can gain a level up.
-	PrimarySkill::PrimarySkill nextPrimarySkill() const;
-
-	/// Returns the next secondary skill randomly on level up. Can only be called if hero can gain a level up.
-	boost::optional<SecondarySkill> nextSecondarySkill() const;
-
-	/// Gets 0, 1 or 2 secondary skills which are proposed on hero level up.
-	std::vector<SecondarySkill> getLevelUpProposedSecondarySkills() const;
-
-	ui8 getSecSkillLevel(SecondarySkill skill) const; //0 - no skill
-
-	/// Returns true if hero has free secondary skill slot.
-	bool canLearnSkill() const;
-
-	void setPrimarySkill(PrimarySkill::PrimarySkill primarySkill, si64 value, ui8 abs);
-	void setSecSkillLevel(SecondarySkill which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value
-	void levelUp(std::vector<SecondarySkill> skills);
-
-	int maxMovePoints(bool onLand) const;
-	int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false) const;
-
-	//int getSpellSecLevel(int spell) const; //returns level of secondary ability (fire, water, earth, air magic) known to this hero and applicable to given spell; -1 if error
-	static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
-	double getFightingStrength() const; // takes attack / defense skill into account
-	double getMagicStrength() const; // takes knowledge / spell power skill into account
-	double getHeroStrength() const; // includes fighting and magic strength
-	ui64 getTotalStrength() const; // includes fighting strength and army strength
-	TExpType calculateXp(TExpType exp) const; //apply learning skill
-	ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const; //returns level on which given spell would be cast by this hero (0 - none, 1 - basic etc); optionally returns number of selected school by arg - 0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic,
-	bool canCastThisSpell(const CSpell * spell) const; //determines if this hero can cast given spell; takes into account existing spell in spellbook, existing spellbook and artifact bonuses
-	CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const;
-	void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const;
-	ECanDig diggingStatus() const; //0 - can dig; 1 - lack of movement; 2 -
-
-	//////////////////////////////////////////////////////////////////////////
-
-	void initHero();
-	void initHero(HeroTypeID SUBID);
-
-	void putArtifact(ArtifactPosition pos, CArtifactInstance *art);
-	void putInBackpack(CArtifactInstance *art);
-	void initExp();
-	void initArmy(IArmyDescriptor *dst = nullptr);
-	//void giveArtifact (ui32 aid);
-	void pushPrimSkill(PrimarySkill::PrimarySkill which, int val);
-	ui8 maxlevelsToMagicSchool() const;
-	ui8 maxlevelsToWisdom() const;
-	void Updatespecialty();
-	void recreateSecondarySkillsBonuses();
-	void updateSkill(SecondarySkill which, int val);
-
-	CGHeroInstance();
-	virtual ~CGHeroInstance();
-	//////////////////////////////////////////////////////////////////////////
-	//
-	ArtBearer::ArtBearer bearerType() const override;
-	//////////////////////////////////////////////////////////////////////////
-
-	CBonusSystemNode *whereShouldBeAttached(CGameState *gs) override;
-	std::string nodeName() const override;
-	void deserializationFix();
-
-	void initObj() override;
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	const std::string & getHoverText() const override;
-protected:
-	void setPropertyDer(ui8 what, ui32 val) override;//synchr
-
-private:
-	void levelUpAutomatically();
-
-public:
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CArmedInstance&>(*this);
-		h & static_cast<CArtifactSet&>(*this);
-		h & exp & level & name & biography & portrait & mana & secSkills & movement
-			& sex & inTownGarrison & spells & patrol & moveDir & skillsInfo;
-		h & visitedTown & boat;
-		h & type & specialty & commander;
-		BONUS_TREE_DESERIALIZATION_FIX
-		//visitied town pointer will be restored by map serialization method
-	}
-};
-
-class DLL_LINKAGE CSpecObjInfo
-{
-public:
-	virtual ~CSpecObjInfo(){};
-	PlayerColor player; //owner
-};
-
-class DLL_LINKAGE CCreGenAsCastleInfo : public virtual CSpecObjInfo
-{
-public:
-	bool asCastle;
-	ui32 identifier;
-	ui8 castles[2]; //allowed castles
-};
-
-class DLL_LINKAGE CCreGenLeveledInfo : public virtual CSpecObjInfo
-{
-public:
-	ui8 minLevel, maxLevel; //minimal and maximal level of creature in dwelling: <0, 6>
-};
-
-class DLL_LINKAGE CCreGenLeveledCastleInfo : public CCreGenAsCastleInfo, public CCreGenLeveledInfo
-{
-};
-
-class DLL_LINKAGE CGDwelling : public CArmedInstance
-{
-public:
-	typedef std::vector<std::pair<ui32, std::vector<CreatureID> > > TCreaturesSet;
-
-	CSpecObjInfo * info; //h3m info about dewlling
-	TCreaturesSet creatures; //creatures[level] -> <vector of alternative ids (base creature and upgrades, creatures amount>
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CArmedInstance&>(*this) & creatures;
-	}
-
-	void initObj() override;
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void newTurn() const override;
-	void setProperty(ui8 what, ui32 val) override;
-	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
-	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
-
-private:
-	void heroAcceptsCreatures(const CGHeroInstance *h) const;
-};
-
-
-class DLL_LINKAGE CGVisitableOPH : public CGObjectInstance //objects visitable only once per hero
-{
-public:
-	std::set<ObjectInstanceID> visitors; //ids of heroes who have visited this obj
-	TResources treePrice; //used only by trees of knowledge: empty, 2000 gold, 10 gems
-
-	const std::string & getHoverText() const override;
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void initObj() override;
-	bool wasVisited (const CGHeroInstance * h) const override;
-	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
-
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-		h & visitors & treePrice;
-	}
-protected:
-	void setPropertyDer(ui8 what, ui32 val) override;//synchr
-private:
-	void onNAHeroVisit(const CGHeroInstance * h, bool alreadyVisited) const;
-	///dialog callbacks
-	void treeSelected(const CGHeroInstance * h, ui32 result) const;
-	void schoolSelected(const CGHeroInstance * h, ui32 which) const;
-	void arenaSelected(const CGHeroInstance * h, int primSkill) const;
-};
-class DLL_LINKAGE CGTownBuilding : public IObjectInterface
-{
-///basic class for town structures handled as map objects
-public:
-	BuildingID ID; //from buildig list
-	si32 id; //identifies its index on towns vector
-	CGTownInstance *town;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & ID & id;
-	}
-};
-class DLL_LINKAGE COPWBonus : public CGTownBuilding
-{///used for OPW bonusing structures
-public:
-	std::set<si32> visitors;
-	void setProperty(ui8 what, ui32 val) override;
-	void onHeroVisit (const CGHeroInstance * h) const override;
-
-	COPWBonus (BuildingID index, CGTownInstance *TOWN);
-	COPWBonus (){ID = BuildingID::NONE; town = nullptr;};
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGTownBuilding&>(*this);
-		h & visitors;
-	}
-};
-
-class DLL_LINKAGE CTownBonus : public CGTownBuilding
-{
-///used for one-time bonusing structures
-///feel free to merge inheritance tree
-public:
-	std::set<ObjectInstanceID> visitors;
-	void setProperty(ui8 what, ui32 val) override;
-	void onHeroVisit (const CGHeroInstance * h) const override;
-
-	CTownBonus (BuildingID index, CGTownInstance *TOWN);
-	CTownBonus (){ID = BuildingID::NONE; town = nullptr;};
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGTownBuilding&>(*this);
-		h & visitors;
-	}
-};
-
-class DLL_LINKAGE CTownAndVisitingHero : public CBonusSystemNode
-{
-public:
-	CTownAndVisitingHero();
-};
-
-struct DLL_LINKAGE GrowthInfo
-{
-	struct Entry
-	{
-		int count;
-		std::string description;
-		Entry(const std::string &format, int _count);
-		Entry(int subID, BuildingID building, int _count);
-	};
-
-	std::vector<Entry> entries;
-	int totalGrowth() const;
-};
-
-class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public IMarket
-{
-public:
-	enum EFortLevel {NONE = 0, FORT = 1, CITADEL = 2, CASTLE = 3};
-
-	CTownAndVisitingHero townAndVis;
-	const CTown * town;
-	std::string name; // name of town
-	si32 builded; //how many buildings has been built this turn
-	si32 destroyed; //how many buildings has been destroyed this turn
-	ConstTransitivePtr<CGHeroInstance> garrisonHero, visitingHero;
-	ui32 identifier; //special identifier from h3m (only > RoE maps)
-	si32 alignment;
-	std::set<BuildingID> forbiddenBuildings, builtBuildings;
-	std::vector<CGTownBuilding*> bonusingBuildings;
-	std::vector<SpellID> possibleSpells, obligatorySpells;
-	std::vector<std::vector<SpellID> > spells; //spells[level] -> vector of spells, first will be available in guild
-	std::list<CCastleEvent> events;
-	std::pair<si32, si32> bonusValue;//var to store town bonuses (rampart = resources from mystic pond);
-
-	//////////////////////////////////////////////////////////////////////////
-	static std::vector<const CArtifact *> merchantArtifacts; //vector of artifacts available at Artifact merchant, NULLs possible (for making empty space when artifact is bought)
-	static std::vector<int> universitySkills;//skills for university of magic
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGDwelling&>(*this);
-		h & static_cast<IShipyard&>(*this);
-		h & static_cast<IMarket&>(*this);
-		h & name & builded & destroyed & identifier;
-		h & garrisonHero & visitingHero;
-		h & alignment & forbiddenBuildings & builtBuildings & bonusValue
-			& possibleSpells & obligatorySpells & spells & /*strInfo & */events & bonusingBuildings;
-
-		for (std::vector<CGTownBuilding*>::iterator i = bonusingBuildings.begin(); i!=bonusingBuildings.end(); i++)
-			(*i)->town = this;
-
-		h & town & townAndVis;
-		BONUS_TREE_DESERIALIZATION_FIX
-
-		vstd::erase_if(builtBuildings, [this](BuildingID building) -> bool
-		{
-			if(!town->buildings.count(building) ||  !town->buildings.at(building))
-			{
-				logGlobal->errorStream() << boost::format("#1444-like issue in CGTownInstance::serialize. From town %s at %s removing the bogus builtBuildings item %s")
-					% name % pos % building;
-				return true;
-			}
-			return false;
-		});
-	}
-	//////////////////////////////////////////////////////////////////////////
-
-	CBonusSystemNode *whatShouldBeAttached() override;
-	std::string nodeName() const override;
-	void updateMoraleBonusFromArmy() override;
-	void deserializationFix();
-	void recreateBuildingsBonuses();
-	bool addBonusIfBuilt(BuildingID building, Bonus::BonusType type, int val, TPropagatorPtr &prop, int subtype = -1); //returns true if building is built and bonus has been added
-	bool addBonusIfBuilt(BuildingID building, Bonus::BonusType type, int val, int subtype = -1); //convienence version of above
-	void setVisitingHero(CGHeroInstance *h);
-	void setGarrisonedHero(CGHeroInstance *h);
-	const CArmedInstance *getUpperArmy() const; //garrisoned hero if present or the town itself
-
-	//////////////////////////////////////////////////////////////////////////
-
-	ui8 getPassableness() const; //bitmap - if the bit is set the corresponding player can pass through the visitable tiles of object, even if it's blockvis; if not set - default properties from definfo are used
-	int3 getSightCenter() const override; //"center" tile from which the sight distance is calculated
-	int getSightRadious() const override; //returns sight distance
-	int getBoatType() const; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral
-	void getOutOffsets(std::vector<int3> &offsets) const; //offsets to obj pos when we boat can be placed
-	int getMarketEfficiency() const override; //=market count
-	bool allowsTrade(EMarketMode::EMarketMode mode) const;
-	std::vector<int> availableItemsIds(EMarketMode::EMarketMode mode) const;
-
-	void updateAppearance();
-
-	//////////////////////////////////////////////////////////////////////////
-
-	bool needsLastStack() const;
-	CGTownInstance::EFortLevel fortLevel() const;
-	int hallLevel() const; // -1 - none, 0 - village, 1 - town, 2 - city, 3 - capitol
-	int mageGuildLevel() const; // -1 - none, 0 - village, 1 - town, 2 - city, 3 - capitol
-	int getHordeLevel(const int & HID) const; //HID - 0 or 1; returns creature level or -1 if that horde structure is not present
-	int creatureGrowth(const int & level) const;
-	GrowthInfo getGrowthInfo(int level) const;
-	bool hasFort() const;
-	bool hasCapitol() const;
-	//checks if building is constructed and town has same subID
-	bool hasBuilt(BuildingID buildingID) const;
-	bool hasBuilt(BuildingID buildingID, int townID) const;
-	TResources dailyIncome() const; //calculates daily income of this town
-	int spellsAtLevel(int level, bool checkGuild) const; //levels are counted from 1 (1 - 5)
-	bool armedGarrison() const; //true if town has creatures in garrison or garrisoned hero
-	int getTownLevel() const;
-
-	void removeCapitols (PlayerColor owner) const;
-	void addHeroToStructureVisitors(const CGHeroInstance *h, si32 structureInstanceID) const; //hero must be visiting or garrisoned in town
-
-	CGTownInstance();
-	virtual ~CGTownInstance();
-
-	///IObjectInterface overrides
-	void newTurn() const override;
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void onHeroLeave(const CGHeroInstance * h) const override;
-	void initObj() override;
-	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
-protected:
-	void setPropertyDer(ui8 what, ui32 val) override;
-};
-class DLL_LINKAGE CGPandoraBox : public CArmedInstance
-{
-public:
-	std::string message;
-	bool hasGuardians; //helper - after battle even though we have no stacks, allows us to know that there was battle
-
-	//gained things:
-	ui32 gainedExp;
-	si32 manaDiff; //amount of gained / lost mana
-	si32 moraleDiff; //morale modifier
-	si32 luckDiff; //luck modifier
-	TResources resources;//gained / lost resources
-	std::vector<si32> primskills;//gained / lost prim skills
-	std::vector<SecondarySkill> abilities; //gained abilities
-	std::vector<si32> abilityLevels; //levels of gained abilities
-	std::vector<ArtifactID> artifacts; //gained artifacts
-	std::vector<SpellID> spells; //gained spells
-	CCreatureSet creatures; //gained creatures
-
-	void initObj() override;
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
-	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
-	void heroLevelUpDone(const CGHeroInstance *hero) const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CArmedInstance&>(*this);
-		h & message & hasGuardians & gainedExp & manaDiff & moraleDiff & luckDiff & resources & primskills
-			& abilities & abilityLevels & artifacts & spells & creatures;
-	}
-protected:
-	void giveContentsUpToExp(const CGHeroInstance *h) const;
-	void giveContentsAfterExp(const CGHeroInstance *h) const;
-private:
-	void getText( InfoWindow &iw, bool &afterBattle, int val, int negative, int positive, const CGHeroInstance * h ) const;
-	void getText( InfoWindow &iw, bool &afterBattle, int text, const CGHeroInstance * h ) const;
-};
-
-class DLL_LINKAGE CGEvent : public CGPandoraBox  //event objects
-{
-public:
-	bool removeAfterVisit; //true if event is removed after occurring
-	ui8 availableFor; //players whom this event is available for
-	bool computerActivate; //true if computer player can activate this event
-	bool humanActivate; //true if human player can activate this event
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGPandoraBox &>(*this);
-		h & removeAfterVisit & availableFor & computerActivate & humanActivate;
-	}
-
-	void onHeroVisit(const CGHeroInstance * h) const override;
-private:
-	void activated(const CGHeroInstance * h) const;
-};
-
-class DLL_LINKAGE CGCreature : public CArmedInstance //creatures on map
-{
-	enum Action {
-		FIGHT = -2, FLEE = -1, JOIN_FOR_FREE = 0 //values > 0 mean gold price
-	};
-
-public:
-	ui32 identifier; //unique code for this monster (used in missions)
-	si8 character; //character of this set of creatures (0 - the most friendly, 4 - the most hostile) => on init changed to -4 (compliant) ... 10 value (savage)
-	std::string message; //message printed for attacking hero
-	TResources resources; // resources given to hero that has won with monsters
-	ArtifactID gainedArtifact; //ID of artifact gained to hero, -1 if none
-	bool neverFlees; //if true, the troops will never flee
-	bool notGrowingTeam; //if true, number of units won't grow
-	ui64 temppower; //used to handle fractional stack growth for tiny stacks
-
-	bool refusedJoining;
-
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	const std::string & getHoverText() const override;
-	void initObj() override;
-	void newTurn() const override;
-	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
-	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
-
-
-	struct DLL_LINKAGE formationInfo // info about merging stacks after battle back into one
-	{
-		si32 basicType;
-		ui32 randomFormation; //random seed used to determine number of stacks and is there's upgraded stack
-		template <typename Handler> void serialize(Handler &h, const int version)
-		{
-			h & basicType & randomFormation;
-		}
-	} formation;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CArmedInstance&>(*this);
-		h & identifier & character & message & resources & gainedArtifact & neverFlees & notGrowingTeam & temppower;
-		h & refusedJoining & formation;
-	}
-protected:
-	void setPropertyDer(ui8 what, ui32 val) override;
-private:
-
-	void fight(const CGHeroInstance *h) const;
-	void flee( const CGHeroInstance * h ) const;
-	void fleeDecision(const CGHeroInstance *h, ui32 pursue) const;
-	void joinDecision(const CGHeroInstance *h, int cost, ui32 accept) const;
-
-	int takenAction(const CGHeroInstance *h, bool allowJoin=true) const; //action on confrontation: -2 - fight, -1 - flee, >=0 - will join for given value of gold (may be 0)
-
-};
-
-
-class DLL_LINKAGE CGSignBottle : public CGObjectInstance //signs and ocean bottles
-{
-public:
-	std::string message;
-
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void initObj() override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-		h & message;
-	}
-};
-
-class DLL_LINKAGE IQuestObject
-{
-public:
-	CQuest * quest;
-
-	IQuestObject(): quest(new CQuest()){};
-	virtual ~IQuestObject() {};
-	virtual void getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h = nullptr) const;
-	virtual bool checkQuest (const CGHeroInstance * h) const;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & quest;
-	}
-};
-
-class DLL_LINKAGE CGSeerHut : public CArmedInstance, public IQuestObject //army is used when giving reward
-{
-public:
-	enum ERewardType {NOTHING, EXPERIENCE, MANA_POINTS, MORALE_BONUS, LUCK_BONUS, RESOURCES, PRIMARY_SKILL, SECONDARY_SKILL, ARTIFACT, SPELL, CREATURE};
-	ERewardType rewardType;
-	si32 rID; //reward ID
-	si32 rVal; //reward value
-	std::string seerName;
-
-	CGSeerHut() : IQuestObject(){};
-	void initObj() override;
-	const std::string & getHoverText() const override;
-	void newTurn() const override;
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
-
-	virtual void init();
-	int checkDirection() const; //calculates the region of map where monster is placed
-	void setObjToKill(); //remember creatures / heroes to kill after they are initialized
-	const CGHeroInstance *getHeroToKill(bool allowNull = false) const;
-	const CGCreature *getCreatureToKill(bool allowNull = false) const;
-	void getRolloverText (MetaString &text, bool onHover) const;
-	void getCompletionText(MetaString &text, std::vector<Component> &components, bool isCustom, const CGHeroInstance * h = nullptr) const;
-	void finishQuest (const CGHeroInstance * h, ui32 accept) const; //common for both objects
-	virtual void completeQuest (const CGHeroInstance * h) const;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CArmedInstance&>(*this) & static_cast<IQuestObject&>(*this);
-		h & rewardType & rID & rVal & seerName;
-	}
-protected:
-	void setPropertyDer(ui8 what, ui32 val) override;
-};
-
-class DLL_LINKAGE CGQuestGuard : public CGSeerHut
-{
-public:
-	CGQuestGuard() : CGSeerHut(){};
-	void init() override;
-	void completeQuest (const CGHeroInstance * h) const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGSeerHut&>(*this);
-	}
-};
-
-class DLL_LINKAGE CGWitchHut : public CPlayersVisited
-{
-public:
-	std::vector<si32> allowedAbilities;
-	ui32 ability;
-
-	const std::string & getHoverText() const override;
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void initObj() override;
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CPlayersVisited&>(*this);
-		h & allowedAbilities & ability;
-	}
-};
-
-
-class DLL_LINKAGE CGScholar : public CGObjectInstance
-{
-public:
-	enum EBonusType {PRIM_SKILL, SECONDARY_SKILL, SPELL, RANDOM = 255};
-	EBonusType bonusType;
-	ui16 bonusID; //ID of skill/spell
-
-//	void giveAnyBonus(const CGHeroInstance * h) const; //TODO: remove
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void initObj() override;
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-		h & bonusType & bonusID;
-	}
-};
-
-class DLL_LINKAGE CGGarrison : public CArmedInstance
-{
-public:
-	bool removableUnits;
-
-	ui8 getPassableness() const;
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CArmedInstance&>(*this);
-		h & removableUnits;
-	}
-};
-
-class DLL_LINKAGE CGArtifact : public CArmedInstance
-{
-public:
-	CArtifactInstance *storedArtifact;
-	std::string message;
-
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
-	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
-
-	void pick( const CGHeroInstance * h ) const;
-	void initObj() override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CArmedInstance&>(*this);
-		h & message & storedArtifact;
-	}
-};
-
-class DLL_LINKAGE CGResource : public CArmedInstance
-{
-public:
-	ui32 amount; //0 if random
-	std::string message;
-
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void initObj() override;
-	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
-	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
-
-	void collectRes(PlayerColor player) const;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CArmedInstance&>(*this);
-		h & amount & message;
-	}
-};
-
-class DLL_LINKAGE CGPickable : public CGObjectInstance //campfire, treasure chest, Flotsam, Shipwreck Survivor, Sea Chest
-{
-public:
-	ui32 type, val1, val2;
-
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void initObj() override;
-	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-		h & type & val1 & val2;
-	}
-};
-
-class DLL_LINKAGE CGShrine : public CPlayersVisited
-{
-public:
-	SpellID spell; //id of spell or NONE if random
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void initObj() override;
-	const std::string & getHoverText() const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CPlayersVisited&>(*this);;
-		h & spell;
-	}
-};
-
-class DLL_LINKAGE CGMine : public CArmedInstance
-{
-public:
-	Res::ERes producedResource;
-	ui32 producedQuantity;
-	
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
-	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
-
-	void flagMine(PlayerColor player) const;
-	void newTurn() const override;
-	void initObj() override;
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CArmedInstance&>(*this);
-		h & producedResource & producedQuantity;
-	}
-	ui32 defaultResProduction();
-};
-
-class DLL_LINKAGE CGVisitableOPW : public CGObjectInstance //objects visitable OPW
-{
-public:
-	ui8 visited; //true if object has been visited this week
-
-	bool wasVisited(PlayerColor player) const;
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	virtual void newTurn() const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-		h & visited;
-	}
-protected:
-	void setPropertyDer(ui8 what, ui32 val) override;
-};
-
-class DLL_LINKAGE CGTeleport : public CGObjectInstance //teleports and subterranean gates
-{
-public:
-	static std::map<Obj, std::map<int, std::vector<ObjectInstanceID> > > objs; //teleports: map[ID][subID] => vector of ids
-	static std::vector<std::pair<ObjectInstanceID, ObjectInstanceID> > gates; //subterranean gates: pairs of ids
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void initObj() override;
-	static void postInit();
-	static ObjectInstanceID getMatchingGate(ObjectInstanceID id); //receives id of one subterranean gate and returns id of the paired one, -1 if none
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-	}
-};
-
-class DLL_LINKAGE CGBonusingObject : public CGObjectInstance //objects giving bonuses to luck/morale/movement
-{
-public:
-	bool wasVisited (const CGHeroInstance * h) const;
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	const std::string & getHoverText() const override;
-	void initObj() override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-	}
-};
-
-class DLL_LINKAGE CGMagicSpring : public CGVisitableOPW
-{///unfortunately, this one is quite different than others
-	enum EVisitedEntrance
-	{
-		CLEAR = 0, LEFT = 1, RIGHT
-	};
-public:
-	EVisitedEntrance visitedTile; //only one entrance was visited - there are two
-
-	std::vector<int3> getVisitableOffsets() const;
-	int3 getVisitableOffset() const override;
-	void setPropertyDer(ui8 what, ui32 val) override;
-	void newTurn() const override;
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	const std::string & getHoverText() const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-		h & visitedTile & visited;
-	}
-};
-
-class DLL_LINKAGE CGMagicWell : public CGObjectInstance //objects giving bonuses to luck/morale/movement
-{
-public:
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	const std::string & getHoverText() const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-	}
-};
-
-class DLL_LINKAGE CGSirens : public CGObjectInstance
-{
-public:
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	const std::string & getHoverText() const override;
-	void initObj() override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-	}
-};
-
-class DLL_LINKAGE CGObservatory : public CGObjectInstance //Redwood observatory
-{
-public:
-	void onHeroVisit(const CGHeroInstance * h) const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-	}
-};
-
-
-class DLL_LINKAGE CGKeys : public CGObjectInstance //Base class for Keymaster and guards
-{
-public:
-	static std::map <PlayerColor, std::set <ui8> > playerKeyMap; //[players][keysowned]
-	//SubID 0 - lightblue, 1 - green, 2 - red, 3 - darkblue, 4 - brown, 5 - purple, 6 - white, 7 - black
-
-	const std::string getName() const; //depending on color
-	bool wasMyColorVisited (PlayerColor player) const;
-
-	const std::string & getHoverText() const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-	}
-protected:
-	void setPropertyDer(ui8 what, ui32 val) override;
-};
-
-class DLL_LINKAGE CGKeymasterTent : public CGKeys
-{
-public:
-	bool wasVisited (PlayerColor player) const;
-	void onHeroVisit(const CGHeroInstance * h) const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-	}
-};
-
-class DLL_LINKAGE CGBorderGuard : public CGKeys, public IQuestObject
-{
-public:
-	CGBorderGuard() : IQuestObject(){};
-	void initObj() override;
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
-
-	void getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h = nullptr) const;
-	void getRolloverText (MetaString &text, bool onHover) const;
-	bool checkQuest (const CGHeroInstance * h) const;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<IQuestObject&>(*this);
-		h & static_cast<CGObjectInstance&>(*this);
-		h & blockVisit;
-	}
-};
-
-class DLL_LINKAGE CGBorderGate : public CGBorderGuard
-{
-public:
-	CGBorderGate() : CGBorderGuard(){};
-	void onHeroVisit(const CGHeroInstance * h) const override;
-
-	ui8 getPassableness() const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGBorderGuard&>(*this); //need to serialize or object will be empty
-	}
-};
-
-class DLL_LINKAGE CGBoat : public CGObjectInstance
-{
-public:
-	ui8 direction;
-	const CGHeroInstance *hero;  //hero on board
-
-	void initObj() override;
-
-	CGBoat()
-	{
-		hero = nullptr;
-		direction = 4;
-	}
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this) & direction & hero;
-	}
-};
-
-class DLL_LINKAGE CGOnceVisitable : public CPlayersVisited
-///wagon, corpse, lean to, warriors tomb
-{
-public:
-	ui8 artOrRes; //0 - nothing; 1 - artifact; 2 - resource
-	ui32 bonusType, //id of res or artifact
-		bonusVal; //resource amount (or not used)
-
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	const std::string & getHoverText() const override;
-	void initObj() override;
-	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CPlayersVisited&>(*this);;
-		h & artOrRes & bonusType & bonusVal;
-	}
-};
-
-class DLL_LINKAGE CBank : public CArmedInstance
-{
-	public:
-	int index; //banks have unusal numbering - see ZCRBANK.txt and initObj()
-	BankConfig *bc;
-	double multiplier; //for improved banks script
-	std::vector<ui32> artifacts; //fixed and deterministic
-	ui32 daycounter;
-
-	void initObj() override;
-	const std::string & getHoverText() const override;
-	void initialize() const;
-	void reset(ui16 var1);
-	void newTurn() const override;
-	bool wasVisited (PlayerColor player) const override;
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
-	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CArmedInstance&>(*this);
-		h & index & multiplier & artifacts & daycounter & bc;
-	}
-protected:
-	void setPropertyDer(ui8 what, ui32 val) override;
-};
-class DLL_LINKAGE CGPyramid : public CBank
-{
-public:
-	ui16 spell;
-
-	void initObj() override;
-	const std::string & getHoverText() const override;
-	void newTurn() const override {}; //empty, no reset
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CBank&>(*this);
-		h & spell;
-	}
-};
-
-class CGShipyard : public CGObjectInstance, public IShipyard
-{
-public:
-	void getOutOffsets(std::vector<int3> &offsets) const; //offsets to obj pos when we boat can be placed
-	CGShipyard();
-	void onHeroVisit(const CGHeroInstance * h) const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-		h & static_cast<IShipyard&>(*this);
-	}
-};
-
-class DLL_LINKAGE CGMagi : public CGObjectInstance
-{
-public:
-	static std::map <si32, std::vector<ObjectInstanceID> > eyelist; //[subID][id], supports multiple sets as in H5
-
-	void initObj() override;
-	void onHeroVisit(const CGHeroInstance * h) const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-	}
-};
-
-
-
-class DLL_LINKAGE CCartographer : public CPlayersVisited
-{
-///behaviour varies depending on surface and  floor
-public:
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CPlayersVisited&>(*this);
-	}
-};
-
-class DLL_LINKAGE CGDenOfthieves : public CGObjectInstance
-{
-	void onHeroVisit(const CGHeroInstance * h) const override;
-};
-
-class DLL_LINKAGE CGObelisk : public CPlayersVisited
-{
-public:
-	static ui8 obeliskCount; //how many obelisks are on map
-	static std::map<TeamID, ui8> visited; //map: team_id => how many obelisks has been visited
-
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void initObj() override;
-	const std::string & getHoverText() const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CPlayersVisited&>(*this);
-	}
-protected:
-	void setPropertyDer(ui8 what, ui32 val) override;
-};
-
-class DLL_LINKAGE CGLighthouse : public CGObjectInstance
-{
-public:
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void initObj() override;
-	const std::string & getHoverText() const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-	}
-	void giveBonusTo( PlayerColor player ) const;
-};
-
-class DLL_LINKAGE CGMarket : public CGObjectInstance, public IMarket
-{
-public:
-	CGMarket();
-	///IObjectIntercae
-	void onHeroVisit(const CGHeroInstance * h) const override; //open trading window
-
-	///IMarket
-	int getMarketEfficiency() const override;
-	bool allowsTrade(EMarketMode::EMarketMode mode) const override;
-	int availableUnits(EMarketMode::EMarketMode mode, int marketItemSerial) const override; //-1 if unlimited
-	std::vector<int> availableItemsIds(EMarketMode::EMarketMode mode) const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-		h & static_cast<IMarket&>(*this);
-	}
-};
-
-class DLL_LINKAGE CGBlackMarket : public CGMarket
-{
-public:
-	std::vector<const CArtifact *> artifacts; //available artifacts
-
-	void newTurn() const override; //reset artifacts for black market every month
-	std::vector<int> availableItemsIds(EMarketMode::EMarketMode mode) const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGMarket&>(*this);
-		h & artifacts;
-	}
-};
-
-class DLL_LINKAGE CGUniversity : public CGMarket
-{
-public:
-	std::vector<int> skills; //available skills
-
-	std::vector<int> availableItemsIds(EMarketMode::EMarketMode mode) const;
-	void initObj() override;//set skills for trade
-	void onHeroVisit(const CGHeroInstance * h) const override; //open window
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGMarket&>(*this);
-		h & skills;
-	}
-};
-
-struct BankConfig
-{
-	BankConfig() {level = chance = upgradeChance = combatValue = value = rewardDifficulty = easiest = 0; };
-	ui8 level; //1 - 4, how hard the battle will be
-	ui8 chance; //chance for this level being chosen
-	ui8 upgradeChance; //chance for creatures to be in upgraded versions
-	std::vector< std::pair <CreatureID, ui32> > guards; //creature ID, amount
-	ui32 combatValue; //how hard are guards of this level
-	Res::ResourceSet resources; //resources given in case of victory
-	std::vector< std::pair <CreatureID, ui32> > creatures; //creatures granted in case of victory (creature ID, amount)
-	std::vector<ui16> artifacts; //number of artifacts given in case of victory [0] -> treasure, [1] -> minor [2] -> major [3] -> relic
-	ui32 value; //overall value of given things
-	ui32 rewardDifficulty; //proportion of reward value to difficulty of guards; how profitable is this creature Bank config
-	ui16 easiest; //?!?
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & level & chance & upgradeChance & guards & combatValue & resources & creatures & artifacts & value & rewardDifficulty & easiest;
-	}
-};
-
-class DLL_LINKAGE CObjectHandler
-{
-public:
-	std::map<si32, CreatureID> cregens; //type 17. dwelling subid -> creature ID
-	std::map <ui32, std::vector < ConstTransitivePtr<BankConfig> > > banksInfo; //[index][preset]
-	std::map <ui32, std::string> creBanksNames; //[crebank index] -> name of this creature bank
-	std::vector<ui32> resVals; //default values of resources in gold
-
-	CObjectHandler();
-	~CObjectHandler();
-
-	int bankObjToIndex (const CGObjectInstance * obj);
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & cregens & banksInfo & creBanksNames & resVals;
-	}
-};

+ 10 - 0
lib/CRandomGenerator.h

@@ -110,4 +110,14 @@ namespace RandomGeneratorUtil
 		assert(!container.empty());
 		return std::next(container.begin(), rand.nextInt(container.size() - 1));
 	}
+
+	template<typename T>
+	void randomShuffle(std::vector<T>& container, CRandomGenerator & rand)
+	{
+		int n = (container.end() - container.begin());
+		for (int i = n-1; i>0; --i)
+		{
+			std::swap (container.begin()[i],container.begin()[rand.nextInt(i+1)]);
+		}
+	}
 }

+ 0 - 0
client/CSoundBase.h → lib/CSoundBase.h


+ 76 - 132
lib/CTownHandler.cpp

@@ -11,8 +11,8 @@
 #include "CArtHandler.h"
 #include "CSpellHandler.h"
 #include "filesystem/Filesystem.h"
-#include "CDefObjInfoHandler.h"
-#include "CObjectHandler.h"
+#include "mapObjects/CObjectClassesHandler.h"
+#include "mapObjects/CObjectHandler.h"
 
 /*
  * CTownHandler.cpp, part of VCMI engine
@@ -272,24 +272,12 @@ void CTownHandler::loadBuildingRequirements(CTown &town, CBuilding & building, c
 {
 	if (source.isNull())
 		return;
-	if (source.Vector()[0].getType() == JsonNode::DATA_FLOAT)
-	{
-		// MODS COMPATIBILITY
-		CBuilding::TRequired::OperatorAll required;
-
-		for(const JsonNode &building : source.Vector())
-			required.expressions.push_back(BuildingID(building.Float()));
 
-		building.requirements = CBuilding::TRequired(required);
-	}
-	else
-	{
-		BuildingRequirementsHelper hlp;
-		hlp.building = &building;
-		hlp.faction  = town.faction;
-		hlp.json = source;
-		requirementsToLoad.push_back(hlp);
-	}
+	BuildingRequirementsHelper hlp;
+	hlp.building = &building;
+	hlp.faction  = town.faction;
+	hlp.json = source;
+	requirementsToLoad.push_back(hlp);
 }
 
 void CTownHandler::loadBuilding(CTown &town, const std::string & stringID, const JsonNode & source)
@@ -308,56 +296,49 @@ void CTownHandler::loadBuilding(CTown &town, const std::string & stringID, const
 	ret->resources = TResources(source["cost"]);
 	ret->produce =   TResources(source["produce"]);
 
-	//for compatibility with older town mods
+	//MODS COMPATIBILITY FOR 0.96
 	if(!ret->produce.nonZero())
 	{
-	if (ret->bid == BuildingID::VILLAGE_HALL) ret->produce[Res::GOLD] = 500;
-
-	if (ret->bid == BuildingID::TOWN_HALL) ret->produce[Res::GOLD] = 1000;
-
-	if (ret->bid == BuildingID::CITY_HALL) ret->produce[Res::GOLD] = 2000;
-
-	if (ret->bid == BuildingID::CAPITOL) ret->produce[Res::GOLD] = 4000;
-
-	if (ret->bid == BuildingID::GRAIL) ret->produce[Res::GOLD] = 5000;
-	//
-	if (ret->bid == BuildingID::RESOURCE_SILO)
-		{
-		if ((ret->town->primaryRes != Res::WOOD) && (ret->town->primaryRes != Res::ORE) && (ret->town->primaryRes != Res::GOLD))
-			ret->produce[ret->town->primaryRes] = 1;
-		else
-		{
-			if (ret->town->primaryRes == Res::GOLD) ret->produce[ret->town->primaryRes] = 500;
-			else
+		switch (ret->bid) {
+			break; case BuildingID::VILLAGE_HALL: ret->produce[Res::GOLD] = 500;
+			break; case BuildingID::TOWN_HALL :   ret->produce[Res::GOLD] = 1000;
+			break; case BuildingID::CITY_HALL :   ret->produce[Res::GOLD] = 2000;
+			break; case BuildingID::CAPITOL :     ret->produce[Res::GOLD] = 4000;
+			break; case BuildingID::GRAIL :       ret->produce[Res::GOLD] = 5000;
+			break; case BuildingID::RESOURCE_SILO :
 			{
-				ret->produce[Res::WOOD] = 1;
-				ret->produce[Res::ORE] = 1;
+				switch (ret->town->primaryRes)
+				{
+					case Res::GOLD:
+						ret->produce[ret->town->primaryRes] = 500;
+						break;
+					case Res::WOOD_AND_ORE:
+						ret->produce[Res::WOOD] = 1;
+						ret->produce[Res::ORE] = 1;
+						break;
+					default:
+						ret->produce[ret->town->primaryRes] = 1;
+						break;
+				}
 			}
 		}
-		}
 	}
 
 	loadBuildingRequirements(town, *ret, source["requires"]);
 
 	if (!source["upgrades"].isNull())
 	{
-		//MODS COMPATIBILITY
-		if (source["upgrades"].getType() == JsonNode::DATA_FLOAT)
-			ret->upgrade = BuildingID(source["upgrades"].Float());
-		else
+		// building id and upgrades can't be the same
+		if(stringID == source["upgrades"].String())
 		{
-			// building id and upgrades can't be the same
-			if(stringID == source["upgrades"].String())
-			{
-				throw std::runtime_error(boost::str(boost::format("Building with ID '%s' of town '%s' can't be an upgrade of the same building.") %
-													stringID % town.faction->name));
-			}
-
-			VLC->modh->identifiers.requestIdentifier("building." + town.faction->identifier, source["upgrades"], [=](si32 identifier)
-			{
-				ret->upgrade = BuildingID(identifier);
-			});
+			throw std::runtime_error(boost::str(boost::format("Building with ID '%s' of town '%s' can't be an upgrade of the same building.") %
+												stringID % town.faction->name));
 		}
+
+		VLC->modh->identifiers.requestIdentifier("building." + town.faction->identifier, source["upgrades"], [=](si32 identifier)
+		{
+			ret->upgrade = BuildingID(identifier);
+		});
 	}
 	else
 		ret->upgrade = BuildingID::NONE;
@@ -381,7 +362,6 @@ void CTownHandler::loadStructure(CTown &town, const std::string & stringID, cons
 {
 	auto ret = new CStructure;
 
-	//Note: MODS COMPATIBILITY CODE
 	ret->building = nullptr;
 	ret->buildable = nullptr;
 
@@ -399,17 +379,10 @@ void CTownHandler::loadStructure(CTown &town, const std::string & stringID, cons
 	}
 	else
 	{
-		if (source["builds"].getType() == JsonNode::DATA_FLOAT)
-		{
-			ret->buildable = town.buildings[BuildingID(source["builds"].Float())];
-		}
-		else
+		VLC->modh->identifiers.requestIdentifier("building." + town.faction->identifier, source["builds"], [=, &town](si32 identifier) mutable
 		{
-			VLC->modh->identifiers.requestIdentifier("building." + town.faction->identifier, source["builds"], [=, &town](si32 identifier) mutable
-			{
-				ret->buildable = town.buildings[BuildingID(identifier)];
-			});
-		}
+			ret->buildable = town.buildings[BuildingID(identifier)];
+		});
 	}
 
 	ret->identifier = stringID;
@@ -457,16 +430,10 @@ void CTownHandler::loadTownHall(CTown &town, const JsonNode & source)
 				auto & dst = dstBox[k];
 				auto & src = srcBox[k];
 
-				//MODS COMPATIBILITY
-				if (src.getType() == JsonNode::DATA_FLOAT)
-					dst = BuildingID(src.Float());
-				else
+				VLC->modh->identifiers.requestIdentifier("building." + town.faction->identifier, src, [&](si32 identifier)
 				{
-					VLC->modh->identifiers.requestIdentifier("building." + town.faction->identifier, src, [&](si32 identifier)
-					{
-						dst = BuildingID(identifier);
-					});
-				}
+					dst = BuildingID(identifier);
+				});
 			}
 		}
 	}
@@ -554,10 +521,6 @@ void CTownHandler::loadClientData(CTown &town, const JsonNode & source)
 		info.tavernVideo = "TAVERN.BIK";
 	//end of legacy assignment 
 
-	info.advMapVillage = source["adventureMap"]["village"].String();
-	info.advMapCastle  = source["adventureMap"]["castle"].String();
-	info.advMapCapitol = source["adventureMap"]["capitol"].String();
-
 	loadTownHall(town,   source["hallSlots"]);
 	loadStructures(town, source["structures"]);
 	loadSiegeScreen(town, source["siege"]);
@@ -677,20 +640,6 @@ CFaction * CTownHandler::loadFromJson(const JsonNode &source, std::string identi
 	faction->name = source["name"].String();
 	faction->identifier = identifier;
 
-	//FIXME: MODS COMPATIBILITY
-	if (!source["commander"].isNull())
-	{
-		VLC->modh->identifiers.requestIdentifier ("creature", source["commander"],
-		[=](si32 commanderID)
-		{
-			for (auto ptr : VLC->heroh->classes.heroClasses)
-			{
-				if (ptr->commander == nullptr && ptr->faction == faction->index)
-					ptr->commander = VLC->creh->creatures[commanderID];
-			}
-		});
-	}
-
 	faction->creatureBg120 = source["creatureBackground"]["120px"].String();
 	faction->creatureBg130 = source["creatureBackground"]["130px"].String();
 
@@ -723,6 +672,8 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
 	auto object = loadFromJson(data, name);
 
 	object->index = factions.size();
+	factions.push_back(object);
+
 	if (object->town)
 	{
 		auto & info = object->town->clientInfo;
@@ -730,9 +681,16 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
 		info.icons[0][1] = 8 + object->index * 4 + 1;
 		info.icons[1][0] = 8 + object->index * 4 + 2;
 		info.icons[1][1] = 8 + object->index * 4 + 3;
-	}
 
-	factions.push_back(object);
+		VLC->modh->identifiers.requestIdentifier(scope, "object", "town", [=](si32 index)
+		{
+			// register town once objects are loaded
+			JsonNode config = data["town"]["mapObject"];
+			config["faction"].String() = object->identifier;
+			config["faction"].meta = scope;
+			VLC->objtypeh->loadSubObject(object->identifier, config, index, object->index);
+		});
+	}
 
 	VLC->modh->identifiers.registerObject(scope, "faction", name, object->index);
 }
@@ -741,6 +699,9 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
 {
 	auto object = loadFromJson(data, name);
 	object->index = index;
+	assert(factions[index] == nullptr); // ensure that this id was not loaded before
+	factions[index] = object;
+
 	if (object->town)
 	{
 		auto & info = object->town->clientInfo;
@@ -748,10 +709,25 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
 		info.icons[0][1] = (GameConstants::F_NUMBER + object->index) * 2 + 1;
 		info.icons[1][0] = object->index * 2 + 0;
 		info.icons[1][1] = object->index * 2 + 1;
-	}
 
-	assert(factions[index] == nullptr); // ensure that this id was not loaded before
-	factions[index] = object;
+		VLC->modh->identifiers.requestIdentifier(scope, "object", "town", [=](si32 index)
+		{
+			// register town once objects are loaded
+			JsonNode config = data["town"]["mapObject"];
+			config["faction"].String() = object->identifier;
+			config["faction"].meta = scope;
+			VLC->objtypeh->loadSubObject(object->identifier, config, index, object->index);
+
+			// MODS COMPATIBILITY FOR 0.96
+			auto & advMap = data["town"]["adventureMap"];
+			if (!advMap["fort"].isNull())
+			{
+				JsonNode config;
+				config["appearance"] = advMap["fort"];
+				VLC->objtypeh->getHandlerFor(index, object->index)->addTemplate(config);
+			}
+		});
+	}
 
 	VLC->modh->identifiers.registerObject(scope, "faction", name, object->index);
 }
@@ -759,38 +735,6 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
 void CTownHandler::afterLoadFinalization()
 {
 	initializeRequirements();
-	ObjectTemplate base = VLC->dobjinfo->pickCandidates(Obj::TOWN, 0).front();
-	for (CFaction * fact : factions)
-	{
-		if (fact->town)
-		{
-			base.animationFile = fact->town->clientInfo.advMapCastle;
-			base.subid = fact->index;
-
-			// replace existing (if any) and add new template.
-			// Necessary for objects added via mods that don't have any templates in H3
-			VLC->dobjinfo->eraseAll(Obj::TOWN, fact->index);
-			VLC->dobjinfo->registerTemplate(base);
-
-			assert(fact->town->dwellings.size() == fact->town->dwellingNames.size());
-			for (size_t i=0; i<fact->town->dwellings.size(); i++)
-			{
-				ObjectTemplate base = VLC->dobjinfo->pickCandidates(Obj::CREATURE_GENERATOR1, 0).front();
-
-				//both unupgraded and upgraded get same dwelling
-				 for (auto cre : fact->town->creatures[i])
-				 {
-					base.subid = 80 + cre;
-					base.animationFile = fact->town->dwellings[i];
-					if (VLC->objh->cregens.count(cre) == 0)
-					{
-						VLC->dobjinfo->registerTemplate(base);
-						VLC->objh->cregens[80 + cre] = cre; //map of dwelling -> creature id
-					}
-				 }
-			}
-		}
-	}
 }
 
 void CTownHandler::initializeRequirements()
@@ -824,4 +768,4 @@ std::set<TFaction> CTownHandler::getAllowedFactions() const
 			allowedFactions.insert(i);
 
 	return allowedFactions;
-}
+}

+ 1 - 5
lib/CTownHandler.h

@@ -190,10 +190,6 @@ public:
 		/// NOTE: index in vector is meaningless. Vector used instead of list for a bit faster access
 		std::vector<ConstTransitivePtr<CStructure> > structures;
 
-		std::string advMapVillage;
-		std::string advMapCastle;
-		std::string advMapCapitol;
-
 		std::string siegePrefix;
 		std::vector<Point> siegePositions;
 		CreatureID siegeShooter; // shooter creature ID
@@ -201,7 +197,7 @@ public:
 		template <typename Handler> void serialize(Handler &h, const int version)
 		{
 			h & icons & iconSmall & iconLarge & tavernVideo & musicTheme & townBackground & guildBackground & guildWindow & buildingsIcons & hallBackground;
-			h & advMapVillage & advMapCastle & advMapCapitol & hallSlots & structures;
+			h & hallSlots & structures;
 			h & siegePrefix & siegePositions & siegeShooter;
 		}
 	} clientInfo;

+ 1 - 2
lib/Connection.h

@@ -14,7 +14,6 @@
 #include <typeinfo> //XXX this is in namespace std if you want w/o use typeinfo.h?
 #include <type_traits>
 
-#include <boost/variant.hpp>
 #include <boost/mpl/eval_if.hpp>
 #include <boost/mpl/equal_to.hpp>
 #include <boost/mpl/int.hpp>
@@ -24,7 +23,7 @@
 
 #include "ConstTransitivePtr.h"
 #include "CCreatureSet.h" //for CStackInstance
-#include "CObjectHandler.h" //for CArmedInstance
+#include "mapObjects/CGHeroInstance.h"
 #include "mapping/CCampaignHandler.h" //for CCampaignState
 #include "rmg/CMapGenerator.h" // for CMapGenOptions
 

+ 1 - 1
lib/GameConstants.cpp

@@ -13,7 +13,7 @@
 #include "StdInc.h"
 
 #include "VCMI_Lib.h"
-#include "CDefObjInfoHandler.h"
+#include "mapObjects/CObjectClassesHandler.h"
 #include "CArtHandler.h"
 #include "CCreatureHandler.h"
 #include "CSpellHandler.h"

+ 2 - 1
lib/IGameCallback.cpp

@@ -14,9 +14,10 @@
 #include "CHeroHandler.h" // for CHeroHandler
 #include "CSpellHandler.h" // for CSpell
 #include "NetPacks.h"
-#include "CBonusTypeHandler.h" // for CBonusTypeHandler
+#include "CBonusTypeHandler.h"
 
 #include "Connection.h" // for SAVEGAME_MAGIC
+#include "mapObjects/CObjectClassesHandler.h"
 
 void CPrivilagedInfoCallback::getFreeTiles (std::vector<int3> &tiles) const
 {

+ 1 - 1
lib/JsonNode.cpp

@@ -737,7 +737,7 @@ void JsonUtils::mergeCopy(JsonNode & dest, JsonNode source)
 
 void JsonUtils::inherit(JsonNode & descendant, const JsonNode & base)
 {
-	JsonNode inheritedNode(base);		
+	JsonNode inheritedNode(base);
 	merge(inheritedNode,descendant);
 	descendant.swap(inheritedNode);
 }

+ 8 - 13
lib/JsonNode.h

@@ -108,19 +108,14 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & meta;
-		// simple saving - save json in its string interpretation
-		if (h.saving)
-		{
-			std::ostringstream stream;
-			stream << *this;
-			std::string str = stream.str();
-			h & str;
-		}
-		else
-		{
-			std::string str;
-			h & str;
-			JsonNode(str.c_str(), str.size()).swap(*this);
+		h & type;
+		switch (type) {
+			break; case DATA_NULL:
+			break; case DATA_BOOL:   h & data.Bool;
+			break; case DATA_FLOAT:  h & data.Float;
+			break; case DATA_STRING: h & data.String;
+			break; case DATA_VECTOR: h & data.Vector;
+			break; case DATA_STRUCT: h & data.Struct;
 		}
 	}
 };

+ 39 - 12
lib/NetPacks.h

@@ -1,11 +1,10 @@
 #pragma once
 
-#include <boost/variant.hpp>
-
 #include "NetPacksBase.h"
 
 #include "BattleAction.h"
 #include "HeroBonus.h"
+#include "mapObjects/CGHeroInstance.h"
 #include "CCreatureSet.h"
 #include "mapping/CMapInfo.h"
 #include "StartInfo.h"
@@ -1022,11 +1021,10 @@ namespace ObjProperty
 		BONUS_VALUE_FIRST, BONUS_VALUE_SECOND, //used in Rampart for special building that generates resources (storing resource type and quantity)
 
 		//creature-bank specific
-		BANK_DAYCOUNTER, BANK_CLEAR_ARTIFACTS, BANK_ADD_ARTIFACT, BANK_MULTIPLIER, BANK_CONFIG_PRESET, 
-		BANK_CLEAR_CONFIG, BANK_INIT_ARMY, BANK_RESET,
+		BANK_DAYCOUNTER, BANK_RESET, BANK_CLEAR,
 
-		//magic spring
-		LEFT_VISITED, RIGHT_VISITED, LEFTRIGHT_CLEAR
+		//object with reward
+		REWARD_RESET, REWARD_SELECT
 	};
 }
 
@@ -1036,7 +1034,7 @@ struct SetObjectProperty : public CPackForClient//1001
 	void applyCl(CClient *cl);
 
 	ObjectInstanceID id;
-	ui8 what; //1 - owner; 2 - blockvis; 3 - first stack count; 4 - visitors; 5 - visited; 6 - ID (if 34 then also def is replaced)
+	ui8 what; // see ObjProperty enum
 	ui32 val;
 	SetObjectProperty(){type = 1001;};
 	SetObjectProperty(ObjectInstanceID ID, ui8 What, ui32 Val):id(ID),what(What),val(Val){type = 1001;};
@@ -1053,14 +1051,44 @@ struct SetHoverName : public CPackForClient//1002
 
 	ObjectInstanceID id;
 	MetaString name;
-	SetHoverName(){type = 1002;};
-	SetHoverName(ObjectInstanceID ID, MetaString& Name):id(ID),name(Name){type = 1002;};
+	SetHoverName(){type = 1002;}
+	SetHoverName(ObjectInstanceID ID, MetaString& Name):id(ID),name(Name){type = 1002;}
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & id & name;
 	}
 };
+
+struct ChangeObjectVisitors : public CPackForClient // 1003
+{
+	enum VisitMode
+	{
+		VISITOR_ADD,    // mark hero as one that have visited this object
+		VISITOR_REMOVE, // unmark visitor, reversed to ADD
+		VISITOR_CLEAR   // clear all visitors from this object (object reset)
+	};
+	ui32 mode; // uses VisitMode enum
+	ObjectInstanceID object;
+	ObjectInstanceID hero; // note: hero owner will be also marked as "visited" this object
+
+	DLL_LINKAGE void applyGs(CGameState *gs);
+
+	ChangeObjectVisitors()
+	{ type = 1003; }
+
+	ChangeObjectVisitors(ui32 mode, ObjectInstanceID object, ObjectInstanceID heroID = ObjectInstanceID(-1)):
+		mode(mode),
+		object(object),
+		hero(heroID)
+	{ type = 1003; }
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & object & hero & mode;
+	}
+};
+
 struct HeroLevelUp : public Query//2000
 {
 	void applyCl(CClient *cl);
@@ -1499,7 +1527,7 @@ struct ObstaclesRemoved : public CPackForClient //3014
 	}
 };
 
-struct CatapultAttack : public CPackForClient //3015
+struct DLL_LINKAGE CatapultAttack : public CPackForClient //3015
 {
 	struct AttackInfo
 	{
@@ -1515,8 +1543,7 @@ struct CatapultAttack : public CPackForClient //3015
 		}
 	};
 
-	DLL_LINKAGE CatapultAttack();
-	DLL_LINKAGE ~CatapultAttack();
+	CatapultAttack() {type = 3015;}
 
 	DLL_LINKAGE void applyGs(CGameState *gs);
 	void applyCl(CClient *cl);

+ 3 - 3
lib/NetPacksBase.h

@@ -25,7 +25,7 @@ struct ArtSlotInfo;
 #include "GameConstants.h"
 
 
-struct CPack
+struct DLL_LINKAGE CPack
 {
 	ui16 type;
 
@@ -37,7 +37,7 @@ struct CPack
         logNetwork->errorStream() << "CPack serialized... this should not happen!";
 	}
 	void applyGs(CGameState *gs) { }
-	virtual std::string toString() const { return boost::str(boost::format("{CPack: type '%d'}") % type); }
+	DLL_LINKAGE virtual std::string toString() const { return boost::str(boost::format("{CPack: type '%d'}") % type); }
 };
 
 std::ostream & operator<<(std::ostream & out, const CPack * pack);
@@ -197,4 +197,4 @@ struct ArtifactLocation
 	{
 		h & artHolder & slot;
 	}
-};
+};

+ 27 - 19
lib/NetPacksLib.cpp

@@ -2,10 +2,10 @@
 #include "NetPacks.h"
 
 #include "CGeneralTextHandler.h"
-#include "CDefObjInfoHandler.h"
+#include "mapObjects/CObjectClassesHandler.h"
 #include "CArtHandler.h"
 #include "CHeroHandler.h"
-#include "CObjectHandler.h"
+#include "mapObjects/CObjectHandler.h"
 #include "CModHandler.h"
 #include "VCMI_Lib.h"
 #include "mapping/CMap.h"
@@ -124,6 +124,8 @@ DLL_LINKAGE void HeroVisitCastle::applyGs( CGameState *gs )
 	CGHeroInstance *h = gs->getHero(hid);
 	CGTownInstance *t = gs->getTown(tid);
 
+	assert(h);
+	assert(t);
 	if(start())
 		t->setVisitingHero(h);
 	else
@@ -227,15 +229,14 @@ DLL_LINKAGE void GiveBonus::applyGs( CGameState *gs )
 		&& gs->map->objects[bonus.sid]->ID == Obj::EVENT) //it's morale/luck bonus from an event without description
 	{
 		descr = VLC->generaltexth->arraytxt[bonus.val > 0 ? 110 : 109]; //+/-%d Temporary until next battle"
-
-		// Some of(?) versions of H3 use %s here instead of %d. Try to replace both of them
-		boost::replace_first(descr,"%d",boost::lexical_cast<std::string>(std::abs(bonus.val)));
-		boost::replace_first(descr,"%s",boost::lexical_cast<std::string>(std::abs(bonus.val)));
 	}
 	else
 	{
 		bdescr.toString(descr);
 	}
+	// Some of(?) versions of H3 use %s here instead of %d. Try to replace both of them
+	boost::replace_first(descr,"%d",boost::lexical_cast<std::string>(std::abs(bonus.val)));
+	boost::replace_first(descr,"%s",boost::lexical_cast<std::string>(std::abs(bonus.val)));
 }
 
 DLL_LINKAGE void ChangeObjPos::applyGs( CGameState *gs )
@@ -251,6 +252,23 @@ DLL_LINKAGE void ChangeObjPos::applyGs( CGameState *gs )
 	gs->map->addBlockVisTiles(obj);
 }
 
+DLL_LINKAGE void ChangeObjectVisitors::applyGs( CGameState *gs )
+{
+	switch (mode) {
+		case VISITOR_ADD:
+			gs->getHero(hero)->visitedObjects.insert(object);
+			gs->getPlayer(gs->getHero(hero)->tempOwner)->visitedObjects.insert(object);
+			break;
+		case VISITOR_CLEAR:
+			for (CGHeroInstance * hero : gs->map->allHeroes)
+				hero->visitedObjects.erase(object); // remove visit info from all heroes, including those that are not present on map
+			break;
+		case VISITOR_REMOVE:
+			gs->getHero(hero)->visitedObjects.erase(object);
+			break;
+	}
+}
+
 DLL_LINKAGE void PlayerEndsGame::applyGs( CGameState *gs )
 {
 	PlayerState *p = gs->getPlayer(player);
@@ -547,7 +565,7 @@ DLL_LINKAGE void GiveHero::applyGs( CGameState *gs )
 	//bonus system
 	h->detachFrom(&gs->globalEffects);
 	h->attachTo(gs->getPlayer(player));
-	h->appearance = VLC->dobjinfo->pickCandidates(Obj::HERO, h->type->heroClass->id).front();
+	h->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, h->type->heroClass->id)->getTemplates().front();
 
 	gs->map->removeBlockVisTiles(h,true);
 	h->setOwner(player);
@@ -588,9 +606,9 @@ DLL_LINKAGE void NewObject::applyGs( CGameState *gs )
 	o->subID = subID;
 	o->pos = pos;
 	const TerrainTile &t = gs->map->getTile(pos);
-	o->appearance = VLC->dobjinfo->pickCandidates(o->ID, o->subID, t.terType).front();
+	o->appearance = VLC->objtypeh->getHandlerFor(o->ID, o->subID)->getTemplates(t.terType).front();
 	id = o->id = ObjectInstanceID(gs->map->objects.size());
-	o->hoverName = VLC->generaltexth->names[ID];
+	o->hoverName = VLC->objtypeh->getObjectName(ID);
 
 	gs->map->objects.push_back(o);
 	gs->map->addBlockVisTiles(o);
@@ -1477,16 +1495,6 @@ DLL_LINKAGE void ObstaclesRemoved::applyGs( CGameState *gs )
 	}
 }
 
-DLL_LINKAGE CatapultAttack::CatapultAttack()
-{
-	type = 3015;
-}
-
-DLL_LINKAGE CatapultAttack::~CatapultAttack()
-{
-
-}
-
 DLL_LINKAGE void CatapultAttack::applyGs( CGameState *gs )
 {
 	if(gs->curB && gs->curB->siege != CGTownInstance::NONE) //if there is a battle and it's a siege

+ 7 - 7
lib/VCMI_Lib.cpp

@@ -14,9 +14,9 @@
 #include "CArtHandler.h"
 #include "CBonusTypeHandler.h"
 #include "CCreatureHandler.h"
-#include "CDefObjInfoHandler.h"
+#include "mapObjects/CObjectClassesHandler.h"
 #include "CHeroHandler.h"
-#include "CObjectHandler.h"
+#include "mapObjects/CObjectHandler.h"
 #include "CTownHandler.h"
 #include "CBuildingHandler.h"
 #include "CSpellHandler.h"
@@ -45,11 +45,11 @@ DLL_LINKAGE void preinitDLL(CConsoleHandler *Console)
 
 DLL_LINKAGE void loadDLLClasses()
 {
-	try
+	//try
 	{
 		VLC->init();
 	}
-	HANDLE_EXCEPTION;
+	//HANDLE_EXCEPTION;
 }
 
 const IBonusTypeHandler * LibClasses::getBth() const
@@ -109,7 +109,7 @@ void LibClasses::init()
 
 	createHandler(objh, "Object", pomtime);
 
-	createHandler(dobjinfo, "Def information", pomtime);
+	createHandler(objtypeh, "Object types information", pomtime);
 
 	createHandler(spellh, "Spell", pomtime);
 
@@ -135,7 +135,7 @@ void LibClasses::clear()
 	delete creh;
 	delete townh;
 	delete objh;
-	delete dobjinfo;
+	delete objtypeh;
 	delete spellh;
 	delete modh;
 	delete bth;
@@ -152,7 +152,7 @@ void LibClasses::makeNull()
 	creh = nullptr;
 	townh = nullptr;
 	objh = nullptr;
-	dobjinfo = nullptr;
+	objtypeh = nullptr;
 	spellh = nullptr;
 	modh = nullptr;
 	bth = nullptr;

+ 3 - 3
lib/VCMI_Lib.h

@@ -16,7 +16,7 @@ class CCreatureHandler;
 class CSpellHandler;
 class CBuildingHandler;
 class CObjectHandler;
-class CDefObjInfoHandler;
+class CObjectClassesHandler;
 class CTownHandler;
 class CGeneralTextHandler;
 class CModHandler;
@@ -42,7 +42,7 @@ public:
 	CCreatureHandler * creh;
 	CSpellHandler * spellh;
 	CObjectHandler * objh;
-	CDefObjInfoHandler * dobjinfo;
+	CObjectClassesHandler * objtypeh;
 	CTownHandler * townh;
 	CGeneralTextHandler * generaltexth;
 	CModHandler * modh;
@@ -60,7 +60,7 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & heroh & arth & creh & townh & objh & dobjinfo & spellh & modh & IS_AI_ENABLED;
+		h & heroh & arth & creh & townh & objh & objtypeh & spellh & modh & IS_AI_ENABLED;
 		h & bth;
 		if(!h.saving)
 		{

+ 32 - 4
lib/VCMI_lib.cbp

@@ -132,8 +132,6 @@
 		<Unit filename="CCreatureHandler.h" />
 		<Unit filename="CCreatureSet.cpp" />
 		<Unit filename="CCreatureSet.h" />
-		<Unit filename="CDefObjInfoHandler.cpp" />
-		<Unit filename="CDefObjInfoHandler.h" />
 		<Unit filename="CGameInfoCallback.cpp" />
 		<Unit filename="CGameInfoCallback.h" />
 		<Unit filename="CGameInterface.cpp" />
@@ -146,8 +144,6 @@
 		<Unit filename="CHeroHandler.h" />
 		<Unit filename="CModHandler.cpp" />
 		<Unit filename="CModHandler.h" />
-		<Unit filename="CObjectHandler.cpp" />
-		<Unit filename="CObjectHandler.h" />
 		<Unit filename="CObstacleInstance.cpp" />
 		<Unit filename="CObstacleInstance.h" />
 		<Unit filename="CRandomGenerator.cpp" />
@@ -226,6 +222,37 @@
 		<Unit filename="logging/CBasicLogConfigurator.h" />
 		<Unit filename="logging/CLogger.cpp" />
 		<Unit filename="logging/CLogger.h" />
+		<Unit filename="mapObjects/CArmedInstance.cpp" />
+		<Unit filename="mapObjects/CArmedInstance.h" />
+		<Unit filename="mapObjects/CBank.cpp" />
+		<Unit filename="mapObjects/CBank.h" />
+		<Unit filename="mapObjects/CGHeroInstance.cpp" />
+		<Unit filename="mapObjects/CGHeroInstance.h" />
+		<Unit filename="mapObjects/CGMarket.cpp" />
+		<Unit filename="mapObjects/CGMarket.h" />
+		<Unit filename="mapObjects/CGPandoraBox.cpp" />
+		<Unit filename="mapObjects/CGPandoraBox.h" />
+		<Unit filename="mapObjects/CGTownInstance.cpp" />
+		<Unit filename="mapObjects/CGTownInstance.h" />
+		<Unit filename="mapObjects/CObjectClassesHandler.cpp" />
+		<Unit filename="mapObjects/CObjectClassesHandler.h" />
+		<Unit filename="mapObjects/CObjectHandler.cpp" />
+		<Unit filename="mapObjects/CObjectHandler.h" />
+		<Unit filename="mapObjects/CQuest.cpp" />
+		<Unit filename="mapObjects/CQuest.h" />
+		<Unit filename="mapObjects/CRewardableConstructor.cpp" />
+		<Unit filename="mapObjects/CRewardableConstructor.h" />
+		<Unit filename="mapObjects/CRewardableObject.cpp" />
+		<Unit filename="mapObjects/CRewardableObject.h" />
+		<Unit filename="mapObjects/CommonConstructors.cpp" />
+		<Unit filename="mapObjects/CommonConstructors.h" />
+		<Unit filename="mapObjects/JsonRandom.cpp" />
+		<Unit filename="mapObjects/JsonRandom.h" />
+		<Unit filename="mapObjects/MapObjects.h" />
+		<Unit filename="mapObjects/MiscObjects.cpp" />
+		<Unit filename="mapObjects/MiscObjects.h" />
+		<Unit filename="mapObjects/ObjectTemplate.cpp" />
+		<Unit filename="mapObjects/ObjectTemplate.h" />
 		<Unit filename="mapping/CCampaignHandler.cpp" />
 		<Unit filename="mapping/CCampaignHandler.h" />
 		<Unit filename="mapping/CMap.cpp" />
@@ -246,6 +273,7 @@
 		<Unit filename="registerTypes/TypesClientPacks2.cpp" />
 		<Unit filename="registerTypes/TypesMapObjects1.cpp" />
 		<Unit filename="registerTypes/TypesMapObjects2.cpp" />
+		<Unit filename="registerTypes/TypesMapObjects3.cpp" />
 		<Unit filename="registerTypes/TypesPregamePacks.cpp" />
 		<Unit filename="registerTypes/TypesServerPacks.cpp" />
 		<Unit filename="rmg/CMapGenOptions.cpp" />

+ 128 - 0
lib/mapObjects/CArmedInstance.cpp

@@ -0,0 +1,128 @@
+/*
+ * CArmedInstance.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 "CArmedInstance.h"
+
+#include "../CTownHandler.h"
+#include "../CCreatureHandler.h"
+#include "../CGeneralTextHandler.h"
+#include "../CGameState.h"
+
+using namespace boost::assign;
+
+void CArmedInstance::randomizeArmy(int type)
+{
+	for (auto & elem : stacks)
+	{
+		int & randID = elem.second->idRand;
+		if(randID >= 0)
+		{
+			int level = randID / 2;
+			bool upgrade = randID % 2;
+			elem.second->setType(VLC->townh->factions[type]->town->creatures[level][upgrade]);
+
+			randID = -1;
+		}
+		assert(elem.second->valid(false));
+		assert(elem.second->armyObj == this);
+	}
+	return;
+}
+
+CArmedInstance::CArmedInstance()
+{
+	battle = nullptr;
+}
+
+void CArmedInstance::updateMoraleBonusFromArmy()
+{
+	if(!validTypes(false)) //object not randomized, don't bother
+		return;
+
+	Bonus *b = getBonusList().getFirst(Selector::sourceType(Bonus::ARMY).And(Selector::type(Bonus::MORALE)));
+	if(!b)
+	{
+		b = new Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, 0, -1);
+		addNewBonus(b);
+	}
+
+	//number of alignments and presence of undead
+	std::set<TFaction> factions;
+	bool hasUndead = false;
+
+	for(auto slot : Slots())
+	{
+		const CStackInstance * inst = slot.second;
+		const CCreature * creature  = VLC->creh->creatures[inst->getCreatureID()];
+
+		factions.insert(creature->faction);
+		// Check for undead flag instead of faction (undead mummies are neutral)
+		hasUndead |= inst->hasBonusOfType(Bonus::UNDEAD);
+	}
+
+	size_t factionsInArmy = factions.size(); //town garrison seems to take both sets into account
+
+	// Take Angelic Alliance troop-mixing freedom of non-evil units into account.
+	if (hasBonusOfType(Bonus::NONEVIL_ALIGNMENT_MIX))
+	{
+		size_t mixableFactions = 0;
+
+		for(TFaction f : factions)
+		{
+			if (VLC->townh->factions[f]->alignment != EAlignment::EVIL)
+				mixableFactions++;
+		}
+		if (mixableFactions > 0)
+			factionsInArmy -= mixableFactions - 1;
+	}
+
+	if(factionsInArmy == 1)
+	{
+		b->val = +1;
+		b->description = VLC->generaltexth->arraytxt[115]; //All troops of one alignment +1
+	}
+	else if (!factions.empty()) // no bonus from empty garrison
+	{
+	 	b->val = 2 - factionsInArmy;
+		b->description = boost::str(boost::format(VLC->generaltexth->arraytxt[114]) % factionsInArmy % b->val); //Troops of %d alignments %d
+	}
+	boost::algorithm::trim(b->description);
+
+	//-1 modifier for any Undead unit in army
+	const ui8 UNDEAD_MODIFIER_ID = -2;
+	Bonus *undeadModifier = getBonusList().getFirst(Selector::source(Bonus::ARMY, UNDEAD_MODIFIER_ID));
+ 	if(hasUndead)
+	{
+		if(!undeadModifier)
+			addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, -1, UNDEAD_MODIFIER_ID, VLC->generaltexth->arraytxt[116]));
+	}
+	else if(undeadModifier)
+		removeBonus(undeadModifier);
+
+}
+
+void CArmedInstance::armyChanged()
+{
+	updateMoraleBonusFromArmy();
+}
+
+CBonusSystemNode * CArmedInstance::whereShouldBeAttached(CGameState *gs)
+{
+	if(tempOwner < PlayerColor::PLAYER_LIMIT)
+		return gs->getPlayer(tempOwner);
+	else
+		return &gs->globalEffects;
+}
+
+CBonusSystemNode * CArmedInstance::whatShouldBeAttached()
+{
+	return this;
+}

+ 40 - 0
lib/mapObjects/CArmedInstance.h

@@ -0,0 +1,40 @@
+#pragma once
+
+#include "CObjectHandler.h"
+#include "../CCreatureSet.h"
+
+/*
+ * CArmedInstance.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
+ *
+ */
+
+class DLL_LINKAGE CArmedInstance: public CGObjectInstance, public CBonusSystemNode, public CCreatureSet
+{
+public:
+	BattleInfo *battle; //set to the current battle, if engaged
+
+	void randomizeArmy(int type);
+	virtual void updateMoraleBonusFromArmy();
+
+	void armyChanged() override;
+
+	//////////////////////////////////////////////////////////////////////////
+//	int valOfGlobalBonuses(CSelector selector) const; //used only for castle interface								???
+	virtual CBonusSystemNode *whereShouldBeAttached(CGameState *gs);
+	virtual CBonusSystemNode *whatShouldBeAttached();
+	//////////////////////////////////////////////////////////////////////////
+
+	CArmedInstance();
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CGObjectInstance&>(*this);
+		h & static_cast<CBonusSystemNode&>(*this);
+		h & static_cast<CCreatureSet&>(*this);
+	}
+};

+ 329 - 0
lib/mapObjects/CBank.cpp

@@ -0,0 +1,329 @@
+/*
+ * CBank.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 "CBank.h"
+
+#include "../NetPacks.h"
+#include "../CGeneralTextHandler.h"
+#include "../CSoundBase.h"
+#include "CommonConstructors.h"
+#include "../CSpellHandler.h"
+
+using namespace boost::assign;
+
+///helpers
+static std::string & visitedTxt(const bool visited)
+{
+	int id = visited ? 352 : 353;
+	return VLC->generaltexth->allTexts[id];
+}
+
+CBank::CBank()
+{
+}
+
+CBank::~CBank()
+{
+}
+
+void CBank::initObj()
+{
+	daycounter = 0;
+	resetDuration = 0;
+	VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, cb->gameState()->getRandomGenerator());
+}
+
+const std::string & CBank::getHoverText() const
+{
+	bool visited = (bc == nullptr);
+	hoverName = visitedTxt(visited); // FIXME: USE BANK_SPECIFIC NAMES
+	return hoverName;
+}
+
+void CBank::setConfig(const BankConfig & config)
+{
+	bc.reset(new BankConfig(config));
+	clear(); // remove all stacks, if any
+
+	for (auto & stack : config.guards)
+		setCreature (SlotID(stacksCount()), stack.type->idNumber, stack.count);
+}
+
+void CBank::setPropertyDer (ui8 what, ui32 val)
+{
+	switch (what)
+	{
+		case ObjProperty::BANK_DAYCOUNTER: //daycounter
+				daycounter+=val;
+			break;
+		case ObjProperty::BANK_RESET:
+			initObj();
+			daycounter = 1; //yes, 1 since "today" daycounter won't be incremented
+			break;
+		case ObjProperty::BANK_CLEAR:
+			bc.reset();
+			break;
+	}
+}
+
+void CBank::newTurn() const
+{
+	if (bc == nullptr)
+	{
+		if (resetDuration != 0)
+		{
+			if (daycounter >= resetDuration)
+				cb->setObjProperty (id, ObjProperty::BANK_RESET, 0); //daycounter 0
+			else
+				cb->setObjProperty (id, ObjProperty::BANK_DAYCOUNTER, 1); //daycounter++
+		}
+	}
+}
+
+bool CBank::wasVisited (PlayerColor player) const
+{
+	return !bc; //FIXME: player A should not know about visit done by player B
+}
+
+void CBank::onHeroVisit (const CGHeroInstance * h) const
+{
+	if (bc)
+	{
+		int banktext = 0;
+		ui16 soundID = soundBase::ROGUE;
+		switch (ID)
+		{
+		case Obj::CREATURE_BANK:
+			banktext = 32;
+			break;
+		case Obj::DERELICT_SHIP:
+			banktext = 41;
+			break;
+		case Obj::DRAGON_UTOPIA:
+			banktext = 47;
+			break;
+		case Obj::CRYPT:
+			banktext = 119;
+			break;
+		case Obj::SHIPWRECK:
+			banktext = 122;
+			break;
+		case Obj::PYRAMID:
+			soundID = soundBase::MYSTERY;
+			banktext = 105;
+			break;
+		}
+		BlockingDialog bd (true, false);
+		bd.player = h->getOwner();
+		bd.soundID = soundID;
+		bd.text.addTxt(MetaString::ADVOB_TXT, banktext);
+		//if (ID == Obj::CREATURE_BANK)
+		//	bd.text.addReplacement(VLC->objh->creBanksNames[index]); // FIXME: USE BANK SPECIFIC NAMES
+		cb->showBlockingDialog (&bd);
+	}
+	else
+	{
+		InfoWindow iw;
+		iw.soundID = soundBase::GRAVEYARD;
+		iw.player = h->getOwner();
+		if (ID == Obj::PYRAMID) // You come upon the pyramid ... pyramid is completely empty.
+		{
+			iw.text << VLC->generaltexth->advobtxt[107];
+			iw.components.push_back (Component (Component::LUCK, 0 , -2, 0));
+			GiveBonus gb;
+			gb.bonus = Bonus(Bonus::ONE_BATTLE,Bonus::LUCK,Bonus::OBJECT,-2,id.getNum(),VLC->generaltexth->arraytxt[70]);
+			gb.id = h->id.getNum();
+			cb->giveHeroBonus(&gb);
+		}
+		else
+		{
+			iw.text << VLC->generaltexth->advobtxt[33];// This was X, now is completely empty
+			//iw.text.addReplacement(VLC->objh->creBanksNames[index]); // FIXME: USE BANK SPECIFIC NAMES
+		}
+		cb->showInfoDialog(&iw);
+	}
+}
+
+void CBank::doVisit(const CGHeroInstance * hero) const
+{
+	int textID = -1;
+	InfoWindow iw;
+	iw.player = hero->getOwner();
+	MetaString loot;
+
+	switch (ID)
+	{
+	case Obj::CREATURE_BANK:
+	case Obj::DRAGON_UTOPIA:
+		textID = 34;
+		break;
+	case Obj::DERELICT_SHIP:
+		if (!bc)
+			textID = 43;
+		else
+		{
+			GiveBonus gbonus;
+			gbonus.id = hero->id.getNum();
+			gbonus.bonus.duration = Bonus::ONE_BATTLE;
+			gbonus.bonus.source = Bonus::OBJECT;
+			gbonus.bonus.sid = ID;
+			gbonus.bdescr << "\n" << VLC->generaltexth->arraytxt[101];
+			gbonus.bonus.type = Bonus::MORALE;
+			gbonus.bonus.val = -1;
+			cb->giveHeroBonus(&gbonus);
+			textID = 42;
+			iw.components.push_back (Component (Component::MORALE, 0 , -1, 0));
+		}
+		break;
+	case Obj::CRYPT:
+		if (bc)
+			textID = 121;
+		else
+		{
+			iw.components.push_back (Component (Component::MORALE, 0 , -1, 0));
+			GiveBonus gbonus;
+			gbonus.id = hero->id.getNum();
+			gbonus.bonus.duration = Bonus::ONE_BATTLE;
+			gbonus.bonus.source = Bonus::OBJECT;
+			gbonus.bonus.sid = ID;
+			gbonus.bdescr << "\n" << VLC->generaltexth->arraytxt[ID];
+			gbonus.bonus.type = Bonus::MORALE;
+			gbonus.bonus.val = -1;
+			cb->giveHeroBonus(&gbonus);
+			textID = 120;
+			iw.components.push_back (Component (Component::MORALE, 0 , -1, 0));
+		}
+		break;
+	case Obj::SHIPWRECK:
+		if (bc)
+			textID = 124;
+		else
+			textID = 123;
+		break;
+	case Obj::PYRAMID:
+		textID = 106;
+	}
+
+	//grant resources
+	if (bc)
+	{
+		for (int it = 0; it < bc->resources.size(); it++)
+		{
+			if (bc->resources[it] != 0)
+			{
+				iw.components.push_back (Component (Component::RESOURCE, it, bc->resources[it], 0));
+				loot << "%d %s";
+				loot.addReplacement(iw.components.back().val);
+				loot.addReplacement(MetaString::RES_NAMES, iw.components.back().subtype);
+				cb->giveResource (hero->getOwner(), static_cast<Res::ERes>(it), bc->resources[it]);
+			}
+		}
+		//grant artifacts
+		for (auto & elem : bc->artifacts)
+		{
+			iw.components.push_back (Component (Component::ARTIFACT, elem, 0, 0));
+			loot << "%s";
+			loot.addReplacement(MetaString::ART_NAMES, elem);
+			cb->giveHeroNewArtifact (hero, VLC->arth->artifacts[elem], ArtifactPosition::FIRST_AVAILABLE);
+		}
+		//display loot
+		if (!iw.components.empty())
+		{
+			iw.text.addTxt (MetaString::ADVOB_TXT, textID);
+			if (textID == 34)
+			{
+				const CCreature * strongest = boost::range::max_element(bc->guards, [](const CStackBasicDescriptor & a, const CStackBasicDescriptor & b)
+				{
+					return a.type->fightValue < b.type->fightValue;
+				})->type;
+
+				iw.text.addReplacement(MetaString::CRE_PL_NAMES, strongest->idNumber);
+				iw.text.addReplacement(loot.buildList());
+			}
+			cb->showInfoDialog(&iw);
+		}
+
+		if (!bc->spells.empty())
+		{
+			std::set<SpellID> spells;
+
+			bool noWisdom = false;
+			for (SpellID spell : bc->spells)
+			{
+				iw.text.addTxt (MetaString::SPELL_NAME, spell);
+				if (VLC->spellh->objects[spell]->level <= hero->getSecSkillLevel(SecondarySkill::WISDOM) + 2)
+				{
+					spells.insert(spell);
+					iw.components.push_back(Component (Component::SPELL, spell, 0, 0));
+				}
+				else
+					noWisdom = true;
+			}
+
+			if (!hero->getArt(ArtifactPosition::SPELLBOOK))
+				iw.text.addTxt (MetaString::ADVOB_TXT, 109); //no spellbook
+			else if (noWisdom)
+				iw.text.addTxt (MetaString::ADVOB_TXT, 108); //no expert Wisdom
+			if (spells.empty())
+				cb->changeSpells (hero, true, spells);
+		}
+		loot.clear();
+		iw.components.clear();
+		iw.text.clear();
+
+		//grant creatures
+		CCreatureSet ourArmy;
+		for (auto slot : bc->creatures)
+		{
+			ourArmy.addToSlot(ourArmy.getSlotFor(slot.type->idNumber), slot.type->idNumber, slot.count);
+		}
+
+		for (auto & elem : ourArmy.Slots())
+		{
+			iw.components.push_back(Component(*elem.second));
+			loot << "%s";
+			loot.addReplacement(*elem.second);
+		}
+
+		if (ourArmy.Slots().size())
+		{
+			if (ourArmy.Slots().size() == 1 && ourArmy.Slots().begin()->second->count == 1)
+				iw.text.addTxt (MetaString::ADVOB_TXT, 185);
+			else
+				iw.text.addTxt (MetaString::ADVOB_TXT, 186);
+
+			iw.text.addReplacement(loot.buildList());
+			iw.text.addReplacement(hero->name);
+			cb->showInfoDialog(&iw);
+			cb->giveCreatures(this, hero, ourArmy, false);
+		}
+	cb->setObjProperty (id, ObjProperty::BANK_CLEAR, 0); //bc = nullptr
+	}
+}
+
+void CBank::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
+{
+	if (result.winner == 0)
+	{
+		doVisit(hero);
+	}
+}
+
+void CBank::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
+{
+	if (answer)
+	{
+		if (bc) // not looted bank
+			cb->startBattleI(hero, this, true);
+		else
+			doVisit(hero);
+	}
+}

+ 49 - 0
lib/mapObjects/CBank.h

@@ -0,0 +1,49 @@
+#pragma once
+
+#include "CObjectHandler.h"
+#include "CArmedInstance.h"
+
+/*
+ * CBank.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
+ *
+ */
+
+class BankConfig;
+class CBankInstanceConstructor;
+
+class DLL_LINKAGE CBank : public CArmedInstance
+{
+	std::unique_ptr<BankConfig> bc;
+	ui32 daycounter;
+	ui32 resetDuration;
+
+	void setPropertyDer(ui8 what, ui32 val) override;
+	void doVisit(const CGHeroInstance * hero) const;
+
+public:
+	CBank();
+	~CBank();
+
+	void setConfig(const BankConfig & bc);
+
+	void initObj() override;
+	const std::string & getHoverText() const override;
+	void newTurn() const override;
+	bool wasVisited (PlayerColor player) const override;
+	void onHeroVisit(const CGHeroInstance * h) const override;
+	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
+	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CArmedInstance&>(*this);
+		h & daycounter & bc & resetDuration;
+	}
+
+	friend class CBankInstanceConstructor;
+};

+ 1372 - 0
lib/mapObjects/CGHeroInstance.cpp

@@ -0,0 +1,1372 @@
+/*
+ * CGHeroInstance.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 "CGHeroInstance.h"
+
+#include "../NetPacks.h"
+#include "../CGeneralTextHandler.h"
+#include "../CHeroHandler.h"
+#include "../CSoundBase.h"
+#include "../CSpellHandler.h"
+#include "CObjectClassesHandler.h"
+
+
+using namespace boost::assign;
+
+///helpers
+static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const ui16 soundID)
+{
+	InfoWindow iw;
+	iw.soundID = soundID;
+	iw.player = playerID;
+	iw.text.addTxt(MetaString::ADVOB_TXT,txtID);
+	IObjectInterface::cb->sendAndApply(&iw);
+}
+
+static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 soundID)
+{
+	const PlayerColor playerID = h->getOwner();
+	showInfoDialog(playerID,txtID,soundID);
+}
+
+static int lowestSpeed(const CGHeroInstance * chi)
+{
+	if(!chi->Slots().size())
+	{
+        logGlobal->errorStream() << "Error! Hero " << chi->id.getNum() << " ("<<chi->name<<") has no army!";
+		return 20;
+	}
+	auto i = chi->Slots().begin();
+	//TODO? should speed modifiers (eg from artifacts) affect hero movement?
+	int ret = (i++)->second->valOfBonuses(Bonus::STACKS_SPEED);
+	for (;i!=chi->Slots().end();i++)
+	{
+		ret = std::min(ret, i->second->valOfBonuses(Bonus::STACKS_SPEED));
+	}
+	return ret;
+}
+
+ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &from) const
+{
+	//base move cost
+	unsigned ret = 100;
+
+	//if there is road both on dest and src tiles - use road movement cost
+    if(dest.roadType != ERoadType::NO_ROAD && from.roadType != ERoadType::NO_ROAD)
+	{
+        int road = std::min(dest.roadType,from.roadType); //used road ID
+		switch(road)
+		{
+        case ERoadType::DIRT_ROAD:
+			ret = 75;
+			break;
+        case ERoadType::GRAVEL_ROAD:
+			ret = 65;
+			break;
+        case ERoadType::COBBLESTONE_ROAD:
+			ret = 50;
+			break;
+		default:
+            logGlobal->errorStream() << "Unknown road type: " << road << "... Something wrong!";
+			break;
+		}
+	}
+	else
+	{
+		//FIXME: in H3 presence of Nomad in army will remove terrain penalty for sand. Bonus not implemented in VCMI
+
+		// NOTE: in H3 neutral stacks will ignore terrain penalty only if placed as topmost stack(s) in hero army.
+		// This is clearly bug in H3 however intended behaviour is not clear.
+		// Current VCMI behaviour will ignore neutrals in calculations so army in VCMI
+		// will always have best penalty without any influence from player-defined stacks order
+
+		bool nativeArmy = true;
+		for(auto stack : stacks)
+		{
+			int nativeTerrain = VLC->townh->factions[stack.second->type->faction]->nativeTerrain;
+
+            if (nativeTerrain != -1 && nativeTerrain != from.terType)
+			{
+				nativeArmy = false;
+				break;
+			}
+		}
+		if (!nativeArmy)
+            ret = VLC->heroh->terrCosts[from.terType];
+ 	}
+	return ret;
+}
+
+int3 CGHeroInstance::convertPosition(int3 src, bool toh3m) //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
+{
+	if (toh3m)
+	{
+		src.x+=1;
+		return src;
+	}
+	else
+	{
+		src.x-=1;
+		return src;
+	}
+}
+int3 CGHeroInstance::getPosition(bool h3m) const //h3m=true - returns position of hero object; h3m=false - returns position of hero 'manifestation'
+{
+	if (h3m)
+	{
+		return pos;
+	}
+	else
+	{
+		return convertPosition(pos,false);
+	}
+}
+
+bool CGHeroInstance::canWalkOnSea() const
+{
+	return hasBonusOfType(Bonus::FLYING_MOVEMENT) || hasBonusOfType(Bonus::WATER_WALKING);
+}
+
+ui8 CGHeroInstance::getSecSkillLevel(SecondarySkill skill) const
+{
+	for(auto & elem : secSkills)
+		if(elem.first == skill)
+			return elem.second;
+	return 0;
+}
+
+void CGHeroInstance::setSecSkillLevel(SecondarySkill which, int val, bool abs)
+{
+	if(getSecSkillLevel(which) == 0)
+	{
+		secSkills.push_back(std::pair<SecondarySkill,ui8>(which, val));
+		updateSkill(which, val);
+	}
+	else
+	{
+		for (auto & elem : secSkills)
+		{
+			if(elem.first == which)
+			{
+				if(abs)
+					elem.second = val;
+				else
+					elem.second += val;
+
+				if(elem.second > 3) //workaround to avoid crashes when same sec skill is given more than once
+				{
+                    logGlobal->warnStream() << "Warning: Skill " << which << " increased over limit! Decreasing to Expert.";
+					elem.second = 3;
+				}
+				updateSkill(which, elem.second); //when we know final value
+			}
+		}
+	}
+}
+
+bool CGHeroInstance::canLearnSkill() const
+{
+	return secSkills.size() < GameConstants::SKILL_PER_HERO;
+}
+
+int CGHeroInstance::maxMovePoints(bool onLand) const
+{
+	int base;
+
+	if(onLand)
+	{
+		// used function is f(x) = 66.6x + 1300, rounded to second digit, where x is lowest speed in army
+		static const int baseSpeed = 1300; // base speed from creature with 0 speed
+
+		int armySpeed = lowestSpeed(this) * 20 / 3;
+
+		base = armySpeed * 10 + baseSpeed; // separate *10 is intentional to receive same rounding as in h3
+		vstd::abetween(base, 1500, 2000); // base speed is limited by these values
+	}
+	else
+	{
+		base = 1500; //on water base movement is always 1500 (speed of army doesn't matter)
+	}
+
+	const Bonus::BonusType bt = onLand ? Bonus::LAND_MOVEMENT : Bonus::SEA_MOVEMENT;
+	const int bonus = valOfBonuses(Bonus::MOVEMENT) + valOfBonuses(bt);
+
+	const int subtype = onLand ? SecondarySkill::LOGISTICS : SecondarySkill::NAVIGATION;
+	const double modifier = valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, subtype) / 100.0;
+
+	return int(base* (1+modifier)) + bonus;
+}
+
+CGHeroInstance::CGHeroInstance()
+ : IBoatGenerator(this)
+{
+	setNodeType(HERO);
+	ID = Obj::HERO;
+	tacticFormationEnabled = inTownGarrison = false;
+	mana = movement = portrait = level = -1;
+	isStanding = true;
+	moveDir = 4;
+	exp = 0xffffffff;
+	visitedTown = nullptr;
+	type = nullptr;
+	boat = nullptr;
+	commander = nullptr;
+	sex = 0xff;
+	secSkills.push_back(std::make_pair(SecondarySkill::DEFAULT, -1));
+}
+
+void CGHeroInstance::initHero(HeroTypeID SUBID)
+{
+	subID = SUBID.getNum();
+	initHero();
+}
+
+void CGHeroInstance::setType(si32 ID, si32 subID)
+{
+	assert(ID == Obj::HERO); // just in case
+	type = VLC->heroh->heroes[subID];
+	portrait = type->imageIndex;
+	CGObjectInstance::setType(ID, type->heroClass->id);
+	randomizeArmy(type->heroClass->faction);
+}
+
+void CGHeroInstance::initHero()
+{
+	assert(validTypes(true));
+	if(!type)
+		type = VLC->heroh->heroes[subID];
+
+	if (ID == Obj::HERO)
+		appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->id)->getTemplates().front();
+
+	if(!vstd::contains(spells, SpellID::PRESET)) //hero starts with a spell
+	{
+		for(auto spellID : type->spells)
+			spells.insert(spellID);
+	}
+	else //remove placeholder
+		spells -= SpellID::PRESET;
+
+	if(!getArt(ArtifactPosition::MACH4) && !getArt(ArtifactPosition::SPELLBOOK) && type->haveSpellBook) //no catapult means we haven't read pre-existent set -> use default rules for spellbook
+		putArtifact(ArtifactPosition::SPELLBOOK, CArtifactInstance::createNewArtifactInstance(0));
+
+	if(!getArt(ArtifactPosition::MACH4))
+		putArtifact(ArtifactPosition::MACH4, CArtifactInstance::createNewArtifactInstance(3)); //everyone has a catapult
+
+	if(portrait < 0 || portrait == 255)
+		portrait = type->imageIndex;
+	if(!hasBonus(Selector::sourceType(Bonus::HERO_BASE_SKILL)))
+	{
+		for(int g=0; g<GameConstants::PRIMARY_SKILLS; ++g)
+		{
+			pushPrimSkill(static_cast<PrimarySkill::PrimarySkill>(g), type->heroClass->primarySkillInitial[g]);
+		}
+	}
+	if(secSkills.size() == 1 && secSkills[0] == std::pair<SecondarySkill,ui8>(SecondarySkill::DEFAULT, -1)) //set secondary skills to default
+		secSkills = type->secSkillsInit;
+	if (!name.length())
+		name = type->name;
+
+	if (sex == 0xFF)//sex is default
+		sex = type->sex;
+
+	setFormation(false);
+	if (!stacksCount()) //standard army//initial army
+	{
+		initArmy();
+	}
+	assert(validTypes());
+
+	level = 1;
+	if(exp == 0xffffffff)
+	{
+		initExp();
+	}
+	else
+	{
+		levelUpAutomatically();
+	}
+
+	if (VLC->modh->modules.COMMANDERS && !commander)
+	{
+		commander = new CCommanderInstance(type->heroClass->commander->idNumber);
+		commander->setArmyObj (castToArmyObj()); //TODO: separate function for setting commanders
+		commander->giveStackExp (exp); //after our exp is set
+	}
+
+	if (mana < 0)
+		mana = manaLimit();
+}
+
+void CGHeroInstance::initArmy(IArmyDescriptor *dst /*= nullptr*/)
+{
+	if(!dst)
+		dst = this;
+
+	int howManyStacks = 0; //how many stacks will hero receives <1 - 3>
+	int pom = cb->gameState()->getRandomGenerator().nextInt(99);
+	int warMachinesGiven = 0;
+
+	if(pom < 9)
+		howManyStacks = 1;
+	else if(pom < 79)
+		howManyStacks = 2;
+	else
+		howManyStacks = 3;
+
+	vstd::amin(howManyStacks, type->initialArmy.size());
+
+	for(int stackNo=0; stackNo < howManyStacks; stackNo++)
+	{
+		auto & stack = type->initialArmy[stackNo];
+
+		int count = cb->gameState()->getRandomGenerator().nextInt(stack.minAmount, stack.maxAmount);
+
+		if(stack.creature >= CreatureID::CATAPULT &&
+		   stack.creature <= CreatureID::ARROW_TOWERS) //war machine
+		{
+			warMachinesGiven++;
+			if(dst != this)
+				continue;
+
+			int slot = -1;
+			ArtifactID aid = ArtifactID::NONE;
+			switch (stack.creature)
+			{
+			case CreatureID::CATAPULT:
+				slot = ArtifactPosition::MACH4;
+				aid = ArtifactID::CATAPULT;
+				break;
+			default:
+				aid = CArtHandler::creatureToMachineID(stack.creature);
+				slot = 9 + aid;
+				break;
+			}
+			auto convSlot = ArtifactPosition(slot);
+			if(!getArt(convSlot))
+				putArtifact(convSlot, CArtifactInstance::createNewArtifactInstance(aid));
+			else
+                logGlobal->warnStream() << "Hero " << name << " already has artifact at " << slot << ", omitting giving " << aid;
+		}
+		else
+			dst->setCreature(SlotID(stackNo-warMachinesGiven), stack.creature, count);
+	}
+}
+
+CGHeroInstance::~CGHeroInstance()
+{
+	commander.dellNull();
+}
+
+bool CGHeroInstance::needsLastStack() const
+{
+	return true;
+}
+
+void CGHeroInstance::onHeroVisit(const CGHeroInstance * h) const
+{
+	if(h == this) return; //exclude potential self-visiting
+
+	if (ID == Obj::HERO)
+	{
+		if( cb->gameState()->getPlayerRelations(tempOwner, h->tempOwner)) //our or ally hero
+		{
+			//exchange
+			cb->heroExchange(h->id, id);
+		}
+		else //battle
+		{
+			if(visitedTown) //we're in town
+				visitedTown->onHeroVisit(h); //town will handle attacking
+			else
+				cb->startBattleI(h,	this);
+		}
+	}
+	else if(ID == Obj::PRISON)
+	{
+		int txt_id;
+
+		if (cb->getHeroCount(h->tempOwner, false) < VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)//GameConstants::MAX_HEROES_PER_PLAYER) //free hero slot
+		{
+			cb->changeObjPos(id,pos+int3(1,0,0),0);
+			//update hero parameters
+			SetMovePoints smp;
+			smp.hid = id;
+			smp.val = maxMovePoints (true); //TODO: hota prison on water?
+			cb->setMovePoints (&smp);
+			cb->setManaPoints (id, manaLimit());
+
+			cb->setObjProperty(id, ObjProperty::ID, Obj::HERO); //set ID to 34
+			cb->giveHero(id,h->tempOwner); //recreates def and adds hero to player
+
+			txt_id = 102;
+		}
+		else //already 8 wandering heroes
+		{
+			txt_id = 103;
+		}
+
+		showInfoDialog(h,txt_id,soundBase::ROGUE);
+	}
+}
+
+const std::string & CGHeroInstance::getHoverText() const
+{
+	if(ID != Obj::PRISON)
+	{
+		hoverName = VLC->generaltexth->allTexts[15];
+		boost::algorithm::replace_first(hoverName,"%s",name);
+		boost::algorithm::replace_first(hoverName,"%s", type->heroClass->name);
+		return hoverName;
+	}
+	else
+		hoverName = VLC->objtypeh->getObjectName(ID);
+
+	return hoverName;
+}
+
+const std::string & CGHeroInstance::getBiography() const
+{
+	if (biography.length())
+		return biography;
+	return type->biography;
+}
+
+ui8 CGHeroInstance::maxlevelsToMagicSchool() const
+{
+	return type->heroClass->isMagicHero() ? 3 : 4;
+}
+ui8 CGHeroInstance::maxlevelsToWisdom() const
+{
+	return type->heroClass->isMagicHero() ? 3 : 6;
+}
+
+void CGHeroInstance::SecondarySkillsInfo::resetMagicSchoolCounter()
+{
+	magicSchoolCounter = 1;
+}
+void CGHeroInstance::SecondarySkillsInfo::resetWisdomCounter()
+{
+	wisdomCounter = 1;
+}
+
+void CGHeroInstance::initObj()
+{
+	blockVisit = true;
+	auto  hs = new HeroSpecial();
+	hs->setNodeType(CBonusSystemNode::SPECIALTY);
+	attachTo(hs); //do we ever need to detach it?
+
+	if(!type)
+		initHero(); //TODO: set up everything for prison before specialties are configured
+
+	skillsInfo.rand.setSeed(cb->gameState()->getRandomGenerator().nextInt());
+	skillsInfo.resetMagicSchoolCounter();
+	skillsInfo.resetWisdomCounter();
+
+	auto customApp = VLC->objtypeh->getHandlerFor(ID, type->heroClass->id)->getOverride(cb->gameState()->getTile(visitablePos())->terType, this);
+	if (customApp)
+		appearance = customApp.get();
+
+	for(const auto &spec : type->spec) //TODO: unfity with bonus system
+	{
+		auto bonus = new Bonus();
+		bonus->val = spec.val;
+		bonus->sid = id.getNum(); //from the hero, specialty has no unique id
+		bonus->duration = Bonus::PERMANENT;
+		bonus->source = Bonus::HERO_SPECIAL;
+		switch (spec.type)
+		{
+			case 1:// creature specialty
+				{
+					hs->growsWithLevel = true;
+
+					const CCreature &specCreature = *VLC->creh->creatures[spec.additionalinfo]; //creature in which we have specialty
+
+					//int creLevel = specCreature.level;
+					//if(!creLevel)
+					//{
+					//	if(spec.additionalinfo == 146)
+					//		creLevel = 5; //treat ballista as 5-level
+					//	else
+					//	{
+					//        logGlobal->warnStream() << "Warning: unknown level of " << specCreature.namePl;
+					//		continue;
+					//	}
+					//}
+
+					//bonus->additionalInfo = spec.additionalinfo; //creature id, should not be used again - this works only with limiter
+					bonus->limiter.reset(new CCreatureTypeLimiter (specCreature, true)); //with upgrades
+					bonus->type = Bonus::PRIMARY_SKILL;
+					bonus->valType = Bonus::ADDITIVE_VALUE;
+
+					bonus->subtype = PrimarySkill::ATTACK;
+					hs->addNewBonus(bonus);
+
+					bonus = new Bonus(*bonus);
+					bonus->subtype = PrimarySkill::DEFENSE;
+					hs->addNewBonus(bonus);
+					//values will be calculated later
+
+					bonus = new Bonus(*bonus);
+					bonus->type = Bonus::STACKS_SPEED;
+					bonus->val = 1; //+1 speed
+					hs->addNewBonus(bonus);
+				}
+				break;
+			case 2://secondary skill
+				hs->growsWithLevel = true;
+				bonus->type = Bonus::SPECIAL_SECONDARY_SKILL; //needs to be recalculated with level, based on this value
+				bonus->valType = Bonus::BASE_NUMBER; // to receive nonzero value
+				bonus->subtype = spec.subtype; //skill id
+				bonus->val = spec.val; //value per level, in percent
+				hs->addNewBonus(bonus);
+				bonus = new Bonus(*bonus);
+
+				switch (spec.additionalinfo)
+				{
+					case 0: //normal
+						bonus->valType = Bonus::PERCENT_TO_BASE;
+						break;
+					case 1: //when it's navigation or there's no 'base' at all
+						bonus->valType = Bonus::PERCENT_TO_ALL;
+						break;
+				}
+				bonus->type = Bonus::SECONDARY_SKILL_PREMY; //value will be calculated later
+				hs->addNewBonus(bonus);
+				break;
+			case 3://spell damage bonus, level dependent but calculated elsewhere
+				bonus->type = Bonus::SPECIAL_SPELL_LEV;
+				bonus->subtype = spec.subtype;
+				hs->addNewBonus(bonus);
+				break;
+			case 4://creature stat boost
+				switch (spec.subtype)
+				{
+					case 1://attack
+						bonus->type = Bonus::PRIMARY_SKILL;
+						bonus->subtype = PrimarySkill::ATTACK;
+						break;
+					case 2://defense
+						bonus->type = Bonus::PRIMARY_SKILL;
+						bonus->subtype = PrimarySkill::DEFENSE;
+						break;
+					case 3:
+						bonus->type = Bonus::CREATURE_DAMAGE;
+						bonus->subtype = 0; //both min and max
+						break;
+					case 4://hp
+						bonus->type = Bonus::STACK_HEALTH;
+						break;
+					case 5:
+						bonus->type = Bonus::STACKS_SPEED;
+						break;
+					default:
+						continue;
+				}
+				bonus->additionalInfo = spec.additionalinfo; //creature id
+				bonus->valType = Bonus::ADDITIVE_VALUE;
+				bonus->limiter.reset(new CCreatureTypeLimiter (*VLC->creh->creatures[spec.additionalinfo], true));
+				hs->addNewBonus(bonus);
+				break;
+			case 5://spell damage bonus in percent
+				bonus->type = Bonus::SPECIFIC_SPELL_DAMAGE;
+				bonus->valType = Bonus::BASE_NUMBER; // current spell system is screwed
+				bonus->subtype = spec.subtype; //spell id
+				hs->addNewBonus(bonus);
+				break;
+			case 6://damage bonus for bless (Adela)
+				bonus->type = Bonus::SPECIAL_BLESS_DAMAGE;
+				bonus->subtype = spec.subtype; //spell id if you ever wanted to use it otherwise
+				bonus->additionalInfo = spec.additionalinfo; //damage factor
+				hs->addNewBonus(bonus);
+				break;
+			case 7://maxed mastery for spell
+				bonus->type = Bonus::MAXED_SPELL;
+				bonus->subtype = spec.subtype; //spell i
+				hs->addNewBonus(bonus);
+				break;
+			case 8://peculiar spells - enchantments
+				bonus->type = Bonus::SPECIAL_PECULIAR_ENCHANT;
+				bonus->subtype = spec.subtype; //spell id
+				bonus->additionalInfo = spec.additionalinfo;//0, 1 for Coronius
+				hs->addNewBonus(bonus);
+				break;
+			case 9://upgrade creatures
+			{
+				const auto &creatures = VLC->creh->creatures;
+				bonus->type = Bonus::SPECIAL_UPGRADE;
+				bonus->subtype = spec.subtype; //base id
+				bonus->additionalInfo = spec.additionalinfo; //target id
+				hs->addNewBonus(bonus);
+				bonus = new Bonus(*bonus);
+
+				for(auto cre_id : creatures[spec.subtype]->upgrades)
+				{
+					bonus->subtype = cre_id; //propagate for regular upgrades of base creature
+					hs->addNewBonus(bonus);
+					bonus = new Bonus(*bonus);
+				}
+				vstd::clear_pointer(bonus);
+				break;
+			}
+			case 10://resource generation
+				bonus->type = Bonus::GENERATE_RESOURCE;
+				bonus->subtype = spec.subtype;
+				hs->addNewBonus(bonus);
+				break;
+			case 11://starting skill with mastery (Adrienne)
+				setSecSkillLevel(SecondarySkill(spec.val), spec.additionalinfo, true);
+				break;
+			case 12://army speed
+				bonus->type = Bonus::STACKS_SPEED;
+				hs->addNewBonus(bonus);
+				break;
+			case 13://Dragon bonuses (Mutare)
+				bonus->type = Bonus::PRIMARY_SKILL;
+				bonus->valType = Bonus::ADDITIVE_VALUE;
+				switch (spec.subtype)
+				{
+					case 1:
+						bonus->subtype = PrimarySkill::ATTACK;
+						break;
+					case 2:
+						bonus->subtype = PrimarySkill::DEFENSE;
+						break;
+				}
+				bonus->limiter.reset(new HasAnotherBonusLimiter(Bonus::DRAGON_NATURE));
+				hs->addNewBonus(bonus);
+				break;
+			default:
+                logGlobal->warnStream() << "Unexpected hero specialty " << type;
+		}
+	}
+	specialty.push_back(hs); //will it work?
+
+	for (auto hs2 : type->specialty) //copy active (probably growing) bonuses from hero prootype to hero object
+	{
+		auto  hs = new HeroSpecial();
+		attachTo(hs); //do we ever need to detach it?
+
+		hs->setNodeType(CBonusSystemNode::SPECIALTY);
+		for (auto bonus : hs2.bonuses)
+		{
+			hs->addNewBonus (bonus);
+		}
+		hs->growsWithLevel = hs2.growsWithLevel;
+
+		specialty.push_back(hs); //will it work?
+	}
+
+	//initialize bonuses
+	recreateSecondarySkillsBonuses();
+	Updatespecialty();
+
+	mana = manaLimit(); //after all bonuses are taken into account, make sure this line is the last one
+	type->name = name;
+}
+void CGHeroInstance::Updatespecialty() //TODO: calculate special value of bonuses on-the-fly?
+{
+	for (auto hs : specialty)
+	{
+		if (hs->growsWithLevel)
+		{
+			//const auto &creatures = VLC->creh->creatures;
+
+			for(Bonus * b : hs->getBonusList())
+			{
+				switch (b->type)
+				{
+					case Bonus::SECONDARY_SKILL_PREMY:
+						b->val = (hs->valOfBonuses(Bonus::SPECIAL_SECONDARY_SKILL, b->subtype) * level);
+						break; //use only hero skills as bonuses to avoid feedback loop
+					case Bonus::PRIMARY_SKILL: //for creatures, that is
+					{
+						const CCreature * cre = nullptr;
+						int creLevel = 0;
+						if (auto creatureLimiter = std::dynamic_pointer_cast<CCreatureTypeLimiter>(b->limiter)) //TODO: more general eveluation of bonuses?
+						{
+							cre = creatureLimiter->creature;
+							creLevel = cre->level;
+							if (!creLevel)
+							{
+								creLevel = 5; //treat ballista as tier 5
+							}
+						}
+						else //no creature found, can't calculate value
+						{
+                            logGlobal->warnStream() << "Primary skill specialty growth supported only with creature type limiters";
+							break;
+						}
+
+						double primSkillModifier = (int)(level / creLevel) / 20.0;
+						int param;
+						switch (b->subtype)
+						{
+							case PrimarySkill::ATTACK:
+								param = cre->Attack();
+								break;
+							case PrimarySkill::DEFENSE:
+								param = cre->Defense();
+								break;
+							default:
+								continue;
+						}
+						b->val = ceil(param * (1 + primSkillModifier)) - param; //yep, overcomplicated but matches original
+						break;
+					}
+				}
+			}
+		}
+	}
+}
+
+void CGHeroInstance::recreateSecondarySkillsBonuses()
+{
+	auto secondarySkillsBonuses = getBonuses(Selector::sourceType(Bonus::SECONDARY_SKILL));
+	for(auto bonus : *secondarySkillsBonuses)
+		removeBonus(bonus);
+
+	for(auto skill_info : secSkills)
+		updateSkill(SecondarySkill(skill_info.first), skill_info.second);
+}
+
+void CGHeroInstance::updateSkill(SecondarySkill which, int val)
+{
+	if(which == SecondarySkill::LEADERSHIP || which == SecondarySkill::LUCK)
+	{ //luck-> VLC->generaltexth->arraytxt[73+luckSkill]; VLC->generaltexth->arraytxt[104+moraleSkill]
+		bool luck = which == SecondarySkill::LUCK;
+		Bonus::BonusType type[] = {Bonus::MORALE, Bonus::LUCK};
+
+		Bonus *b = getBonusLocalFirst(Selector::type(type[luck]).And(Selector::sourceType(Bonus::SECONDARY_SKILL)));
+		if(!b)
+		{
+			b = new Bonus(Bonus::PERMANENT, type[luck], Bonus::SECONDARY_SKILL, +val, which, which, Bonus::BASE_NUMBER);
+			addNewBonus(b);
+		}
+		else
+			b->val = +val;
+	}
+	else if(which == SecondarySkill::DIPLOMACY) //surrender discount: 20% per level
+	{
+
+		if(Bonus *b = getBonusLocalFirst(Selector::type(Bonus::SURRENDER_DISCOUNT).And(Selector::sourceType(Bonus::SECONDARY_SKILL))))
+			b->val = +val;
+		else
+			addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::SURRENDER_DISCOUNT, Bonus::SECONDARY_SKILL, val * 20, which));
+	}
+
+	int skillVal = 0;
+	switch (which)
+	{
+	case SecondarySkill::ARCHERY:
+		switch (val)
+		{
+		case 1:
+			skillVal = 10; break;
+		case 2:
+			skillVal = 25; break;
+		case 3:
+			skillVal = 50; break;
+		}
+		break;
+	case SecondarySkill::LOGISTICS:
+		skillVal = 10 * val; break;
+	case SecondarySkill::NAVIGATION:
+		skillVal = 50 * val; break;
+	case SecondarySkill::MYSTICISM:
+		skillVal = val; break;
+	case SecondarySkill::EAGLE_EYE:
+		skillVal = 30 + 10 * val; break;
+	case SecondarySkill::NECROMANCY:
+		skillVal = 10 * val; break;
+	case SecondarySkill::LEARNING:
+		skillVal = 5 * val; break;
+	case SecondarySkill::OFFENCE:
+		skillVal = 10 * val; break;
+	case SecondarySkill::ARMORER:
+		skillVal = 5 * val; break;
+	case SecondarySkill::INTELLIGENCE:
+		skillVal = 25 << (val-1); break;
+	case SecondarySkill::SORCERY:
+		skillVal = 5 * val; break;
+	case SecondarySkill::RESISTANCE:
+		skillVal = 5 << (val-1); break;
+	case SecondarySkill::FIRST_AID:
+		skillVal = 25 + 25*val; break;
+	case SecondarySkill::ESTATES:
+		skillVal = 125 << (val-1); break;
+	}
+
+
+	Bonus::ValueType skillValType = skillVal ? Bonus::BASE_NUMBER : Bonus::INDEPENDENT_MIN;
+	if(Bonus * b = getBonusList().getFirst(Selector::typeSubtype(Bonus::SECONDARY_SKILL_PREMY, which)
+										.And(Selector::sourceType(Bonus::SECONDARY_SKILL)))) //only local hero bonus
+	{
+		b->val = skillVal;
+		b->valType = skillValType;
+	}
+	else
+	{
+		auto bonus = new Bonus(Bonus::PERMANENT, Bonus::SECONDARY_SKILL_PREMY, Bonus::SECONDARY_SKILL, skillVal, id.getNum(), which, skillValType);
+		bonus->source = Bonus::SECONDARY_SKILL;
+		addNewBonus(bonus);
+	}
+
+}
+void CGHeroInstance::setPropertyDer( ui8 what, ui32 val )
+{
+	if(what == ObjProperty::PRIMARY_STACK_COUNT)
+		setStackCount(SlotID(0), val);
+}
+
+double CGHeroInstance::getFightingStrength() const
+{
+	return sqrt((1.0 + 0.05*getPrimSkillLevel(PrimarySkill::ATTACK)) * (1.0 + 0.05*getPrimSkillLevel(PrimarySkill::DEFENSE)));
+}
+
+double CGHeroInstance::getMagicStrength() const
+{
+	return sqrt((1.0 + 0.05*getPrimSkillLevel(PrimarySkill::KNOWLEDGE)) * (1.0 + 0.05*getPrimSkillLevel(PrimarySkill::SPELL_POWER)));
+}
+
+double CGHeroInstance::getHeroStrength() const
+{
+	return sqrt(pow(getFightingStrength(), 2.0) * pow(getMagicStrength(), 2.0));
+}
+
+ui64 CGHeroInstance::getTotalStrength() const
+{
+	double ret = getFightingStrength() * getArmyStrength();
+	return (ui64) ret;
+}
+
+TExpType CGHeroInstance::calculateXp(TExpType exp) const
+{
+	return exp * (100 + valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::LEARNING))/100.0;
+}
+
+ui8 CGHeroInstance::getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool) const
+{
+	si16 skill = -1; //skill level
+
+#define TRY_SCHOOL(schoolName, schoolMechanicsId, schoolOutId)	\
+	if(spell-> schoolName)									\
+	{															\
+		int thisSchool = std::max<int>(getSecSkillLevel( \
+			SecondarySkill(14 + (schoolMechanicsId))), \
+			valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 1 << (schoolMechanicsId))); \
+		if(thisSchool > skill)									\
+		{														\
+			skill = thisSchool;									\
+			if(outSelectedSchool)								\
+				*outSelectedSchool = schoolOutId;				\
+		}														\
+	}
+	TRY_SCHOOL(fire, 0, 1)
+	TRY_SCHOOL(air, 1, 0)
+	TRY_SCHOOL(water, 2, 2)
+	TRY_SCHOOL(earth, 3, 3)
+#undef TRY_SCHOOL
+
+
+
+	vstd::amax(skill, valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 0)); //any school bonus
+	vstd::amax(skill, valOfBonuses(Bonus::SPELL, spell->id.toEnum())); //given by artifact or other effect
+	if (hasBonusOfType(Bonus::MAXED_SPELL, spell->id))//hero specialty (Daremyth, Melodia)
+		skill = 3;
+	assert(skill >= 0 && skill <= 3);
+	return skill;
+}
+
+bool CGHeroInstance::canCastThisSpell(const CSpell * spell) const
+{
+	if(!getArt(ArtifactPosition::SPELLBOOK)) //if hero has no spellbook
+		return false;
+
+    if (spell->isSpecialSpell())
+    {
+        if (vstd::contains(spells, spell->id))
+        {//hero has this spell in spellbook
+            logGlobal->errorStream() << "Special spell in spellbook "<<spell->name;
+        }
+
+        if (hasBonusOfType(Bonus::SPELL, spell->id))
+            return true;
+
+        return false;
+    }
+    else
+    {
+        if(vstd::contains(spells, spell->id) //hero has this spell in spellbook
+            || (spell->air && hasBonusOfType(Bonus::AIR_SPELLS)) // this is air spell and hero can cast all air spells
+            || (spell->fire && hasBonusOfType(Bonus::FIRE_SPELLS)) // this is fire spell and hero can cast all fire spells
+            || (spell->water && hasBonusOfType(Bonus::WATER_SPELLS)) // this is water spell and hero can cast all water spells
+            || (spell->earth && hasBonusOfType(Bonus::EARTH_SPELLS)) // this is earth spell and hero can cast all earth spells
+            || hasBonusOfType(Bonus::SPELL, spell->id)
+            || hasBonusOfType(Bonus::SPELLS_OF_LEVEL, spell->level)
+            )
+            return true;
+
+        return false;
+    }
+}
+
+/**
+ * Calculates what creatures and how many to be raised from a battle.
+ * @param battleResult The results of the battle.
+ * @return Returns a pair with the first value indicating the ID of the creature
+ * type and second value the amount. Both values are returned as -1 if necromancy
+ * could not be applied.
+ */
+CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &battleResult) const
+{
+	const ui8 necromancyLevel = getSecSkillLevel(SecondarySkill::NECROMANCY);
+
+	// Hero knows necromancy or has Necromancer Cloak
+	if (necromancyLevel > 0 || hasBonusOfType(Bonus::IMPROVED_NECROMANCY))
+	{
+		double necromancySkill = valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::NECROMANCY)/100.0;
+		vstd::amin(necromancySkill, 1.0); //it's impossible to raise more creatures than all...
+		const std::map<ui32,si32> &casualties = battleResult.casualties[!battleResult.winner];
+		ui32 raisedUnits = 0;
+
+		// Figure out what to raise and how many.
+		const CreatureID creatureTypes[] = {CreatureID::SKELETON, CreatureID::WALKING_DEAD, CreatureID::WIGHTS, CreatureID::LICHES};
+		const bool improvedNecromancy = hasBonusOfType(Bonus::IMPROVED_NECROMANCY);
+		const CCreature *raisedUnitType = VLC->creh->creatures[creatureTypes[improvedNecromancy ? necromancyLevel : 0]];
+		const ui32 raisedUnitHP = raisedUnitType->valOfBonuses(Bonus::STACK_HEALTH);
+
+		//calculate creatures raised from each defeated stack
+		for (auto & casualtie : casualties)
+		{
+			// Get lost enemy hit points convertible to units.
+			CCreature * c = VLC->creh->creatures[casualtie.first];
+
+			const ui32 raisedHP = c->valOfBonuses(Bonus::STACK_HEALTH) * casualtie.second * necromancySkill;
+			raisedUnits += std::min<ui32>(raisedHP / raisedUnitHP, casualtie.second * necromancySkill); //limit to % of HP and % of original stack count
+		}
+
+		// Make room for new units.
+		SlotID slot = getSlotFor(raisedUnitType->idNumber);
+		if (slot == SlotID())
+		{
+			// If there's no room for unit, try it's upgraded version 2/3rds the size.
+			raisedUnitType = VLC->creh->creatures[*raisedUnitType->upgrades.begin()];
+			raisedUnits = (raisedUnits*2)/3;
+
+			slot = getSlotFor(raisedUnitType->idNumber);
+		}
+		if (raisedUnits <= 0)
+			raisedUnits = 1;
+
+		return CStackBasicDescriptor(raisedUnitType->idNumber, raisedUnits);
+	}
+
+	return CStackBasicDescriptor();
+}
+
+/**
+ * Show the necromancy dialog with information about units raised.
+ * @param raisedStack Pair where the first element represents ID of the raised creature
+ * and the second element the amount.
+ */
+void CGHeroInstance::showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const
+{
+	InfoWindow iw;
+	iw.soundID = soundBase::pickup01 + cb->gameState()->getRandomGenerator().nextInt(6);
+	iw.player = tempOwner;
+	iw.components.push_back(Component(raisedStack));
+
+	if (raisedStack.count > 1) // Practicing the dark arts of necromancy, ... (plural)
+	{
+		iw.text.addTxt(MetaString::GENERAL_TXT, 145);
+		iw.text.addReplacement(raisedStack.count);
+	}
+	else // Practicing the dark arts of necromancy, ... (singular)
+	{
+		iw.text.addTxt(MetaString::GENERAL_TXT, 146);
+	}
+	iw.text.addReplacement(raisedStack);
+
+	cb->showInfoDialog(&iw);
+}
+
+int3 CGHeroInstance::getSightCenter() const
+{
+	return getPosition(false);
+}
+
+int CGHeroInstance::getSightRadious() const
+{
+	return 5 + getSecSkillLevel(SecondarySkill::SCOUTING) + valOfBonuses(Bonus::SIGHT_RADIOUS); //default + scouting
+}
+
+si32 CGHeroInstance::manaRegain() const
+{
+	if (hasBonusOfType(Bonus::FULL_MANA_REGENERATION))
+		return manaLimit();
+
+	return 1 + valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 8) + valOfBonuses(Bonus::MANA_REGENERATION); //1 + Mysticism level
+}
+
+// /**
+//  * Places an artifact in hero's backpack. If it's a big artifact equips it
+//  * or discards it if it cannot be equipped.
+//  */
+// void CGHeroInstance::giveArtifact (ui32 aid) //use only for fixed artifacts
+// {
+// 	CArtifact * const artifact = VLC->arth->artifacts[aid]; //pointer to constant object
+// 	CArtifactInstance *ai = CArtifactInstance::createNewArtifactInstance(artifact);
+// 	ai->putAt(this, ai->firstAvailableSlot(this));
+// }
+
+int CGHeroInstance::getBoatType() const
+{
+	switch(type->heroClass->getAlignment())
+	{
+	case EAlignment::GOOD:
+		return 1;
+	case EAlignment::EVIL:
+		return 0;
+	case EAlignment::NEUTRAL:
+		return 2;
+	default:
+		throw std::runtime_error("Wrong alignment!");
+	}
+}
+
+void CGHeroInstance::getOutOffsets(std::vector<int3> &offsets) const
+{
+	static int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0), int3(1,1,0),int3(-1,1,0),int3(1,-1,0),int3(-1,-1,0) };
+	for (auto & dir : dirs)
+		offsets += dir;
+}
+
+int CGHeroInstance::getSpellCost(const CSpell *sp) const
+{
+	return sp->getCost(getSpellSchoolLevel(sp));
+}
+
+void CGHeroInstance::pushPrimSkill( PrimarySkill::PrimarySkill which, int val )
+{
+	assert(!hasBonus(Selector::typeSubtype(Bonus::PRIMARY_SKILL, which)
+						.And(Selector::sourceType(Bonus::HERO_BASE_SKILL))));
+	addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::PRIMARY_SKILL, Bonus::HERO_BASE_SKILL, val, id.getNum(), which));
+}
+
+EAlignment::EAlignment CGHeroInstance::getAlignment() const
+{
+	return type->heroClass->getAlignment();
+}
+
+void CGHeroInstance::initExp()
+{
+	exp = cb->gameState()->getRandomGenerator().nextInt(40, 89);
+}
+
+std::string CGHeroInstance::nodeName() const
+{
+	return "Hero " + name;
+}
+
+void CGHeroInstance::putArtifact(ArtifactPosition pos, CArtifactInstance *art)
+{
+	assert(!getArt(pos));
+	art->putAt(ArtifactLocation(this, pos));
+}
+
+void CGHeroInstance::putInBackpack(CArtifactInstance *art)
+{
+	putArtifact(art->firstBackpackSlot(this), art);
+}
+
+bool CGHeroInstance::hasSpellbook() const
+{
+	return getArt(ArtifactPosition::SPELLBOOK);
+}
+
+void CGHeroInstance::deserializationFix()
+{
+	artDeserializationFix(this);
+
+	for (auto hs : specialty)
+	{
+		attachTo (hs);
+	}
+}
+
+CBonusSystemNode * CGHeroInstance::whereShouldBeAttached(CGameState *gs)
+{
+	if(visitedTown)
+	{
+		if(inTownGarrison)
+			return visitedTown;
+		else
+			return &visitedTown->townAndVis;
+	}
+	else
+		return CArmedInstance::whereShouldBeAttached(gs);
+}
+
+int CGHeroInstance::movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark /*= false*/) const
+{
+	if(hasBonusOfType(Bonus::FREE_SHIP_BOARDING))
+		return (MPsBefore - basicCost) * static_cast<double>(maxMovePoints(disembark)) / maxMovePoints(!disembark);
+
+	return 0; //take all MPs otherwise
+}
+
+CGHeroInstance::ECanDig CGHeroInstance::diggingStatus() const
+{
+	if(movement < maxMovePoints(true))
+		return LACK_OF_MOVEMENT;
+    else if(cb->getTile(getPosition(false))->terType == ETerrainType::WATER)
+		return WRONG_TERRAIN;
+	else
+	{
+		const TerrainTile *t = cb->getTile(getPosition());
+		//TODO look for hole
+		//CGI->mh->getTerrainDescr(h->getPosition(false), hlp, false);
+		if(/*hlp.length() || */t->blockingObjects.size() > 1)
+			return TILE_OCCUPIED;
+		else
+			return CAN_DIG;
+	}
+}
+
+ArtBearer::ArtBearer CGHeroInstance::bearerType() const
+{
+	return ArtBearer::HERO;
+}
+
+std::vector<SecondarySkill> CGHeroInstance::getLevelUpProposedSecondarySkills() const
+{
+	std::vector<SecondarySkill> obligatorySkills; //hero is offered magic school or wisdom if possible
+	if (!skillsInfo.wisdomCounter)
+	{
+		if (cb->isAllowed(2, SecondarySkill::WISDOM) && !getSecSkillLevel(SecondarySkill::WISDOM))
+			obligatorySkills.push_back(SecondarySkill::WISDOM);
+	}
+	if (!skillsInfo.magicSchoolCounter)
+	{
+		std::vector<SecondarySkill> ss;
+		ss += SecondarySkill::FIRE_MAGIC, SecondarySkill::AIR_MAGIC, SecondarySkill::WATER_MAGIC, SecondarySkill::EARTH_MAGIC;
+
+		std::shuffle(ss.begin(), ss.end(), skillsInfo.rand.getStdGenerator());
+
+		for (auto skill : ss)
+		{
+			if (cb->isAllowed(2, skill) && !getSecSkillLevel(skill)) //only schools hero doesn't know yet
+			{
+				obligatorySkills.push_back(skill);
+				break; //only one
+			}
+		}
+	}
+
+	std::vector<SecondarySkill> skills;
+	//picking sec. skills for choice
+	std::set<SecondarySkill> basicAndAdv, expert, none;
+	for(int i=0;i<GameConstants::SKILL_QUANTITY;i++)
+		if (cb->isAllowed(2,i))
+			none.insert(SecondarySkill(i));
+
+	for(auto & elem : secSkills)
+	{
+		if(elem.second < SecSkillLevel::EXPERT)
+			basicAndAdv.insert(elem.first);
+		else
+			expert.insert(elem.first);
+		none.erase(elem.first);
+	}
+	for (auto s : obligatorySkills) //don't duplicate them
+	{
+		none.erase (s);
+		basicAndAdv.erase (s);
+		expert.erase (s);
+	}
+
+	//first offered skill:
+	// 1) give obligatory skill
+	// 2) give any other new skill
+	// 3) upgrade existing
+	if (canLearnSkill() && obligatorySkills.size() > 0)
+	{
+		skills.push_back (obligatorySkills[0]);
+	}
+	else if(none.size() && canLearnSkill()) //hero have free skill slot
+	{
+		skills.push_back(type->heroClass->chooseSecSkill(none, skillsInfo.rand)); //new skill
+		none.erase(skills.back());
+	}
+	else if(!basicAndAdv.empty())
+	{
+		skills.push_back(type->heroClass->chooseSecSkill(basicAndAdv, skillsInfo.rand)); //upgrade existing
+		basicAndAdv.erase(skills.back());
+	}
+
+	//second offered skill:
+	//1) upgrade existing
+	//2) give obligatory skill
+	//3) give any other new skill
+	if(!basicAndAdv.empty())
+	{
+		SecondarySkill s = type->heroClass->chooseSecSkill(basicAndAdv, skillsInfo.rand);//upgrade existing
+		skills.push_back(s);
+		basicAndAdv.erase(s);
+	}
+	else if (canLearnSkill() && obligatorySkills.size() > 1)
+	{
+		skills.push_back (obligatorySkills[1]);
+	}
+	else if(none.size() && canLearnSkill())
+	{
+		skills.push_back(type->heroClass->chooseSecSkill(none, skillsInfo.rand)); //give new skill
+		none.erase(skills.back());
+	}
+
+	return skills;
+}
+
+PrimarySkill::PrimarySkill CGHeroInstance::nextPrimarySkill() const
+{
+	assert(gainsLevel());
+	int randomValue = cb->gameState()->getRandomGenerator().nextInt(99), pom = 0, primarySkill = 0;
+	const auto & skillChances = (level > 9) ? type->heroClass->primarySkillLowLevel : type->heroClass->primarySkillHighLevel;
+
+	for(; primarySkill < GameConstants::PRIMARY_SKILLS; ++primarySkill)
+	{
+		pom += skillChances[primarySkill];
+		if(randomValue < pom)
+		{
+			break;
+		}
+	}
+
+	logGlobal->traceStream() << "The hero gets the primary skill " << primarySkill << " with a probability of " << randomValue << "%.";
+	return static_cast<PrimarySkill::PrimarySkill>(primarySkill);
+}
+
+boost::optional<SecondarySkill> CGHeroInstance::nextSecondarySkill() const
+{
+	assert(gainsLevel());
+
+	boost::optional<SecondarySkill> chosenSecondarySkill;
+	const auto proposedSecondarySkills = getLevelUpProposedSecondarySkills();
+	if(!proposedSecondarySkills.empty())
+	{
+		std::vector<SecondarySkill> learnedSecondarySkills;
+		for(auto secondarySkill : proposedSecondarySkills)
+		{
+			if(getSecSkillLevel(secondarySkill) > 0)
+			{
+				learnedSecondarySkills.push_back(secondarySkill);
+			}
+		}
+
+		auto & rand = cb->gameState()->getRandomGenerator();
+		if(learnedSecondarySkills.empty())
+		{
+			// there are only new skills to learn, so choose anyone of them
+			chosenSecondarySkill = *RandomGeneratorUtil::nextItem(proposedSecondarySkills, rand);
+		}
+		else
+		{
+			// preferably upgrade a already learned secondary skill
+			chosenSecondarySkill = *RandomGeneratorUtil::nextItem(learnedSecondarySkills, rand);
+		}
+	}
+	return chosenSecondarySkill;
+}
+
+void CGHeroInstance::setPrimarySkill(PrimarySkill::PrimarySkill primarySkill, si64 value, ui8 abs)
+{
+	if(primarySkill < PrimarySkill::EXPERIENCE)
+	{
+		Bonus * skill = getBonusLocalFirst(Selector::type(Bonus::PRIMARY_SKILL)
+											.And(Selector::subtype(primarySkill))
+											.And(Selector::sourceType(Bonus::HERO_BASE_SKILL)));
+		assert(skill);
+
+		if(abs)
+		{
+			skill->val = value;
+		}
+		else
+		{
+			skill->val += value;
+		}
+	}
+	else if(primarySkill == PrimarySkill::EXPERIENCE)
+	{
+		if(abs)
+		{
+			exp = value;
+		}
+		else
+		{
+			exp += value;
+		}
+	}
+}
+
+bool CGHeroInstance::gainsLevel() const
+{
+	return exp >= VLC->heroh->reqExp(level+1);
+}
+
+void CGHeroInstance::levelUp(std::vector<SecondarySkill> skills)
+{
+	++level;
+
+	//deterministic secondary skills
+	skillsInfo.magicSchoolCounter = (skillsInfo.magicSchoolCounter + 1) % maxlevelsToMagicSchool();
+	skillsInfo.wisdomCounter = (skillsInfo.wisdomCounter + 1) % maxlevelsToWisdom();
+	if(vstd::contains(skills, SecondarySkill::WISDOM))
+	{
+		skillsInfo.resetWisdomCounter();
+	}
+
+	SecondarySkill spellSchools[] = {
+		SecondarySkill::FIRE_MAGIC, SecondarySkill::AIR_MAGIC, SecondarySkill::WATER_MAGIC, SecondarySkill::EARTH_MAGIC};
+	for(auto skill : spellSchools)
+	{
+		if(vstd::contains(skills, skill))
+		{
+			skillsInfo.resetMagicSchoolCounter();
+			break;
+		}
+	}
+
+	//specialty
+	Updatespecialty();
+}
+
+void CGHeroInstance::levelUpAutomatically()
+{
+	while(gainsLevel())
+	{
+		const auto primarySkill = nextPrimarySkill();
+		setPrimarySkill(primarySkill, 1, false);
+
+		auto proposedSecondarySkills = getLevelUpProposedSecondarySkills();
+
+		const auto secondarySkill = nextSecondarySkill();
+		if(secondarySkill)
+		{
+			setSecSkillLevel(*secondarySkill, 1, false);
+		}
+
+		//TODO why has the secondary skills to be passed to the method?
+		levelUp(proposedSecondarySkills);
+	}
+}

+ 227 - 0
lib/mapObjects/CGHeroInstance.h

@@ -0,0 +1,227 @@
+#pragma once
+
+#include "CObjectHandler.h"
+#include "CArmedInstance.h"
+
+#include "../CArtHandler.h" // For CArtifactSet
+#include "../CRandomGenerator.h"
+
+/*
+ * CGHeroInstance.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
+ *
+ */
+
+class CHero;
+class CGBoat;
+
+class CGHeroPlaceholder : public CGObjectInstance
+{
+public:
+	//subID stores id of hero type. If it's 0xff then following field is used
+	ui8 power;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CGObjectInstance&>(*this);
+		h & power;
+	}
+};
+
+
+class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet
+{
+public:
+	enum ECanDig
+	{
+		CAN_DIG, LACK_OF_MOVEMENT, WRONG_TERRAIN, TILE_OCCUPIED
+	};
+	//////////////////////////////////////////////////////////////////////////
+
+	ui8 moveDir; //format:	123
+					//		8 4
+					//		765
+	mutable ui8 isStanding, tacticFormationEnabled;
+
+	//////////////////////////////////////////////////////////////////////////
+
+	ConstTransitivePtr<CHero> type;
+	TExpType exp; //experience points
+	ui32 level; //current level of hero
+	std::string name; //may be custom
+	std::string biography; //if custom
+	si32 portrait; //may be custom
+	si32 mana; // remaining spell points
+	std::vector<std::pair<SecondarySkill,ui8> > secSkills; //first - ID of skill, second - level of skill (1 - basic, 2 - adv., 3 - expert); if hero has ability (-1, -1) it meansthat it should have default secondary abilities
+	ui32 movement; //remaining movement points
+	ui8 sex;
+	bool inTownGarrison; // if hero is in town garrison
+	ConstTransitivePtr<CGTownInstance> visitedTown; //set if hero is visiting town or in the town garrison
+	ConstTransitivePtr<CCommanderInstance> commander;
+	const CGBoat *boat; //set to CGBoat when sailing
+
+
+	//std::vector<const CArtifact*> artifacts; //hero's artifacts from bag
+	//std::map<ui16, const CArtifact*> artifWorn; //map<position,artifact_id>; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5
+	std::set<SpellID> spells; //known spells (spell IDs)
+	std::set<ObjectInstanceID> visitedObjects;
+
+	struct DLL_LINKAGE Patrol
+	{
+		Patrol(){patrolling=false;patrolRadious=-1;};
+		bool patrolling;
+		ui32 patrolRadious;
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & patrolling & patrolRadious;
+		}
+	} patrol;
+
+	struct DLL_LINKAGE HeroSpecial : CBonusSystemNode
+	{
+		bool growsWithLevel;
+
+		HeroSpecial(){growsWithLevel = false;};
+
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & static_cast<CBonusSystemNode&>(*this);
+			h & growsWithLevel;
+		}
+	};
+
+	std::vector<HeroSpecial*> specialty;
+
+	struct DLL_LINKAGE SecondarySkillsInfo
+	{
+		//skills are determined, initialized at map start
+		//FIXME remove mutable
+		mutable CRandomGenerator rand;
+		ui8 magicSchoolCounter;
+		ui8 wisdomCounter;
+
+		void resetMagicSchoolCounter();
+		void resetWisdomCounter();
+
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & magicSchoolCounter & wisdomCounter & rand;
+		}
+	} skillsInfo;
+
+	int3 getSightCenter() const; //"center" tile from which the sight distance is calculated
+	int getSightRadious() const; //sight distance (should be used if player-owned structure)
+	//////////////////////////////////////////////////////////////////////////
+
+	int getBoatType() const; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral
+	void getOutOffsets(std::vector<int3> &offsets) const; //offsets to obj pos when we boat can be placed
+
+	//////////////////////////////////////////////////////////////////////////
+
+	bool hasSpellbook() const;
+	EAlignment::EAlignment getAlignment() const;
+	const std::string &getBiography() const;
+	bool needsLastStack()const;
+	ui32 getTileCost(const TerrainTile &dest, const TerrainTile &from) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling
+	ui32 getLowestCreatureSpeed() const;
+	int3 getPosition(bool h3m = false) const; //h3m=true - returns position of hero object; h3m=false - returns position of hero 'manifestation'
+	si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day
+	bool canWalkOnSea() const;
+	int getCurrentLuck(int stack=-1, bool town=false) const;
+	int getSpellCost(const CSpell *sp) const; //do not use during battles -> bonuses from army would be ignored
+
+	// ----- primary and secondary skill, experience, level handling -----
+
+	/// Returns true if hero has lower level than should upon his experience.
+	bool gainsLevel() const;
+
+	/// Returns the next primary skill on level up. Can only be called if hero can gain a level up.
+	PrimarySkill::PrimarySkill nextPrimarySkill() const;
+
+	/// Returns the next secondary skill randomly on level up. Can only be called if hero can gain a level up.
+	boost::optional<SecondarySkill> nextSecondarySkill() const;
+
+	/// Gets 0, 1 or 2 secondary skills which are proposed on hero level up.
+	std::vector<SecondarySkill> getLevelUpProposedSecondarySkills() const;
+
+	ui8 getSecSkillLevel(SecondarySkill skill) const; //0 - no skill
+
+	/// Returns true if hero has free secondary skill slot.
+	bool canLearnSkill() const;
+
+	void setPrimarySkill(PrimarySkill::PrimarySkill primarySkill, si64 value, ui8 abs);
+	void setSecSkillLevel(SecondarySkill which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value
+	void levelUp(std::vector<SecondarySkill> skills);
+
+	int maxMovePoints(bool onLand) const;
+	int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false) const;
+
+	//int getSpellSecLevel(int spell) const; //returns level of secondary ability (fire, water, earth, air magic) known to this hero and applicable to given spell; -1 if error
+	static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
+	double getFightingStrength() const; // takes attack / defense skill into account
+	double getMagicStrength() const; // takes knowledge / spell power skill into account
+	double getHeroStrength() const; // includes fighting and magic strength
+	ui64 getTotalStrength() const; // includes fighting strength and army strength
+	TExpType calculateXp(TExpType exp) const; //apply learning skill
+	ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const; //returns level on which given spell would be cast by this hero (0 - none, 1 - basic etc); optionally returns number of selected school by arg - 0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic,
+	bool canCastThisSpell(const CSpell * spell) const; //determines if this hero can cast given spell; takes into account existing spell in spellbook, existing spellbook and artifact bonuses
+	CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const;
+	void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const;
+	ECanDig diggingStatus() const; //0 - can dig; 1 - lack of movement; 2 -
+
+	//////////////////////////////////////////////////////////////////////////
+
+	void setType(si32 ID, si32 subID);
+
+	void initHero();
+	void initHero(HeroTypeID SUBID);
+
+	void putArtifact(ArtifactPosition pos, CArtifactInstance *art);
+	void putInBackpack(CArtifactInstance *art);
+	void initExp();
+	void initArmy(IArmyDescriptor *dst = nullptr);
+	//void giveArtifact (ui32 aid);
+	void pushPrimSkill(PrimarySkill::PrimarySkill which, int val);
+	ui8 maxlevelsToMagicSchool() const;
+	ui8 maxlevelsToWisdom() const;
+	void Updatespecialty();
+	void recreateSecondarySkillsBonuses();
+	void updateSkill(SecondarySkill which, int val);
+
+	CGHeroInstance();
+	virtual ~CGHeroInstance();
+	//////////////////////////////////////////////////////////////////////////
+	//
+	ArtBearer::ArtBearer bearerType() const override;
+	//////////////////////////////////////////////////////////////////////////
+
+	CBonusSystemNode *whereShouldBeAttached(CGameState *gs) override;
+	std::string nodeName() const override;
+	void deserializationFix();
+
+	void initObj() override;
+	void onHeroVisit(const CGHeroInstance * h) const override;
+	const std::string & getHoverText() const override;
+protected:
+	void setPropertyDer(ui8 what, ui32 val) override;//synchr
+
+private:
+	void levelUpAutomatically();
+
+public:
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CArmedInstance&>(*this);
+		h & static_cast<CArtifactSet&>(*this);
+		h & exp & level & name & biography & portrait & mana & secSkills & movement
+			& sex & inTownGarrison & spells & patrol & moveDir & skillsInfo;
+		h & visitedTown & boat;
+		h & type & specialty & commander;
+		BONUS_TREE_DESERIALIZATION_FIX
+		//visitied town pointer will be restored by map serialization method
+	}
+};

+ 335 - 0
lib/mapObjects/CGMarket.cpp

@@ -0,0 +1,335 @@
+/*
+ *
+ * CGMarket.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 "CGMarket.h"
+
+#include "../NetPacks.h"
+#include "../CGeneralTextHandler.h"
+
+using namespace boost::assign;
+
+///helpers
+static void openWindow(const OpenWindow::EWindow type, const int id1, const int id2 = -1)
+{
+	OpenWindow ow;
+	ow.window = type;
+	ow.id1 = id1;
+	ow.id2 = id2;
+	IObjectInterface::cb->sendAndApply(&ow);
+}
+
+bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode::EMarketMode mode) const
+{
+	switch(mode)
+	{
+	case EMarketMode::RESOURCE_RESOURCE:
+		{
+			double effectiveness = std::min((getMarketEfficiency() + 1.0) / 20.0, 0.5);
+
+			double r = VLC->objh->resVals[id1], //value of given resource
+				g = VLC->objh->resVals[id2] / effectiveness; //value of wanted resource
+
+			if(r>g) //if given resource is more expensive than wanted
+			{
+				val2 = ceil(r / g);
+				val1 = 1;
+			}
+			else //if wanted resource is more expensive
+			{
+				val1 = (g / r) + 0.5;
+				val2 = 1;
+			}
+		}
+		break;
+	case EMarketMode::CREATURE_RESOURCE:
+		{
+			const double effectivenessArray[] = {0.0, 0.3, 0.45, 0.50, 0.65, 0.7, 0.85, 0.9, 1.0};
+			double effectiveness = effectivenessArray[std::min(getMarketEfficiency(), 8)];
+
+			double r = VLC->creh->creatures[id1]->cost[6], //value of given creature in gold
+				g = VLC->objh->resVals[id2] / effectiveness; //value of wanted resource
+
+			if(r>g) //if given resource is more expensive than wanted
+			{
+				val2 = ceil(r / g);
+				val1 = 1;
+			}
+			else //if wanted resource is more expensive
+			{
+				val1 = (g / r) + 0.5;
+				val2 = 1;
+			}
+		}
+		break;
+	case EMarketMode::RESOURCE_PLAYER:
+		val1 = 1;
+		val2 = 1;
+		break;
+	case EMarketMode::RESOURCE_ARTIFACT:
+		{
+			double effectiveness = std::min((getMarketEfficiency() + 3.0) / 20.0, 0.6);
+			double r = VLC->objh->resVals[id1], //value of offered resource
+				g = VLC->arth->artifacts[id2]->price / effectiveness; //value of bought artifact in gold
+
+			if(id1 != 6) //non-gold prices are doubled
+				r /= 2;
+
+			val1 = std::max(1, (int)((g / r) + 0.5)); //don't sell arts for less than 1 resource
+			val2 = 1;
+		}
+		break;
+	case EMarketMode::ARTIFACT_RESOURCE:
+		{
+			double effectiveness = std::min((getMarketEfficiency() + 3.0) / 20.0, 0.6);
+			double r = VLC->arth->artifacts[id1]->price * effectiveness,
+				g = VLC->objh->resVals[id2];
+
+// 			if(id2 != 6) //non-gold prices are doubled
+// 				r /= 2;
+
+			val1 = 1;
+			val2 = std::max(1, (int)((r / g) + 0.5)); //at least one resource is given in return
+		}
+		break;
+	case EMarketMode::CREATURE_EXP:
+		{
+			val1 = 1;
+			val2 = (VLC->creh->creatures[id1]->AIValue / 40) * 5;
+		}
+		break;
+	case EMarketMode::ARTIFACT_EXP:
+		{
+			val1 = 1;
+
+			int givenClass = VLC->arth->artifacts[id1]->getArtClassSerial();
+			if(givenClass < 0 || givenClass > 3)
+			{
+				val2 = 0;
+				return false;
+			}
+
+			static const int expPerClass[] = {1000, 1500, 3000, 6000};
+			val2 = expPerClass[givenClass];
+		}
+		break;
+	default:
+		assert(0);
+		return false;
+	}
+
+	return true;
+}
+
+bool IMarket::allowsTrade(EMarketMode::EMarketMode mode) const
+{
+	return false;
+}
+
+int IMarket::availableUnits(EMarketMode::EMarketMode mode, int marketItemSerial) const
+{
+	switch(mode)
+	{
+	case EMarketMode::RESOURCE_RESOURCE:
+	case EMarketMode::ARTIFACT_RESOURCE:
+	case EMarketMode::CREATURE_RESOURCE:
+			return -1;
+	default:
+			return 1;
+	}
+}
+
+std::vector<int> IMarket::availableItemsIds(EMarketMode::EMarketMode mode) const
+{
+	std::vector<int> ret;
+	switch(mode)
+	{
+	case EMarketMode::RESOURCE_RESOURCE:
+	case EMarketMode::ARTIFACT_RESOURCE:
+	case EMarketMode::CREATURE_RESOURCE:
+		for (int i = 0; i < 7; i++)
+			ret.push_back(i);
+	}
+	return ret;
+}
+
+const IMarket * IMarket::castFrom(const CGObjectInstance *obj, bool verbose /*= true*/)
+{
+	switch(obj->ID)
+	{
+	case Obj::TOWN:
+		return static_cast<const CGTownInstance*>(obj);
+	case Obj::ALTAR_OF_SACRIFICE:
+	case Obj::BLACK_MARKET:
+	case Obj::TRADING_POST:
+	case Obj::TRADING_POST_SNOW:
+	case Obj::FREELANCERS_GUILD:
+		return static_cast<const CGMarket*>(obj);
+	case Obj::UNIVERSITY:
+		return static_cast<const CGUniversity*>(obj);
+	default:
+		if(verbose)
+            logGlobal->errorStream() << "Cannot cast to IMarket object with ID " << obj->ID;
+		return nullptr;
+	}
+}
+
+IMarket::IMarket(const CGObjectInstance *O)
+	:o(O)
+{
+
+}
+
+std::vector<EMarketMode::EMarketMode> IMarket::availableModes() const
+{
+	std::vector<EMarketMode::EMarketMode> ret;
+	for (int i = 0; i < EMarketMode::MARTKET_AFTER_LAST_PLACEHOLDER; i++)
+		if(allowsTrade((EMarketMode::EMarketMode)i))
+			ret.push_back((EMarketMode::EMarketMode)i);
+
+	return ret;
+}
+
+void CGMarket::onHeroVisit(const CGHeroInstance * h) const
+{
+	openWindow(OpenWindow::MARKET_WINDOW,id.getNum(),h->id.getNum());
+}
+
+int CGMarket::getMarketEfficiency() const
+{
+	return 5;
+}
+
+bool CGMarket::allowsTrade(EMarketMode::EMarketMode mode) const
+{
+	switch(mode)
+	{
+	case EMarketMode::RESOURCE_RESOURCE:
+	case EMarketMode::RESOURCE_PLAYER:
+		switch(ID)
+		{
+		case Obj::TRADING_POST:
+		case Obj::TRADING_POST_SNOW:
+			return true;
+		default:
+			return false;
+		}
+	case EMarketMode::CREATURE_RESOURCE:
+		return ID == Obj::FREELANCERS_GUILD;
+	//case ARTIFACT_RESOURCE:
+	case EMarketMode::RESOURCE_ARTIFACT:
+		return ID == Obj::BLACK_MARKET;
+	case EMarketMode::ARTIFACT_EXP:
+	case EMarketMode::CREATURE_EXP:
+		return ID == Obj::ALTAR_OF_SACRIFICE; //TODO? check here for alignment of visiting hero? - would not be coherent with other checks here
+	case EMarketMode::RESOURCE_SKILL:
+		return ID == Obj::UNIVERSITY;
+	default:
+		return false;
+	}
+}
+
+int CGMarket::availableUnits(EMarketMode::EMarketMode mode, int marketItemSerial) const
+{
+	return -1;
+}
+
+std::vector<int> CGMarket::availableItemsIds(EMarketMode::EMarketMode mode) const
+{
+	switch(mode)
+	{
+	case EMarketMode::RESOURCE_RESOURCE:
+	case EMarketMode::RESOURCE_PLAYER:
+		return IMarket::availableItemsIds(mode);
+	default:
+		return std::vector<int>();
+	}
+}
+
+CGMarket::CGMarket()
+	:IMarket(this)
+{
+}
+
+std::vector<int> CGBlackMarket::availableItemsIds(EMarketMode::EMarketMode mode) const
+{
+	switch(mode)
+	{
+	case EMarketMode::ARTIFACT_RESOURCE:
+		return IMarket::availableItemsIds(mode);
+	case EMarketMode::RESOURCE_ARTIFACT:
+		{
+			std::vector<int> ret;
+			for(const CArtifact *a : artifacts)
+				if(a)
+					ret.push_back(a->id);
+				else
+					ret.push_back(-1);
+			return ret;
+		}
+	default:
+		return std::vector<int>();
+	}
+}
+
+void CGBlackMarket::newTurn() const
+{
+	if(cb->getDate(Date::DAY_OF_MONTH) != 1) //new month
+		return;
+
+	SetAvailableArtifacts saa;
+	saa.id = id.getNum();
+	cb->pickAllowedArtsSet(saa.arts);
+	cb->sendAndApply(&saa);
+}
+
+void CGUniversity::initObj()
+{
+	std::vector<int> toChoose;
+	for(int i = 0; i < GameConstants::SKILL_QUANTITY; ++i)
+	{
+		if(cb->isAllowed(2, i))
+		{
+			toChoose.push_back(i);
+		}
+	}
+	if(toChoose.size() < 4)
+	{
+        logGlobal->warnStream()<<"Warning: less then 4 available skills was found by University initializer!";
+		return;
+	}
+
+	// get 4 skills
+	for(int i = 0; i < 4; ++i)
+	{
+		// move randomly one skill to selected and remove from list
+		auto it = RandomGeneratorUtil::nextItem(toChoose, cb->gameState()->getRandomGenerator());
+		skills.push_back(*it);
+		toChoose.erase(it);
+	}
+}
+
+std::vector<int> CGUniversity::availableItemsIds(EMarketMode::EMarketMode mode) const
+{
+	switch (mode)
+	{
+		case EMarketMode::RESOURCE_SKILL:
+			return skills;
+
+		default:
+			return std::vector <int> ();
+	}
+}
+
+void CGUniversity::onHeroVisit(const CGHeroInstance * h) const
+{
+	openWindow(OpenWindow::UNIVERSITY_WINDOW,id.getNum(),h->id.getNum());
+}

+ 88 - 0
lib/mapObjects/CGMarket.h

@@ -0,0 +1,88 @@
+#pragma once
+
+#include "CObjectHandler.h"
+
+/*
+ * CGMarket.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
+ *
+ */
+
+class DLL_LINKAGE IMarket
+{
+public:
+	const CGObjectInstance *o;
+
+	IMarket(const CGObjectInstance *O);
+	virtual ~IMarket() {}
+
+	virtual int getMarketEfficiency() const =0;
+	virtual bool allowsTrade(EMarketMode::EMarketMode mode) const;
+	virtual int availableUnits(EMarketMode::EMarketMode mode, int marketItemSerial) const; //-1 if unlimited
+	virtual std::vector<int> availableItemsIds(EMarketMode::EMarketMode mode) const;
+
+	bool getOffer(int id1, int id2, int &val1, int &val2, EMarketMode::EMarketMode mode) const; //val1 - how many units of id1 player has to give to receive val2 units
+	std::vector<EMarketMode::EMarketMode> availableModes() const;
+
+	static const IMarket *castFrom(const CGObjectInstance *obj, bool verbose = true);
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & o;
+	}
+};
+
+class DLL_LINKAGE CGMarket : public CGObjectInstance, public IMarket
+{
+public:
+	CGMarket();
+	///IObjectIntercae
+	void onHeroVisit(const CGHeroInstance * h) const override; //open trading window
+
+	///IMarket
+	int getMarketEfficiency() const override;
+	bool allowsTrade(EMarketMode::EMarketMode mode) const override;
+	int availableUnits(EMarketMode::EMarketMode mode, int marketItemSerial) const override; //-1 if unlimited
+	std::vector<int> availableItemsIds(EMarketMode::EMarketMode mode) const override;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CGObjectInstance&>(*this);
+		h & static_cast<IMarket&>(*this);
+	}
+};
+
+class DLL_LINKAGE CGBlackMarket : public CGMarket
+{
+public:
+	std::vector<const CArtifact *> artifacts; //available artifacts
+
+	void newTurn() const override; //reset artifacts for black market every month
+	std::vector<int> availableItemsIds(EMarketMode::EMarketMode mode) const override;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CGMarket&>(*this);
+		h & artifacts;
+	}
+};
+
+class DLL_LINKAGE CGUniversity : public CGMarket
+{
+public:
+	std::vector<int> skills; //available skills
+
+	std::vector<int> availableItemsIds(EMarketMode::EMarketMode mode) const;
+	void initObj() override;//set skills for trade
+	void onHeroVisit(const CGHeroInstance * h) const override; //open window
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CGMarket&>(*this);
+		h & skills;
+	}
+};

+ 364 - 0
lib/mapObjects/CGPandoraBox.cpp

@@ -0,0 +1,364 @@
+/*
+ * CGPandoraBox.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 "CGPandoraBox.h"
+
+#include "../NetPacks.h"
+#include "../CSoundBase.h"
+
+#include "../CSpellHandler.h"
+
+using namespace boost::assign;
+
+///helpers
+static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const ui16 soundID)
+{
+	InfoWindow iw;
+	iw.soundID = soundID;
+	iw.player = playerID;
+	iw.text.addTxt(MetaString::ADVOB_TXT,txtID);
+	IObjectInterface::cb->sendAndApply(&iw);
+}
+
+static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 soundID)
+{
+	const PlayerColor playerID = h->getOwner();
+	showInfoDialog(playerID,txtID,soundID);
+}
+
+void CGPandoraBox::initObj()
+{
+	blockVisit = (ID==Obj::PANDORAS_BOX); //block only if it's really pandora's box (events also derive from that class)
+	hasGuardians = stacks.size();
+}
+
+void CGPandoraBox::onHeroVisit(const CGHeroInstance * h) const
+{
+		BlockingDialog bd (true, false);
+		bd.player = h->getOwner();
+		bd.soundID = soundBase::QUEST;
+		bd.text.addTxt (MetaString::ADVOB_TXT, 14);
+		cb->showBlockingDialog (&bd);
+}
+
+void CGPandoraBox::giveContentsUpToExp(const CGHeroInstance *h) const
+{
+	cb->removeAfterVisit(this);
+
+	InfoWindow iw;
+	iw.player = h->getOwner();
+
+	bool changesPrimSkill = false;
+	for (auto & elem : primskills)
+	{
+		if(elem)
+		{
+			changesPrimSkill = true;
+			break;
+		}
+	}
+
+	if(gainedExp || changesPrimSkill || abilities.size())
+	{
+		TExpType expVal = h->calculateXp(gainedExp);
+		//getText(iw,afterBattle,175,h); //wtf?
+		iw.text.addTxt(MetaString::ADVOB_TXT, 175); //%s learns something
+		iw.text.addReplacement(h->name);
+
+		if(expVal)
+			iw.components.push_back(Component(Component::EXPERIENCE,0,expVal,0));
+		for(int i=0; i<primskills.size(); i++)
+			if(primskills[i])
+				iw.components.push_back(Component(Component::PRIM_SKILL,i,primskills[i],0));
+
+		for(int i=0; i<abilities.size(); i++)
+			iw.components.push_back(Component(Component::SEC_SKILL,abilities[i],abilityLevels[i],0));
+
+		cb->showInfoDialog(&iw);
+
+		//give sec skills
+		for(int i=0; i<abilities.size(); i++)
+		{
+			int curLev = h->getSecSkillLevel(abilities[i]);
+
+			if( (curLev  &&  curLev < abilityLevels[i])	|| (h->canLearnSkill() ))
+			{
+				cb->changeSecSkill(h,abilities[i],abilityLevels[i],true);
+			}
+		}
+
+		//give prim skills
+		for(int i=0; i<primskills.size(); i++)
+			if(primskills[i])
+				cb->changePrimSkill(h,static_cast<PrimarySkill::PrimarySkill>(i),primskills[i],false);
+
+		assert(!cb->isVisitCoveredByAnotherQuery(this, h));
+
+		//give exp
+		if(expVal)
+			cb->changePrimSkill(h, PrimarySkill::EXPERIENCE, expVal, false);
+	}
+
+	if(!cb->isVisitCoveredByAnotherQuery(this, h))
+		giveContentsAfterExp(h);
+	//Otherwise continuation occurs via post-level-up callback.
+}
+
+void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const
+{
+	bool hadGuardians = hasGuardians; //copy, because flag will be emptied after issuing first post-battle message
+
+	std::string msg = message; //in case box is removed in the meantime
+	InfoWindow iw;
+	iw.player = h->getOwner();
+
+	if(spells.size())
+	{
+		std::set<SpellID> spellsToGive;
+		iw.components.clear();
+		if (spells.size() > 1)
+		{
+			iw.text.addTxt(MetaString::ADVOB_TXT, 188); //%s learns spells
+		}
+		else
+		{
+			iw.text.addTxt(MetaString::ADVOB_TXT, 184); //%s learns a spell
+		}
+		iw.text.addReplacement(h->name);
+		std::vector<ConstTransitivePtr<CSpell> > * sp = &VLC->spellh->objects;
+		for(auto i=spells.cbegin(); i != spells.cend(); i++)
+		{
+			if ((*sp)[*i]->level <= h->getSecSkillLevel(SecondarySkill::WISDOM) + 2) //enough wisdom
+			{
+				iw.components.push_back(Component(Component::SPELL,*i,0,0));
+				spellsToGive.insert(*i);
+			}
+		}
+		if(!spellsToGive.empty())
+		{
+			cb->changeSpells(h,true,spellsToGive);
+			cb->showInfoDialog(&iw);
+		}
+	}
+
+	if(manaDiff)
+	{
+		getText(iw,hadGuardians,manaDiff,176,177,h);
+		iw.components.push_back(Component(Component::PRIM_SKILL,5,manaDiff,0));
+		cb->showInfoDialog(&iw);
+		cb->setManaPoints(h->id, h->mana + manaDiff);
+	}
+
+	if(moraleDiff)
+	{
+		getText(iw,hadGuardians,moraleDiff,178,179,h);
+		iw.components.push_back(Component(Component::MORALE,0,moraleDiff,0));
+		cb->showInfoDialog(&iw);
+		GiveBonus gb;
+		gb.bonus = Bonus(Bonus::ONE_BATTLE,Bonus::MORALE,Bonus::OBJECT,moraleDiff,id.getNum(),"");
+		gb.id = h->id.getNum();
+		cb->giveHeroBonus(&gb);
+	}
+
+	if(luckDiff)
+	{
+		getText(iw,hadGuardians,luckDiff,180,181,h);
+		iw.components.push_back(Component(Component::LUCK,0,luckDiff,0));
+		cb->showInfoDialog(&iw);
+		GiveBonus gb;
+		gb.bonus = Bonus(Bonus::ONE_BATTLE,Bonus::LUCK,Bonus::OBJECT,luckDiff,id.getNum(),"");
+		gb.id = h->id.getNum();
+		cb->giveHeroBonus(&gb);
+	}
+
+	iw.components.clear();
+	iw.text.clear();
+	for(int i=0; i<resources.size(); i++)
+	{
+		if(resources[i] < 0)
+			iw.components.push_back(Component(Component::RESOURCE,i,resources[i],0));
+	}
+	if(iw.components.size())
+	{
+		getText(iw,hadGuardians,182,h);
+		cb->showInfoDialog(&iw);
+	}
+
+	iw.components.clear();
+	iw.text.clear();
+	for(int i=0; i<resources.size(); i++)
+	{
+		if(resources[i] > 0)
+			iw.components.push_back(Component(Component::RESOURCE,i,resources[i],0));
+	}
+	if(iw.components.size())
+	{
+		getText(iw,hadGuardians,183,h);
+		cb->showInfoDialog(&iw);
+	}
+
+	iw.components.clear();
+	// 	getText(iw,afterBattle,183,h);
+	iw.text.addTxt(MetaString::ADVOB_TXT, 183); //% has found treasure
+	iw.text.addReplacement(h->name);
+	for(auto & elem : artifacts)
+	{
+		iw.components.push_back(Component(Component::ARTIFACT,elem,0,0));
+		if(iw.components.size() >= 14)
+		{
+			cb->showInfoDialog(&iw);
+			iw.components.clear();
+			iw.text.addTxt(MetaString::ADVOB_TXT, 183); //% has found treasure - once more?
+			iw.text.addReplacement(h->name);
+		}
+	}
+	if(iw.components.size())
+	{
+		cb->showInfoDialog(&iw);
+	}
+
+	for(int i=0; i<resources.size(); i++)
+		if(resources[i])
+			cb->giveResource(h->getOwner(),static_cast<Res::ERes>(i),resources[i]);
+
+	for(auto & elem : artifacts)
+		cb->giveHeroNewArtifact(h, VLC->arth->artifacts[elem],ArtifactPosition::FIRST_AVAILABLE);
+
+	iw.components.clear();
+	iw.text.clear();
+
+	if (creatures.Slots().size())
+	{ //this part is taken straight from creature bank
+		MetaString loot;
+		for(auto & elem : creatures.Slots())
+		{ //build list of joined creatures
+			iw.components.push_back(Component(*elem.second));
+			loot << "%s";
+			loot.addReplacement(*elem.second);
+		}
+
+		if (creatures.Slots().size() == 1 && creatures.Slots().begin()->second->count == 1)
+			iw.text.addTxt(MetaString::ADVOB_TXT, 185);
+		else
+			iw.text.addTxt(MetaString::ADVOB_TXT, 186);
+
+		iw.text.addReplacement(loot.buildList());
+		iw.text.addReplacement(h->name);
+
+		cb->showInfoDialog(&iw);
+		cb->giveCreatures(this, h, creatures, true);
+	}
+	if(!hasGuardians && msg.size())
+	{
+		iw.text << msg;
+		cb->showInfoDialog(&iw);
+	}
+}
+
+void CGPandoraBox::getText( InfoWindow &iw, bool &afterBattle, int text, const CGHeroInstance * h ) const
+{
+	if(afterBattle || !message.size())
+	{
+		iw.text.addTxt(MetaString::ADVOB_TXT,text);//%s has lost treasure.
+		iw.text.addReplacement(h->name);
+	}
+	else
+	{
+		iw.text << message;
+		afterBattle = true;
+	}
+}
+
+void CGPandoraBox::getText( InfoWindow &iw, bool &afterBattle, int val, int negative, int positive, const CGHeroInstance * h ) const
+{
+	iw.components.clear();
+	iw.text.clear();
+	if(afterBattle || !message.size())
+	{
+		iw.text.addTxt(MetaString::ADVOB_TXT,val < 0 ? negative : positive); //%s's luck takes a turn for the worse / %s's luck increases
+		iw.text.addReplacement(h->name);
+	}
+	else
+	{
+		iw.text << message;
+		afterBattle = true;
+	}
+}
+
+void CGPandoraBox::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
+{
+	if(result.winner)
+		return;
+
+	giveContentsUpToExp(hero);
+}
+
+void CGPandoraBox::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
+{
+	if (answer)
+	{
+		if (stacksCount() > 0) //if pandora's box is protected by army
+		{
+			showInfoDialog(hero,16,0);
+			cb->startBattleI(hero, this); //grants things after battle
+		}
+		else if (message.size() == 0 && resources.size() == 0
+			&& primskills.size() == 0 && abilities.size() == 0
+			&& abilityLevels.size() == 0 &&  artifacts.size() == 0
+			&& spells.size() == 0 && creatures.Slots().size() > 0
+			&& gainedExp == 0 && manaDiff == 0 && moraleDiff == 0 && luckDiff == 0) //if it gives nothing without battle
+		{
+			showInfoDialog(hero,15,0);
+			cb->removeObject(this);
+		}
+		else //if it gives something without battle
+		{
+			giveContentsUpToExp(hero);
+		}
+	}
+}
+
+void CGPandoraBox::heroLevelUpDone(const CGHeroInstance *hero) const
+{
+	giveContentsAfterExp(hero);
+}
+
+void CGEvent::onHeroVisit( const CGHeroInstance * h ) const
+{
+	if(!(availableFor & (1 << h->tempOwner.getNum())))
+		return;
+	if(cb->getPlayerSettings(h->tempOwner)->playerID)
+	{
+		if(humanActivate)
+			activated(h);
+	}
+	else if(computerActivate)
+		activated(h);
+}
+
+void CGEvent::activated( const CGHeroInstance * h ) const
+{
+	if(stacksCount() > 0)
+	{
+		InfoWindow iw;
+		iw.player = h->tempOwner;
+		if(message.size())
+			iw.text << message;
+		else
+			iw.text.addTxt(MetaString::ADVOB_TXT, 16);
+		cb->showInfoDialog(&iw);
+		cb->startBattleI(h, this);
+	}
+	else
+	{
+		giveContentsUpToExp(h);
+	}
+}

Деякі файли не було показано, через те що забагато файлів було змінено