浏览代码

Merged changes from upstream and fixed compilation caused by API changes

Ivan Savenko 11 年之前
父节点
当前提交
32b6568b65
共有 100 个文件被更改,包括 6013 次插入6025 次删除
  1. 3 0
      .gitignore
  2. 14 6
      AI/BattleAI/BattleAI.cpp
  3. 3 1
      AI/BattleAI/BattleAI.vcxproj
  4. 8 2
      AI/BattleAI/main.cpp
  5. 4 2
      AI/EmptyAI/CEmptyAI.cpp
  6. 3 1
      AI/EmptyAI/EmptyAI.vcxproj
  7. 1 1
      AI/FuzzyLite/FuzzyLite.vcxproj
  8. 8 4
      AI/StupidAI/StupidAI.cpp
  9. 3 1
      AI/StupidAI/StupidAI.vcxproj
  10. 8 2
      AI/StupidAI/main.cpp
  11. 3 2
      AI/VCAI/AIUtility.cpp
  12. 1 1
      AI/VCAI/AIUtility.h
  13. 19 29
      AI/VCAI/VCAI.cpp
  14. 0 3
      AI/VCAI/VCAI.h
  15. 5 5
      AI/VCAI/VCAI.vcxproj
  16. 8 2
      AI/VCAI/main.cpp
  17. 4 1
      AUTHORS
  18. 3 1
      CCallback.h
  19. 0 1
      COPYING
  20. 7 1
      ChangeLog
  21. 3 22
      Global.h
  22. 0 5
      Mods/WoG/mod.json
  23. 0 0
      NEWS
  24. 17 19
      README
  25. 25 27
      VCMI_VS11.sln
  26. 2 1
      client/CAnimation.cpp
  27. 11 5
      client/CCastleInterface.cpp
  28. 1 1
      client/CCastleInterface.h
  29. 956 955
      client/CKingdomInterface.cpp
  30. 11 0
      client/CMT.cpp
  31. 10 11
      client/CMessage.cpp
  32. 3 5
      client/CMusicHandler.cpp
  33. 3 2
      client/CPreGame.cpp
  34. 12 9
      client/CSpellWindow.cpp
  35. 1 1
      client/CVideoHandler.cpp
  36. 12 0
      client/Client.cpp
  37. 14 5
      client/GUIClasses.cpp
  38. 0 1
      client/NetPacksClient.cpp
  39. 265 263
      client/VCMI_client.vcxproj
  40. 13 0
      client/battle/CBattleAnimations.cpp
  41. 3 2
      client/battle/CBattleInterface.cpp
  42. 1 1
      client/gui/CIntObjectClasses.h
  43. 20 20
      client/mapHandler.cpp
  44. 0 3
      client/mapHandler.h
  45. 1 1
      config/battles_graphics.json
  46. 2 1
      config/creatures/castle.json
  47. 37 0
      config/creatures/conflux.json
  48. 4 1
      config/defaultMods.json
  49. 9 8
      config/factions/castle.json
  50. 8 7
      config/factions/conflux.json
  51. 7 6
      config/factions/dungeon.json
  52. 9 8
      config/factions/fortress.json
  53. 8 7
      config/factions/inferno.json
  54. 9 7
      config/factions/necropolis.json
  55. 8 7
      config/factions/rampart.json
  56. 9 7
      config/factions/stronghold.json
  57. 8 7
      config/factions/tower.json
  58. 8 4
      config/gameConfig.json
  59. 1 1
      config/heroes/stronghold.json
  60. 22 22
      config/rmg.json
  61. 12 2
      config/schemas/faction.json
  62. 31 0
      config/schemas/object.json
  63. 27 0
      config/schemas/objectType.json
  64. 24 16
      config/schemas/spell.json
  65. 15 0
      config/schemas/townBuilding.json
  66. 0 3755
      config/spell_info.json
  67. 300 0
      config/spells/ability.json
  68. 152 0
      config/spells/adventure.json
  69. 281 0
      config/spells/offensive.json
  70. 298 0
      config/spells/other.json
  71. 1204 0
      config/spells/timed.json
  72. 2 0
      debian/control
  73. 0 2
      debian/docs
  74. 170 166
      launcher/VCMI_launcher.vcxproj
  75. 2 36
      lib/BattleState.cpp
  76. 1 3
      lib/BattleState.h
  77. 168 134
      lib/CBattleCallback.cpp
  78. 21 2
      lib/CBattleCallback.h
  79. 736 0
      lib/CGameInfoCallback.cpp
  80. 148 0
      lib/CGameInfoCallback.h
  81. 27 0
      lib/CGameInterface.cpp
  82. 26 36
      lib/CGameState.cpp
  83. 1 2
      lib/CGameState.h
  84. 5 3
      lib/CHeroHandler.cpp
  85. 2 1
      lib/CHeroHandler.h
  86. 2 1
      lib/CMakeLists.txt
  87. 11 6
      lib/CModHandler.cpp
  88. 3 1
      lib/CModHandler.h
  89. 210 54
      lib/CObjectHandler.cpp
  90. 42 36
      lib/CObjectHandler.h
  91. 86 0
      lib/CRandomGenerator.cpp
  92. 46 44
      lib/CRandomGenerator.h
  93. 5 2
      lib/CScriptingModule.h
  94. 164 133
      lib/CSpellHandler.cpp
  95. 54 50
      lib/CSpellHandler.h
  96. 58 0
      lib/CTownHandler.cpp
  97. 12 4
      lib/CTownHandler.h
  98. 1 1
      lib/Connection.h
  99. 15 4
      lib/GameConstants.h
  100. 13 13
      lib/HeroBonus.cpp

+ 3 - 0
.gitignore

@@ -5,3 +5,6 @@
 *.a
 *.res
 *.layout
+*.pro.user
+*.pro.user.*
+/CMakeLists.txt.user

+ 14 - 6
AI/BattleAI/BattleAI.cpp

@@ -8,7 +8,7 @@
 #include "../../lib/VCMI_Lib.h"
 
 using boost::optional;
-shared_ptr<CBattleCallback> cbc;
+static shared_ptr<CBattleCallback> cbc;
 
 #define LOGL(text) print(text)
 #define LOGFL(text, formattingEl) print(boost::str(boost::format(text) % formattingEl))
@@ -28,8 +28,12 @@ struct Priorities
 		range::copy(VLC->objh->resVals, std::back_inserter(resourceTypeBaseValues));
 		stackEvaluator = [](const CStack*){ return 1.0; };
 	}
-} priorities;
+};
+
+Priorities *priorities = nullptr;
+
 
+namespace {
 
 int distToNearestNeighbour(BattleHex hex, const ReachabilityInfo::TDistances& dists, BattleHex *chosenHex = nullptr)
 {
@@ -52,6 +56,8 @@ bool isCloser(const EnemyInfo & ei1, const EnemyInfo & ei2, const ReachabilityIn
 	return distToNearestNeighbour(ei1.s->position, dists) < distToNearestNeighbour(ei2.s->position, dists);
 }
 
+}
+
 template <typename Container, typename Pred>
 auto sum(const Container & c, Pred p) -> decltype(p(*std::begin(c)))
 {
@@ -453,9 +459,9 @@ void CBattleAI::attemptCastingSpell()
 				int damageDealt = 0, damageReceived = 0;
 
 				auto stacksSuffering = cb->getAffectedCreatures(ps.spell, skillLevel, playerID, ps.dest);
-				vstd::erase_if(stacksSuffering, [&](const CStack *stack) -> bool
+				vstd::erase_if(stacksSuffering, [&](const CStack * s) -> bool
 				{
-					return cb->battleIsImmune(hero, ps.spell, ECastingMode::HERO_CASTING, ps.dest);
+					return  ESpellCastProblem::OK != cb->battleStackIsImmune(hero, ps.spell, ECastingMode::HERO_CASTING, s);
 				});
 
 				if(stacksSuffering.empty())
@@ -624,8 +630,10 @@ const TBonusListPtr StackWithBonuses::getAllBonuses(const CSelector &selector, c
 
 int AttackPossibility::damageDiff() const
 {
-	const auto dealtDmgValue = priorities.stackEvaluator(enemy) * damageDealt;
-	const auto receivedDmgValue = priorities.stackEvaluator(attack.attacker) * damageReceived;
+	if (!priorities)
+		priorities = new Priorities;
+	const auto dealtDmgValue = priorities->stackEvaluator(enemy) * damageDealt;
+	const auto receivedDmgValue = priorities->stackEvaluator(attack.attacker) * damageReceived;
 	return dealtDmgValue - receivedDmgValue;
 }
 

+ 3 - 1
AI/BattleAI/BattleAI.vcxproj

@@ -80,7 +80,7 @@
     <OutDir>$(VCMI_Out)\AI\</OutDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
-    <OutDir>$(VCMI_Out)\AI\</OutDir>
+    <OutDir>..</OutDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">
     <OutDir>$(VCMI_Out)\AI\</OutDir>
@@ -93,6 +93,7 @@
     </ClCompile>
     <Link>
       <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\..\..\libs;..\..;..</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -115,6 +116,7 @@
     </ClCompile>
     <Link>
       <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\..\..\libs;..\..</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">

+ 8 - 2
AI/BattleAI/main.cpp

@@ -7,7 +7,13 @@
 #define strcpy_s(a, b, c) strncpy(a, c, b)
 #endif
 
-const char *g_cszAiName = "Battle AI";
+#ifdef __ANDROID__
+#define GetGlobalAiVersion BattleAI_GetGlobalAiVersion
+#define GetAiName BattleAI_GetAiName
+#define GetNewBattleAI BattleAI_GetNewBattleAI
+#endif
+
+static const char *g_cszAiName = "Battle AI";
 
 extern "C" DLL_EXPORT int GetGlobalAiVersion()
 {
@@ -22,4 +28,4 @@ extern "C" DLL_EXPORT void GetAiName(char* name)
 extern "C" DLL_EXPORT void GetNewBattleAI(shared_ptr<CBattleGameInterface> &out)
 {
 	out = make_shared<CBattleAI>();
-}
+}

+ 4 - 2
AI/EmptyAI/CEmptyAI.cpp

@@ -1,6 +1,8 @@
 #include "StdInc.h"
 #include "CEmptyAI.h"
 
+#include "../../lib/CRandomGenerator.h"
+
 void CEmptyAI::init(shared_ptr<CCallback> CB)
 {
 	cb = CB;
@@ -15,12 +17,12 @@ void CEmptyAI::yourTurn()
 
 void CEmptyAI::heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID)
 {
-	cb->selectionMade(rand() % skills.size(), queryID);
+	cb->selectionMade(CRandomGenerator::getDefault().nextInt(skills.size() - 1), queryID);
 }
 
 void CEmptyAI::commanderGotLevel(const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID)
 {
-	cb->selectionMade(rand() % skills.size(), queryID);
+	cb->selectionMade(CRandomGenerator::getDefault().nextInt(skills.size() - 1), queryID);
 }
 
 void CEmptyAI::showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel)

+ 3 - 1
AI/EmptyAI/EmptyAI.vcxproj

@@ -96,7 +96,7 @@
     <OutDir>$(VCMI_Out)\AI\</OutDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
-    <OutDir>$(VCMI_Out)\AI\</OutDir>
+    <OutDir>..</OutDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">
     <OutDir>$(VCMI_Out)\AI\</OutDir>
@@ -115,6 +115,7 @@
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <OutputFile>$(OutDir)EmptyAI.dll</OutputFile>
+      <AdditionalLibraryDirectories>..\..\..\libs;..\..</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -150,6 +151,7 @@
       <OptimizeReferences>true</OptimizeReferences>
       <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <OutputFile>$(OutDir)EmptyAI.dll</OutputFile>
+      <AdditionalLibraryDirectories>..\..\..\libs;..\..</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">

+ 1 - 1
AI/FuzzyLite/FuzzyLite.vcxproj

@@ -123,7 +123,7 @@
     <IntDir>$(Configuration)\</IntDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
-    <OutDir>$(VCMI_Out)\AI\</OutDir>
+    <OutDir>..</OutDir>
     <IncludePath>$(IncludePath)</IncludePath>
     <LibraryPath>$(LibraryPath)</LibraryPath>
   </PropertyGroup>

+ 8 - 4
AI/StupidAI/StupidAI.cpp

@@ -5,7 +5,7 @@
 #include "../../CCallback.h"
 #include "../../lib/CCreatureHandler.h"
 
-shared_ptr<CBattleCallback> cbc;
+static shared_ptr<CBattleCallback> cbc;
 
 CStupidAI::CStupidAI(void)
 	: side(-1)
@@ -60,6 +60,8 @@ bool isMoreProfitable(const EnemyInfo &ei1, const EnemyInfo& ei2)
 	return (ei1.adi-ei1.adr) < (ei2.adi - ei2.adr);
 }
 
+namespace {
+
 int distToNearestNeighbour(BattleHex hex, const ReachabilityInfo::TDistances& dists, BattleHex *chosenHex = nullptr)
 {
 	int ret = 1000000;
@@ -81,6 +83,8 @@ bool isCloser(const EnemyInfo & ei1, const EnemyInfo & ei2, const ReachabilityIn
 	return distToNearestNeighbour(ei1.s->position, dists) < distToNearestNeighbour(ei2.s->position, dists);
 }
 
+}
+
 static bool willSecondHexBlockMoreEnemyShooters(const BattleHex &h1, const BattleHex &h2)
 {
 	int shooters[2] = {0}; //count of shooters on hexes
@@ -104,8 +108,9 @@ BattleAction CStupidAI::activeStack( const CStack * stack )
 	if(stack->type->idNumber == CreatureID::CATAPULT)
 	{
 		BattleAction attack;
-		static const int wallHexes[] = {50, 183, 182, 130, 62, 29, 12, 95};
-		attack.destinationTile = wallHexes[ rand()%ARRAY_COUNT(wallHexes) ];
+		static const std::vector<int> wallHexes = boost::assign::list_of(50)(183)(182)(130)(62)(29)(12)(95);
+
+		attack.destinationTile = *RandomGeneratorUtil::nextItem(wallHexes, CRandomGenerator::getDefault());
 		attack.actionType = Battle::CATAPULT;
 		attack.additionalInfo = 0;
 		attack.side = side;
@@ -327,4 +332,3 @@ void CStupidAI::loadGame(CISer<CLoadFile> &h, const int version)
 	//TODO to be implemented with saving/loading during the battles
 	assert(0);
 }
-

+ 3 - 1
AI/StupidAI/StupidAI.vcxproj

@@ -80,7 +80,7 @@
     <OutDir>$(VCMI_Out)\AI\</OutDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
-    <OutDir>$(VCMI_Out)\AI\</OutDir>
+    <OutDir>..</OutDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">
     <OutDir>$(VCMI_Out)\AI\</OutDir>
@@ -94,6 +94,7 @@
     <Link>
       <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalOptions>-Zm150 %(AdditionalOptions)</AdditionalOptions>
+      <AdditionalLibraryDirectories>..\..\..\libs;..\..;..</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -114,6 +115,7 @@
     </ClCompile>
     <Link>
       <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\..\..\libs;..\..</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">

+ 8 - 2
AI/StupidAI/main.cpp

@@ -7,7 +7,13 @@
 #define strcpy_s(a, b, c) strncpy(a, c, b)
 #endif
 
-const char *g_cszAiName = "Stupid AI 0.1";
+#ifdef __ANDROID__
+#define GetGlobalAiVersion StupidAI_GetGlobalAiVersion
+#define GetAiName StupidAI_GetAiName
+#define GetNewBattleAI StupidAI_GetNewBattleAI
+#endif
+
+static const char *g_cszAiName = "Stupid AI 0.1";
 
 extern "C" DLL_EXPORT int GetGlobalAiVersion()
 {
@@ -22,4 +28,4 @@ extern "C" DLL_EXPORT void GetAiName(char* name)
 extern "C" DLL_EXPORT void GetNewBattleAI(shared_ptr<CBattleGameInterface> &out)
 {
 	out = make_shared<CStupidAI>();
-}
+}

+ 3 - 2
AI/VCAI/AIUtility.cpp

@@ -344,11 +344,12 @@ bool isReachable(const CGObjectInstance *obj)
 	return cb->getPathInfo(obj->visitablePos())->turns < 255;
 }
 
-bool canBeEmbarkmentPoint(const TerrainTile *t)
+bool canBeEmbarkmentPoint(const TerrainTile *t, bool fromWater)
 {
 	//tile must be free of with unoccupied boat
 	return !t->blocked
-        || (t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT);
+        || (!fromWater && t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT);
+	//do not try to board when in water sector
 }
 
 int3 whereToExplore(HeroPtr h)

+ 1 - 1
AI/VCAI/AIUtility.h

@@ -183,7 +183,7 @@ int howManyTilesWillBeDiscovered(const int3 &pos, int radious, CCallback * cbp);
 int howManyTilesWillBeDiscovered(int radious, int3 pos, crint3 dir);
 void getVisibleNeighbours(const std::vector<int3> &tiles, std::vector<int3> &out);
 
-bool canBeEmbarkmentPoint(const TerrainTile *t);
+bool canBeEmbarkmentPoint(const TerrainTile *t, bool fromWater);
 bool isBlockedBorderGate(int3 tileToHit);
 bool isReachable(const CGObjectInstance *obj);
 bool isCloser(const CGObjectInstance *lhs, const CGObjectInstance *rhs);

+ 19 - 29
AI/VCAI/VCAI.cpp

@@ -7,6 +7,7 @@
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CHeroHandler.h"
 
+
 /*
  * CCreatureHandler.h, part of VCMI engine
  *
@@ -1143,7 +1144,8 @@ void VCAI::buildStructure(const CGTownInstance * t)
 	//Possible - allow "locking" on specific building (build prerequisites and then building itself)
 
 	TResources currentRes = cb->getResourceAmount();
-	int townIncome = t->dailyIncome();
+	TResources currentIncome = t->dailyIncome();
+	int townIncome = currentIncome[Res::GOLD];
 
 	if (tryBuildAnyStructure(t, std::vector<BuildingID>(essential, essential + ARRAY_COUNT(essential))))
 		return;
@@ -1212,7 +1214,6 @@ std::vector<const CGObjectInstance *> VCAI::getPossibleDestinations(HeroPtr h)
 	std::vector<const CGObjectInstance *> possibleDestinations;
 	for(const CGObjectInstance *obj : visitableObjs)
 	{
-		const int3 pos = obj->visitablePos();
 		if (isGoodForVisit(obj, h))
 		{
 			possibleDestinations.push_back(obj);
@@ -1304,7 +1305,7 @@ void VCAI::wander(HeroPtr h)
 			if(townsReachable.size())
 			{
 				boost::sort(townsReachable, compareReinforcements);
-				dests.emplace_back(townsReachable.back());
+				dests.push_back(townsReachable.back());
 			}
 			else if(townsNotReachable.size())
 			{
@@ -1680,12 +1681,13 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 		}
 		ret = !i;
 	}
-	if (auto visitedObject = frontOrNull(cb->getVisitableObjs(h->visitablePos()))) //we stand on something interesting
+	if (h)
 	{
-		if (visitedObject != *h)
-			performObjectInteraction (visitedObject, h);
-		//BNLOG("Hero %s moved from %s to %s at %s", h->name % startHpos % visitedObject->hoverName % h->visitablePos());
-		//throw goalFulfilledException (CGoal(GET_OBJ).setobjid(visitedObject->id));
+		if (auto visitedObject = frontOrNull(cb->getVisitableObjs(h->visitablePos()))) //we stand on something interesting
+		{
+			if (visitedObject != *h)
+				performObjectInteraction (visitedObject, h);
+		}
 	}
 	if(h) //we could have lost hero after last move
 	{
@@ -1703,8 +1705,8 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 			erase_if_present (lockedHeroes, h); //hero seemingly is confused
 			throw cannotFulfillGoalException("Invalid path found!"); //FIXME: should never happen
 		}
+		logAi->debugStream() << boost::format("Hero %s moved from %s to %s. Returning %d.") % h->name % startHpos % h->visitablePos() % ret;
 	}
-    logAi->debugStream() << boost::format("Hero %s moved from %s to %s. Returning %d.") % h->name % startHpos % h->visitablePos() % ret;
 	return ret;
 }
 void VCAI::tryRealize(Goals::Explore & g)
@@ -2344,23 +2346,11 @@ TResources VCAI::estimateIncome() const
 	TResources ret;
 	for(const CGTownInstance *t : cb->getTownsInfo())
 	{
-		ret[Res::GOLD] += t->dailyIncome();
-
-		//TODO duplikuje newturn
-		if(t->hasBuilt(BuildingID::RESOURCE_SILO)) //there is resource silo
-		{
-			if(t->town->primaryRes == Res::WOOD_AND_ORE) //we'll give wood and ore
-			{
-				ret[Res::WOOD] ++;
-				ret[Res::ORE] ++;
-			}
-			else
-			{
-				ret[t->town->primaryRes] ++;
-			}
-		}
+		ret += t->dailyIncome();
 	}
 
+
+
 	for(const CGObjectInstance *obj : getFlaggedObjects())
 	{
 		if(obj->ID == Obj::MINE)
@@ -2736,7 +2726,7 @@ void SectorMap::exploreNewSector(crint3 pos, int num, CCallback * cbp)
 							//parent[neighPos] = curPos;
 						}
 						const TerrainTile *nt = cbp->getTile(neighPos, false);
-						if(nt && nt->isWater() != s.water && canBeEmbarkmentPoint(nt))
+						if(nt && nt->isWater() != s.water && canBeEmbarkmentPoint(nt, s.water))
 						{
 							s.embarkmentPoints.push_back(neighPos);
 						}
@@ -2856,9 +2846,9 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
 			}
 			return false;
 		}
-		case Obj::MONOLITH1:
-		case Obj::MONOLITH2:
-		case Obj::MONOLITH3:
+		case Obj::MONOLITH_ONE_WAY_ENTRANCE:
+		case Obj::MONOLITH_ONE_WAY_EXIT:
+		case Obj::MONOLITH_TWO_WAY:
 		case Obj::WHIRLPOOL:
 			//TODO: mechanism for handling monoliths
 			return false;
@@ -2884,7 +2874,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
 		case Obj::MAGIC_WELL:
 			return h->mana < h->manaLimit();
 		case Obj::PRISON:
-			return ai->myCb->getHeroesInfo().size() < GameConstants::MAX_HEROES_PER_PLAYER;
+			return ai->myCb->getHeroesInfo().size() < VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER;// GameConstants::MAX_HEROES_PER_PLAYER;
 
 		case Obj::BOAT:
 			return false;

+ 0 - 3
AI/VCAI/VCAI.h

@@ -21,9 +21,6 @@
 #include "../../lib/NetPacks.h"
 #include "../../lib/CondSh.h"
 
-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) };
-
 struct QuestInfo;
 
 /*

+ 5 - 5
AI/VCAI/VCAI.vcxproj

@@ -80,7 +80,7 @@
     <OutDir>$(VCMI_Out)\AI\</OutDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
-    <OutDir>$(VCMI_Out)\AI\</OutDir>
+    <OutDir>..</OutDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">
     <OutDir>$(VCMI_Out)\AI\</OutDir>
@@ -91,11 +91,11 @@
       </AdditionalIncludeDirectories>
       <PrecompiledHeader>Use</PrecompiledHeader>
       <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
-      <AdditionalOptions>/Zm195 %(AdditionalOptions)</AdditionalOptions>
+      <AdditionalOptions>/Zm210 %(AdditionalOptions)</AdditionalOptions>
     </ClCompile>
     <Link>
       <AdditionalDependencies>VCMI_lib.lib;FuzzyLite.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>$(VCMI_Out);$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>..\..\..\libs;..\..;..</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -117,11 +117,11 @@
       </AdditionalIncludeDirectories>
       <PrecompiledHeader>Use</PrecompiledHeader>
       <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
-      <AdditionalOptions>/Zm150 %(AdditionalOptions)</AdditionalOptions>
+      <AdditionalOptions>/Zm199 %(AdditionalOptions)</AdditionalOptions>
     </ClCompile>
     <Link>
       <AdditionalDependencies>VCMI_lib.lib;FuzzyLite.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>$(VCMI_Out);$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>..\..\..\libs;..\..;..</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">

+ 8 - 2
AI/VCAI/main.cpp

@@ -5,7 +5,13 @@
 #define strcpy_s(a, b, c) strncpy(a, c, b)
 #endif
 
-const char *g_cszAiName = "VCAI";
+#ifdef __ANDROID__
+#define GetGlobalAiVersion VCAI_GetGlobalAiVersion
+#define GetAiName VCAI_GetAiName
+#define GetNewAI VCAI_GetNewAI
+#endif
+
+static const char *g_cszAiName = "VCAI";
 
 extern "C" DLL_EXPORT int GetGlobalAiVersion()
 {
@@ -20,4 +26,4 @@ extern "C" DLL_EXPORT void GetAiName(char* name)
 extern "C" DLL_EXPORT void GetNewAI(shared_ptr<CGlobalAI> &out)
 {
 	out = make_shared<VCAI>();
-}
+}

+ 4 - 1
AUTHORS

@@ -1,4 +1,4 @@
-VCMI PROJECT CODE CONTRIBUTORS:
+VCMI PROJECT CODE CONTRIBUTORS:
 
 Michał Urbańczyk aka Tow,        <[email protected]>
    * project originator; programming, making releases, website
@@ -46,5 +46,8 @@ Ivan Savenko,				<[email protected]>
 Benjamin Gentner aka beegee,		<>
    * battle support, programming
 
+Alexey aka Macron1Robot,                 <>
+   * minor modding changes
+
 Alexander Shishkin aka alexvins,
    * MinGW platform support, modding related programming

+ 3 - 1
CCallback.h

@@ -1,7 +1,8 @@
 #pragma once
 
 
-#include "lib/IGameCallback.h"
+#include "lib/CGameInfoCallback.h"
+#include "lib/int3.h" // for int3
 
 /*
  * CCallback.h, part of VCMI engine
@@ -29,6 +30,7 @@ struct CPathsInfo;
 struct CPack;
 class IBattleEventsReceiver;
 class IGameEventsReceiver;
+struct ArtifactLocation;
 
 class IBattleCallback
 {

+ 0 - 1
COPYING

@@ -1 +0,0 @@
-See license.txt.

+ 7 - 1
ChangeLog

@@ -5,8 +5,10 @@ GENERAL:
 ADVENTURE AI:
 
 ADVENTURE MAP:
+* Heroes auto-level primary and secondary skill levels according to experience
 
 BATTLES:
+* Wall hit/miss sound will be played when using catapult during siege
 
 SPELLS:
 * New configuration format: http://wiki.vcmi.eu/index.php?title=Spell_Format
@@ -14,7 +16,11 @@ SPELLS:
 MODS:
 * Support for submods - mod may have their own "submods" located in <modname>/Mods directory
 * Mods may provide their own changelogs and screenshots that will be visible in Launcher
-* Mods cas now add new (offensive, buffs, debuffs) spells and change existing
+* Mods can now add new (offensive, buffs, debuffs) spells and change existing
+* Mods can use custom mage guild background pictures and videos for taverns, setting of resources daily income for buildings
+
+GENERAL:
+* Added configuring of heroes quantity per player allowed in game
 
 0.94 -> 0.95 (Mar 01 2014)
 GENERAL:

+ 3 - 22
Global.h

@@ -24,12 +24,12 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #  define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__ * 10 + __GNUC_PATCHLEVEL__)
 #endif
 
-#if !defined(__clang__) && defined(__GNUC__) && (GCC_VERSION < 460)
-#  error VCMI requires at least gcc-4.6 for successful compilation or clang-3.1. Please update your compiler
+#if !defined(__clang__) && defined(__GNUC__) && (GCC_VERSION < 470)
+#  error VCMI requires at least gcc-4.7.2 for successful compilation or clang-3.1. Please update your compiler
 #endif
 
 #if defined(__GNUC__) && (GCC_VERSION == 470 || GCC_VERSION == 471)
-#  error This GCC version has buggy std::array::at version and should not be used. Please update to 4.7.2 or use 4.6.x.
+#  error This GCC version has buggy std::array::at version and should not be used. Please update to 4.7.2 or later
 #endif
 
 /* ---------------------------------------------------------------------------- */
@@ -42,11 +42,6 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #  define CPP11_USE_INITIALIZERS_LIST
 #endif
 
-//override keyword - not present in gcc-4.6
-#if !defined(_MSC_VER) && !defined(__clang__) && !(defined(__GNUC__) && (GCC_VERSION >= 470))
-#  define override
-#endif
-
 /* ---------------------------------------------------------------------------- */
 /* Suppress some compiler warnings */
 /* ---------------------------------------------------------------------------- */
@@ -114,7 +109,6 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #include <boost/range/algorithm.hpp>
 #include <boost/thread.hpp>
 #include <boost/variant.hpp>
-
 #include <boost/math/special_functions/round.hpp>
 
 
@@ -553,19 +547,6 @@ namespace vstd
 		});
 	}
 
-	static inline int retreiveRandNum(const std::function<int()> &randGen)
-	{
-		if (randGen)
-			return randGen();
-		else
-			return rand();
-	}
-
-	template <typename T> const T & pickRandomElementOf(const std::vector<T> &v, const std::function<int()> &randGen)
-	{
-		return v.at(retreiveRandNum(randGen) % v.size());
-	}
-
 	template<typename T>
 	void advance(T &obj, int change)
 	{

+ 0 - 5
Mods/WoG/mod.json

@@ -22,11 +22,6 @@
 		"config/wog/heroClasses.json"
 	],
 
-        "spells" :
-        [
-                "config/wog/spells.json"
-        ],
-
 	"filesystem":
 	{
 		"" :

+ 0 - 0
NEWS


+ 17 - 19
README

@@ -1,22 +1,20 @@
-    VCMI Project
-    Copyright (C) 2007-2014  VCMI Team (check AUTHORS file for the contributors list)
+# VCMI Project
+VCMI is work-in-progress attempt to recreate engine for Heroes III, giving it new and extended possibilities. To use VCMI you need to own original data files.
 
-    VCMI is an open-source project aiming to reimplement HMM3:WoG game engine, 
-	giving it new and extended possibilities. 
-	Wiki:   http://wiki.vcmi.eu
-	Forums: http://forum.vcmi.eu
-	Bugtracker: http://bugs.vcmi.eu/
-	
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 (included in the license.txt)
-    of the License, or (at your option) any later version.
+## Links
 
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
+ * Homepage:   http://vcmi.eu/
+ * Wiki:       http://wiki.vcmi.eu/
+ * Forums:     http://forum.vcmi.eu/
+ * Bugtracker: http://bugs.vcmi.eu/
 
-    You should have received a copy of the GNU General Public License along
-    with this program; if not, write to the Free Software Foundation, Inc.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+## Installation
+For installation of latest release see release announcement on http://vcmi.eu/
+
+For building from source see project wiki at http://wiki.vcmi.eu/
+
+## Copyright and license
+
+VCMI Project is released under GPL version 2 or later
+
+Copyright (C) 2007-2014  VCMI Team (check AUTHORS file for the contributors list)

+ 25 - 27
VCMI_VS11.sln

@@ -57,102 +57,100 @@ Global
 		RD|x64 = RD|x64
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.Debug|Win32.ActiveCfg = Debug|Win32
-		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.Debug|Win32.Build.0 = Debug|Win32
+		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.Debug|Win32.ActiveCfg = RD|Win32
+		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.Debug|Win32.Build.0 = RD|Win32
 		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.Debug|x64.ActiveCfg = Debug|x64
 		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.Debug|x64.Build.0 = Debug|x64
 		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.RD|Win32.ActiveCfg = RD|Win32
 		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.RD|Win32.Build.0 = RD|Win32
 		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.RD|x64.ActiveCfg = RD|x64
 		{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}.RD|x64.Build.0 = RD|x64
-		{B952FFC5-3039-4DE1-9F08-90ACDA483D8F}.Debug|Win32.ActiveCfg = Debug|Win32
-		{B952FFC5-3039-4DE1-9F08-90ACDA483D8F}.Debug|Win32.Build.0 = Debug|Win32
+		{B952FFC5-3039-4DE1-9F08-90ACDA483D8F}.Debug|Win32.ActiveCfg = RD|Win32
+		{B952FFC5-3039-4DE1-9F08-90ACDA483D8F}.Debug|Win32.Build.0 = RD|Win32
 		{B952FFC5-3039-4DE1-9F08-90ACDA483D8F}.Debug|x64.ActiveCfg = Debug|x64
 		{B952FFC5-3039-4DE1-9F08-90ACDA483D8F}.Debug|x64.Build.0 = Debug|x64
 		{B952FFC5-3039-4DE1-9F08-90ACDA483D8F}.RD|Win32.ActiveCfg = RD|Win32
 		{B952FFC5-3039-4DE1-9F08-90ACDA483D8F}.RD|Win32.Build.0 = RD|Win32
 		{B952FFC5-3039-4DE1-9F08-90ACDA483D8F}.RD|x64.ActiveCfg = RD|x64
 		{B952FFC5-3039-4DE1-9F08-90ACDA483D8F}.RD|x64.Build.0 = RD|x64
-		{8AF697C3-465E-4910-B31B-576A9ECDB309}.Debug|Win32.ActiveCfg = Debug|Win32
-		{8AF697C3-465E-4910-B31B-576A9ECDB309}.Debug|Win32.Build.0 = Debug|Win32
+		{8AF697C3-465E-4910-B31B-576A9ECDB309}.Debug|Win32.ActiveCfg = RD|Win32
+		{8AF697C3-465E-4910-B31B-576A9ECDB309}.Debug|Win32.Build.0 = RD|Win32
 		{8AF697C3-465E-4910-B31B-576A9ECDB309}.Debug|x64.ActiveCfg = Debug|x64
 		{8AF697C3-465E-4910-B31B-576A9ECDB309}.Debug|x64.Build.0 = Debug|x64
 		{8AF697C3-465E-4910-B31B-576A9ECDB309}.RD|Win32.ActiveCfg = RD|Win32
 		{8AF697C3-465E-4910-B31B-576A9ECDB309}.RD|Win32.Build.0 = RD|Win32
 		{8AF697C3-465E-4910-B31B-576A9ECDB309}.RD|x64.ActiveCfg = RD|x64
 		{8AF697C3-465E-4910-B31B-576A9ECDB309}.RD|x64.Build.0 = RD|x64
-		{15DABC90-234A-4B6B-9EEB-777C4768B82B}.Debug|Win32.ActiveCfg = Debug|Win32
-		{15DABC90-234A-4B6B-9EEB-777C4768B82B}.Debug|Win32.Build.0 = Debug|Win32
+		{15DABC90-234A-4B6B-9EEB-777C4768B82B}.Debug|Win32.ActiveCfg = RD|Win32
+		{15DABC90-234A-4B6B-9EEB-777C4768B82B}.Debug|Win32.Build.0 = RD|Win32
 		{15DABC90-234A-4B6B-9EEB-777C4768B82B}.Debug|x64.ActiveCfg = Debug|x64
 		{15DABC90-234A-4B6B-9EEB-777C4768B82B}.Debug|x64.Build.0 = Debug|x64
 		{15DABC90-234A-4B6B-9EEB-777C4768B82B}.RD|Win32.ActiveCfg = RD|Win32
 		{15DABC90-234A-4B6B-9EEB-777C4768B82B}.RD|Win32.Build.0 = RD|Win32
 		{15DABC90-234A-4B6B-9EEB-777C4768B82B}.RD|x64.ActiveCfg = RD|x64
 		{15DABC90-234A-4B6B-9EEB-777C4768B82B}.RD|x64.Build.0 = RD|x64
-		{8F202F43-106D-4F63-AD9D-B1D43E803E8C}.Debug|Win32.ActiveCfg = Debug|Win32
-		{8F202F43-106D-4F63-AD9D-B1D43E803E8C}.Debug|Win32.Build.0 = Debug|Win32
+		{8F202F43-106D-4F63-AD9D-B1D43E803E8C}.Debug|Win32.ActiveCfg = RD|Win32
+		{8F202F43-106D-4F63-AD9D-B1D43E803E8C}.Debug|Win32.Build.0 = RD|Win32
 		{8F202F43-106D-4F63-AD9D-B1D43E803E8C}.Debug|x64.ActiveCfg = Debug|x64
 		{8F202F43-106D-4F63-AD9D-B1D43E803E8C}.Debug|x64.Build.0 = Debug|x64
 		{8F202F43-106D-4F63-AD9D-B1D43E803E8C}.RD|Win32.ActiveCfg = RD|Win32
 		{8F202F43-106D-4F63-AD9D-B1D43E803E8C}.RD|Win32.Build.0 = RD|Win32
 		{8F202F43-106D-4F63-AD9D-B1D43E803E8C}.RD|x64.ActiveCfg = RD|x64
 		{8F202F43-106D-4F63-AD9D-B1D43E803E8C}.RD|x64.Build.0 = RD|x64
-		{276C3DB0-7A6B-4417-8E5C-322B08633AAC}.Debug|Win32.ActiveCfg = Debug|Win32
-		{276C3DB0-7A6B-4417-8E5C-322B08633AAC}.Debug|Win32.Build.0 = Debug|Win32
+		{276C3DB0-7A6B-4417-8E5C-322B08633AAC}.Debug|Win32.ActiveCfg = RD|Win32
+		{276C3DB0-7A6B-4417-8E5C-322B08633AAC}.Debug|Win32.Build.0 = RD|Win32
 		{276C3DB0-7A6B-4417-8E5C-322B08633AAC}.Debug|x64.ActiveCfg = Debug|x64
 		{276C3DB0-7A6B-4417-8E5C-322B08633AAC}.Debug|x64.Build.0 = Debug|x64
 		{276C3DB0-7A6B-4417-8E5C-322B08633AAC}.RD|Win32.ActiveCfg = RD|Win32
 		{276C3DB0-7A6B-4417-8E5C-322B08633AAC}.RD|Win32.Build.0 = RD|Win32
 		{276C3DB0-7A6B-4417-8E5C-322B08633AAC}.RD|x64.ActiveCfg = RD|x64
 		{276C3DB0-7A6B-4417-8E5C-322B08633AAC}.RD|x64.Build.0 = RD|x64
-		{D15B34EC-A32C-4968-9B0B-66998B579364}.Debug|Win32.ActiveCfg = Debug|Win32
-		{D15B34EC-A32C-4968-9B0B-66998B579364}.Debug|Win32.Build.0 = Debug|Win32
+		{D15B34EC-A32C-4968-9B0B-66998B579364}.Debug|Win32.ActiveCfg = RD|Win32
+		{D15B34EC-A32C-4968-9B0B-66998B579364}.Debug|Win32.Build.0 = RD|Win32
 		{D15B34EC-A32C-4968-9B0B-66998B579364}.Debug|x64.ActiveCfg = Debug|x64
 		{D15B34EC-A32C-4968-9B0B-66998B579364}.Debug|x64.Build.0 = Debug|x64
 		{D15B34EC-A32C-4968-9B0B-66998B579364}.RD|Win32.ActiveCfg = RD|Win32
 		{D15B34EC-A32C-4968-9B0B-66998B579364}.RD|Win32.Build.0 = RD|Win32
 		{D15B34EC-A32C-4968-9B0B-66998B579364}.RD|x64.ActiveCfg = RD|x64
 		{D15B34EC-A32C-4968-9B0B-66998B579364}.RD|x64.Build.0 = RD|x64
-		{C0300513-E845-43B4-9A4F-E8817EAEF57C}.Debug|Win32.ActiveCfg = Debug|Win32
-		{C0300513-E845-43B4-9A4F-E8817EAEF57C}.Debug|Win32.Build.0 = Debug|Win32
+		{C0300513-E845-43B4-9A4F-E8817EAEF57C}.Debug|Win32.ActiveCfg = RD|Win32
+		{C0300513-E845-43B4-9A4F-E8817EAEF57C}.Debug|Win32.Build.0 = RD|Win32
 		{C0300513-E845-43B4-9A4F-E8817EAEF57C}.Debug|x64.ActiveCfg = Debug|x64
 		{C0300513-E845-43B4-9A4F-E8817EAEF57C}.Debug|x64.Build.0 = Debug|x64
 		{C0300513-E845-43B4-9A4F-E8817EAEF57C}.RD|Win32.ActiveCfg = RD|Win32
 		{C0300513-E845-43B4-9A4F-E8817EAEF57C}.RD|Win32.Build.0 = RD|Win32
 		{C0300513-E845-43B4-9A4F-E8817EAEF57C}.RD|x64.ActiveCfg = RD|x64
 		{C0300513-E845-43B4-9A4F-E8817EAEF57C}.RD|x64.Build.0 = RD|x64
-		{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Win32.ActiveCfg = Debug|Win32
-		{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Win32.Build.0 = Debug|Win32
+		{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Win32.ActiveCfg = Release|Win32
 		{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x64.ActiveCfg = Debug|Win32
 		{B12702AD-ABFB-343A-A199-8E24837244A3}.RD|Win32.ActiveCfg = Release|Win32
-		{B12702AD-ABFB-343A-A199-8E24837244A3}.RD|Win32.Build.0 = Release|Win32
 		{B12702AD-ABFB-343A-A199-8E24837244A3}.RD|x64.ActiveCfg = Release|Win32
-		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.Debug|Win32.ActiveCfg = Debug|Win32
-		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.Debug|Win32.Build.0 = Debug|Win32
+		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.Debug|Win32.ActiveCfg = RD|Win32
+		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.Debug|Win32.Build.0 = RD|Win32
 		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.Debug|x64.ActiveCfg = Debug|x64
 		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.Debug|x64.Build.0 = Debug|x64
 		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.RD|Win32.ActiveCfg = RD|Win32
 		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.RD|Win32.Build.0 = RD|Win32
 		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.RD|x64.ActiveCfg = RD|x64
 		{C41C4EB6-6F74-4F37-9FB0-9FA6BF377837}.RD|x64.Build.0 = RD|x64
-		{BA25F3F0-EB87-4164-AAB9-073C50A3557A}.Debug|Win32.ActiveCfg = Debug|Win32
-		{BA25F3F0-EB87-4164-AAB9-073C50A3557A}.Debug|Win32.Build.0 = Debug|Win32
+		{BA25F3F0-EB87-4164-AAB9-073C50A3557A}.Debug|Win32.ActiveCfg = RD|Win32
+		{BA25F3F0-EB87-4164-AAB9-073C50A3557A}.Debug|Win32.Build.0 = RD|Win32
 		{BA25F3F0-EB87-4164-AAB9-073C50A3557A}.Debug|x64.ActiveCfg = Debug|x64
 		{BA25F3F0-EB87-4164-AAB9-073C50A3557A}.Debug|x64.Build.0 = Debug|x64
 		{BA25F3F0-EB87-4164-AAB9-073C50A3557A}.RD|Win32.ActiveCfg = RD|Win32
 		{BA25F3F0-EB87-4164-AAB9-073C50A3557A}.RD|Win32.Build.0 = RD|Win32
 		{BA25F3F0-EB87-4164-AAB9-073C50A3557A}.RD|x64.ActiveCfg = RD|x64
 		{BA25F3F0-EB87-4164-AAB9-073C50A3557A}.RD|x64.Build.0 = RD|x64
-		{AA3CC588-9D08-4178-A1E8-C71561E99723}.Debug|Win32.ActiveCfg = Debug|Win32
-		{AA3CC588-9D08-4178-A1E8-C71561E99723}.Debug|Win32.Build.0 = Debug|Win32
+		{AA3CC588-9D08-4178-A1E8-C71561E99723}.Debug|Win32.ActiveCfg = RD|Win32
+		{AA3CC588-9D08-4178-A1E8-C71561E99723}.Debug|Win32.Build.0 = RD|Win32
 		{AA3CC588-9D08-4178-A1E8-C71561E99723}.Debug|x64.ActiveCfg = Debug|x64
 		{AA3CC588-9D08-4178-A1E8-C71561E99723}.Debug|x64.Build.0 = Debug|x64
 		{AA3CC588-9D08-4178-A1E8-C71561E99723}.RD|Win32.ActiveCfg = RD|Win32
 		{AA3CC588-9D08-4178-A1E8-C71561E99723}.RD|Win32.Build.0 = RD|Win32
 		{AA3CC588-9D08-4178-A1E8-C71561E99723}.RD|x64.ActiveCfg = RD|x64
 		{AA3CC588-9D08-4178-A1E8-C71561E99723}.RD|x64.Build.0 = RD|x64
-		{5B6946C8-A24F-4223-8415-5E16A238ACED}.Debug|Win32.ActiveCfg = Debug|Win32
-		{5B6946C8-A24F-4223-8415-5E16A238ACED}.Debug|Win32.Build.0 = Debug|Win32
+		{5B6946C8-A24F-4223-8415-5E16A238ACED}.Debug|Win32.ActiveCfg = RD|Win32
+		{5B6946C8-A24F-4223-8415-5E16A238ACED}.Debug|Win32.Build.0 = RD|Win32
 		{5B6946C8-A24F-4223-8415-5E16A238ACED}.Debug|x64.ActiveCfg = Debug|Win32
 		{5B6946C8-A24F-4223-8415-5E16A238ACED}.RD|Win32.ActiveCfg = RD|Win32
 		{5B6946C8-A24F-4223-8415-5E16A238ACED}.RD|Win32.Build.0 = RD|Win32

+ 2 - 1
client/CAnimation.cpp

@@ -4,6 +4,7 @@
 #include "../lib/filesystem/Filesystem.h"
 #include "../lib/filesystem/ISimpleResourceLoader.h"
 #include "../lib/JsonNode.h"
+#include "../lib/CRandomGenerator.h"
 
 #include "CBitmapHandler.h"
 #include "Graphics.h"
@@ -1432,7 +1433,7 @@ void CCreatureAnim::loopPreview(bool warMachine)
 		if (anim.size(elem))
 			available.push_back(elem);
 
-	size_t rnd = rand()%(available.size()*2);
+	size_t rnd = CRandomGenerator::getDefault().nextInt(available.size() * 2 - 1);
 
 	if (rnd >= available.size())
 	{

+ 11 - 5
client/CCastleInterface.cpp

@@ -358,7 +358,7 @@ void CHeroGSlot::clickLeft(tribool down, bool previousState)
 			bool allow = true;
 			if(upg) //moving hero out of town - check if it is allowed
 			{
-				if(!hero && LOCPLINT->cb->howManyHeroes(false) >= 8)
+				if(!hero && LOCPLINT->cb->howManyHeroes(false) >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)
 				{
 					std::string tmp = CGI->generaltexth->allTexts[18]; //You already have %d adventuring heroes under your command.
 					boost::algorithm::replace_first(tmp,"%d",boost::lexical_cast<std::string>(LOCPLINT->cb->howManyHeroes(false)));
@@ -847,7 +847,9 @@ void CCastleBuildings::enterTownHall()
 
 void CCastleBuildings::openMagesGuild()
 {
-	GH.pushInt(new CMageGuildScreen(LOCPLINT->castleInt));
+	std::string mageGuildBackground;
+	mageGuildBackground = LOCPLINT->castleInt->town->town->clientInfo.guildBackground;
+	GH.pushInt(new CMageGuildScreen(LOCPLINT->castleInt,mageGuildBackground));
 }
 
 void CCastleBuildings::openTownHall()
@@ -964,7 +966,8 @@ void CCastleInterface::recreateIcons()
 	size_t iconIndex = town->town->clientInfo.icons[town->hasFort()][town->builded >= CGI->modh->settings.MAX_BUILDING_PER_TURN];
 
 	icon->setFrame(iconIndex);
-	income->setText(boost::lexical_cast<std::string>(town->dailyIncome()));
+	TResources townIncome = town->dailyIncome();
+	income->setText(boost::lexical_cast<std::string>(townIncome[Res::GOLD]));
 
 	hall = new CTownInfo( 80, 413, town, true);
 	fort = new CTownInfo(122, 413, town, false);
@@ -1636,9 +1639,12 @@ void CFortScreen::RecruitArea::clickRight(tribool down, bool previousState)
 	clickLeft(down, false); //r-click does same as l-click - opens recr. window
 }
 
-CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner):
-    CWindowObject(BORDERED, "TPMAGE")
+
+
+
+CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner,std::string imagem) :CWindowObject(BORDERED,imagem)
 {
+
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 
 	window = new CPicture(owner->town->town->clientInfo.guildWindow , 332, 76);

+ 1 - 1
client/CCastleInterface.h

@@ -360,7 +360,7 @@ class CMageGuildScreen : public CWindowObject
 	CGStatusBar *statusBar;
 
 public:
-	CMageGuildScreen(CCastleInterface * owner);
+	CMageGuildScreen(CCastleInterface * owner,std::string image);
 };
 
 /// The blacksmith window where you can buy available in town war machine

+ 956 - 955
client/CKingdomInterface.cpp

@@ -1,955 +1,956 @@
-#include "StdInc.h"
-#include "CKingdomInterface.h"
-
-#include "../CCallback.h"
-#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/CHeroHandler.h" // only for calculating required xp? worth it?
-#include "../lib/CTownHandler.h"
-#include "CAnimation.h" //CAnimImage
-#include "CAdvmapInterface.h" //CResDataBar
-#include "CCastleInterface.h" //various town-specific classes
-#include "../lib/CConfigHandler.h"
-#include "CGameInfo.h"
-#include "CPlayerInterface.h" //LOCPLINT
-#include "gui/CGuiHandler.h"
-#include "gui/CIntObjectClasses.h"
-#include "CMT.h"
-
-/*
- * CKingdomInterface.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
- *
- */
-
-InfoBox::InfoBox(Point position, InfoPos Pos, InfoSize Size, IInfoBoxData *Data):
-	size(Size),
-	infoPos(Pos),
-	data(Data),
-	value(nullptr),
-	name(nullptr)
-{
-	assert(data);
-	addUsedEvents(LCLICK | RCLICK);
-	EFonts font = (size < SIZE_MEDIUM)? FONT_SMALL: FONT_MEDIUM;
-
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	pos+=position;
-
-	image = new CAnimImage(data->getImageName(size), data->getImageIndex());
-	pos = image->pos;
-
-	if (infoPos == POS_CORNER)
-		value = new CLabel(pos.w, pos.h, font, BOTTOMRIGHT, Colors::WHITE, data->getValueText());
-
-	if (infoPos == POS_INSIDE)
-		value = new CLabel(pos.w/2, pos.h-6, font, CENTER, Colors::WHITE, data->getValueText());
-
-	if (infoPos == POS_UP_DOWN || infoPos == POS_DOWN)
-		value = new CLabel(pos.w/2, pos.h+8, font, CENTER, Colors::WHITE, data->getValueText());
-
-	if (infoPos == POS_UP_DOWN)
-		name = new CLabel(pos.w/2, -12, font, CENTER, Colors::WHITE, data->getNameText());
-
-	if (infoPos == POS_RIGHT)
-	{
-		name = new CLabel(pos.w+6, 6, font, TOPLEFT, Colors::WHITE, data->getNameText());
-		value = new CLabel(pos.w+6, pos.h-16, font, TOPLEFT, Colors::WHITE, data->getValueText());
-	}
-	pos = image->pos;
-	if (name)
-		pos = pos | name->pos;
-	if (value)
-		pos = pos | value->pos;
-	
-	hover = new CHoverableArea;
-	hover->hoverText = data->getHoverText();
-	hover->pos = pos;
-}
-
-InfoBox::~InfoBox()
-{
-	delete data;
-}
-
-void InfoBox::clickRight(tribool down, bool previousState)
-{
-	if (down)
-	{
-		CComponent *comp = nullptr;
-		std::string text;
-		data->prepareMessage(text, &comp);
-		if (comp)
-			CRClickPopup::createAndPush(text, CInfoWindow::TCompsInfo(1, comp));
-		else if (!text.empty())
-			adventureInt->handleRightClick(text, down);
-	}
-}
-
-void InfoBox::clickLeft(tribool down, bool previousState)
-{
-	if((!down) && previousState)
-	{
-		CComponent *comp = nullptr;
-		std::string text;
-		data->prepareMessage(text, &comp);
-
-		std::vector<CComponent*> compVector;
-		if (comp)
-		{
-			compVector.push_back(comp);
-			LOCPLINT->showInfoDialog(text, compVector);
-		}
-	}
-}
-
-//TODO?
-/*
-void InfoBox::update()
-{
-
-}
-*/
-
-IInfoBoxData::IInfoBoxData(InfoType Type):
-	type(Type)
-{
-}
-
-InfoBoxAbstractHeroData::InfoBoxAbstractHeroData(InfoType Type):
-	IInfoBoxData(Type)
-{
-}
-
-std::string InfoBoxAbstractHeroData::getValueText()
-{
-	switch (type)
-	{
-	case HERO_MANA:
-	case HERO_EXPERIENCE:
-	case HERO_PRIMARY_SKILL:
-		return boost::lexical_cast<std::string>(getValue());
-	case HERO_SPECIAL:
-		return CGI->generaltexth->jktexts[5];
-	case HERO_SECONDARY_SKILL:
-		{
-			si64 value = getValue();
-			if (value)
-				return CGI->generaltexth->levels[value];
-		}
-	default:
-		assert(0);
-	}
-	return "";
-}
-
-std::string InfoBoxAbstractHeroData::getNameText()
-{
-	switch (type)
-	{
-	case HERO_PRIMARY_SKILL:
-		return CGI->generaltexth->primarySkillNames[getSubID()];
-	case HERO_MANA:
-		return CGI->generaltexth->allTexts[387];
-	case HERO_EXPERIENCE:
-		return CGI->generaltexth->jktexts[6];
-	case HERO_SPECIAL:
-		return CGI->heroh->heroes[getSubID()]->specName;
-	case HERO_SECONDARY_SKILL:
-		if (getValue())
-			return CGI->generaltexth->skillName[getSubID()];
-		else
-			return "";
-	default:
-		assert(0);
-	}
-	return "";
-}
-
-std::string InfoBoxAbstractHeroData::getImageName(InfoBox::InfoSize size)
-{
-	//TODO: sizes
-	switch(size)
-	{
-	case InfoBox::SIZE_SMALL:
-		{
-			switch(type)
-			{
-			case HERO_PRIMARY_SKILL:
-			case HERO_MANA:
-			case HERO_EXPERIENCE:
-				return "PSKIL32";
-			case HERO_SPECIAL:
-				return "UN32";
-			case HERO_SECONDARY_SKILL:
-				return "SECSK32";
-			default:
-				assert(0);
-			}
-		}
-	case InfoBox::SIZE_BIG:
-		{
-			switch(type)
-			{
-			case HERO_PRIMARY_SKILL:
-			case HERO_MANA:
-			case HERO_EXPERIENCE:
-				return "PSKIL42";
-			case HERO_SPECIAL:
-				return "UN44";
-			case HERO_SECONDARY_SKILL:
-				return "SECSKILL";
-			default:
-				assert(0);
-			}
-		}
-	default:
-		assert(0);
-	}
-	return "";
-}
-
-std::string InfoBoxAbstractHeroData::getHoverText()
-{
-	//TODO: any texts here?
-	return "";
-}
-
-size_t InfoBoxAbstractHeroData::getImageIndex()
-{
-	switch (type)
-	{
-	case HERO_SPECIAL:
-		return VLC->heroh->heroes[getSubID()]->imageIndex;
-	case HERO_PRIMARY_SKILL:
-		return getSubID();
-	case HERO_MANA:
-		return 5;
-	case HERO_EXPERIENCE:
-		return 4;
-	case HERO_SECONDARY_SKILL:
-		{
-			si64 value = getValue();
-			if (value)
-				return getSubID()*3 + value + 2;
-			else
-				return 0;//FIXME: Should be transparent instead of empty
-		}
-	default:
-		assert(0);
-		return 0;
-	}
-}
-
-bool InfoBoxAbstractHeroData::prepareMessage(std::string &text, CComponent **comp)
-{
-	switch (type)
-	{
-	case HERO_SPECIAL:
-		text = CGI->heroh->heroes[getSubID()]->specDescr;
-		*comp = nullptr;
-		return true;
-	case HERO_PRIMARY_SKILL:
-		text = CGI->generaltexth->arraytxt[2+getSubID()];
-		*comp =new CComponent(CComponent::primskill, getSubID(), getValue());
-		return true;
-	case HERO_MANA:
-		text = CGI->generaltexth->allTexts[149];
-		*comp = nullptr;
-		return true;
-	case HERO_EXPERIENCE:
-		text = CGI->generaltexth->allTexts[241];
-		*comp = nullptr;
-		return true;
-	case HERO_SECONDARY_SKILL:
-		{
-			si64 value = getValue();
-			int  subID = getSubID();
-			if (!value)
-				return false;
-
-			text = CGI->generaltexth->skillInfoTexts[subID][value-1];
-			*comp = new CComponent(CComponent::secskill, subID, value);
-			return true;
-		}
-	default:
-		assert(0);
-		return false;
-	}
-}
-
-InfoBoxHeroData::InfoBoxHeroData(InfoType Type, const CGHeroInstance * Hero, int Index):
-	InfoBoxAbstractHeroData(Type),
-	hero(Hero),
-	index(Index)
-{
-}
-
-int InfoBoxHeroData::getSubID()
-{
-	switch(type)
-	{
-		case HERO_PRIMARY_SKILL:
-			return index;
-		case HERO_SECONDARY_SKILL:
-			if (hero->secSkills.size() > index)
-				return hero->secSkills[index].first;
-		case HERO_MANA:
-		case HERO_EXPERIENCE:
-		case HERO_SPECIAL:
-			return 0;
-		default:
-			assert(0);
-			return 0;
-	}
-}
-
-si64 InfoBoxHeroData::getValue()
-{
-	switch(type)
-	{
-	case HERO_PRIMARY_SKILL:
-		return hero->getPrimSkillLevel(static_cast<PrimarySkill::PrimarySkill>(index));
-	case HERO_MANA:
-		return hero->mana;
-	case HERO_EXPERIENCE:
-		return hero->exp;
-	case HERO_SECONDARY_SKILL:
-		if (hero->secSkills.size() > index)
-			return hero->secSkills[index].second;
-	case HERO_SPECIAL:
-			return 0;
-	default:
-		assert(0);
-		return 0;
-	}
-}
-
-std::string InfoBoxHeroData::getHoverText()
-{
-	switch (type)
-	{
-	case HERO_PRIMARY_SKILL:
-		return boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % CGI->generaltexth->primarySkillNames[index]);
-	case HERO_MANA:
-		return CGI->generaltexth->heroscrn[22];
-	case HERO_EXPERIENCE:
-		return CGI->generaltexth->heroscrn[9];
-	case HERO_SPECIAL:
-		return CGI->generaltexth->heroscrn[27];
-	case HERO_SECONDARY_SKILL:
-		{
-		if (hero->secSkills.size() > index)
-		{
-			std::string level = CGI->generaltexth->levels[hero->secSkills[index].second-1];
-			std::string skill = CGI->generaltexth->skillName[hero->secSkills[index].first];
-			return boost::str(boost::format(CGI->generaltexth->heroscrn[21]) % level % skill);
-		}
-		else
-			return "";
-		}
-	default:
-		return InfoBoxAbstractHeroData::getHoverText();
-	}
-}
-
-std::string InfoBoxHeroData::getValueText()
-{
-	switch (type)
-	{
-	case HERO_MANA:
-		if (hero)
-			return boost::lexical_cast<std::string>(hero->mana) + '/' +
-			       boost::lexical_cast<std::string>(hero->manaLimit());
-	case HERO_EXPERIENCE:
-		return boost::lexical_cast<std::string>(hero->exp);
-	default:
-		return InfoBoxAbstractHeroData::getValueText();
-	}
-}
-
-bool InfoBoxHeroData::prepareMessage(std::string &text, CComponent**comp)
-{
-	switch(type)
-	{
-	case HERO_MANA:
-		text = CGI->generaltexth->allTexts[205];
-		boost::replace_first(text, "%s", boost::lexical_cast<std::string>(hero->name));
-		boost::replace_first(text, "%d", boost::lexical_cast<std::string>(hero->mana));
-		boost::replace_first(text, "%d", boost::lexical_cast<std::string>(hero->manaLimit()));
-		*comp = nullptr;
-		return true;
-
-	case HERO_EXPERIENCE:
-		text = CGI->generaltexth->allTexts[2];
-		boost::replace_first(text, "%d", boost::lexical_cast<std::string>(hero->level));
-		boost::replace_first(text, "%d", boost::lexical_cast<std::string>(CGI->heroh->reqExp(hero->level+1)));
-		boost::replace_first(text, "%d", boost::lexical_cast<std::string>(hero->exp));
-		*comp = nullptr;
-		return true;
-
-	default:
-		return InfoBoxAbstractHeroData::prepareMessage(text, comp);
-	}
-}
-
-InfoBoxCustomHeroData::InfoBoxCustomHeroData(InfoType Type, int SubID, si64 Value):
-	InfoBoxAbstractHeroData(Type),
-	subID(SubID),
-	value(Value)
-{
-}
-
-int InfoBoxCustomHeroData::getSubID()
-{
-	return subID;
-}
-
-si64 InfoBoxCustomHeroData::getValue()
-{
-	return value;
-}
-
-InfoBoxCustom::InfoBoxCustom(std::string ValueText, std::string NameText, std::string ImageName, size_t ImageIndex, std::string HoverText):
-	IInfoBoxData(CUSTOM),
-	valueText(ValueText),
-	nameText(NameText),
-	imageName(ImageName),
-	hoverText(HoverText),
-	imageIndex(ImageIndex)
-{
-}
-
-std::string InfoBoxCustom::getHoverText()
-{
-	return hoverText;
-}
-
-size_t InfoBoxCustom::getImageIndex()
-{
-	return imageIndex;
-}
-
-std::string InfoBoxCustom::getImageName(InfoBox::InfoSize size)
-{
-	return imageName;
-}
-
-std::string InfoBoxCustom::getNameText()
-{
-	return nameText;
-}
-
-std::string InfoBoxCustom::getValueText()
-{
-	return valueText;
-}
-
-bool InfoBoxCustom::prepareMessage(std::string &text, CComponent **comp)
-{
-	return false;
-}
-
-CKingdomInterface::CKingdomInterface():
-    CWindowObject(PLAYER_COLORED | BORDERED, conf.go()->ac.overviewBg)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	ui32 footerPos = conf.go()->ac.overviewSize * 116;
-
-	tabArea = new CTabbedInt(boost::bind(&CKingdomInterface::createMainTab, this, _1), CTabbedInt::DestroyFunc(), Point(4,4));
-
-	std::vector<const CGObjectInstance * > ownedObjects = LOCPLINT->cb->getMyObjects();
-	generateObjectsList(ownedObjects);
-	generateMinesList(ownedObjects);
-	generateButtons();
-
-	statusbar = new CGStatusBar(new CPicture("KSTATBAR", 10,pos.h - 45));
-	resdatabar= new CResDataBar("KRESBAR", 3, 111+footerPos, 32, 2, 76, 76);
-}
-
-void CKingdomInterface::generateObjectsList(const std::vector<const CGObjectInstance * > &ownedObjects)
-{
-	ui32 footerPos = conf.go()->ac.overviewSize * 116;
-	size_t dwellSize = (footerPos - 64)/57;
-
-	//Map used to determine image number for several objects
-	std::map<std::pair<int,int>,int> idToImage;
-	idToImage[std::make_pair( 20, 1)] = 81;//Golem factory
-	idToImage[std::make_pair( 42, 0)] = 82;//Lighthouse
-	idToImage[std::make_pair( 33, 0)] = 83;//Garrison
-	idToImage[std::make_pair(219, 0)] = 83;//Garrison
-	idToImage[std::make_pair( 33, 1)] = 84;//Anti-magic Garrison
-	idToImage[std::make_pair(219, 1)] = 84;//Anti-magic Garrison
-	idToImage[std::make_pair( 53, 7)] = 85;//Abandoned mine
-	idToImage[std::make_pair( 20, 0)] = 86;//Conflux
-	idToImage[std::make_pair( 87, 0)] = 87;//Harbor
-
-	std::map<int, OwnedObjectInfo> visibleObjects;
-	for(const CGObjectInstance * object : ownedObjects)
-	{
-		//Dwellings
-		if ( object->ID == Obj::CREATURE_GENERATOR1 )
-		{
-			OwnedObjectInfo &info = visibleObjects[object->subID];
-			if (info.count++ == 0)
-			{
-				info.hoverText = CGI->creh->creatures[CGI->objh->cregens.find(object->subID)->second]->namePl;
-				info.imageID = object->subID;
-			}
-		}
-		//Special objects from idToImage map that should be displayed in objects list
-		auto iter = idToImage.find(std::make_pair(object->ID, object->subID));
-		if (iter != idToImage.end())
-		{
-			OwnedObjectInfo &info = visibleObjects[iter->second];
-			if (info.count++ == 0)
-			{
-				info.hoverText = object->hoverName;
-				info.imageID = iter->second;
-			}
-		}
-	}
-	objects.reserve(visibleObjects.size());
-
-	for(auto & element : visibleObjects)
-	{
-		objects.push_back(element.second);
-	}
-	dwellingsList = new CListBox(boost::bind(&CKingdomInterface::createOwnedObject, this, _1), CListBox::DestroyFunc(),
-	                             Point(740,44), Point(0,57), dwellSize, visibleObjects.size());
-}
-
-CIntObject* CKingdomInterface::createOwnedObject(size_t index)
-{
-	if (index < objects.size())
-	{
-		OwnedObjectInfo &obj = objects[index];
-		std::string value = boost::lexical_cast<std::string>(obj.count);
-		return new InfoBox(Point(), InfoBox::POS_CORNER, InfoBox::SIZE_SMALL,
-			   new InfoBoxCustom(value,"", "FLAGPORT", obj.imageID, obj.hoverText));
-	}
-	return nullptr;
-}
-
-CIntObject * CKingdomInterface::createMainTab(size_t index)
-{
-	size_t size = conf.go()->ac.overviewSize;
-	switch (index)
-	{
-	case 0: return new CKingdHeroList(size);
-	case 1: return new CKingdTownList(size);
-	default:return nullptr;
-	}
-}
-
-void CKingdomInterface::generateMinesList(const std::vector<const CGObjectInstance * > &ownedObjects)
-{
-	ui32 footerPos = conf.go()->ac.overviewSize * 116;
-	std::vector<int> minesCount(GameConstants::RESOURCE_QUANTITY, 0);
-	int totalIncome=0;
-
-	for(const CGObjectInstance * object : ownedObjects)
-	{
-		//Mines
-		if ( object->ID == Obj::MINE )
-		{
-			const CGMine *mine = dynamic_cast<const CGMine*>(object);
-			assert(mine);
-			minesCount[mine->producedResource]++;
-
-			if (mine->producedResource == Res::GOLD)
-				totalIncome += mine->producedQuantity;
-		}
-	}
-
-	//Heroes can produce gold as well - skill, specialty or arts
-	std::vector<const CGHeroInstance*> heroes = LOCPLINT->cb->getHeroesInfo(true);
-	for(auto & heroe : heroes)
-	{
-		totalIncome += heroe->valOfBonuses(Selector::typeSubtype(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::ESTATES));
-		totalIncome += heroe->valOfBonuses(Selector::typeSubtype(Bonus::GENERATE_RESOURCE, Res::GOLD));
-	}
-
-	//Add town income of all towns
-	std::vector<const CGTownInstance*> towns = LOCPLINT->cb->getTownsInfo(true);
-	for(auto & town : towns)
-	{
-		totalIncome += town->dailyIncome();
-	}
-	for (int i=0; i<7; i++)
-	{
-		std::string value = boost::lexical_cast<std::string>(minesCount[i]);
-		minesBox[i] = new InfoBox(Point(20+i*80, 31+footerPos), InfoBox::POS_INSIDE, InfoBox::SIZE_SMALL,
-		              new InfoBoxCustom(value, "", "OVMINES", i, CGI->generaltexth->mines[i].first));
-
-		minesBox[i]->removeUsedEvents(LCLICK|RCLICK); //fixes #890 - mines boxes ignore clicks
-	}
-	incomeArea = new CHoverableArea;
-	incomeArea->pos = Rect(pos.x+580, pos.y+31+footerPos, 136, 68);
-	incomeArea->hoverText = CGI->generaltexth->allTexts[255];
-	incomeAmount = new CLabel(628, footerPos + 70, FONT_SMALL, TOPLEFT, Colors::WHITE, boost::lexical_cast<std::string>(totalIncome));
-}
-
-void CKingdomInterface::generateButtons()
-{
-	ui32 footerPos = conf.go()->ac.overviewSize * 116;
-
-	//Main control buttons
-	btnHeroes = new CAdventureMapButton (CGI->generaltexth->overview[11], CGI->generaltexth->overview[6],
-	                                    boost::bind(&CKingdomInterface::activateTab, this, 0),748,28+footerPos,"OVBUTN1.DEF", SDLK_h);
-	btnHeroes->block(true);
-
-	btnTowns = new CAdventureMapButton (CGI->generaltexth->overview[12], CGI->generaltexth->overview[7],
-	                                   boost::bind(&CKingdomInterface::activateTab, this, 1),748,64+footerPos,"OVBUTN6.DEF", SDLK_t);
-
-	btnExit = new CAdventureMapButton (CGI->generaltexth->allTexts[600],"",
-	                                  boost::bind(&CKingdomInterface::close, this),748,99+footerPos,"OVBUTN1.DEF", SDLK_RETURN);
-	btnExit->assignedKeys.insert(SDLK_ESCAPE);
-	btnExit->setOffset(3);
-
-	//Object list control buttons
-	dwellTop = new CAdventureMapButton ("", "", boost::bind(&CListBox::moveToPos, dwellingsList, 0),
-	                                   733, 4, "OVBUTN4.DEF");
-
-	dwellBottom = new CAdventureMapButton ("", "", boost::bind(&CListBox::moveToPos, dwellingsList, -1),
-	                                      733, footerPos+2, "OVBUTN4.DEF");
-	dwellBottom->setOffset(2);
-
-	dwellUp = new CAdventureMapButton ("", "", boost::bind(&CListBox::moveToPrev, dwellingsList),
-	                                  733, 24, "OVBUTN4.DEF");
-	dwellUp->setOffset(4);
-
-	dwellDown = new CAdventureMapButton ("", "", boost::bind(&CListBox::moveToNext, dwellingsList),
-	                                    733, footerPos-18, "OVBUTN4.DEF");
-	dwellDown->setOffset(6);
-}
-
-void CKingdomInterface::activateTab(size_t which)
-{
-	btnHeroes->block(which == 0);
-	btnTowns->block(which == 1);
-	tabArea->setActive(which);
-}
-
-void CKingdomInterface::townChanged(const CGTownInstance *town)
-{
-	if (CKingdTownList * townList = dynamic_cast<CKingdTownList*>(tabArea->getItem()))
-		townList->townChanged(town);
-}
-
-void CKingdomInterface::updateGarrisons()
-{
-	if (CGarrisonHolder * garrison = dynamic_cast<CGarrisonHolder*>(tabArea->getItem()))
-		garrison->updateGarrisons();
-}
-
-void CKingdomInterface::artifactAssembled(const ArtifactLocation& artLoc)
-{
-	if (CArtifactHolder * arts = dynamic_cast<CArtifactHolder*>(tabArea->getItem()))
-		arts->artifactAssembled(artLoc);
-}
-
-void CKingdomInterface::artifactDisassembled(const ArtifactLocation& artLoc)
-{
-	if (CArtifactHolder * arts = dynamic_cast<CArtifactHolder*>(tabArea->getItem()))
-		arts->artifactDisassembled(artLoc);
-}
-
-void CKingdomInterface::artifactMoved(const ArtifactLocation& artLoc, const ArtifactLocation& destLoc)
-{
-	if (CArtifactHolder * arts = dynamic_cast<CArtifactHolder*>(tabArea->getItem()))
-		arts->artifactMoved(artLoc, destLoc);
-}
-
-void CKingdomInterface::artifactRemoved(const ArtifactLocation& artLoc)
-{
-	if (CArtifactHolder * arts = dynamic_cast<CArtifactHolder*>(tabArea->getItem()))
-		arts->artifactRemoved(artLoc);
-}
-
-CKingdHeroList::CKingdHeroList(size_t maxSize)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	title = new CPicture("OVTITLE",16,0);
-	title->colorize(LOCPLINT->playerID);
-	heroLabel =   new CLabel(150, 10, FONT_MEDIUM, CENTER, Colors::WHITE, CGI->generaltexth->overview[0]);
-	skillsLabel = new CLabel(500, 10, FONT_MEDIUM, CENTER, Colors::WHITE, CGI->generaltexth->overview[1]);
-
-	ui32 townCount = LOCPLINT->cb->howManyHeroes(false);
-	ui32 size = conf.go()->ac.overviewSize*116 + 19;
-	heroes = new CListBox(boost::bind(&CKingdHeroList::createHeroItem, this, _1), boost::bind(&CKingdHeroList::destroyHeroItem, this, _1),
-	                      Point(19,21), Point(0,116), maxSize, townCount, 0, 1, Rect(-19, -21, size, size) );
-}
-
-void CKingdHeroList::updateGarrisons()
-{
-	std::list<CIntObject*> list = heroes->getItems();
-	for(CIntObject* object : list)
-	{
-		if (CGarrisonHolder * garrison = dynamic_cast<CGarrisonHolder*>(object) )
-			garrison->updateGarrisons();
-	}
-}
-
-CIntObject* CKingdHeroList::createHeroItem(size_t index)
-{
-	ui32 picCount = conf.go()->ac.overviewPics;
-	size_t heroesCount = LOCPLINT->cb->howManyHeroes(false);
-
-	if (index < heroesCount)
-	{
-		auto   hero = new CHeroItem(LOCPLINT->cb->getHeroBySerial(index, false), &artsCommonPart);
-		artsCommonPart.participants.insert(hero->heroArts);
-		artSets.push_back(hero->heroArts);
-		return hero;
-	}
-	else
-	{
-		return new CAnimImage("OVSLOT", (index-2) % picCount );
-	}
-}
-
-void CKingdHeroList::destroyHeroItem(CIntObject *object)
-{
-	if (CHeroItem * hero = dynamic_cast<CHeroItem*>(object))
-	{
-		artSets.erase(std::find(artSets.begin(), artSets.end(), hero->heroArts));
-		artsCommonPart.participants.erase(hero->heroArts);
-	}
-	delete object;
-}
-
-CKingdTownList::CKingdTownList(size_t maxSize)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	title = new CPicture("OVTITLE",16,0);
-	title->colorize(LOCPLINT->playerID);
-	townLabel   = new CLabel(146,10,FONT_MEDIUM, CENTER, Colors::WHITE, CGI->generaltexth->overview[3]);
-	garrHeroLabel  = new CLabel(375,10,FONT_MEDIUM, CENTER, Colors::WHITE, CGI->generaltexth->overview[4]);
-	visitHeroLabel = new CLabel(608,10,FONT_MEDIUM, CENTER, Colors::WHITE, CGI->generaltexth->overview[5]);
-
-	ui32 townCount = LOCPLINT->cb->howManyTowns();
-	ui32 size = conf.go()->ac.overviewSize*116 + 19;
-	towns = new CListBox(boost::bind(&CKingdTownList::createTownItem, this, _1), CListBox::DestroyFunc(),
-	                     Point(19,21), Point(0,116), maxSize, townCount, 0, 1, Rect(-19, -21, size, size) );
-}
-
-void CKingdTownList::townChanged(const CGTownInstance *town)
-{
-	std::list<CIntObject*> list = towns->getItems();
-	for(CIntObject* object : list)
-	{
-		CTownItem * townItem = dynamic_cast<CTownItem*>(object);
-		if ( townItem && townItem->town == town)
-			townItem->update();
-	}
-}
-
-void CKingdTownList::updateGarrisons()
-{
-	std::list<CIntObject*> list = towns->getItems();
-	for(CIntObject* object : list)
-	{
-		if (CGarrisonHolder * garrison = dynamic_cast<CGarrisonHolder*>(object) )
-			garrison->updateGarrisons();
-	}
-}
-
-CIntObject* CKingdTownList::createTownItem(size_t index)
-{
-	ui32 picCount = conf.go()->ac.overviewPics;
-	size_t townsCount = LOCPLINT->cb->howManyTowns();
-
-	if (index < townsCount)
-		return new CTownItem(LOCPLINT->cb->getTownBySerial(index));
-	else
-		return new CAnimImage("OVSLOT", (index-2) % picCount );
-}
-
-CTownItem::CTownItem(const CGTownInstance* Town):
-	town(Town)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	background =  new CAnimImage("OVSLOT", 6);
-	name = new CLabel(74, 8, FONT_SMALL, TOPLEFT, Colors::WHITE, town->name);
-
-	income = new CLabel( 190, 60, FONT_SMALL, CENTER, Colors::WHITE, boost::lexical_cast<std::string>(town->dailyIncome()));
-	hall = new CTownInfo( 69, 31, town, true);
-	fort = new CTownInfo(111, 31, town, false);
-
-	garr = new CGarrisonInt(313, 3, 4, Point(232,0),  nullptr, Point(313,2), town->getUpperArmy(), town->visitingHero, true, true, true);
-	heroes = new HeroSlots(town, Point(244,6), Point(475,6), garr, false);
-
-	size_t iconIndex = town->town->clientInfo.icons[town->hasFort()][town->builded >= CGI->modh->settings.MAX_BUILDING_PER_TURN];
-
-	picture = new CAnimImage("ITPT", iconIndex, 0, 5, 6);
-	townArea = new LRClickableAreaOpenTown;
-	townArea->pos = Rect(pos.x+5, pos.y+6, 58, 64);
-	townArea->town = town;
-
-	for (size_t i=0; i<town->creatures.size(); i++)
-	{
-		growth.push_back(new CCreaInfo(Point(401+37*i, 78), town, i, true, true));
-		available.push_back(new CCreaInfo(Point(48+37*i, 78), town, i, true, false));
-	}
-}
-
-void CTownItem::updateGarrisons()
-{
-	garr->selectSlot(nullptr);
-	garr->setArmy(town->getUpperArmy(), 0);
-	garr->setArmy(town->visitingHero, 1);
-	garr->recreateSlots();
-}
-
-void CTownItem::update()
-{
-	std::string incomeVal = boost::lexical_cast<std::string>(town->dailyIncome());
-	if (incomeVal != income->text)
-		income->setText(incomeVal);
-
-	heroes->update();
-
-	for (size_t i=0; i<town->creatures.size(); i++)
-	{
-		growth[i]->update();
-		available[i]->update();
-	}
-}
-
-class ArtSlotsTab : public CIntObject
-{
-public:
-	CAnimImage * background;
-	std::vector<CArtPlace*> arts;
-
-	ArtSlotsTab()
-	{
-		OBJ_CONSTRUCTION_CAPTURING_ALL;
-		background = new CAnimImage("OVSLOT", 4);
-		pos = background->pos;
-		for (size_t i=0; i<9; i++)
-			arts.push_back(new CArtPlace(Point(270+i*48, 65)));
-	}
-};
-
-class BackpackTab : public CIntObject
-{
-public:
-	CAnimImage * background;
-	std::vector<CArtPlace*> arts;
-	CAdventureMapButton *btnLeft;
-	CAdventureMapButton *btnRight;
-
-	BackpackTab()
-	{
-		OBJ_CONSTRUCTION_CAPTURING_ALL;
-		background = new CAnimImage("OVSLOT", 5);
-		pos = background->pos;
-		btnLeft = new CAdventureMapButton(std::string(), std::string(), CFunctionList<void()>(), 269, 66, "HSBTNS3");
-		btnRight = new CAdventureMapButton(std::string(), std::string(), CFunctionList<void()>(), 675, 66, "HSBTNS5");
-		for (size_t i=0; i<8; i++)
-			arts.push_back(new CArtPlace(Point(295+i*48, 65)));
-	}
-};
-
-CHeroItem::CHeroItem(const CGHeroInstance* Hero, CArtifactsOfHero::SCommonPart * artsCommonPart):
-	hero(Hero)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	artTabs.resize(3);
-	auto   arts1 = new ArtSlotsTab;
-	auto   arts2 = new ArtSlotsTab;
-	auto   backpack = new BackpackTab;
-	artTabs[0] = arts1;
-	artTabs[1] = arts2;
-	artTabs[2] = backpack;
-	arts1->recActions = DISPOSE | SHARE_POS;
-	arts2->recActions = DISPOSE | SHARE_POS;
-	backpack->recActions = DISPOSE | SHARE_POS;
-
-	name = new CLabel(75, 7, FONT_SMALL, TOPLEFT, Colors::WHITE, hero->name);
-
-	std::vector<CArtPlace*> arts;
-	arts.insert(arts.end(), arts1->arts.begin(), arts1->arts.end());
-	arts.insert(arts.end(), arts2->arts.begin(), arts2->arts.end());
-
-	heroArts = new CArtifactsOfHero(arts, backpack->arts, backpack->btnLeft, backpack->btnRight, false);
-	heroArts->commonInfo = artsCommonPart;
-	heroArts->setHero(hero);
-
-	artsTabs = new CTabbedInt(boost::bind(&CHeroItem::onTabSelected, this, _1), boost::bind(&CHeroItem::onTabDeselected, this, _1));
-
-	artButtons = new CHighlightableButtonsGroup(0);
-	for (size_t it = 0; it<3; it++)
-	{
-		int stringID[3] = {259, 261, 262};
-
-		std::map<int,std::string> tooltip;
-		tooltip[0] = CGI->generaltexth->overview[13+it];
-		std::string overlay = CGI->generaltexth->overview[8+it];
-
-		artButtons->addButton(tooltip, overlay, "OVBUTN3",364+it*112, 46, it);
-		artButtons->buttons[it]->addTextOverlay(CGI->generaltexth->allTexts[stringID[it]], FONT_SMALL, Colors::YELLOW);
-	}
-	artButtons->onChange += boost::bind(&CTabbedInt::setActive, artsTabs, _1);
-	artButtons->onChange += boost::bind(&CHeroItem::onArtChange, this, _1);
-	artButtons->select(0,0);
-
-	garr = new CGarrisonInt(6, 78, 4, Point(), nullptr, Point(), hero, nullptr, true, true);
-
-	portrait = new CAnimImage("PortraitsLarge", hero->portrait, 0, 5, 6);
-	heroArea = new CHeroArea(5, 6, hero);
-
-	name = new CLabel(73, 7, FONT_SMALL, TOPLEFT, Colors::WHITE, hero->name);
-	artsText = new CLabel(320, 55, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->overview[2]);
-
-	for (size_t i=0; i<GameConstants::PRIMARY_SKILLS; i++)
-		heroInfo.push_back(new InfoBox(Point(78+i*36, 26), InfoBox::POS_DOWN, InfoBox::SIZE_SMALL, 
-		                   new InfoBoxHeroData(IInfoBoxData::HERO_PRIMARY_SKILL, hero, i)));
-
-	for (size_t i=0; i<GameConstants::SKILL_PER_HERO; i++)
-		heroInfo.push_back(new InfoBox(Point(410+i*36, 5), InfoBox::POS_NONE, InfoBox::SIZE_SMALL,
-		                   new InfoBoxHeroData(IInfoBoxData::HERO_SECONDARY_SKILL, hero, i)));
-
-	heroInfo.push_back(new InfoBox(Point(375, 5), InfoBox::POS_NONE, InfoBox::SIZE_SMALL,
-	                   new InfoBoxHeroData(IInfoBoxData::HERO_SPECIAL, hero)));
-
-	heroInfo.push_back(new InfoBox(Point(330, 5), InfoBox::POS_INSIDE, InfoBox::SIZE_SMALL,
-	                   new InfoBoxHeroData(IInfoBoxData::HERO_EXPERIENCE, hero)));
-
-	heroInfo.push_back(new InfoBox(Point(280, 5), InfoBox::POS_INSIDE, InfoBox::SIZE_SMALL,
-	                   new InfoBoxHeroData(IInfoBoxData::HERO_MANA, hero)));
-
-	morale = new MoraleLuckBox(true, Rect(225, 53, 30, 22), true);
-	luck  = new MoraleLuckBox(false, Rect(225, 28, 30, 22), true);
-
-	morale->set(hero);
-	luck->set(hero);
-}
-
-CIntObject * CHeroItem::onTabSelected(size_t index)
-{
-	return artTabs[index];
-}
-
-void CHeroItem::onTabDeselected(CIntObject *object)
-{
-	addChild(object, false);
-	object->deactivate();
-	object->recActions = DISPOSE | SHARE_POS;
-}
-
-void CHeroItem::onArtChange(int tabIndex)
-{
-	//redraw item after background change
-	if (active)
-		redraw();
-}
+#include "StdInc.h"
+#include "CKingdomInterface.h"
+
+#include "../CCallback.h"
+#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/CHeroHandler.h" // only for calculating required xp? worth it?
+#include "../lib/CTownHandler.h"
+#include "CAnimation.h" //CAnimImage
+#include "CAdvmapInterface.h" //CResDataBar
+#include "CCastleInterface.h" //various town-specific classes
+#include "../lib/CConfigHandler.h"
+#include "CGameInfo.h"
+#include "CPlayerInterface.h" //LOCPLINT
+#include "gui/CGuiHandler.h"
+#include "gui/CIntObjectClasses.h"
+#include "CMT.h"
+
+/*
+ * CKingdomInterface.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
+ *
+ */
+
+InfoBox::InfoBox(Point position, InfoPos Pos, InfoSize Size, IInfoBoxData *Data):
+	size(Size),
+	infoPos(Pos),
+	data(Data),
+	value(nullptr),
+	name(nullptr)
+{
+	assert(data);
+	addUsedEvents(LCLICK | RCLICK);
+	EFonts font = (size < SIZE_MEDIUM)? FONT_SMALL: FONT_MEDIUM;
+
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	pos+=position;
+
+	image = new CAnimImage(data->getImageName(size), data->getImageIndex());
+	pos = image->pos;
+
+	if (infoPos == POS_CORNER)
+		value = new CLabel(pos.w, pos.h, font, BOTTOMRIGHT, Colors::WHITE, data->getValueText());
+
+	if (infoPos == POS_INSIDE)
+		value = new CLabel(pos.w/2, pos.h-6, font, CENTER, Colors::WHITE, data->getValueText());
+
+	if (infoPos == POS_UP_DOWN || infoPos == POS_DOWN)
+		value = new CLabel(pos.w/2, pos.h+8, font, CENTER, Colors::WHITE, data->getValueText());
+
+	if (infoPos == POS_UP_DOWN)
+		name = new CLabel(pos.w/2, -12, font, CENTER, Colors::WHITE, data->getNameText());
+
+	if (infoPos == POS_RIGHT)
+	{
+		name = new CLabel(pos.w+6, 6, font, TOPLEFT, Colors::WHITE, data->getNameText());
+		value = new CLabel(pos.w+6, pos.h-16, font, TOPLEFT, Colors::WHITE, data->getValueText());
+	}
+	pos = image->pos;
+	if (name)
+		pos = pos | name->pos;
+	if (value)
+		pos = pos | value->pos;
+	
+	hover = new CHoverableArea;
+	hover->hoverText = data->getHoverText();
+	hover->pos = pos;
+}
+
+InfoBox::~InfoBox()
+{
+	delete data;
+}
+
+void InfoBox::clickRight(tribool down, bool previousState)
+{
+	if (down)
+	{
+		CComponent *comp = nullptr;
+		std::string text;
+		data->prepareMessage(text, &comp);
+		if (comp)
+			CRClickPopup::createAndPush(text, CInfoWindow::TCompsInfo(1, comp));
+		else if (!text.empty())
+			adventureInt->handleRightClick(text, down);
+	}
+}
+
+void InfoBox::clickLeft(tribool down, bool previousState)
+{
+	if((!down) && previousState)
+	{
+		CComponent *comp = nullptr;
+		std::string text;
+		data->prepareMessage(text, &comp);
+
+		std::vector<CComponent*> compVector;
+		if (comp)
+		{
+			compVector.push_back(comp);
+			LOCPLINT->showInfoDialog(text, compVector);
+		}
+	}
+}
+
+//TODO?
+/*
+void InfoBox::update()
+{
+
+}
+*/
+
+IInfoBoxData::IInfoBoxData(InfoType Type):
+	type(Type)
+{
+}
+
+InfoBoxAbstractHeroData::InfoBoxAbstractHeroData(InfoType Type):
+	IInfoBoxData(Type)
+{
+}
+
+std::string InfoBoxAbstractHeroData::getValueText()
+{
+	switch (type)
+	{
+	case HERO_MANA:
+	case HERO_EXPERIENCE:
+	case HERO_PRIMARY_SKILL:
+		return boost::lexical_cast<std::string>(getValue());
+	case HERO_SPECIAL:
+		return CGI->generaltexth->jktexts[5];
+	case HERO_SECONDARY_SKILL:
+		{
+			si64 value = getValue();
+			if (value)
+				return CGI->generaltexth->levels[value];
+		}
+	default:
+		assert(0);
+	}
+	return "";
+}
+
+std::string InfoBoxAbstractHeroData::getNameText()
+{
+	switch (type)
+	{
+	case HERO_PRIMARY_SKILL:
+		return CGI->generaltexth->primarySkillNames[getSubID()];
+	case HERO_MANA:
+		return CGI->generaltexth->allTexts[387];
+	case HERO_EXPERIENCE:
+		return CGI->generaltexth->jktexts[6];
+	case HERO_SPECIAL:
+		return CGI->heroh->heroes[getSubID()]->specName;
+	case HERO_SECONDARY_SKILL:
+		if (getValue())
+			return CGI->generaltexth->skillName[getSubID()];
+		else
+			return "";
+	default:
+		assert(0);
+	}
+	return "";
+}
+
+std::string InfoBoxAbstractHeroData::getImageName(InfoBox::InfoSize size)
+{
+	//TODO: sizes
+	switch(size)
+	{
+	case InfoBox::SIZE_SMALL:
+		{
+			switch(type)
+			{
+			case HERO_PRIMARY_SKILL:
+			case HERO_MANA:
+			case HERO_EXPERIENCE:
+				return "PSKIL32";
+			case HERO_SPECIAL:
+				return "UN32";
+			case HERO_SECONDARY_SKILL:
+				return "SECSK32";
+			default:
+				assert(0);
+			}
+		}
+	case InfoBox::SIZE_BIG:
+		{
+			switch(type)
+			{
+			case HERO_PRIMARY_SKILL:
+			case HERO_MANA:
+			case HERO_EXPERIENCE:
+				return "PSKIL42";
+			case HERO_SPECIAL:
+				return "UN44";
+			case HERO_SECONDARY_SKILL:
+				return "SECSKILL";
+			default:
+				assert(0);
+			}
+		}
+	default:
+		assert(0);
+	}
+	return "";
+}
+
+std::string InfoBoxAbstractHeroData::getHoverText()
+{
+	//TODO: any texts here?
+	return "";
+}
+
+size_t InfoBoxAbstractHeroData::getImageIndex()
+{
+	switch (type)
+	{
+	case HERO_SPECIAL:
+		return VLC->heroh->heroes[getSubID()]->imageIndex;
+	case HERO_PRIMARY_SKILL:
+		return getSubID();
+	case HERO_MANA:
+		return 5;
+	case HERO_EXPERIENCE:
+		return 4;
+	case HERO_SECONDARY_SKILL:
+		{
+			si64 value = getValue();
+			if (value)
+				return getSubID()*3 + value + 2;
+			else
+				return 0;//FIXME: Should be transparent instead of empty
+		}
+	default:
+		assert(0);
+		return 0;
+	}
+}
+
+bool InfoBoxAbstractHeroData::prepareMessage(std::string &text, CComponent **comp)
+{
+	switch (type)
+	{
+	case HERO_SPECIAL:
+		text = CGI->heroh->heroes[getSubID()]->specDescr;
+		*comp = nullptr;
+		return true;
+	case HERO_PRIMARY_SKILL:
+		text = CGI->generaltexth->arraytxt[2+getSubID()];
+		*comp =new CComponent(CComponent::primskill, getSubID(), getValue());
+		return true;
+	case HERO_MANA:
+		text = CGI->generaltexth->allTexts[149];
+		*comp = nullptr;
+		return true;
+	case HERO_EXPERIENCE:
+		text = CGI->generaltexth->allTexts[241];
+		*comp = nullptr;
+		return true;
+	case HERO_SECONDARY_SKILL:
+		{
+			si64 value = getValue();
+			int  subID = getSubID();
+			if (!value)
+				return false;
+
+			text = CGI->generaltexth->skillInfoTexts[subID][value-1];
+			*comp = new CComponent(CComponent::secskill, subID, value);
+			return true;
+		}
+	default:
+		assert(0);
+		return false;
+	}
+}
+
+InfoBoxHeroData::InfoBoxHeroData(InfoType Type, const CGHeroInstance * Hero, int Index):
+	InfoBoxAbstractHeroData(Type),
+	hero(Hero),
+	index(Index)
+{
+}
+
+int InfoBoxHeroData::getSubID()
+{
+	switch(type)
+	{
+		case HERO_PRIMARY_SKILL:
+			return index;
+		case HERO_SECONDARY_SKILL:
+			if (hero->secSkills.size() > index)
+				return hero->secSkills[index].first;
+		case HERO_MANA:
+		case HERO_EXPERIENCE:
+		case HERO_SPECIAL:
+			return 0;
+		default:
+			assert(0);
+			return 0;
+	}
+}
+
+si64 InfoBoxHeroData::getValue()
+{
+	switch(type)
+	{
+	case HERO_PRIMARY_SKILL:
+		return hero->getPrimSkillLevel(static_cast<PrimarySkill::PrimarySkill>(index));
+	case HERO_MANA:
+		return hero->mana;
+	case HERO_EXPERIENCE:
+		return hero->exp;
+	case HERO_SECONDARY_SKILL:
+		if (hero->secSkills.size() > index)
+			return hero->secSkills[index].second;
+	case HERO_SPECIAL:
+			return 0;
+	default:
+		assert(0);
+		return 0;
+	}
+}
+
+std::string InfoBoxHeroData::getHoverText()
+{
+	switch (type)
+	{
+	case HERO_PRIMARY_SKILL:
+		return boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % CGI->generaltexth->primarySkillNames[index]);
+	case HERO_MANA:
+		return CGI->generaltexth->heroscrn[22];
+	case HERO_EXPERIENCE:
+		return CGI->generaltexth->heroscrn[9];
+	case HERO_SPECIAL:
+		return CGI->generaltexth->heroscrn[27];
+	case HERO_SECONDARY_SKILL:
+		{
+		if (hero->secSkills.size() > index)
+		{
+			std::string level = CGI->generaltexth->levels[hero->secSkills[index].second-1];
+			std::string skill = CGI->generaltexth->skillName[hero->secSkills[index].first];
+			return boost::str(boost::format(CGI->generaltexth->heroscrn[21]) % level % skill);
+		}
+		else
+			return "";
+		}
+	default:
+		return InfoBoxAbstractHeroData::getHoverText();
+	}
+}
+
+std::string InfoBoxHeroData::getValueText()
+{
+	if (hero)
+	{
+		switch (type)
+		{
+		case HERO_MANA:
+			return boost::lexical_cast<std::string>(hero->mana) + '/' +
+				boost::lexical_cast<std::string>(hero->manaLimit());
+		case HERO_EXPERIENCE:
+			return boost::lexical_cast<std::string>(hero->exp);
+		}
+	}
+	return InfoBoxAbstractHeroData::getValueText();
+}
+
+bool InfoBoxHeroData::prepareMessage(std::string &text, CComponent**comp)
+{
+	switch(type)
+	{
+	case HERO_MANA:
+		text = CGI->generaltexth->allTexts[205];
+		boost::replace_first(text, "%s", boost::lexical_cast<std::string>(hero->name));
+		boost::replace_first(text, "%d", boost::lexical_cast<std::string>(hero->mana));
+		boost::replace_first(text, "%d", boost::lexical_cast<std::string>(hero->manaLimit()));
+		*comp = nullptr;
+		return true;
+
+	case HERO_EXPERIENCE:
+		text = CGI->generaltexth->allTexts[2];
+		boost::replace_first(text, "%d", boost::lexical_cast<std::string>(hero->level));
+		boost::replace_first(text, "%d", boost::lexical_cast<std::string>(CGI->heroh->reqExp(hero->level+1)));
+		boost::replace_first(text, "%d", boost::lexical_cast<std::string>(hero->exp));
+		*comp = nullptr;
+		return true;
+
+	default:
+		return InfoBoxAbstractHeroData::prepareMessage(text, comp);
+	}
+}
+
+InfoBoxCustomHeroData::InfoBoxCustomHeroData(InfoType Type, int SubID, si64 Value):
+	InfoBoxAbstractHeroData(Type),
+	subID(SubID),
+	value(Value)
+{
+}
+
+int InfoBoxCustomHeroData::getSubID()
+{
+	return subID;
+}
+
+si64 InfoBoxCustomHeroData::getValue()
+{
+	return value;
+}
+
+InfoBoxCustom::InfoBoxCustom(std::string ValueText, std::string NameText, std::string ImageName, size_t ImageIndex, std::string HoverText):
+	IInfoBoxData(CUSTOM),
+	valueText(ValueText),
+	nameText(NameText),
+	imageName(ImageName),
+	hoverText(HoverText),
+	imageIndex(ImageIndex)
+{
+}
+
+std::string InfoBoxCustom::getHoverText()
+{
+	return hoverText;
+}
+
+size_t InfoBoxCustom::getImageIndex()
+{
+	return imageIndex;
+}
+
+std::string InfoBoxCustom::getImageName(InfoBox::InfoSize size)
+{
+	return imageName;
+}
+
+std::string InfoBoxCustom::getNameText()
+{
+	return nameText;
+}
+
+std::string InfoBoxCustom::getValueText()
+{
+	return valueText;
+}
+
+bool InfoBoxCustom::prepareMessage(std::string &text, CComponent **comp)
+{
+	return false;
+}
+
+CKingdomInterface::CKingdomInterface():
+    CWindowObject(PLAYER_COLORED | BORDERED, conf.go()->ac.overviewBg)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	ui32 footerPos = conf.go()->ac.overviewSize * 116;
+
+	tabArea = new CTabbedInt(boost::bind(&CKingdomInterface::createMainTab, this, _1), CTabbedInt::DestroyFunc(), Point(4,4));
+
+	std::vector<const CGObjectInstance * > ownedObjects = LOCPLINT->cb->getMyObjects();
+	generateObjectsList(ownedObjects);
+	generateMinesList(ownedObjects);
+	generateButtons();
+
+	statusbar = new CGStatusBar(new CPicture("KSTATBAR", 10,pos.h - 45));
+	resdatabar= new CResDataBar("KRESBAR", 3, 111+footerPos, 32, 2, 76, 76);
+}
+
+void CKingdomInterface::generateObjectsList(const std::vector<const CGObjectInstance * > &ownedObjects)
+{
+	ui32 footerPos = conf.go()->ac.overviewSize * 116;
+	size_t dwellSize = (footerPos - 64)/57;
+
+	//Map used to determine image number for several objects
+	std::map<std::pair<int,int>,int> idToImage;
+	idToImage[std::make_pair( 20, 1)] = 81;//Golem factory
+	idToImage[std::make_pair( 42, 0)] = 82;//Lighthouse
+	idToImage[std::make_pair( 33, 0)] = 83;//Garrison
+	idToImage[std::make_pair(219, 0)] = 83;//Garrison
+	idToImage[std::make_pair( 33, 1)] = 84;//Anti-magic Garrison
+	idToImage[std::make_pair(219, 1)] = 84;//Anti-magic Garrison
+	idToImage[std::make_pair( 53, 7)] = 85;//Abandoned mine
+	idToImage[std::make_pair( 20, 0)] = 86;//Conflux
+	idToImage[std::make_pair( 87, 0)] = 87;//Harbor
+
+	std::map<int, OwnedObjectInfo> visibleObjects;
+	for(const CGObjectInstance * object : ownedObjects)
+	{
+		//Dwellings
+		if ( object->ID == Obj::CREATURE_GENERATOR1 )
+		{
+			OwnedObjectInfo &info = visibleObjects[object->subID];
+			if (info.count++ == 0)
+			{
+				info.hoverText = CGI->creh->creatures[CGI->objh->cregens.find(object->subID)->second]->namePl;
+				info.imageID = object->subID;
+			}
+		}
+		//Special objects from idToImage map that should be displayed in objects list
+		auto iter = idToImage.find(std::make_pair(object->ID, object->subID));
+		if (iter != idToImage.end())
+		{
+			OwnedObjectInfo &info = visibleObjects[iter->second];
+			if (info.count++ == 0)
+			{
+				info.hoverText = object->hoverName;
+				info.imageID = iter->second;
+			}
+		}
+	}
+	objects.reserve(visibleObjects.size());
+
+	for(auto & element : visibleObjects)
+	{
+		objects.push_back(element.second);
+	}
+	dwellingsList = new CListBox(boost::bind(&CKingdomInterface::createOwnedObject, this, _1), CListBox::DestroyFunc(),
+	                             Point(740,44), Point(0,57), dwellSize, visibleObjects.size());
+}
+
+CIntObject* CKingdomInterface::createOwnedObject(size_t index)
+{
+	if (index < objects.size())
+	{
+		OwnedObjectInfo &obj = objects[index];
+		std::string value = boost::lexical_cast<std::string>(obj.count);
+		return new InfoBox(Point(), InfoBox::POS_CORNER, InfoBox::SIZE_SMALL,
+			   new InfoBoxCustom(value,"", "FLAGPORT", obj.imageID, obj.hoverText));
+	}
+	return nullptr;
+}
+
+CIntObject * CKingdomInterface::createMainTab(size_t index)
+{
+	size_t size = conf.go()->ac.overviewSize;
+	switch (index)
+	{
+	case 0: return new CKingdHeroList(size);
+	case 1: return new CKingdTownList(size);
+	default:return nullptr;
+	}
+}
+
+void CKingdomInterface::generateMinesList(const std::vector<const CGObjectInstance * > &ownedObjects)
+{
+	ui32 footerPos = conf.go()->ac.overviewSize * 116;
+	std::vector<int> minesCount(GameConstants::RESOURCE_QUANTITY, 0);
+	int totalIncome=0;
+
+	for(const CGObjectInstance * object : ownedObjects)
+	{
+		//Mines
+		if ( object->ID == Obj::MINE )
+		{
+			const CGMine *mine = dynamic_cast<const CGMine*>(object);
+			assert(mine);
+			minesCount[mine->producedResource]++;
+
+			if (mine->producedResource == Res::GOLD)
+				totalIncome += mine->producedQuantity;
+		}
+	}
+
+	//Heroes can produce gold as well - skill, specialty or arts
+	std::vector<const CGHeroInstance*> heroes = LOCPLINT->cb->getHeroesInfo(true);
+	for(auto & heroe : heroes)
+	{
+		totalIncome += heroe->valOfBonuses(Selector::typeSubtype(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::ESTATES));
+		totalIncome += heroe->valOfBonuses(Selector::typeSubtype(Bonus::GENERATE_RESOURCE, Res::GOLD));
+	}
+
+	//Add town income of all towns
+	std::vector<const CGTownInstance*> towns = LOCPLINT->cb->getTownsInfo(true);
+	for(auto & town : towns)
+	{
+		totalIncome += town->dailyIncome()[Res::GOLD];
+	}
+	for (int i=0; i<7; i++)
+	{
+		std::string value = boost::lexical_cast<std::string>(minesCount[i]);
+		minesBox[i] = new InfoBox(Point(20+i*80, 31+footerPos), InfoBox::POS_INSIDE, InfoBox::SIZE_SMALL,
+		              new InfoBoxCustom(value, "", "OVMINES", i, CGI->generaltexth->mines[i].first));
+
+		minesBox[i]->removeUsedEvents(LCLICK|RCLICK); //fixes #890 - mines boxes ignore clicks
+	}
+	incomeArea = new CHoverableArea;
+	incomeArea->pos = Rect(pos.x+580, pos.y+31+footerPos, 136, 68);
+	incomeArea->hoverText = CGI->generaltexth->allTexts[255];
+	incomeAmount = new CLabel(628, footerPos + 70, FONT_SMALL, TOPLEFT, Colors::WHITE, boost::lexical_cast<std::string>(totalIncome));
+}
+
+void CKingdomInterface::generateButtons()
+{
+	ui32 footerPos = conf.go()->ac.overviewSize * 116;
+
+	//Main control buttons
+	btnHeroes = new CAdventureMapButton (CGI->generaltexth->overview[11], CGI->generaltexth->overview[6],
+	                                    boost::bind(&CKingdomInterface::activateTab, this, 0),748,28+footerPos,"OVBUTN1.DEF", SDLK_h);
+	btnHeroes->block(true);
+
+	btnTowns = new CAdventureMapButton (CGI->generaltexth->overview[12], CGI->generaltexth->overview[7],
+	                                   boost::bind(&CKingdomInterface::activateTab, this, 1),748,64+footerPos,"OVBUTN6.DEF", SDLK_t);
+
+	btnExit = new CAdventureMapButton (CGI->generaltexth->allTexts[600],"",
+	                                  boost::bind(&CKingdomInterface::close, this),748,99+footerPos,"OVBUTN1.DEF", SDLK_RETURN);
+	btnExit->assignedKeys.insert(SDLK_ESCAPE);
+	btnExit->setOffset(3);
+
+	//Object list control buttons
+	dwellTop = new CAdventureMapButton ("", "", boost::bind(&CListBox::moveToPos, dwellingsList, 0),
+	                                   733, 4, "OVBUTN4.DEF");
+
+	dwellBottom = new CAdventureMapButton ("", "", boost::bind(&CListBox::moveToPos, dwellingsList, -1),
+	                                      733, footerPos+2, "OVBUTN4.DEF");
+	dwellBottom->setOffset(2);
+
+	dwellUp = new CAdventureMapButton ("", "", boost::bind(&CListBox::moveToPrev, dwellingsList),
+	                                  733, 24, "OVBUTN4.DEF");
+	dwellUp->setOffset(4);
+
+	dwellDown = new CAdventureMapButton ("", "", boost::bind(&CListBox::moveToNext, dwellingsList),
+	                                    733, footerPos-18, "OVBUTN4.DEF");
+	dwellDown->setOffset(6);
+}
+
+void CKingdomInterface::activateTab(size_t which)
+{
+	btnHeroes->block(which == 0);
+	btnTowns->block(which == 1);
+	tabArea->setActive(which);
+}
+
+void CKingdomInterface::townChanged(const CGTownInstance *town)
+{
+	if (CKingdTownList * townList = dynamic_cast<CKingdTownList*>(tabArea->getItem()))
+		townList->townChanged(town);
+}
+
+void CKingdomInterface::updateGarrisons()
+{
+	if (CGarrisonHolder * garrison = dynamic_cast<CGarrisonHolder*>(tabArea->getItem()))
+		garrison->updateGarrisons();
+}
+
+void CKingdomInterface::artifactAssembled(const ArtifactLocation& artLoc)
+{
+	if (CArtifactHolder * arts = dynamic_cast<CArtifactHolder*>(tabArea->getItem()))
+		arts->artifactAssembled(artLoc);
+}
+
+void CKingdomInterface::artifactDisassembled(const ArtifactLocation& artLoc)
+{
+	if (CArtifactHolder * arts = dynamic_cast<CArtifactHolder*>(tabArea->getItem()))
+		arts->artifactDisassembled(artLoc);
+}
+
+void CKingdomInterface::artifactMoved(const ArtifactLocation& artLoc, const ArtifactLocation& destLoc)
+{
+	if (CArtifactHolder * arts = dynamic_cast<CArtifactHolder*>(tabArea->getItem()))
+		arts->artifactMoved(artLoc, destLoc);
+}
+
+void CKingdomInterface::artifactRemoved(const ArtifactLocation& artLoc)
+{
+	if (CArtifactHolder * arts = dynamic_cast<CArtifactHolder*>(tabArea->getItem()))
+		arts->artifactRemoved(artLoc);
+}
+
+CKingdHeroList::CKingdHeroList(size_t maxSize)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	title = new CPicture("OVTITLE",16,0);
+	title->colorize(LOCPLINT->playerID);
+	heroLabel =   new CLabel(150, 10, FONT_MEDIUM, CENTER, Colors::WHITE, CGI->generaltexth->overview[0]);
+	skillsLabel = new CLabel(500, 10, FONT_MEDIUM, CENTER, Colors::WHITE, CGI->generaltexth->overview[1]);
+
+	ui32 townCount = LOCPLINT->cb->howManyHeroes(false);
+	ui32 size = conf.go()->ac.overviewSize*116 + 19;
+	heroes = new CListBox(boost::bind(&CKingdHeroList::createHeroItem, this, _1), boost::bind(&CKingdHeroList::destroyHeroItem, this, _1),
+	                      Point(19,21), Point(0,116), maxSize, townCount, 0, 1, Rect(-19, -21, size, size) );
+}
+
+void CKingdHeroList::updateGarrisons()
+{
+	std::list<CIntObject*> list = heroes->getItems();
+	for(CIntObject* object : list)
+	{
+		if (CGarrisonHolder * garrison = dynamic_cast<CGarrisonHolder*>(object) )
+			garrison->updateGarrisons();
+	}
+}
+
+CIntObject* CKingdHeroList::createHeroItem(size_t index)
+{
+	ui32 picCount = conf.go()->ac.overviewPics;
+	size_t heroesCount = LOCPLINT->cb->howManyHeroes(false);
+
+	if (index < heroesCount)
+	{
+		auto   hero = new CHeroItem(LOCPLINT->cb->getHeroBySerial(index, false), &artsCommonPart);
+		artsCommonPart.participants.insert(hero->heroArts);
+		artSets.push_back(hero->heroArts);
+		return hero;
+	}
+	else
+	{
+		return new CAnimImage("OVSLOT", (index-2) % picCount );
+	}
+}
+
+void CKingdHeroList::destroyHeroItem(CIntObject *object)
+{
+	if (CHeroItem * hero = dynamic_cast<CHeroItem*>(object))
+	{
+		artSets.erase(std::find(artSets.begin(), artSets.end(), hero->heroArts));
+		artsCommonPart.participants.erase(hero->heroArts);
+	}
+	delete object;
+}
+
+CKingdTownList::CKingdTownList(size_t maxSize)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	title = new CPicture("OVTITLE",16,0);
+	title->colorize(LOCPLINT->playerID);
+	townLabel   = new CLabel(146,10,FONT_MEDIUM, CENTER, Colors::WHITE, CGI->generaltexth->overview[3]);
+	garrHeroLabel  = new CLabel(375,10,FONT_MEDIUM, CENTER, Colors::WHITE, CGI->generaltexth->overview[4]);
+	visitHeroLabel = new CLabel(608,10,FONT_MEDIUM, CENTER, Colors::WHITE, CGI->generaltexth->overview[5]);
+
+	ui32 townCount = LOCPLINT->cb->howManyTowns();
+	ui32 size = conf.go()->ac.overviewSize*116 + 19;
+	towns = new CListBox(boost::bind(&CKingdTownList::createTownItem, this, _1), CListBox::DestroyFunc(),
+	                     Point(19,21), Point(0,116), maxSize, townCount, 0, 1, Rect(-19, -21, size, size) );
+}
+
+void CKingdTownList::townChanged(const CGTownInstance *town)
+{
+	std::list<CIntObject*> list = towns->getItems();
+	for(CIntObject* object : list)
+	{
+		CTownItem * townItem = dynamic_cast<CTownItem*>(object);
+		if ( townItem && townItem->town == town)
+			townItem->update();
+	}
+}
+
+void CKingdTownList::updateGarrisons()
+{
+	std::list<CIntObject*> list = towns->getItems();
+	for(CIntObject* object : list)
+	{
+		if (CGarrisonHolder * garrison = dynamic_cast<CGarrisonHolder*>(object) )
+			garrison->updateGarrisons();
+	}
+}
+
+CIntObject* CKingdTownList::createTownItem(size_t index)
+{
+	ui32 picCount = conf.go()->ac.overviewPics;
+	size_t townsCount = LOCPLINT->cb->howManyTowns();
+
+	if (index < townsCount)
+		return new CTownItem(LOCPLINT->cb->getTownBySerial(index));
+	else
+		return new CAnimImage("OVSLOT", (index-2) % picCount );
+}
+
+CTownItem::CTownItem(const CGTownInstance* Town):
+	town(Town)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	background =  new CAnimImage("OVSLOT", 6);
+	name = new CLabel(74, 8, FONT_SMALL, TOPLEFT, Colors::WHITE, town->name);
+
+	income = new CLabel( 190, 60, FONT_SMALL, CENTER, Colors::WHITE, boost::lexical_cast<std::string>(town->dailyIncome()));
+	hall = new CTownInfo( 69, 31, town, true);
+	fort = new CTownInfo(111, 31, town, false);
+
+	garr = new CGarrisonInt(313, 3, 4, Point(232,0),  nullptr, Point(313,2), town->getUpperArmy(), town->visitingHero, true, true, true);
+	heroes = new HeroSlots(town, Point(244,6), Point(475,6), garr, false);
+
+	size_t iconIndex = town->town->clientInfo.icons[town->hasFort()][town->builded >= CGI->modh->settings.MAX_BUILDING_PER_TURN];
+
+	picture = new CAnimImage("ITPT", iconIndex, 0, 5, 6);
+	townArea = new LRClickableAreaOpenTown;
+	townArea->pos = Rect(pos.x+5, pos.y+6, 58, 64);
+	townArea->town = town;
+
+	for (size_t i=0; i<town->creatures.size(); i++)
+	{
+		growth.push_back(new CCreaInfo(Point(401+37*i, 78), town, i, true, true));
+		available.push_back(new CCreaInfo(Point(48+37*i, 78), town, i, true, false));
+	}
+}
+
+void CTownItem::updateGarrisons()
+{
+	garr->selectSlot(nullptr);
+	garr->setArmy(town->getUpperArmy(), 0);
+	garr->setArmy(town->visitingHero, 1);
+	garr->recreateSlots();
+}
+
+void CTownItem::update()
+{
+	std::string incomeVal = boost::lexical_cast<std::string>(town->dailyIncome());
+	if (incomeVal != income->text)
+		income->setText(incomeVal);
+
+	heroes->update();
+
+	for (size_t i=0; i<town->creatures.size(); i++)
+	{
+		growth[i]->update();
+		available[i]->update();
+	}
+}
+
+class ArtSlotsTab : public CIntObject
+{
+public:
+	CAnimImage * background;
+	std::vector<CArtPlace*> arts;
+
+	ArtSlotsTab()
+	{
+		OBJ_CONSTRUCTION_CAPTURING_ALL;
+		background = new CAnimImage("OVSLOT", 4);
+		pos = background->pos;
+		for (size_t i=0; i<9; i++)
+			arts.push_back(new CArtPlace(Point(270+i*48, 65)));
+	}
+};
+
+class BackpackTab : public CIntObject
+{
+public:
+	CAnimImage * background;
+	std::vector<CArtPlace*> arts;
+	CAdventureMapButton *btnLeft;
+	CAdventureMapButton *btnRight;
+
+	BackpackTab()
+	{
+		OBJ_CONSTRUCTION_CAPTURING_ALL;
+		background = new CAnimImage("OVSLOT", 5);
+		pos = background->pos;
+		btnLeft = new CAdventureMapButton(std::string(), std::string(), CFunctionList<void()>(), 269, 66, "HSBTNS3");
+		btnRight = new CAdventureMapButton(std::string(), std::string(), CFunctionList<void()>(), 675, 66, "HSBTNS5");
+		for (size_t i=0; i<8; i++)
+			arts.push_back(new CArtPlace(Point(295+i*48, 65)));
+	}
+};
+
+CHeroItem::CHeroItem(const CGHeroInstance* Hero, CArtifactsOfHero::SCommonPart * artsCommonPart):
+	hero(Hero)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	artTabs.resize(3);
+	auto   arts1 = new ArtSlotsTab;
+	auto   arts2 = new ArtSlotsTab;
+	auto   backpack = new BackpackTab;
+	artTabs[0] = arts1;
+	artTabs[1] = arts2;
+	artTabs[2] = backpack;
+	arts1->recActions = DISPOSE | SHARE_POS;
+	arts2->recActions = DISPOSE | SHARE_POS;
+	backpack->recActions = DISPOSE | SHARE_POS;
+
+	name = new CLabel(75, 7, FONT_SMALL, TOPLEFT, Colors::WHITE, hero->name);
+
+	std::vector<CArtPlace*> arts;
+	arts.insert(arts.end(), arts1->arts.begin(), arts1->arts.end());
+	arts.insert(arts.end(), arts2->arts.begin(), arts2->arts.end());
+
+	heroArts = new CArtifactsOfHero(arts, backpack->arts, backpack->btnLeft, backpack->btnRight, false);
+	heroArts->commonInfo = artsCommonPart;
+	heroArts->setHero(hero);
+
+	artsTabs = new CTabbedInt(boost::bind(&CHeroItem::onTabSelected, this, _1), boost::bind(&CHeroItem::onTabDeselected, this, _1));
+
+	artButtons = new CHighlightableButtonsGroup(0);
+	for (size_t it = 0; it<3; it++)
+	{
+		int stringID[3] = {259, 261, 262};
+
+		std::map<int,std::string> tooltip;
+		tooltip[0] = CGI->generaltexth->overview[13+it];
+		std::string overlay = CGI->generaltexth->overview[8+it];
+
+		artButtons->addButton(tooltip, overlay, "OVBUTN3",364+it*112, 46, it);
+		artButtons->buttons[it]->addTextOverlay(CGI->generaltexth->allTexts[stringID[it]], FONT_SMALL, Colors::YELLOW);
+	}
+	artButtons->onChange += boost::bind(&CTabbedInt::setActive, artsTabs, _1);
+	artButtons->onChange += boost::bind(&CHeroItem::onArtChange, this, _1);
+	artButtons->select(0,0);
+
+	garr = new CGarrisonInt(6, 78, 4, Point(), nullptr, Point(), hero, nullptr, true, true);
+
+	portrait = new CAnimImage("PortraitsLarge", hero->portrait, 0, 5, 6);
+	heroArea = new CHeroArea(5, 6, hero);
+
+	name = new CLabel(73, 7, FONT_SMALL, TOPLEFT, Colors::WHITE, hero->name);
+	artsText = new CLabel(320, 55, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->overview[2]);
+
+	for (size_t i=0; i<GameConstants::PRIMARY_SKILLS; i++)
+		heroInfo.push_back(new InfoBox(Point(78+i*36, 26), InfoBox::POS_DOWN, InfoBox::SIZE_SMALL, 
+		                   new InfoBoxHeroData(IInfoBoxData::HERO_PRIMARY_SKILL, hero, i)));
+
+	for (size_t i=0; i<GameConstants::SKILL_PER_HERO; i++)
+		heroInfo.push_back(new InfoBox(Point(410+i*36, 5), InfoBox::POS_NONE, InfoBox::SIZE_SMALL,
+		                   new InfoBoxHeroData(IInfoBoxData::HERO_SECONDARY_SKILL, hero, i)));
+
+	heroInfo.push_back(new InfoBox(Point(375, 5), InfoBox::POS_NONE, InfoBox::SIZE_SMALL,
+	                   new InfoBoxHeroData(IInfoBoxData::HERO_SPECIAL, hero)));
+
+	heroInfo.push_back(new InfoBox(Point(330, 5), InfoBox::POS_INSIDE, InfoBox::SIZE_SMALL,
+	                   new InfoBoxHeroData(IInfoBoxData::HERO_EXPERIENCE, hero)));
+
+	heroInfo.push_back(new InfoBox(Point(280, 5), InfoBox::POS_INSIDE, InfoBox::SIZE_SMALL,
+	                   new InfoBoxHeroData(IInfoBoxData::HERO_MANA, hero)));
+
+	morale = new MoraleLuckBox(true, Rect(225, 53, 30, 22), true);
+	luck  = new MoraleLuckBox(false, Rect(225, 28, 30, 22), true);
+
+	morale->set(hero);
+	luck->set(hero);
+}
+
+CIntObject * CHeroItem::onTabSelected(size_t index)
+{
+	return artTabs[index];
+}
+
+void CHeroItem::onTabDeselected(CIntObject *object)
+{
+	addChild(object, false);
+	object->deactivate();
+	object->recActions = DISPOSE | SHARE_POS;
+}
+
+void CHeroItem::onArtChange(int tabIndex)
+{
+	//redraw item after background change
+	if (active)
+		redraw();
+}

+ 11 - 0
client/CMT.cpp

@@ -284,6 +284,10 @@ int main(int argc, char** argv)
 	logGlobal->infoStream() << "Creating console and configuring logger: " << pomtime.getDiff();
 	logGlobal->infoStream() << "The log file will be saved to " << logPath;
 
+#ifdef __ANDROID__
+	// boost will crash without this
+	setenv("LANG", "C", 1);
+#endif
     // Init filesystem and settings
 	preinitDLL(::console);
     settings.init();
@@ -361,8 +365,13 @@ int main(int argc, char** argv)
 
 
 
+#ifndef __ANDROID__
 	//we can properly play intro only in the main thread, so we have to move loading to the separate thread
 	boost::thread loading(init);
+#else
+	// on Android threaded init is broken
+	init();
+#endif
 
 	if(!gNoGUI )
 	{
@@ -372,7 +381,9 @@ int main(int argc, char** argv)
 	}
 
 	CSDL_Ext::update(screen);
+#ifndef __ANDROID__
 	loading.join();
+#endif
     logGlobal->infoStream()<<"Initialization of VCMI (together): "<<total.getDiff();
 
 	if(!vm.count("battle"))

+ 10 - 11
client/CMessage.cpp

@@ -1,3 +1,13 @@
+/*
+ * CMessage.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 "CMessage.h"
 
@@ -13,17 +23,6 @@
 #include "CBitmapHandler.h"
 #include "gui/CIntObjectClasses.h"
 
-/*
- * CMessage.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
- *
- */
-
-const int COMPONENT_TO_SUBTITLE = 17;
 const int BETWEEN_COMPS_ROWS = 10;
 const int BEFORE_COMPONENTS = 30;
 const int BETWEEN_COMPS = 30;

+ 3 - 5
client/CMusicHandler.cpp

@@ -9,6 +9,7 @@
 #include "../lib/GameConstants.h"
 #include "../lib/filesystem/Filesystem.h"
 #include "../lib/StringConstants.h"
+#include "../lib/CRandomGenerator.h"
 
 /*
  * CMusicHandler.cpp, part of VCMI engine
@@ -230,7 +231,7 @@ int CSoundHandler::playSound(std::string sound, int repeats)
 // Helper. Randomly select a sound from an array and play it
 int CSoundHandler::playSoundFromSet(std::vector<soundBase::soundID> &sound_vec)
 {
-	return playSound(sound_vec[rand() % sound_vec.size()]);
+	return playSound(*RandomGeneratorUtil::nextItem(sound_vec, CRandomGenerator::getDefault()));
 }
 
 void CSoundHandler::stopSound( int handler )
@@ -504,10 +505,7 @@ bool MusicEntry::play()
 	if (!setName.empty())
 	{
 		auto set = owner->musicsSet[setName];
-		size_t entryID = rand() % set.size();
-		auto iterator = set.begin();
-		std::advance(iterator, entryID);
-		load(iterator->second);
+		load(RandomGeneratorUtil::nextItem(set, CRandomGenerator::getDefault())->second);
 	}
 
     logGlobal->traceStream()<<"Playing music file "<<currentName;

+ 3 - 2
client/CPreGame.cpp

@@ -43,6 +43,7 @@
 #include "gui/CIntObjectClasses.h"
 #include "../lib/mapping/CMapService.h"
 #include "../lib/mapping/CMap.h"
+#include "../lib/CRandomGenerator.h"
 
 /*
  * CPreGame.cpp, part of VCMI engine
@@ -607,7 +608,7 @@ CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMulti
 		bordered = true;
 		//load random background
 		const JsonVector & bgNames = CGPreGameConfig::get().getConfig()["game-select"].Vector();
-		bg = new CPicture(bgNames[rand() % bgNames.size()].String(), 0, 0);
+		bg = new CPicture(RandomGeneratorUtil::nextItem(bgNames, CRandomGenerator::getDefault())->String(), 0, 0);
 		pos = bg->center();
 	}
 
@@ -4136,7 +4137,7 @@ std::string CLoadingScreen::getBackground()
 	}
 	else
 	{
-		return conf[ rand() % conf.size() ].String();
+		return RandomGeneratorUtil::nextItem(conf, CRandomGenerator::getDefault())->String();
 	}
 }
 

+ 12 - 9
client/CSpellWindow.cpp

@@ -744,23 +744,26 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 
 					if (h->getSpellSchoolLevel(CGI->spellh->objects[spell]) < 2) //not advanced or expert - teleport to nearest available city
 					{
-						int nearest = -1; //nearest town's ID
-						double dist = -1;
-						for (int g=0; g<Towns.size(); ++g)
+						auto nearest = Towns.cbegin(); //nearest town's iterator
+						si32 dist = LOCPLINT->cb->getTown((*nearest)->id)->pos.dist2dSQ(h->pos);
+
+						for (auto i = nearest + 1; i != Towns.cend(); ++i)
 						{
-							const CGTownInstance * dest = LOCPLINT->cb->getTown(Towns[g]->id);
-							double curDist = dest->pos.dist2d(h->pos);
-							if (nearest == -1 || curDist < dist)
+							const CGTownInstance * dest = LOCPLINT->cb->getTown((*i)->id);
+							si32 curDist = dest->pos.dist2dSQ(h->pos);
+
+							if (curDist < dist)
 							{
-								nearest = g;
+								nearest = i;
 								dist = curDist;
 							}
 						}
-						if ( Towns[nearest]->visitingHero )
+
+						if ((*nearest)->visitingHero)
 							LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[123]);
 						else
 						{
-							const CGTownInstance * town = LOCPLINT->cb->getTown(Towns[nearest]->id);
+							const CGTownInstance * town = LOCPLINT->cb->getTown((*nearest)->id);
 							LOCPLINT->cb->castSpell(h, spell, town->visitablePos());// - town->getVisitableOffset());
 						}
 					}

+ 1 - 1
client/CVideoHandler.cpp

@@ -139,7 +139,7 @@ bool CBIKHandler::open(std::string name)
         logGlobal->errorStream() << "BIK handler: failed to open " << name;
 		goto checkErrorAndClean;
 	}
-	//GCC wants scope of waveout to dont cross labels/swith/goto
+	//GCC wants scope of waveout to don`t cross labels/swith/goto
     {
 		void *waveout = GetProcAddress(dll,"_BinkOpenWaveOut@4");
 		if(waveout)

+ 12 - 0
client/Client.cpp

@@ -19,7 +19,9 @@
 #include "../lib/CBuildingHandler.h"
 #include "../lib/CSpellHandler.h"
 #include "../lib/Connection.h"
+#ifndef __ANDROID__
 #include "../lib/Interprocess.h"
+#endif
 #include "../lib/NetPacks.h"
 #include "../lib/VCMI_Lib.h"
 #include "../lib/VCMIDirs.h"
@@ -37,7 +39,9 @@
 #include "CMT.h"
 
 extern std::string NAME;
+#ifndef __ANDROID__
 namespace intpr = boost::interprocess;
+#endif
 
 /*
  * Client.cpp, part of VCMI engine
@@ -809,19 +813,25 @@ void CServerHandler::waitForServer()
 		startServer();
 
 	th.update();
+#ifndef __ANDROID__
 	intpr::scoped_lock<intpr::interprocess_mutex> slock(shared->sr->mutex);
 	while(!shared->sr->ready)
 	{
 		shared->sr->cond.wait(slock);
 	}
+#endif
 	if(verbose)
         logNetwork->infoStream() << "Waiting for server: " << th.getDiff();
 }
 
 CConnection * CServerHandler::connectToServer()
 {
+#ifndef __ANDROID__
 	if(!shared->sr->ready)
 		waitForServer();
+#else
+	waitForServer();
+#endif
 
 	th.update(); //put breakpoint here to attach to server before it does something stupid
 	CConnection *ret = justConnectToServer(settings["server"]["server"].String(), port);
@@ -839,11 +849,13 @@ CServerHandler::CServerHandler(bool runServer /*= false*/)
 	port = boost::lexical_cast<std::string>(settings["server"]["port"].Float());
 	verbose = true;
 
+#ifndef __ANDROID__
 	boost::interprocess::shared_memory_object::remove("vcmi_memory"); //if the application has previously crashed, the memory may not have been removed. to avoid problems - try to destroy it
 	try
 	{
 		shared = new SharedMem();
     } HANDLE_EXCEPTIONC(logNetwork->errorStream() << "Cannot open interprocess memory: ";)
+#endif
 }
 
 CServerHandler::~CServerHandler()

+ 14 - 5
client/GUIClasses.cpp

@@ -3726,10 +3726,16 @@ CTavernWindow::CTavernWindow(const CGObjectInstance *TavernObj):
 		recruit->hoverTexts[0] = CGI->generaltexth->tavernInfo[0]; //Cannot afford a Hero
 		recruit->block(true);
 	}
-	else if(LOCPLINT->cb->howManyHeroes(false) >= 8)
+	else if(LOCPLINT->castleInt && LOCPLINT->cb->howManyHeroes(true) >= VLC->modh->settings.MAX_HEROES_AVAILABLE_PER_PLAYER)
 	{
 		recruit->hoverTexts[0] = CGI->generaltexth->tavernInfo[1]; //Cannot recruit. You already have %d Heroes.
-		boost::algorithm::replace_first(recruit->hoverTexts[0],"%d",boost::lexical_cast<std::string>(LOCPLINT->cb->howManyHeroes()));
+		boost::algorithm::replace_first(recruit->hoverTexts[0],"%d",boost::lexical_cast<std::string>(LOCPLINT->cb->howManyHeroes(true)));
+		recruit->block(true);
+	}
+	else if((!LOCPLINT->castleInt) && LOCPLINT->cb->howManyHeroes(false) >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)
+	{
+		recruit->hoverTexts[0] = CGI->generaltexth->tavernInfo[1]; //Cannot recruit. You already have %d Heroes.
+		boost::algorithm::replace_first(recruit->hoverTexts[0], "%d", boost::lexical_cast<std::string>(LOCPLINT->cb->howManyHeroes(false)));
 		recruit->block(true);
 	}
 	else if(LOCPLINT->castleInt && LOCPLINT->castleInt->town->visitingHero)
@@ -3742,8 +3748,10 @@ CTavernWindow::CTavernWindow(const CGObjectInstance *TavernObj):
 		if(selected == -1)
 			recruit->block(true);
 	}
-
-	CCS->videoh->open("TAVERN.BIK");
+	if (LOCPLINT->castleInt)
+		CCS->videoh->open(LOCPLINT->castleInt->town->town->clientInfo.tavernVideo);
+	else
+		CCS->videoh->open("TAVERN.BIK");
 }
 
 void CTavernWindow::recruitb()
@@ -3931,6 +3939,7 @@ void CInGameConsole::keyPressed (const SDL_KeyboardEvent & key)
 			{
 				captureAllKeys = false;
 				endEnteringText(true);
+                CCS->soundh->playSound("CHAT");
 			}
 			break;
 		}
@@ -5753,7 +5762,7 @@ void CHillFortWindow::showAll (SDL_Surface *to)
 
 std::string CHillFortWindow::getTextForSlot(SlotID slot)
 {
-	if ( !hero->getCreature(slot) )//we dont have creature here
+	if ( !hero->getCreature(slot) )//we don`t have creature here
 		return "";
 
 	std::string str = CGI->generaltexth->allTexts[318];

+ 0 - 1
client/NetPacksClient.cpp

@@ -332,7 +332,6 @@ void RemoveObject::applyFirstCl( CClient *cl )
 
 	CGI->mh->hideObject(o);
 
-	int3 pos = o->visitablePos();
 	//notify interfaces about removal
 	for(auto i=cl->playerint.begin(); i!=cl->playerint.end(); i++)
 	{

+ 265 - 263
client/VCMI_client.vcxproj

@@ -1,263 +1,265 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup Label="ProjectConfigurations">
-    <ProjectConfiguration Include="Debug|Win32">
-      <Configuration>Debug</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Debug|x64">
-      <Configuration>Debug</Configuration>
-      <Platform>x64</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="RD|Win32">
-      <Configuration>RD</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="RD|x64">
-      <Configuration>RD</Configuration>
-      <Platform>x64</Platform>
-    </ProjectConfiguration>
-  </ItemGroup>
-  <PropertyGroup Label="Globals">
-    <ProjectGuid>{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}</ProjectGuid>
-    <RootNamespace>VCMI_client</RootNamespace>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <CharacterSet>Unicode</CharacterSet>
-    <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v110_xp</PlatformToolset>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <CharacterSet>Unicode</CharacterSet>
-    <WholeProgramOptimization>true</WholeProgramOptimization>
-    <PlatformToolset>v110_xp</PlatformToolset>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v110</PlatformToolset>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v110</PlatformToolset>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
-  <ImportGroup Label="ExtensionSettings">
-  </ImportGroup>
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'" Label="PropertySheets">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-    <Import Project="..\VCMI_global_release.props" />
-    <Import Project="..\VCMI_global.props" />
-  </ImportGroup>
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'" Label="PropertySheets">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-    <Import Project="..\VCMI_global_release.props" />
-    <Import Project="..\VCMI_global.props" />
-  </ImportGroup>
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-    <Import Project="..\VCMI_global_debug.props" />
-    <Import Project="..\VCMI_global.props" />
-  </ImportGroup>
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-    <Import Project="..\VCMI_global_debug.props" />
-    <Import Project="..\VCMI_global.props" />
-  </ImportGroup>
-  <PropertyGroup Label="UserMacros" />
-  <PropertyGroup>
-    <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion>
-    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(VCMI_Out)</OutDir>
-    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(VCMI_Out)</OutDir>
-    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir>
-    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(Configuration)\</IntDir>
-    <OutDir Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">$(VCMI_Out)</OutDir>
-    <OutDir Condition="'$(Configuration)|$(Platform)'=='RD|x64'">$(VCMI_Out)</OutDir>
-    <IntDir Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">$(Configuration)\</IntDir>
-    <IntDir Condition="'$(Configuration)|$(Platform)'=='RD|x64'">$(Configuration)\</IntDir>
-    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
-    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
-    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
-    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
-    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
-    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
-    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
-    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='RD|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
-    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='RD|Win32'" />
-    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='RD|x64'" />
-    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='RD|Win32'" />
-    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='RD|x64'" />
-  </PropertyGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <ClCompile>
-      <Optimization>
-      </Optimization>
-      <DisableSpecificWarnings>4251;%(DisableSpecificWarnings)</DisableSpecificWarnings>
-      <AssemblerOutput>NoListing</AssemblerOutput>
-      <PrecompiledHeader>Use</PrecompiledHeader>
-      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
-      <AdditionalOptions>/MP4 /Zm150</AdditionalOptions>
-    </ClCompile>
-    <Link>
-      <AdditionalDependencies>SDL.lib;zlib.lib;SDL_image.lib;SDL_ttf.lib;SDL_mixer.lib;VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <ShowProgress>NotSet</ShowProgress>
-      <OptimizeReferences>false</OptimizeReferences>
-      <Profile>true</Profile>
-    </Link>
-    <Manifest>
-      <AdditionalManifestFiles>$(ProjectDir)DPIaware.manifest;%(AdditionalManifestFiles)</AdditionalManifestFiles>
-    </Manifest>
-  </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
-    <ClCompile>
-      <PreprocessToFile>false</PreprocessToFile>
-      <PreprocessSuppressLineNumbers>false</PreprocessSuppressLineNumbers>
-      <DisableSpecificWarnings>4251;%(DisableSpecificWarnings)</DisableSpecificWarnings>
-      <AssemblerOutput>NoListing</AssemblerOutput>
-      <PrecompiledHeader>Use</PrecompiledHeader>
-      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
-      <AdditionalOptions>/MP4 /Zm150</AdditionalOptions>
-    </ClCompile>
-    <Link>
-      <AdditionalDependencies>SDL.lib;zlib.lib;SDL_image.lib;SDL_ttf.lib;SDL_mixer.lib;VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <ShowProgress>LinkVerbose</ShowProgress>
-      <OptimizeReferences>false</OptimizeReferences>
-      <Profile>true</Profile>
-    </Link>
-    <Manifest>
-      <AdditionalManifestFiles>$(ProjectDir)DPIaware.manifest;%(AdditionalManifestFiles)</AdditionalManifestFiles>
-    </Manifest>
-  </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
-    <ClCompile>
-      <PrecompiledHeader>Use</PrecompiledHeader>
-      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
-      <AdditionalOptions>/MP4 /Zm150</AdditionalOptions>
-    </ClCompile>
-    <Link>
-      <AdditionalDependencies>SDL.lib;zlib.lib;SDL_image.lib;SDL_ttf.lib;SDL_mixer.lib;VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <Driver>NotSet</Driver>
-      <LinkTimeCodeGeneration>
-      </LinkTimeCodeGeneration>
-      <ShowProgress>NotSet</ShowProgress>
-    </Link>
-  </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">
-    <ClCompile>
-      <AdditionalOptions>/MP4 /Zm150</AdditionalOptions>
-      <PrecompiledHeader>Use</PrecompiledHeader>
-      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
-    </ClCompile>
-    <Link>
-      <AdditionalDependencies>SDL.lib;zlib.lib;SDL_image.lib;SDL_ttf.lib;SDL_mixer.lib;VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <Driver>NotSet</Driver>
-      <LinkTimeCodeGeneration>
-      </LinkTimeCodeGeneration>
-      <ShowProgress>NotSet</ShowProgress>
-    </Link>
-  </ItemDefinitionGroup>
-  <ItemGroup>
-    <ClCompile Include="AdventureMapClasses.cpp" />
-    <ClCompile Include="battle\CBattleAnimations.cpp" />
-    <ClCompile Include="battle\CBattleInterface.cpp" />
-    <ClCompile Include="battle\CBattleInterfaceClasses.cpp" />
-    <ClCompile Include="battle\CCreatureAnimation.cpp" />
-    <ClCompile Include="CAdvmapInterface.cpp" />
-    <ClCompile Include="CAnimation.cpp" />
-    <ClCompile Include="..\CCallback.cpp" />
-    <ClCompile Include="CBitmapHandler.cpp" />
-    <ClCompile Include="CCastleInterface.cpp" />
-    <ClCompile Include="CCreatureWindow.cpp" />
-    <ClCompile Include="CDefHandler.cpp" />
-    <ClCompile Include="CGameInfo.cpp" />
-    <ClCompile Include="CHeroWindow.cpp" />
-    <ClCompile Include="CKingdomInterface.cpp" />
-    <ClCompile Include="Client.cpp" />
-    <ClCompile Include="CMessage.cpp" />
-    <ClCompile Include="CMT.cpp" />
-    <ClCompile Include="CMusicHandler.cpp" />
-    <ClCompile Include="CPlayerInterface.cpp" />
-    <ClCompile Include="CPreGame.cpp" />
-    <ClCompile Include="CQuestLog.cpp" />
-    <ClCompile Include="CSpellWindow.cpp" />
-    <ClCompile Include="CVideoHandler.cpp" />
-    <ClCompile Include="Graphics.cpp" />
-    <ClCompile Include="GUIClasses.cpp" />
-    <ClCompile Include="mapHandler.cpp" />
-    <ClCompile Include="NetPacksClient.cpp" />
-    <ClCompile Include="StdInc.cpp">
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
-      <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">StdInc.h</PrecompiledHeaderFile>
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">Create</PrecompiledHeader>
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='RD|x64'">Create</PrecompiledHeader>
-    </ClCompile>
-    <ClCompile Include="gui\CCursorHandler.cpp" />
-    <ClCompile Include="gui\CGuiHandler.cpp" />
-    <ClCompile Include="gui\CIntObject.cpp" />
-    <ClCompile Include="gui\CIntObjectClasses.cpp" />
-    <ClCompile Include="gui\Fonts.cpp" />
-    <ClCompile Include="gui\Geometries.cpp" />
-    <ClCompile Include="gui\SDL_Extensions.cpp" />
-  </ItemGroup>
-  <ItemGroup>
-    <ClInclude Include="..\Global.h" />
-    <ClInclude Include="AdventureMapClasses.h" />
-    <ClInclude Include="battle\CBattleAnimations.h" />
-    <ClInclude Include="battle\CBattleInterface.h" />
-    <ClInclude Include="battle\CBattleInterfaceClasses.h" />
-    <ClInclude Include="battle\CCreatureAnimation.h" />
-    <ClInclude Include="CAdvmapInterface.h" />
-    <ClInclude Include="CAnimation.h" />
-    <ClInclude Include="CBitmapHandler.h" />
-    <ClInclude Include="..\CCallback.h" />
-    <ClInclude Include="CCastleInterface.h" />
-    <ClInclude Include="CCreatureWindow.h" />
-    <ClInclude Include="CDefHandler.h" />
-    <ClInclude Include="CGameInfo.h" />
-    <ClInclude Include="CHeroWindow.h" />
-    <ClInclude Include="CKingdomInterface.h" />
-    <ClInclude Include="Client.h" />
-    <ClInclude Include="CMessage.h" />
-    <ClInclude Include="CMusicHandler.h" />
-    <ClInclude Include="CPlayerInterface.h" />
-    <ClInclude Include="CPreGame.h" />
-    <ClInclude Include="CQuestLog.h" />
-    <ClInclude Include="CSoundBase.h" />
-    <ClInclude Include="CSpellWindow.h" />
-    <ClInclude Include="CVideoHandler.h" />
-    <ClInclude Include="FontBase.h" />
-    <ClInclude Include="Graphics.h" />
-    <ClInclude Include="GUIClasses.h" />
-    <ClInclude Include="mapHandler.h" />
-    <ClInclude Include="resource.h" />
-    <ClInclude Include="StdInc.h" />
-    <ClInclude Include="gui\CCursorHandler.h" />
-    <ClInclude Include="gui\CGuiHandler.h" />
-    <ClInclude Include="gui\CIntObject.h" />
-    <ClInclude Include="gui\CIntObjectClasses.h" />
-    <ClInclude Include="gui\Fonts.h" />
-    <ClInclude Include="gui\Geometries.h" />
-    <ClInclude Include="gui\SDL_Extensions.h" />
-    <ClInclude Include="gui\SDL_Pixels.h" />
-  </ItemGroup>
-  <ItemGroup>
-    <None Include="..\ChangeLog" />
-    <None Include="vcmi.ico" />
-  </ItemGroup>
-  <ItemGroup>
-    <ResourceCompile Include="VCMI_client.rc" />
-  </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="..\lib\VCMI_lib.vcxproj">
-      <Project>{b952ffc5-3039-4de1-9f08-90acda483d8f}</Project>
-    </ProjectReference>
-  </ItemGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
-  <ImportGroup Label="ExtensionTargets">
-  </ImportGroup>
-</Project>
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="RD|Win32">
+      <Configuration>RD</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="RD|x64">
+      <Configuration>RD</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{8355EBA8-65C2-44A4-BC2D-78053E1BF2D6}</ProjectGuid>
+    <RootNamespace>VCMI_client</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <PlatformToolset>v110_xp</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <PlatformToolset>v110_xp</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\VCMI_global_release.props" />
+    <Import Project="..\VCMI_global.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\VCMI_global_release.props" />
+    <Import Project="..\VCMI_global.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\VCMI_global_debug.props" />
+    <Import Project="..\VCMI_global.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\VCMI_global_debug.props" />
+    <Import Project="..\VCMI_global.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..</OutDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(VCMI_Out)</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(Configuration)\</IntDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">..</OutDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='RD|x64'">$(VCMI_Out)</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">$(Configuration)\</IntDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='RD|x64'">$(Configuration)\</IntDir>
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='RD|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='RD|Win32'" />
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='RD|x64'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='RD|Win32'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='RD|x64'" />
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <Optimization>
+      </Optimization>
+      <DisableSpecificWarnings>4251;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+      <AssemblerOutput>NoListing</AssemblerOutput>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
+      <AdditionalOptions>/MP4 /Zm150</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>SDL.lib;zlib.lib;SDL_image.lib;SDL_ttf.lib;SDL_mixer.lib;VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <ShowProgress>NotSet</ShowProgress>
+      <OptimizeReferences>false</OptimizeReferences>
+      <Profile>true</Profile>
+      <AdditionalLibraryDirectories>..\..\libs;..</AdditionalLibraryDirectories>
+    </Link>
+    <Manifest>
+      <AdditionalManifestFiles>$(ProjectDir)DPIaware.manifest;%(AdditionalManifestFiles)</AdditionalManifestFiles>
+    </Manifest>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PreprocessToFile>false</PreprocessToFile>
+      <PreprocessSuppressLineNumbers>false</PreprocessSuppressLineNumbers>
+      <DisableSpecificWarnings>4251;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+      <AssemblerOutput>NoListing</AssemblerOutput>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
+      <AdditionalOptions>/MP4 /Zm150</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>SDL.lib;zlib.lib;SDL_image.lib;SDL_ttf.lib;SDL_mixer.lib;VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <ShowProgress>LinkVerbose</ShowProgress>
+      <OptimizeReferences>false</OptimizeReferences>
+      <Profile>true</Profile>
+    </Link>
+    <Manifest>
+      <AdditionalManifestFiles>$(ProjectDir)DPIaware.manifest;%(AdditionalManifestFiles)</AdditionalManifestFiles>
+    </Manifest>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
+      <AdditionalOptions>/MP4 /Zm150</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>SDL.lib;zlib.lib;SDL_image.lib;SDL_ttf.lib;SDL_mixer.lib;VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <Driver>NotSet</Driver>
+      <LinkTimeCodeGeneration>
+      </LinkTimeCodeGeneration>
+      <ShowProgress>NotSet</ShowProgress>
+      <AdditionalLibraryDirectories>..\..\libs;..</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">
+    <ClCompile>
+      <AdditionalOptions>/MP4 /Zm150</AdditionalOptions>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>SDL.lib;zlib.lib;SDL_image.lib;SDL_ttf.lib;SDL_mixer.lib;VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <Driver>NotSet</Driver>
+      <LinkTimeCodeGeneration>
+      </LinkTimeCodeGeneration>
+      <ShowProgress>NotSet</ShowProgress>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="AdventureMapClasses.cpp" />
+    <ClCompile Include="battle\CBattleAnimations.cpp" />
+    <ClCompile Include="battle\CBattleInterface.cpp" />
+    <ClCompile Include="battle\CBattleInterfaceClasses.cpp" />
+    <ClCompile Include="battle\CCreatureAnimation.cpp" />
+    <ClCompile Include="CAdvmapInterface.cpp" />
+    <ClCompile Include="CAnimation.cpp" />
+    <ClCompile Include="..\CCallback.cpp" />
+    <ClCompile Include="CBitmapHandler.cpp" />
+    <ClCompile Include="CCastleInterface.cpp" />
+    <ClCompile Include="CCreatureWindow.cpp" />
+    <ClCompile Include="CDefHandler.cpp" />
+    <ClCompile Include="CGameInfo.cpp" />
+    <ClCompile Include="CHeroWindow.cpp" />
+    <ClCompile Include="CKingdomInterface.cpp" />
+    <ClCompile Include="Client.cpp" />
+    <ClCompile Include="CMessage.cpp" />
+    <ClCompile Include="CMT.cpp" />
+    <ClCompile Include="CMusicHandler.cpp" />
+    <ClCompile Include="CPlayerInterface.cpp" />
+    <ClCompile Include="CPreGame.cpp" />
+    <ClCompile Include="CQuestLog.cpp" />
+    <ClCompile Include="CSpellWindow.cpp" />
+    <ClCompile Include="CVideoHandler.cpp" />
+    <ClCompile Include="Graphics.cpp" />
+    <ClCompile Include="GUIClasses.cpp" />
+    <ClCompile Include="mapHandler.cpp" />
+    <ClCompile Include="NetPacksClient.cpp" />
+    <ClCompile Include="StdInc.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">StdInc.h</PrecompiledHeaderFile>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='RD|x64'">Create</PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include="gui\CCursorHandler.cpp" />
+    <ClCompile Include="gui\CGuiHandler.cpp" />
+    <ClCompile Include="gui\CIntObject.cpp" />
+    <ClCompile Include="gui\CIntObjectClasses.cpp" />
+    <ClCompile Include="gui\Fonts.cpp" />
+    <ClCompile Include="gui\Geometries.cpp" />
+    <ClCompile Include="gui\SDL_Extensions.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\Global.h" />
+    <ClInclude Include="AdventureMapClasses.h" />
+    <ClInclude Include="battle\CBattleAnimations.h" />
+    <ClInclude Include="battle\CBattleInterface.h" />
+    <ClInclude Include="battle\CBattleInterfaceClasses.h" />
+    <ClInclude Include="battle\CCreatureAnimation.h" />
+    <ClInclude Include="CAdvmapInterface.h" />
+    <ClInclude Include="CAnimation.h" />
+    <ClInclude Include="CBitmapHandler.h" />
+    <ClInclude Include="..\CCallback.h" />
+    <ClInclude Include="CCastleInterface.h" />
+    <ClInclude Include="CCreatureWindow.h" />
+    <ClInclude Include="CDefHandler.h" />
+    <ClInclude Include="CGameInfo.h" />
+    <ClInclude Include="CHeroWindow.h" />
+    <ClInclude Include="CKingdomInterface.h" />
+    <ClInclude Include="Client.h" />
+    <ClInclude Include="CMessage.h" />
+    <ClInclude Include="CMusicHandler.h" />
+    <ClInclude Include="CPlayerInterface.h" />
+    <ClInclude Include="CPreGame.h" />
+    <ClInclude Include="CQuestLog.h" />
+    <ClInclude Include="CSoundBase.h" />
+    <ClInclude Include="CSpellWindow.h" />
+    <ClInclude Include="CVideoHandler.h" />
+    <ClInclude Include="FontBase.h" />
+    <ClInclude Include="Graphics.h" />
+    <ClInclude Include="GUIClasses.h" />
+    <ClInclude Include="mapHandler.h" />
+    <ClInclude Include="resource.h" />
+    <ClInclude Include="StdInc.h" />
+    <ClInclude Include="gui\CCursorHandler.h" />
+    <ClInclude Include="gui\CGuiHandler.h" />
+    <ClInclude Include="gui\CIntObject.h" />
+    <ClInclude Include="gui\CIntObjectClasses.h" />
+    <ClInclude Include="gui\Fonts.h" />
+    <ClInclude Include="gui\Geometries.h" />
+    <ClInclude Include="gui\SDL_Extensions.h" />
+    <ClInclude Include="gui\SDL_Pixels.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="..\ChangeLog" />
+    <None Include="vcmi.ico" />
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="VCMI_client.rc" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\lib\VCMI_lib.vcxproj">
+      <Project>{b952ffc5-3039-4de1-9f08-90acda483d8f}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 13 - 0
client/battle/CBattleAnimations.cpp

@@ -834,6 +834,19 @@ void CShootingAnimation::nextFrame()
 
 void CShootingAnimation::endAnim()
 {
+    // play wall hit/miss sound for catapult attack
+    if(!attackedStack)
+    {
+        if(catapultDamage > 0)
+        {
+            CCS->soundh->playSound("WALLHIT");
+        }
+        else
+        {
+            CCS->soundh->playSound("WALLMISS");
+        }
+    }
+
 	CAttackAnimation::endAnim();
 	delete this;
 }

+ 3 - 2
client/battle/CBattleInterface.cpp

@@ -26,6 +26,7 @@
 #include "../CVideoHandler.h"
 #include "../../lib/CTownHandler.h"
 #include "../../lib/mapping/CMap.h"
+#include "../../lib/CRandomGenerator.h"
 
 #include "CBattleAnimations.h"
 #include "CBattleInterfaceClasses.h"
@@ -59,7 +60,7 @@ static void onAnimationFinished(const CStack *stack, CCreatureAnimation * anim)
 
 		if (anim->framesInGroup(CCreatureAnim::MOUSEON) > 0)
 		{
-			if (float(rand() % 100) < creature->animation.timeBetweenFidgets * 10)
+			if (CRandomGenerator::getDefault().nextDouble(99.0) < creature->animation.timeBetweenFidgets * 10)
 				anim->playOnce(CCreatureAnim::MOUSEON);
 			else
 				anim->setType(CCreatureAnim::HOLDING);
@@ -194,7 +195,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
 			logGlobal->errorStream() << bfieldType << " battlefield type does not have any backgrounds!";
 		else
 		{
-			const std::string bgName = vstd::pickRandomElementOf(graphics->battleBacks[bfieldType], rand);
+			const std::string bgName = *RandomGeneratorUtil::nextItem(graphics->battleBacks[bfieldType], CRandomGenerator::getDefault());
 			background = BitmapHandler::loadBitmap(bgName, false);
 		}
 	}

+ 1 - 1
client/gui/CIntObjectClasses.h

@@ -342,7 +342,7 @@ protected:
 public:
 	EAlignment alignment;
 	EFonts font;
-	SDL_Color color; // default font color. Can be overriden by placing "{}" into the string
+	SDL_Color color; // default font color. Can be overridden by placing "{}" into the string
 };
 
 /// Label which shows text

+ 20 - 20
client/mapHandler.cpp

@@ -1,3 +1,13 @@
+/*
+ * mapHandler.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 "mapHandler.h"
 
@@ -17,19 +27,7 @@
 #include "../lib/GameConstants.h"
 #include "../lib/CStopWatch.h"
 #include "CMT.h"
-
-/*
- * mapHandler.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
- *
- */
-
-const bool MARK_BLOCKED_POSITIONS = false;
-const bool MARK_VISITABLE_POSITIONS = false;
+#include "../lib/CRandomGenerator.h"
 
 #define ADVOPT (conf.go()->ac)
 
@@ -129,7 +127,7 @@ void CMapHandler::prepareFOWDefs()
 			elem[j].resize(sizes.z);
 			for(int k = 0; k < sizes.z; ++k)
 			{
-				elem[j][k] = rand()%graphics->FoWfullHide->ourImages.size();
+				elem[j][k] = CRandomGenerator::getDefault().nextInt(graphics->FoWfullHide->ourImages.size() - 1);
 			}
 		}
 	}
@@ -199,6 +197,8 @@ void CMapHandler::borderAndTerrainBitmapInit()
 				{
 					int terBitmapNum = -1;
 
+					auto & rand = CRandomGenerator::getDefault();
+
 					if(i==-1 && j==-1)
 						terBitmapNum = 16;
 					else if(i==-1 && j==(sizes.y))
@@ -208,15 +208,15 @@ void CMapHandler::borderAndTerrainBitmapInit()
 					else if(i==(sizes.x) && j==(sizes.y))
 						terBitmapNum = 18;
 					else if(j == -1 && i > -1 && i < sizes.y)
-						terBitmapNum = 22+rand()%2;
+						terBitmapNum = rand.nextInt(22, 23);
 					else if(i == -1 && j > -1 && j < sizes.y)
-						terBitmapNum = 33+rand()%2;
+						terBitmapNum = rand.nextInt(33, 34);
 					else if(j == sizes.y && i >-1 && i < sizes.x)
-						terBitmapNum = 29+rand()%2;
+						terBitmapNum = rand.nextInt(29, 30);
 					else if(i == sizes.x && j > -1 && j < sizes.y)
-						terBitmapNum = 25+rand()%2;
+						terBitmapNum = rand.nextInt(25, 26);
 					else
-						terBitmapNum = rand()%16;
+						terBitmapNum = rand.nextInt(15);
 
 					if(terBitmapNum != -1)
 					{
@@ -1083,7 +1083,7 @@ ui8 CMapHandler::getPhaseShift(const CGObjectInstance *object) const
 	auto i = animationPhase.find(object);
 	if(i == animationPhase.end())
 	{
-		ui8 ret = rand() % 255;
+		ui8 ret = CRandomGenerator::getDefault().nextInt(254);
 		animationPhase[object] = ret;
 		return ret;
 	}

+ 0 - 3
client/mapHandler.h

@@ -102,9 +102,6 @@ public:
 
 	mutable std::map<const CGObjectInstance*, ui8> animationPhase;
 
-	static const bool MARK_BLOCKED_POSITIONS;
-	static const bool MARK_VISITABLE_POSITIONS;
-
 	CMapHandler(); //c-tor
 	~CMapHandler(); //d-tor
 

+ 1 - 1
config/battles_graphics.json

@@ -59,7 +59,7 @@
 		{ "id": 26, "defnames": [ "C13SPA0.DEF" ] },
 		{ "id": 27, "defnames": [ "C13SPE0.DEF" ] },
 		{ "id": 28, "defnames": [ "C13SPW0.DEF" ] },
-		{ "id": 29, "defnames": [ "C04SPE0.DEF" ] },
+		{ "id": 29, "defnames": [ "C14SPA0.DEF" ] },
 		{ "id": 30, "defnames": [ "C14SPE0.DEF" ] },
 		{ "id": 31, "defnames": [ "C15SPA0.DEF" ] },
 		{ "id": 32, "defnames": [ "C15SPE0.DEF", "C15SPE1.DEF", "C15SPE2.DEF" ] },

+ 2 - 1
config/creatures/castle.json

@@ -338,7 +338,8 @@
 			"resurrects" :
 			{
 				"type" : "SPELLCASTER",
-				"subtype" : "spell.resurrection"
+				"subtype" : "spell.resurrection",
+				"val" : 3
 			},
 			"spellpoints" :
 			{

+ 37 - 0
config/creatures/conflux.json

@@ -81,6 +81,11 @@
 			{
 				"type" : "SPELL_IMMUNITY",
 				"subtype" : "spell.chainLightning"
+			},
+			"armageddonImmunity" :
+			{
+				"type" : "SPELL_IMMUNITY",
+				"subtype" : "spell.armageddon"
 			}
 		},
 		"upgrades": ["magmaElemental"],
@@ -395,6 +400,21 @@
 				"type" : "SPELLCASTER",
 				"subtype" : "spell.protectEarth",
 				"val" : 2
+			},
+			"lightingImmunity" :
+			{
+				"type" : "SPELL_IMMUNITY",
+				"subtype" : "spell.lightningBolt"
+			},
+			"chainLightingImmunity" :
+			{
+				"type" : "SPELL_IMMUNITY",
+				"subtype" : "spell.chainLightning"
+			},			
+			"armageddonImmunity" :
+			{
+				"type" : "SPELL_IMMUNITY",
+				"subtype" : "spell.armageddon"
 			}
 		},
 		"graphics" :
@@ -440,6 +460,23 @@
 				"type" : "SPELLCASTER",
 				"subtype" : "spell.protectAir",
 				"val" : 2
+			},
+			"meteorShowerImmunity" :
+			{
+				"type" : "SPELL_IMMUNITY",
+				"subtype" : "spell.meteorShower"
+			},
+			"lightingVulnerablity" :
+			{
+				"type" : "MORE_DAMAGE_FROM_SPELL",
+				"subtype" : "spell.lightningBolt",
+				"val" : 100
+			},
+			"chainLightingVulnerablity" :
+			{
+				"type" : "MORE_DAMAGE_FROM_SPELL",
+				"subtype" : "spell.chainLightning",
+				"val" : 100
 			}
 		},
 		"graphics" :

+ 4 - 1
config/defaultMods.json

@@ -21,7 +21,10 @@
 		"MAX_BUILDING_PER_TURN" : 1,
 		"DWELLINGS_ACCUMULATE_CREATURES" : true,
 		"ALL_CREATURES_GET_DOUBLE_MONTHS" : false,
-		"NEGATIVE_LUCK" : false
+		"NEGATIVE_LUCK" : false,
+		"MAX_HEROES_AVAILABLE_PER_PLAYER" : 16,
+		"MAX_HEROES_ON_MAP_PER_PLAYER" : 8	
+
 	},
 	"modules":
 	{

+ 9 - 8
config/factions/castle.json

@@ -116,7 +116,8 @@
 
 			"musicTheme" : "music/CstleTown",
 			"defaultTavern" : 5,
-
+			"tavernVideo" : "TAVERN.BIK",
+			"guildBackground" : "TPMAGE.bmp",
 			"townBackground": "TBCSBACK.bmp",
 			"guildWindow": "TPMAGECS.bmp",
 			"buildingsIcons": "HALLCSTL.DEF",
@@ -143,7 +144,7 @@
 			"mageGuild" : 4,
 			"warMachine" : "ballista",
 			"moatDamage" : 70,
-
+			"primaryResource": "ore",
 			"buildings" :
 			{
 				"mageGuild1":     { "id" : 0 },
@@ -155,12 +156,12 @@
 				"fort":           { "id" : 7 },
 				"citadel":        { "id" : 8,  "upgrades" : "fort" },
 				"castle":         { "id" : 9,  "upgrades" : "citadel" },
-				"villageHall":    { "id" : 10, "mode" : "auto" },
-				"townHall":       { "id" : 11, "upgrades" : "villageHall", "requires" : [ "tavern" ] },
-				"cityHall":       { "id" : 12, "upgrades" : "townHall", "requires" : [ "allOf", [ "mageGuild1" ], [ "marketplace" ], [ "blacksmith" ] ] },
-				"capitol":        { "id" : 13, "upgrades" : "cityHall", "requires" : [ "castle" ] },
+				"villageHall":    { "id" : 10, "mode" : "auto", "produce": { "gold": 500 } },
+				"townHall":       { "id" : 11, "upgrades" : "villageHall", "requires" : [ "tavern" ], "produce": { "gold": 1000 } },
+				"cityHall":       { "id" : 12, "upgrades" : "townHall", "requires" : [ "allOf", [ "mageGuild1" ], [ "marketplace" ], [ "blacksmith" ] ], "produce": { "gold": 2000 } },
+				"capitol":        { "id" : 13, "upgrades" : "cityHall", "requires" : [ "castle" ], "produce": { "gold": 4000 } },
 				"marketplace":    { "id" : 14 },
-				"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ] },
+				"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ], "produce": { "ore": 1, "wood": 1 } },
 				"blacksmith":     { "id" : 16 },
 
 				"special1":       { "id" : 17, "requires" : [ "shipyard" ] },
@@ -169,7 +170,7 @@
 				"ship":           { "id" : 20, "upgrades" : "shipyard" },
 				"special2":       { "id" : 21, "requires" : [ "dwellingLvl4" ] },
 				"special3":       { "id" : 22, "upgrades" : "tavern" },
-				"grail":          { "id" : 26, "mode" : "grail"},
+				"grail":          { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
 
 				"dwellingLvl1":   { "id" : 30, "requires" : [ "fort" ] },
 				"dwellingLvl2":   { "id" : 31, "requires" : [ "dwellingLvl1" ] },

+ 8 - 7
config/factions/conflux.json

@@ -120,7 +120,8 @@
 
 			"musicTheme" : "music/ElemTown",
 			"defaultTavern" : 5,
-
+			"tavernVideo" : "TAVERN.BIK",
+			"guildBackground" : "TPMAGE.bmp",
 			"townBackground": "TBELBACK.bmp",
 			"guildWindow": "TPMAGEEL.bmp",
 			"buildingsIcons": "HALLELEM.DEF",
@@ -161,12 +162,12 @@
 				"fort":           { "id" : 7 },
 				"citadel":        { "id" : 8,  "upgrades" : "fort" },
 				"castle":         { "id" : 9,  "upgrades" : "citadel" },
-				"villageHall":    { "id" : 10, "mode" : "auto" },
-				"townHall":       { "id" : 11, "upgrades" : "villageHall", "requires" : [ "tavern" ] },
-				"cityHall":       { "id" : 12, "upgrades" : "townHall", "requires" : [ "allOf", [ "mageGuild1" ], [ "marketplace" ], [ "blacksmith" ] ] },
-				"capitol":        { "id" : 13, "upgrades" : "cityHall", "requires" : [ "castle" ] },
+				"villageHall":    { "id" : 10, "mode" : "auto", "produce": { "gold": 500 } },
+				"townHall":       { "id" : 11, "upgrades" : "villageHall", "requires" : [ "tavern" ], "produce": { "gold": 1000 } },
+				"cityHall":       { "id" : 12, "upgrades" : "townHall", "requires" : [ "allOf", [ "mageGuild1" ], [ "marketplace" ], [ "blacksmith" ] ], "produce": { "gold": 2000 } },
+				"capitol":        { "id" : 13, "upgrades" : "cityHall", "requires" : [ "castle" ], "produce": { "gold": 4000 } },
 				"marketplace":    { "id" : 14 },
-				"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ] },
+				"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ], "produce": { "mercury": 1 } },
 				"blacksmith":     { "id" : 16 },
 
 				"special1":       { "id" : 17, "requires" : [ "marketplace" ] },
@@ -174,7 +175,7 @@
 				"horde1Upgr":     { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
 				"ship":           { "id" : 20, "upgrades" : "shipyard" },
 				"special2":       { "id" : 21, "requires" : [ "mageGuild1" ] },
-				"grail":          { "id" : 26, "mode" : "grail"},
+				"grail":          { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
 				"extraTownHall":  { "id" : 27, "requires" : [ "townHall" ], "mode" : "auto" },
 				"extraCityHall":  { "id" : 28, "requires" : [ "cityHall" ], "mode" : "auto" },
 				"extraCapitol":   { "id" : 29, "requires" : [ "capitol" ], "mode" : "auto" },

+ 7 - 6
config/factions/dungeon.json

@@ -115,7 +115,8 @@
 
 			"musicTheme" : "music/Dungeon",
 			"defaultTavern" : 5,
-
+			"tavernVideo" : "TAVERN.BIK",
+			"guildBackground" : "TPMAGE.bmp",
 			"townBackground": "TBDNBACK.bmp",
 			"guildWindow": "TPMAGEDN.bmp",
 			"buildingsIcons": "HALLDUNG.DEF",
@@ -155,10 +156,10 @@
 				"fort":           { "id" : 7 },
 				"citadel":        { "id" : 8,  "upgrades" : "fort" },
 				"castle":         { "id" : 9,  "upgrades" : "citadel" },
-				"villageHall":    { "id" : 10, "mode" : "auto" },
-				"townHall":       { "id" : 11, "upgrades" : "villageHall", "requires" : [ "tavern" ] },
-				"cityHall":       { "id" : 12, "upgrades" : "townHall", "requires" : [ "allOf", [ "mageGuild1" ], [ "marketplace" ], [ "blacksmith" ] ] },
-				"capitol":        { "id" : 13, "upgrades" : "cityHall", "requires" : [ "castle" ] },
+				"villageHall":    { "id" : 10, "mode" : "auto", "produce": { "gold": 500 } },
+				"townHall":       { "id" : 11, "upgrades" : "villageHall", "requires" : [ "tavern" ], "produce": { "gold": 1000 } },
+				"cityHall":       { "id" : 12, "upgrades" : "townHall", "requires" : [ "allOf", [ "mageGuild1" ], [ "marketplace" ], [ "blacksmith" ] ], "produce": { "gold": 2000 } },
+				"capitol":        { "id" : 13, "upgrades" : "cityHall", "requires" : [ "castle" ], "produce": { "gold": 4000 } },
 				"marketplace":    { "id" : 14 },
 				"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ] },
 				"blacksmith":     { "id" : 16 },
@@ -169,7 +170,7 @@
 				"special2":       { "id" : 21, "requires" : [ "mageGuild1" ] },
 				"special3":       { "id" : 22 },
 				"special4":       { "id" : 23 },
-				"grail":          { "id" : 26, "mode" : "grail"},
+				"grail":          { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
 
 				"dwellingLvl1":   { "id" : 30, "requires" : [ "fort" ] },
 				"dwellingLvl2":   { "id" : 31, "requires" : [ "dwellingLvl1" ] },

+ 9 - 8
config/factions/fortress.json

@@ -116,7 +116,8 @@
 
 			"musicTheme" : "music/FortressTown",
 			"defaultTavern" : 5,
-
+			"tavernVideo" : "TAVERN.BIK",
+			"guildBackground" : "TPMAGE.bmp",
 			"townBackground": "TBFRBACK.bmp",
 			"guildWindow": "TPMAGEFR.bmp",
 			"buildingsIcons": "HALLFORT.DEF",
@@ -143,7 +144,7 @@
 			"mageGuild" : 3,
 			"warMachine" : "firstAidTent",
 			"moatDamage" : 90,
-
+			"primaryResource":"ore",
 			"buildings" :
 			{
 				"mageGuild1":     { "id" : 0 },
@@ -154,12 +155,12 @@
 				"fort":           { "id" : 7 },
 				"citadel":        { "id" : 8,  "upgrades" : "fort" },
 				"castle":         { "id" : 9,  "upgrades" : "citadel" },
-				"villageHall":    { "id" : 10, "mode" : "auto" },
-				"townHall":       { "id" : 11, "upgrades" : "villageHall", "requires" : [ "tavern" ] },
-				"cityHall":       { "id" : 12, "upgrades" : "townHall", "requires" : [ "allOf", [ "mageGuild1" ], [ "marketplace" ], [ "blacksmith" ] ] },
-				"capitol":        { "id" : 13, "upgrades" : "cityHall", "requires" : [ "castle" ] },
+				"villageHall":    { "id" : 10, "mode" : "auto", "produce": { "gold": 500 } },
+				"townHall":       { "id" : 11, "upgrades" : "villageHall", "requires" : [ "tavern" ], "produce": { "gold": 1000 } },
+				"cityHall":       { "id" : 12, "upgrades" : "townHall", "requires" : [ "allOf", [ "mageGuild1" ], [ "marketplace" ], [ "blacksmith" ] ], "produce": { "gold": 2000 } },
+				"capitol":        { "id" : 13, "upgrades" : "cityHall", "requires" : [ "castle" ], "produce": { "gold": 4000 } },
 				"marketplace":    { "id" : 14 },
-				"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ] },
+				"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ], "produce": { "wood": 1, "ore": 1 } },
 				"blacksmith":     { "id" : 16 },
 
 				"special1":       { "id" : 17, "requires" : [ "allOf", [ "townHall" ], [ "special2" ] ] },
@@ -168,7 +169,7 @@
 				"ship":           { "id" : 20, "upgrades" : "shipyard" },
 				"special2":       { "id" : 21, "requires" : [ "fort" ] },
 				"special3":       { "id" : 22, "requires" : [ "special2" ] },
-				"grail":          { "id" : 26, "mode" : "grail"},
+				"grail":          { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
 				"extraCapitol":   { "id" : 29, "requires" : [ "capitol" ], "mode" : "auto" },
 
 				"dwellingLvl1":   { "id" : 30, "requires" : [ "fort" ] },

+ 8 - 7
config/factions/inferno.json

@@ -116,7 +116,8 @@
 
 			"musicTheme" : "music/InfernoTown",
 			"defaultTavern" : 5,
-
+			"tavernVideo" : "TAVERN.BIK",
+			"guildBackground" : "TPMAGE.bmp",
 			"townBackground": "TBINBACK.bmp",
 			"guildWindow": "TPMAGEIN.bmp",
 			"buildingsIcons": "HALLINFR.DEF",
@@ -156,12 +157,12 @@
 				"fort":           { "id" : 7 },
 				"citadel":        { "id" : 8,  "upgrades" : "fort" },
 				"castle":         { "id" : 9,  "upgrades" : "citadel" },
-				"villageHall":    { "id" : 10, "mode" : "auto" },
-				"townHall":       { "id" : 11, "upgrades" : "villageHall", "requires" : [ "tavern" ] },
-				"cityHall":       { "id" : 12, "upgrades" : "townHall", "requires" : [ "allOf", [ "mageGuild1" ], [ "marketplace" ], [ "blacksmith" ] ] },
-				"capitol":        { "id" : 13, "upgrades" : "cityHall", "requires" : [ "castle" ] },
+				"villageHall":    { "id" : 10, "mode" : "auto", "produce": { "gold": 500 } },
+				"townHall":       { "id" : 11, "upgrades" : "villageHall", "requires" : [ "tavern" ], "produce": { "gold": 1000 } },
+				"cityHall":       { "id" : 12, "upgrades" : "townHall", "requires" : [ "allOf", [ "mageGuild1" ], [ "marketplace" ], [ "blacksmith" ] ], "produce": { "gold": 2000 } },
+				"capitol":        { "id" : 13, "upgrades" : "cityHall", "requires" : [ "castle" ], "produce": { "gold": 4000 } },
 				"marketplace":    { "id" : 14 },
-				"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ] },
+				"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ], "produce": { "mercury": 1 } },
 				"blacksmith":     { "id" : 16 },
 
 				"horde1":         { "id" : 18, "upgrades" : "dwellingLvl1" },
@@ -171,7 +172,7 @@
 				"special4":       { "id" : 23, "requires" : [ "mageGuild1" ] },
 				"horde2":         { "id" : 24, "upgrades" : "dwellingLvl3" },
 				"horde2Upgr":     { "id" : 25, "upgrades" : "dwellingUpLvl3", "requires" : [ "horde2" ], "mode" : "auto" },
-				"grail":          { "id" : 26, "mode" : "grail"},
+				"grail":          { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
 
 				"dwellingLvl1":   { "id" : 30, "requires" : [ "fort" ] },
 				"dwellingLvl2":   { "id" : 31, "requires" : [ "dwellingLvl1" ] },

+ 9 - 7
config/factions/necropolis.json

@@ -120,7 +120,8 @@
 
 			"musicTheme" : "music/NecroTown",
 			"defaultTavern" : 5,
-
+			"tavernVideo" : "TAVERN.BIK",
+			"guildBackground" : "TPMAGE.bmp",
 			"townBackground": "TBNCBACK.bmp",
 			"guildWindow": "TPMAGENC.bmp",
 			"buildingsIcons": "HALLNECR.DEF",
@@ -147,6 +148,7 @@
 			"mageGuild" : 5,
 			"warMachine" : "firstAidTent",
 			"moatDamage" : 70,
+			"primaryResource": "ore",
 
 			"buildings" :
 			{
@@ -160,12 +162,12 @@
 				"fort":           { "id" : 7 },
 				"citadel":        { "id" : 8,  "upgrades" : "fort" },
 				"castle":         { "id" : 9,  "upgrades" : "citadel" },
-				"villageHall":    { "id" : 10, "mode" : "auto" },
-				"townHall":       { "id" : 11, "upgrades" : "villageHall", "requires" : [ "tavern" ] },
-				"cityHall":       { "id" : 12, "upgrades" : "townHall", "requires" : [ "allOf", [ "mageGuild1" ], [ "marketplace" ], [ "blacksmith" ] ] },
-				"capitol":        { "id" : 13, "upgrades" : "cityHall", "requires" : [ "castle" ] },
+				"villageHall":    { "id" : 10, "mode" : "auto", "produce": { "gold": 500 } },
+				"townHall":       { "id" : 11, "upgrades" : "villageHall", "requires" : [ "tavern" ], "produce": { "gold": 1000 } },
+				"cityHall":       { "id" : 12, "upgrades" : "townHall", "requires" : [ "allOf", [ "mageGuild1" ], [ "marketplace" ], [ "blacksmith" ] ], "produce": { "gold": 2000 } },
+				"capitol":        { "id" : 13, "upgrades" : "cityHall", "requires" : [ "castle" ], "produce": { "gold": 4000 } },
 				"marketplace":    { "id" : 14 },
-				"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ] },
+				"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ], "produce": { "ore": 1, "wood": 1 } },
 				"blacksmith":     { "id" : 16 },
 
 				"special1":       { "id" : 17, "requires" : [ "fort" ] },
@@ -174,7 +176,7 @@
 				"ship":           { "id" : 20, "upgrades" : "shipyard" },
 				"special2":       { "id" : 21, "requires" : [ "mageGuild1" ] },
 				"special3":       { "id" : 22, "requires" : [ "dwellingLvl1" ] },
-				"grail":          { "id" : 26, "mode" : "grail"},
+				"grail":          { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
 				"extraTownHall":  { "id" : 27, "requires" : [ "townHall" ], "mode" : "auto" },
 				"extraCityHall":  { "id" : 28, "requires" : [ "cityHall" ], "mode" : "auto" },
 				"extraCapitol":   { "id" : 29, "requires" : [ "capitol" ], "mode" : "auto" },

+ 8 - 7
config/factions/rampart.json

@@ -120,7 +120,8 @@
 
 			"musicTheme" : "music/Rampart",
 			"defaultTavern" : 5,
-
+			"tavernVideo" : "TAVERN.BIK",
+			"guildBackground" : "TPMAGE.bmp",
 			"townBackground": "TBRMBACK.bmp",
 			"guildWindow": "TPMAGERM.bmp",
 			"buildingsIcons": "HALLRAMP.DEF",
@@ -160,12 +161,12 @@
 				"fort":           { "id" : 7 },
 				"citadel":        { "id" : 8,  "upgrades" : "fort" },
 				"castle":         { "id" : 9,  "upgrades" : "citadel" },
-				"villageHall":    { "id" : 10, "mode" : "auto" },
-				"townHall":       { "id" : 11, "upgrades" : "villageHall", "requires" : [ "tavern" ] },
-				"cityHall":       { "id" : 12, "upgrades" : "townHall", "requires" : [ "allOf", [ "mageGuild1" ], [ "marketplace" ], [ "blacksmith" ] ] },
-				"capitol":        { "id" : 13, "upgrades" : "cityHall", "requires" : [ "castle" ] },
+				"villageHall":    { "id" : 10, "mode" : "auto", "produce": { "gold": 500 } },
+				"townHall":       { "id" : 11, "upgrades" : "villageHall", "requires" : [ "tavern" ], "produce": { "gold": 1000 } },
+				"cityHall":       { "id" : 12, "upgrades" : "townHall", "requires" : [ "allOf", [ "mageGuild1" ], [ "marketplace" ], [ "blacksmith" ] ], "produce": { "gold": 2000 } },
+				"capitol":        { "id" : 13, "upgrades" : "cityHall", "requires" : [ "castle" ], "produce": { "gold": 4000 } },
 				"marketplace":    { "id" : 14 },
-				"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ] },
+				"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ], "produce": { "crystal": 1 } },
 				"blacksmith":     { "id" : 16 },
 
 				"special1":       { "id" : 17 },
@@ -175,7 +176,7 @@
 				"special3":       { "id" : 22, "requires" : [ "horde1" ] },
 				"horde2":         { "id" : 24, "upgrades" : "dwellingLvl5" },
 				"horde2Upgr":     { "id" : 25, "upgrades" : "dwellingUpLvl5", "requires" : [ "horde2" ], "mode" : "auto" },
-				"grail":          { "id" : 26, "mode" : "grail"},
+				"grail":          { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
 				"extraTownHall":  { "id" : 27, "requires" : [ "townHall" ], "mode" : "auto" },
 				"extraCityHall":  { "id" : 28, "requires" : [ "cityHall" ], "mode" : "auto" },
 				"extraCapitol":   { "id" : 29, "requires" : [ "capitol" ], "mode" : "auto" },

+ 9 - 7
config/factions/stronghold.json

@@ -114,7 +114,9 @@
 
 			"musicTheme" : "music/Stronghold",
 			"defaultTavern" : 5,
-
+			"tavernVideo" : "TAVERN.BIK",
+			"guildBackground" : "TPMAGE.bmp",
+			"primaryResource": "ore",
 			"townBackground": "TBSTBACK.bmp",
 			"guildWindow": "TPMAGEST.bmp",
 			"buildingsIcons": "HALLSTRN.DEF",
@@ -151,12 +153,12 @@
 				"fort":           { "id" : 7 },
 				"citadel":        { "id" : 8,  "upgrades" : "fort" },
 				"castle":         { "id" : 9,  "upgrades" : "citadel" },
-				"villageHall":    { "id" : 10, "mode" : "auto" },
-				"townHall":       { "id" : 11, "upgrades" : "villageHall", "requires" : [ "tavern" ] },
-				"cityHall":       { "id" : 12, "upgrades" : "townHall", "requires" : [ "allOf", [ "mageGuild1" ], [ "marketplace" ], [ "blacksmith" ] ] },
-				"capitol":        { "id" : 13, "upgrades" : "cityHall", "requires" : [ "castle" ] },
+				"villageHall":    { "id" : 10, "mode" : "auto", "produce": { "gold": 500 } },
+				"townHall":       { "id" : 11, "upgrades" : "villageHall", "requires" : [ "tavern" ], "produce": { "gold": 1000 } },
+				"cityHall":       { "id" : 12, "upgrades" : "townHall", "requires" : [ "allOf", [ "mageGuild1" ], [ "marketplace" ], [ "blacksmith" ] ], "produce": { "gold": 2000 } },
+				"capitol":        { "id" : 13, "upgrades" : "cityHall", "requires" : [ "castle" ], "produce": { "gold": 4000 } },
 				"marketplace":    { "id" : 14 },
-				"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ] },
+				"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ], "produce": { "ore": 1, "wood": 1 } },
 				"blacksmith":     { "id" : 16 },
 
 				"special1":       { "id" : 17, "requires" : [ "fort" ] },
@@ -165,7 +167,7 @@
 				"special2":       { "id" : 21, "requires" : [ "marketplace" ] },
 				"special3":       { "id" : 22, "requires" : [ "blacksmith" ] },
 				"special4":       { "id" : 23, "requires" : [ "fort" ] },
-				"grail":          { "id" : 26, "mode" : "grail"},
+				"grail":          { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
 
 				"dwellingLvl1":   { "id" : 30, "requires" : [ "fort" ] },
 				"dwellingLvl2":   { "id" : 31, "requires" : [ "dwellingLvl1" ] },

+ 8 - 7
config/factions/tower.json

@@ -115,7 +115,8 @@
 
 			"musicTheme" : "music/TowerTown",
 			"defaultTavern" : 5,
-
+			"tavernVideo" : "TAVERN.BIK",
+			"guildBackground" : "TPMAGE.bmp",
 			"townBackground": "TBTWBACK.bmp",
 			"guildWindow": "TPMAGETW.bmp",
 			"buildingsIcons": "HALLTOWR.DEF",
@@ -155,12 +156,12 @@
 				"fort":           { "id" : 7 },
 				"citadel":        { "id" : 8,  "upgrades" : "fort" },
 				"castle":         { "id" : 9,  "upgrades" : "citadel" },
-				"villageHall":    { "id" : 10, "mode" : "auto" },
-				"townHall":       { "id" : 11, "upgrades" : "villageHall", "requires" : [ "tavern" ] },
-				"cityHall":       { "id" : 12, "upgrades" : "townHall", "requires" : [ "allOf", [ "mageGuild1" ], [ "marketplace" ], [ "blacksmith" ] ] },
-				"capitol":        { "id" : 13, "upgrades" : "cityHall", "requires" : [ "castle" ] },
+				"villageHall":    { "id" : 10, "mode" : "auto", "produce" : { "gold": 500 } },
+				"townHall":       { "id" : 11, "upgrades" : "villageHall", "requires" : [ "tavern" ], "produce" : { "gold": 1000 } },
+				"cityHall":       { "id" : 12, "upgrades" : "townHall", "requires" : [ "allOf", [ "mageGuild1" ], [ "marketplace" ], [ "blacksmith" ] ], "produce": { "gold": 2000 } },
+				"capitol":        { "id" : 13, "upgrades" : "cityHall", "requires" : [ "castle" ], "produce" : { "gold": 4000 } },
 				"marketplace":    { "id" : 14 },
-				"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ] },
+				"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ], "produce" : { "gems": 1 } },
 				"blacksmith":     { "id" : 16 },
 
 				"special1":       { "id" : 17, "requires" : [ "marketplace" ] },
@@ -169,7 +170,7 @@
 				"special2":       { "id" : 21, "requires" : [ "fort" ] },
 				"special3":       { "id" : 22, "requires" : [ "mageGuild1" ] },
 				"special4":       { "id" : 23, "requires" : [ "mageGuild1" ] },
-				"grail":          { "id" : 26, "mode" : "grail"},
+				"grail":          { "id" : 26, "mode" : "grail", "produce" : { "gold": 5000 } },
 
 				"dwellingLvl1":   { "id" : 30, "requires" : [ "fort" ] },
 				"dwellingLvl2":   { "id" : 31, "requires" : [ "dwellingLvl1" ] },

+ 8 - 4
config/gameConfig.json

@@ -67,8 +67,12 @@
 		"config/bonuses.json",
 		"config/bonuses_texts.json"
 	],
-        "spells" :
-        [
-                "config/spell_info.json"
-        ]
+	"spells" :
+	[
+		"config/spells/adventure.json",
+		"config/spells/offensive.json",
+		"config/spells/other.json",
+		"config/spells/timed.json",
+		"config/spells/ability.json"
+	]
 }

+ 1 - 1
config/heroes/stronghold.json

@@ -239,7 +239,7 @@
 		],
 		"specialties":
 		[
-			{ "type":2, "val": 5, "subtype": 11, "info": 1 }
+			{ "type":10, "val": 1, "subtype": 5, "info": 0 }
 		]
 	}
 }

+ 22 - 22
config/rmg.json

@@ -33,10 +33,10 @@
 		},
 		"connections" :
 		[
-			{ "a" : "1", "b" : "5", "guard" : 1000 },
-			{ "a" : "2", "b" : "5", "guard" : 1000 },
-			{ "a" : "3", "b" : "5", "guard" : 1000 },
-			{ "a" : "4", "b" : "5", "guard" : 1000 }
+			{ "a" : "1", "b" : "5", "guard" : 5000 },
+			{ "a" : "2", "b" : "5", "guard" : 5000 },
+			{ "a" : "3", "b" : "5", "guard" : 5000 },
+			{ "a" : "4", "b" : "5", "guard" : 5000 }
 		]
 	},
 	"Upgrade" :
@@ -111,18 +111,18 @@
 		},
 		"connections" :
 		[
-			{ "a" : "1", "b" : "4", "guard" : 1000 },
-			{ "a" : "1", "b" : "5", "guard" : 1000 },
-			{ "a" : "2", "b" : "6", "guard" : 1000 },
-			{ "a" : "2", "b" : "7", "guard" : 1000 },
-			{ "a" : "3", "b" : "8", "guard" : 1000 },
-			{ "a" : "3", "b" : "9", "guard" : 1000 },
-			{ "a" : "4", "b" : "10", "guard" : 1000 },
-			{ "a" : "5", "b" : "12", "guard" : 1000 },
-			{ "a" : "6", "b" : "10", "guard" : 1000 },
-			{ "a" : "7", "b" : "11", "guard" : 1000 },
-			{ "a" : "8", "b" : "12", "guard" : 1000 },
-			{ "a" : "9", "b" : "11", "guard" : 1000 }
+			{ "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" : "4", "b" : "10", "guard" : 20000 },
+			{ "a" : "5", "b" : "12", "guard" : 20000 },
+			{ "a" : "6", "b" : "10", "guard" : 20000 },
+			{ "a" : "7", "b" : "11", "guard" : 20000 },
+			{ "a" : "8", "b" : "12", "guard" : 20000 },
+			{ "a" : "9", "b" : "11", "guard" : 20000 }
 		]
 	},
 	"Unfair Game" :
@@ -156,12 +156,12 @@
 		},
 		"connections" :
 		[
-			{ "a" : "1", "b" : "3", "guard" : 2000 },
-			{ "a" : "1", "b" : "4", "guard" : 2000 },
-			{ "a" : "2", "b" : "3", "guard" : 2000 },
-			{ "a" : "2", "b" : "4", "guard" : 2000 },
-			{ "a" : "3", "b" : "5", "guard" : 1000 },
-			{ "a" : "4", "b" : "6", "guard" : 1000 }
+			{ "a" : "1", "b" : "3", "guard" : 5000 },
+			{ "a" : "1", "b" : "4", "guard" : 5000 },
+			{ "a" : "2", "b" : "3", "guard" : 5000 },
+			{ "a" : "2", "b" : "4", "guard" : 5000 },
+			{ "a" : "3", "b" : "5", "guard" : 2000 },
+			{ "a" : "4", "b" : "6", "guard" : 2000 }
 		]
 	}
 }

+ 12 - 2
config/schemas/faction.json

@@ -105,8 +105,7 @@
 			"additionalProperties" : false,
 			"required" : [
 				"adventureMap", "buildingsIcons", "buildings", "creatures", "guildWindow", "names",
-				"hallBackground", "hallSlots", "horde", "mageGuild", "moatDamage", "defaultTavern",
-				"musicTheme", "siege", "structures", "townBackground", "warMachine"
+				"hallBackground", "hallSlots", "horde", "mageGuild", "moatDamage", "defaultTavern", "tavernVideo", "guildBackground", "musicTheme", "siege", "structures", "townBackground", "warMachine"
 			],
 			"description": "town",
 			"properties":{
@@ -168,6 +167,11 @@
 						"minimum" : 0
 					}
 				},
+				"tavernVideo" : {
+					"type" : "string",
+					"description" : "Video for tavern window",
+					"format" : "videoFile"
+				},
 				"names" : {
 					"type" : "array",
 					"description" : "Names for towns on adventure map",
@@ -199,6 +203,12 @@
 					"type":"string",
 					"description": "Image with small view on town from mage guild"
 				},
+				"guildBackground": {
+					"type":"string",
+					"description": "Image with background of mage guild",
+					"format" : "imageFile"
+				},
+
 				"hallBackground": {
 					"type":"string",
 					"description": "background image for town hall",

+ 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": {
+			"$ref" : "vcmi:objectType"
+		},
+		"types": {
+			"type":"object",
+			"additionalProperties": {
+				"$ref" : "vcmi:objectType"
+			}
+		}
+	}
+}

+ 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": {
+			"$ref" : "vcmi:objectTemplate"
+		},
+		"types": {
+			"type":"object",
+			"additionalProperties": {
+				"$ref" : "vcmi:objectTemplate"
+			}
+		}
+	}
+}

+ 24 - 16
config/schemas/spell.json

@@ -176,9 +176,12 @@
 		},
 		"limit":{
 				 "$ref" : "#/definitions/flags",
-				 "description": "flags structure of bonus names, presence of all bonuses required to be affected by"
+				 "description": "flags structure of bonus names, presence of all bonuses required to be affected by."
+		},
+		"absoluteLimit":{
+				 "$ref" : "#/definitions/flags",
+				 "description": "flags structure of bonus names, presence of all bonuses required to be affected by, can't be negated."
 		},
-
 		"graphics":{
 				 "type": "object",
 				 "additionalProperties" : false,
@@ -228,20 +231,25 @@
 			 "additionalProperties" : false,
 			 "required" : ["none", "basic", "advanced", "expert"],
 
-			 "properties":{
-				 "none":{
-					  "$ref" : "#/definitions/levelInfo"
-				 },
-				 "basic":{
-					  "$ref" : "#/definitions/levelInfo"
-				 },
-				 "advanced":{
-					  "$ref" : "#/definitions/levelInfo"
-				 },
-				 "expert":{
-					  "$ref" : "#/definitions/levelInfo"
-				 }
-			 }
+			 "properties":{	
+				"base":{
+					"type": "object",
+					"description": "will be merged with all levels",
+					"additionalProperties": true
+				},
+				"none":{
+					"$ref" : "#/definitions/levelInfo"
+				},
+				"basic":{
+					"$ref" : "#/definitions/levelInfo"
+				},
+				"advanced":{
+					"$ref" : "#/definitions/levelInfo"
+				},
+				"expert":{
+					"$ref" : "#/definitions/levelInfo"
+				}
+			}
 		}
 	}
 }

+ 15 - 0
config/schemas/townBuilding.json

@@ -63,6 +63,21 @@
 				"gems":    { "type":"number"},
 				"gold":    { "type":"number"}
 			}
+	    },		
+		"produce": {
+			"type":"object",
+			"additionalProperties" : false,
+			"description": "Resources this building produce each day",
+			"properties":{
+				"wood":    { "type":"number"},
+				"mercury": { "type":"number"},
+				"ore":     { "type":"number"},
+				"sulfur":  { "type":"number"},
+				"crystal": { "type":"number"},
+				"gems":    { "type":"number"},
+				"gold":    { "type":"number"}
+			}
 		}
+	  
 	}
 }

+ 0 - 3755
config/spell_info.json

@@ -1,3755 +0,0 @@
-{
-	"summonBoat" : {
-		"index" : 0,
-		"anim" : -1,
-		"sounds": {
-			"cast": "SUMMBOAT"
-		},
-		"levels" :{
-			"none":{
-				"range" : "X"
-			},
-			"basic":{
-				"range" : "X"
-			},
-			"advanced":{
-				"range" : "X"
-			},
-			"expert":{
-				"range" : "X"
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"scuttleBoat" : {
-		"index" : 1,
-		"anim" : -1,
-		"sounds": {
-			"cast": "SCUTBOAT"
-		},
-		"levels" : {
-			"none":	{
-				"range" : "X"
-			},
-			"basic":{
-				"range" : "X"
-			},
-			"advanced":{
-				"range" : "X"
-			},
-			"expert":{
-				"range" : "X"
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"visions" : {
-		"index" : 2,
-		"anim" : -1,
-		"sounds": {
-			"cast": "VISIONS"
-		},
-		"levels" : {
-			"none":{
-				"range" : "X"
-			},
-			"basic":{
-				"range" : "X"
-			},
-			"advanced":{
-				"range" : "X"
-			},
-			"expert":{
-				"range" : "X"
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"viewEarth" : {
-		"index" : 3,
-		"anim" : -1,
-		"sounds": {
-			"cast": "VIEW"
-		},
-		"levels" : {
-			"none":{
-				"range" : "X"
-			},
-			"basic":{
-				"range" : "X"
-			},
-			"advanced":{
-				"range" : "X"
-			},
-			"expert":{
-				"range" : "X"
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"disguise" : {
-		"index" : 4,
-		"anim" : -1,
-		"sounds": {
-			"cast": "DISGUISE"
-		},
-		"levels" : {
-			"none":{
-				"range" : "X"
-			},
-			"basic":{
-				"range" : "X"
-			},
-			"advanced":{
-				"range" : "X"
-			},
-			"expert":{
-				"range" : "X"
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"viewAir" : {
-		"index" : 5,
-		"anim" : -1,
-		"sounds": {
-			"cast": "VIEW"
-		},
-		"levels" : {
-			"none":{
-				"range" : "X"
-			},
-			"basic":{
-				"range" : "X"
-			},
-			"advanced":{
-				"range" : "X"
-			},
-			"expert":{
-				"range" : "X"
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"fly" : {
-		"index" : 6,
-		"anim" : -1,
-		"sounds": {
-			"cast": "FLYSPELL"
-		},
-		"levels" : {
-			"none":{
-				"range" : "X"
-			},
-			"basic":{
-				"range" : "X"
-			},
-			"advanced":{
-				"range" : "X"
-			},
-			"expert":{
-				"range" : "X"
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"waterWalk" : {
-		"index" : 7,
-		"anim" : -1,
-		"sounds": {
-			"cast": "WATRWALK"
-		},
-		"levels" : {
-			"none":{
-				"range" : "X"
-			},
-			"basic":{
-				"range" : "X"
-			},
-			"advanced":{
-				"range" : "X"
-			},
-			"expert":{
-				"range" : "X"
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"dimensionDoor" : {
-		"index" : 8,
-		"anim" : -1,
-		"sounds": {
-			"cast": "TELPTOUT"
-		},
-		"levels" : {
-			"none":{
-				"range" : "X"
-			},
-			"basic":{
-				"range" : "X"
-			},
-			"advanced":{
-				"range" : "X"
-			},
-			"expert":{
-				"range" : "X"
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"townPortal" : {
-		"index" : 9,
-		"anim" : -1,
-		"sounds": {
-			"cast": "TELPTOUT"
-		},
-		"levels" : {
-			"none":{
-				"range" : "X"
-			},
-			"basic":{
-				"range" : "X"
-			},
-			"advanced":{
-				"range" : "X"
-			},
-			"expert":{
-				"range" : "X"
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"quicksand" : {
-		"index" : 10,
-		"anim" : -1,
-		"sounds": {
-			"cast": "QUIKSAND"
-		},
-		"levels" : {
-			"none":{
-				"range" : "X"
-			},
-			"basic":{
-				"range" : "X"
-			},
-			"advanced":{
-				"range" : "X"
-			},
-			"expert":{
-				"range" : "X"
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"landMine" : {
-		"index" : 11,
-		"anim" : -1,
-		"sounds": {
-			"cast": ""
-		},
-		"levels" : {
-			"none":{
-				"range" : "X"
-			},
-			"basic":{
-				"range" : "X"
-			},
-			"advanced":{
-				"range" : "X"
-			},
-			"expert":{
-				"range" : "X"
-			}
-		},
-		"flags" : {
-			"damage": true,
-			"indifferent": true
-		},
-		"immunity" : {
-			"DIRECT_DAMAGE_IMMUNITY": true
-		}
-	},
-	"forceField" : {
-		"index" : 12,
-		"anim" : -1,
-		"sounds": {
-			"cast": "FORCEFLD"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0"
-			},
-			"basic":{
-				"range" : "0"
-			},
-			"advanced":{
-				"range" : "0"
-			},
-			"expert":{
-				"range" : "0"
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"fireWall" : {
-		"index" : 13,
-		"anim" : -1,
-		"sounds": {
-			"cast": "FIREWALL"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0"
-			},
-			"basic":{
-				"range" : "0"
-			},
-			"advanced":{
-				"range" : "0"
-			},
-			"expert":{
-				"range" : "0"
-			}
-		},
-		"flags" : {
-			"damage": true,
-			"indifferent": true
-		},
-		"immunity" : {
-			"DIRECT_DAMAGE_IMMUNITY": true
-		}
-	},
-	"earthquake" : {
-		"index" : 14,
-		"anim" : -1,
-		"sounds": {
-			"cast": "ERTHQUAK"
-		},
-		"levels" : {
-			"none":{
-				"range" : "X"
-			},
-			"basic":{
-				"range" : "X"
-			},
-			"advanced":{
-				"range" : "X"
-			},
-			"expert":{
-				"range" : "X"
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"magicArrow" : {
-		"index" : 15,
-		"anim" : 64,
-		"sounds": {
-			"cast": "MAGICBLT"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			}
-		},
-		"flags" : {
-			"damage": true,
-			"offensive": true,
-			"negative": true
-		},
-		"immunity" : {
-			"DIRECT_DAMAGE_IMMUNITY": true
-		}
-	},
-	"iceBolt" : {
-		"index" : 16,
-		"anim" : 46,
-		"sounds": {
-			"cast": "ICERAY"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			}
-		},
-		"flags" : {
-			"damage": true,
-			"offensive": true,
-			"negative": true
-		},
-		"immunity" : {
-			"DIRECT_DAMAGE_IMMUNITY": true
-		}
-	},
-	"lightningBolt" : {
-		"index" : 17,
-		"anim" : 38,
-		"sounds": {
-			"cast": "LIGHTBLT"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			}
-		},
-		"flags" : {
-			"damage": true,
-			"offensive": true,
-			"negative": true
-		},
-		"immunity" : {
-			"DIRECT_DAMAGE_IMMUNITY": true
-		}
-	},
-	"implosion" : {
-		"index" : 18,
-		"anim" : 10,
-		"sounds": {
-			"cast": "DECAY"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			}
-		},
-
-		"flags" : {
-			"damage": true,
-			"offensive": true,
-			"negative": true
-		},
-		"immunity" : {
-			"DIRECT_DAMAGE_IMMUNITY": true
-		}
-	},
-	"chainLightning" : {
-		"index" : 19,
-		"anim" : 38,
-		"sounds": {
-			"cast": "CHAINLTE"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			}
-		},
-		"flags" : {
-			"damage": true,
-			"offensive": true,
-			"negative": true
-		}
-	},
-	"frostRing" : {
-		"index" : 20,
-		"anim" : 45,
-		"sounds": {
-			"cast": "FROSTING"
-		},
-		"levels" : {
-			"none":{
-				"range" : "1",
-				"targetModifier":{"smart":false}
-			},
-			"basic":{
-				"range" : "1",
-				"targetModifier":{"smart":false}
-			},
-			"advanced":{
-				"range" : "1",
-				"targetModifier":{"smart":false}
-			},
-			"expert":{
-				"range" : "1",
-				"targetModifier":{"smart":false}
-			}
-		},
-		"flags" : {
-			"damage": true,
-			"offensive": true,
-			"negative": true
-		},
-		"immunity" : {
-			"DIRECT_DAMAGE_IMMUNITY": true
-		}
-	},
-	"fireball" : {
-		"index" : 21,
-		"anim" : 53,
-		"sounds": {
-			"cast": "FIREBALL"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0,1",
-				"targetModifier":{"smart":false}
-			},
-			"basic":{
-				"range" : "0,1",
-				"targetModifier":{"smart":false}
-			},
-			"advanced":{
-				"range" : "0,1",
-				"targetModifier":{"smart":false}
-			},
-			"expert":{
-				"range" : "0,1",
-				"targetModifier":{"smart":false}
-			}
-		},
-		"flags" : {
-			"damage": true,
-			"offensive": true,
-			"negative": true
-		},
-		"immunity" : {
-			"DIRECT_DAMAGE_IMMUNITY": true
-		}
-	},
-	"inferno" : {
-		"index" : 22,
-		"anim" : 9,
-		"sounds": {
-			"cast": "FIREBLST"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0-2",
-				"targetModifier":{"smart":false}
-			},
-			"basic":{
-				"range" : "0-2",
-				"targetModifier":{"smart":false}
-			},
-			"advanced":{
-				"range" : "0-2",
-				"targetModifier":{"smart":false}
-			},
-			"expert":{
-				"range" : "0-2",
-				"targetModifier":{"smart":false}
-			}
-		},
-		"flags" : {
-			"damage": true,
-			"offensive": true,
-			"negative": true
-		},
-		"immunity" : {
-			"DIRECT_DAMAGE_IMMUNITY": true
-		}
-	},
-	"meteorShower" : {
-		"index" : 23,
-		"anim" : 16,
-		"sounds": {
-			"cast": "METEOR"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0,1",
-				"targetModifier":{"smart":false}
-			},
-			"basic":{
-				"range" : "0,1",
-				"targetModifier":{"smart":false}
-			},
-			"advanced":{
-				"range" : "0,1",
-				"targetModifier":{"smart":false}
-			},
-			"expert":{
-				"range" : "0,1",
-				"targetModifier":{"smart":false}
-			}
-		},
-		"flags" : {
-			"damage": true,
-			"offensive": true,
-			"negative": true
-		},
-		"immunity" : {
-			"DIRECT_DAMAGE_IMMUNITY": true
-		}
-	},
-	"deathRipple" : {
-		"index" : 24,
-		"targetType" : "CREATURE",
-		"anim" : 8,
-		"sounds": {
-			"cast": "DEATHRIP"
-		},
-		"levels" : {
-			"none":{
-				"range" : "X",
-				"targetModifier":{"smart":false}
-			},
-			"basic":{
-				"range" : "X",
-				"targetModifier":{"smart":false}
-			},
-			"advanced":{
-				"range" : "X",
-				"targetModifier":{"smart":false}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":false}
-			}
-		},
-		"flags" : {
-			"damage": true,
-			"offensive": true,
-			"negative": true
-		},
-		"immunity" : {
-			"SIEGE_WEAPON": true,
-			"UNDEAD": true,
-			"DIRECT_DAMAGE_IMMUNITY": true
-		}
-	},
-	"destroyUndead" : {
-		"index" : 25,
-		"targetType" : "CREATURE",
-		"anim" : 29,
-		"sounds": {
-			"cast": "COLDRING"
-		},
-		"levels" : {
-			"none":{
-				"range" : "X",
-				"targetModifier":{"smart":false}
-			},
-			"basic":{
-				"range" : "X",
-				"targetModifier":{"smart":false}
-			},
-			"advanced":{
-				"range" : "X",
-				"targetModifier":{"smart":false}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":false}
-			}
-		},
-		"flags" : {
-			"damage": true,
-			"offensive": true,
-			"negative": true
-		},
-		"limit" : {
-			"UNDEAD": true
-		},
-		"immunity" : {
-			"DIRECT_DAMAGE_IMMUNITY": true
-		}
-	},
-	"armageddon" : {
-		"index" : 26,
-		"targetType" : "CREATURE",
-		"anim" : 12,
-		"sounds": {
-			"cast": "ARMGEDN"
-		},
-		"levels" : {
-			"none":{
-				"range" : "X",
-				"targetModifier":{"smart":false}
-			},
-			"basic":{
-				"range" : "X",
-				"targetModifier":{"smart":false}
-			},
-			"advanced":{
-				"range" : "X",
-				"targetModifier":{"smart":false}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":false}
-			}
-		},
-		"flags" : {
-			"damage": true,
-			"offensive": true,
-			"negative": true
-		},
-		"immunity" : {
-			"DIRECT_DAMAGE_IMMUNITY": true
-		}
-	},
-	"shield" : {
-		"index" : 27,
-		"anim" : 27,
-		"sounds": {
-			"cast": "SHIELD"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"generalDamageReduction" : {
-						"type" : "GENERAL_DAMAGE_REDUCTION",
-						"subtype" : 0,
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"generalDamageReduction" : {
-						"type" : "GENERAL_DAMAGE_REDUCTION",
-						"subtype" : 0,
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"generalDamageReduction" : {
-						"type" : "GENERAL_DAMAGE_REDUCTION",
-						"subtype" : 0,
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"generalDamageReduction" : {
-						"type" : "GENERAL_DAMAGE_REDUCTION",
-						"subtype" : 0,
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"airShield" : {
-		"index" : 28,
-		"anim" : 2,
-		"sounds": {
-			"cast": "AIRSHELD"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"generalDamageReduction" : {
-						"type" : "GENERAL_DAMAGE_REDUCTION",
-						"subtype" : 1,
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"generalDamageReduction" : {
-						"type" : "GENERAL_DAMAGE_REDUCTION",
-						"subtype" : 1,
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"generalDamageReduction" : {
-						"type" : "GENERAL_DAMAGE_REDUCTION",
-						"subtype" : 1,
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"generalDamageReduction" : {
-						"type" : "GENERAL_DAMAGE_REDUCTION",
-						"subtype" : 1,
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"fireShield" : {
-		"index" : 29,
-		"anim" : 11,
-		"sounds": {
-			"cast": "FIRESHIE"
-		},
-		// It looks that fireshield has two separate sounds
-		//			"soundfile":"FIRESHLD.wav"
-		//		
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"fireShield" : {
-						"type" : "FIRE_SHIELD",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"fireShield" : {
-						"type" : "FIRE_SHIELD",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"fireShield" : {
-						"type" : "FIRE_SHIELD",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"fireShield" : {
-						"type" : "FIRE_SHIELD",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"protectAir" : {
-		"index" : 30,
-		"anim" : 22,
-		"sounds": {
-			"cast": "PROTECTA"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"spellDamageReduction" : {
-						"type" : "SPELL_DAMAGE_REDUCTION",
-						"subtype" : 0,
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"spellDamageReduction" : {
-						"type" : "SPELL_DAMAGE_REDUCTION",
-						"subtype" : 0,
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"spellDamageReduction" : {
-						"type" : "SPELL_DAMAGE_REDUCTION",
-						"subtype" : 0,
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"spellDamageReduction" : {
-						"type" : "SPELL_DAMAGE_REDUCTION",
-						"subtype" : 0,
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"protectFire" : {
-		"index" : 31,
-		"anim" : 24,
-		"sounds": {
-			"cast": "PROTECTF"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"spellDamageReduction" : {
-						"type" : "SPELL_DAMAGE_REDUCTION",
-						"subtype" : 1,
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"spellDamageReduction" : {
-						"type" : "SPELL_DAMAGE_REDUCTION",
-						"subtype" : 1,
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"spellDamageReduction" : {
-						"type" : "SPELL_DAMAGE_REDUCTION",
-						"subtype" : 1,
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"spellDamageReduction" : {
-						"type" : "SPELL_DAMAGE_REDUCTION",
-						"subtype" : 1,
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"protectWater" : {
-		"index" : 32,
-		"anim" : 23,
-		"sounds": {
-			"cast": "PROTECTW"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"spellDamageReduction" : {
-						"type" : "SPELL_DAMAGE_REDUCTION",
-						"subtype" : 2,
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"spellDamageReduction" : {
-						"type" : "SPELL_DAMAGE_REDUCTION",
-						"subtype" : 2,
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"spellDamageReduction" : {
-						"type" : "SPELL_DAMAGE_REDUCTION",
-						"subtype" : 2,
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"spellDamageReduction" : {
-						"type" : "SPELL_DAMAGE_REDUCTION",
-						"subtype" : 2,
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"protectEarth" : {
-		"index" : 33,
-		"anim" : 26,
-		"sounds": {
-			"cast": "PROTECTE"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"spellDamageReduction" : {
-						"type" : "SPELL_DAMAGE_REDUCTION",
-						"subtype" : 3,
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"spellDamageReduction" : {
-						"type" : "SPELL_DAMAGE_REDUCTION",
-						"subtype" : 3,
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"spellDamageReduction" : {
-						"type" : "SPELL_DAMAGE_REDUCTION",
-						"subtype" : 3,
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"spellDamageReduction" : {
-						"type" : "SPELL_DAMAGE_REDUCTION",
-						"subtype" : 3,
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"antiMagic" : {
-		"index" : 34,
-		"anim" : 5,
-		"sounds": {
-			"cast": "ANTIMAGK"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"levelSpellImmunity" : {
-						"val" : 3,
-						"type" : "LEVEL_SPELL_IMMUNITY",
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"levelSpellImmunity" : {
-						"val" : 3,
-						"type" : "LEVEL_SPELL_IMMUNITY",
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"levelSpellImmunity" : {
-						"val" : 4,
-						"type" : "LEVEL_SPELL_IMMUNITY",
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"levelSpellImmunity" : {
-						"val" : 5,
-						"type" : "LEVEL_SPELL_IMMUNITY",
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"dispel" : {
-		"index" : 35,
-		"anim" : 41,
-		"sounds": {
-			"cast": "DISPELL"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0"
-			},
-			"basic":{
-				"range" : "0"
-			},
-			"advanced":{
-				"range" : "0"
-			},
-			"expert":{
-				"range" : "X"
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"magicMirror" : {
-		"index" : 36,
-		"anim" : 3,
-		"sounds": {
-			"cast": "BACKLASH"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"magicMirror" : {
-						"type" : "MAGIC_MIRROR",
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"magicMirror" : {
-						"type" : "MAGIC_MIRROR",
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"magicMirror" : {
-						"type" : "MAGIC_MIRROR",
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"magicMirror" : {
-						"type" : "MAGIC_MIRROR",
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"cure" : {
-		"index" : 37,
-		"anim" : 39,
-		"sounds": {
-			"cast": "CURE"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			}
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"resurrection" : {
-		"index" : 38,
-		"anim" : 79,
-		"sounds": {
-			"cast": "RESURECT"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			}
-		},
-		"flags" : {
-			"rising": true,
-			"positive": true
-		},
-		"immunity" : {
-			"UNDEAD": true,
-			"NON_LIVING": true
-		}
-	},
-	"animateDead" : {
-		"index" : 39,
-		"anim" : 79,
-		"sounds": {
-			"cast": "ANIMDEAD"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			}
-		},
-		"flags" : {
-			"rising": true,
-			"positive": true
-		},
-		"limit" : {
-			"UNDEAD": true
-		}
-	},
-	"sacrifice" : {
-		"index" : 40,
-		"anim" : 79,
-		"sounds": {
-			"cast": "SACRIF1"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			}
-		},
-		"flags" : {
-			"rising": true,
-			"positive": true
-		},
-		"immunity" : {
-			"UNDEAD": true,
-			"NON_LIVING": true
-		}
-	},
-	"bless" : {
-		"index" : 41,
-		"anim" : 36,
-		"sounds": {
-			"cast": "BLESS"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"alwaysMaximumDamage" : {
-						"val" : 0,
-						"type" : "ALWAYS_MAXIMUM_DAMAGE",
-						"subtype" : -1,
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"alwaysMaximumDamage" : {
-						"val" : 0,
-						"type" : "ALWAYS_MAXIMUM_DAMAGE",
-						"subtype" : -1,
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"alwaysMaximumDamage" : {
-						"val" : 1,
-						"type" : "ALWAYS_MAXIMUM_DAMAGE",
-						"subtype" : -1,
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"alwaysMaximumDamage" : {
-						"val" : 1,
-						"type" : "ALWAYS_MAXIMUM_DAMAGE",
-						"subtype" : -1,
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"counters" : {
-			"spell.curse": true
-		},
-		"immunity" : {
-			"UNDEAD": true
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"curse" : {
-		"index" : 42,
-		"anim" : 40,
-		"sounds": {
-			"cast": "CURSE"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"alwaysMinimumDamage" : {
-						"addInfo" : 0,
-						"val" : 0,
-						"type" : "ALWAYS_MINIMUM_DAMAGE",
-						"subtype" : -1,
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"alwaysMinimumDamage" : {
-						"addInfo" : 0,
-						"val" : 0,
-						"type" : "ALWAYS_MINIMUM_DAMAGE",
-						"subtype" : -1,
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"alwaysMinimumDamage" : {
-						"addInfo" : 20,
-						"val" : 1,
-						"type" : "ALWAYS_MINIMUM_DAMAGE",
-						"subtype" : -1,
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"alwaysMinimumDamage" : {
-						"addInfo" : 20,
-						"val" : 1,
-						"type" : "ALWAYS_MINIMUM_DAMAGE",
-						"subtype" : -1,
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"counters" : {
-			"spell.bless": true
-		},
-		"immunity" : {
-			"UNDEAD": true
-		},
-		"flags" : {
-			"negative": true
-		}
-	},
-	"bloodlust" : {
-		"index" : 43,
-		"anim" : 4,
-		"sounds": {
-			"cast": "BLOODLUS"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : 3,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"effectRange" : "ONLY_MELEE_FIGHT",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : 3,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"effectRange" : "ONLY_MELEE_FIGHT",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : 6,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"effectRange" : "ONLY_MELEE_FIGHT",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : 6,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"effectRange" : "ONLY_MELEE_FIGHT",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"counters" : {
-			"spell.weakness": true
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"precision" : {
-		"index" : 44,
-		"anim" : 25,
-		"sounds": {
-			"cast": "PRECISON"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : 3,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"effectRange" : "ONLY_DISTANCE_FIGHT",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : 3,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"effectRange" : "ONLY_DISTANCE_FIGHT",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : 6,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"effectRange" : "ONLY_DISTANCE_FIGHT",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : 6,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"effectRange" : "ONLY_DISTANCE_FIGHT",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"limit" : {
-			"SHOOTER": true
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"weakness" : {
-		"index" : 45,
-		"anim" : 56,
-		"sounds": {
-			"cast": "WEAKNESS"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : -3,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : -3,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : -6,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : -6,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"counters" : {
-			"spell.bloodlust": true
-		},
-		"flags" : {
-			"negative": true
-		}
-	},
-	"stoneSkin" : {
-		"index" : 46,
-		"anim" : 54,
-		"sounds": {
-			"cast": "TUFFSKIN"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : 3,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.defence",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : 3,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.defence",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : 6,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.defence",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : 6,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.defence",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"disruptingRay" : {
-		"index" : 47,
-				"targetType" : "CREATURE", //fix, dont remove
-		"anim" : 14,
-		"sounds": {
-			"cast": "DISRUPTR"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : -3,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.defence",
-						"valueType" : "ADDITIVE_VALUE",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : -3,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.defence",
-						"valueType" : "ADDITIVE_VALUE",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : -4,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.defence",
-						"valueType" : "ADDITIVE_VALUE",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : -5,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.defence",
-						"valueType" : "ADDITIVE_VALUE",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"flags" : {
-			"negative": true
-		}
-	},
-	"prayer" : {
-		"index" : 48,
-		"anim" : 0,
-		"sounds": {
-			"cast": "PRAYER"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"attack" : {
-						"val" : 2,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"duration" : "N_TURNS"
-					},
-					"defence" : {
-						"val" : 2,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.defence",
-						"duration" : "N_TURNS"
-					},
-					"stacksSpeed" : {
-						"addInfo" : 0,
-						"val" : 2,
-						"type" : "STACKS_SPEED",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"attack" : {
-						"val" : 2,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"duration" : "N_TURNS"
-					},
-					"defence" : {
-						"val" : 2,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.defence",
-						"duration" : "N_TURNS"
-					},
-					"stacksSpeed" : {
-						"addInfo" : 0,
-						"val" : 2,
-						"type" : "STACKS_SPEED",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"attack" : {
-						"val" : 4,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"duration" : "N_TURNS"
-					},
-					"defence" : {
-						"val" : 4,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.defence",
-						"duration" : "N_TURNS"
-					},
-					"stacksSpeed" : {
-						"addInfo" : 0,
-						"val" : 4,
-						"type" : "STACKS_SPEED",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"attack" : {
-						"val" : 4,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"duration" : "N_TURNS"
-					},
-					"defence" : {
-						"val" : 4,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.defence",
-						"duration" : "N_TURNS"
-					},
-					"stacksSpeed" : {
-						"addInfo" : 0,
-						"val" : 4,
-						"type" : "STACKS_SPEED",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"mirth" : {
-		"index" : 49,
-		"anim" : 20,
-		"sounds": {
-			"cast": "MIRTH"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"morale" : {
-						"val" : 1,
-						"type" : "MORALE",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"morale" : {
-						"val" : 1,
-						"type" : "MORALE",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"morale" : {
-						"val" : 2,
-						"type" : "MORALE",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"effects" : {
-					"morale" : {
-						"val" : 2,
-						"type" : "MORALE",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"counters" : {
-			"spell.sorrow":true
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"sorrow" : {
-		"index" : 50,
-		"anim" : 30,
-		"sounds": {
-			"cast": "SORROW"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"morale" : {
-						"val" : -1,
-						"type" : "MORALE",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"morale" : {
-						"val" : -1,
-						"type" : "MORALE",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"morale" : {
-						"val" : -2,
-						"type" : "MORALE",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"morale" : {
-						"val" : -2,
-						"type" : "MORALE",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"counters" : {
-			"spell.mirth":true
-		},
-		"immunity" : {
-			"MIND_IMMUNITY": true,
-			"UNDEAD": true,
-			"NON_LIVING": true
-		},
-		"flags" : {
-			"negative": true
-		}
-	},
-	"fortune" : {
-		"index" : 51,
-		"anim" : 18,
-		"sounds": {
-			"cast": "FORTUNE"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"luck" : {
-						"val" : 1,
-						"type" : "LUCK",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"luck" : {
-						"val" : 1,
-						"type" : "LUCK",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"luck" : {
-						"val" : 2,
-						"type" : "LUCK",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"luck" : {
-						"val" : 2,
-						"type" : "LUCK",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"counters" : {
-			"spell.misfortune": true
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"misfortune" : {
-		"index" : 52,
-		"anim" : 48,
-		"sounds": {
-			"cast": "MISFORT"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"luck" : {
-						"val" : -1,
-						"type" : "LUCK",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"luck" : {
-						"val" : -1,
-						"type" : "LUCK",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"luck" : {
-						"val" : -2,
-						"type" : "LUCK",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"luck" : {
-						"val" : -2,
-						"type" : "LUCK",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"counters" : {
-			"spell.fortune":true
-		},
-		"flags" : {
-			"negative": true
-		}
-	},
-	"haste" : {
-		"index" : 53,
-		"anim" : 31,
-		"sounds": {
-			"cast": "HASTE"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"stacksSpeed" : {
-						"addInfo" : 0,
-						"val" : 3,
-						"type" : "STACKS_SPEED",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"stacksSpeed" : {
-						"addInfo" : 0,
-						"val" : 3,
-						"type" : "STACKS_SPEED",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"stacksSpeed" : {
-						"addInfo" : 0,
-						"val" : 5,
-						"type" : "STACKS_SPEED",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"stacksSpeed" : {
-						"addInfo" : 0,
-						"val" : 5,
-						"type" : "STACKS_SPEED",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"counters" : {
-			"spell.slow": true
-		},
-		"immunity" : {
-			"SIEGE_WEAPON": true
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"slow" : {
-		"index" : 54,
-		"anim" : 19,
-		"sounds": {
-			"cast": "MUCKMIRE"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"stacksSpeed" : {
-						"addInfo" : 0,
-						"val" : -25,
-						"type" : "STACKS_SPEED",
-						"valueType" : "PERCENT_TO_ALL",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"stacksSpeed" : {
-						"addInfo" : 0,
-						"val" : -25,
-						"type" : "STACKS_SPEED",
-						"valueType" : "PERCENT_TO_ALL",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"stacksSpeed" : {
-						"addInfo" : 0,
-						"val" : -50,
-						"type" : "STACKS_SPEED",
-						"valueType" : "PERCENT_TO_ALL",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"stacksSpeed" : {
-						"addInfo" : 0,
-						"val" : -50,
-						"type" : "STACKS_SPEED",
-						"valueType" : "PERCENT_TO_ALL",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-
-		"counters" : {
-			"spell.haste":true
-		},
-		"immunity" : {
-			"SIEGE_WEAPON": true
-		},
-		"flags" : {
-			"negative": true
-		}
-	},
-	"slayer" : {
-		"index" : 55,
-		"anim" : 28,
-		"sounds": {
-			"cast": "SLAYER"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"slayer" : {
-						"val" : 0,
-						"type" : "SLAYER",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"slayer" : {
-						"val" : 1,
-						"type" : "SLAYER",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"slayer" : {
-						"val" : 2,
-						"type" : "SLAYER",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"slayer" : {
-						"val" : 3,
-						"type" : "SLAYER",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"frenzy" : {
-		"index" : 56,
-		"anim" : 17,
-		"sounds": {
-			"cast": "FRENZY"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"inFrenzy" : {
-						"val" : 100,
-						"type" : "IN_FRENZY",
-						"duration" : "STACK_GETS_TURN"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"inFrenzy" : {
-						"val" : 100,
-						"type" : "IN_FRENZY",
-						"duration" : "STACK_GETS_TURN"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"inFrenzy" : {
-						"val" : 150,
-						"type" : "IN_FRENZY",
-						"duration" : "STACK_GETS_TURN"
-					}
-				}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"inFrenzy" : {
-						"val" : 200,
-						"type" : "IN_FRENZY",
-						"duration" : "STACK_GETS_TURN"
-					}
-				}
-			}
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"titanBolt" : {
-		"index" : 57,
-		"anim" : 38,
-		"sounds": {
-			"cast": "LIGHTBLT"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			}
-		},
-		"flags" : {
-			"damage": true,
-			"offensive": true,
-			"negative": true,
-						"special": true
-		}
-	},
-	"counterstrike" : {
-		"index" : 58,
-		"anim" : 7,
-		"sounds": {
-			"cast": "CNTRSTRK"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"additionalRetaliation" : {
-						"val" : 1,
-						"type" : "ADDITIONAL_RETALIATION",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"additionalRetaliation" : {
-						"val" : 1,
-						"type" : "ADDITIONAL_RETALIATION",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"additionalRetaliation" : {
-						"val" : 2,
-						"type" : "ADDITIONAL_RETALIATION",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"additionalRetaliation" : {
-						"val" : 2,
-						"type" : "ADDITIONAL_RETALIATION",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"berserk" : {
-		"index" : 59,
-		"anim" : 35,
-		"sounds": {
-			"cast": "BERSERK"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"attacksNearestCreature" : {
-						"val" : 0,
-						"type" : "ATTACKS_NEAREST_CREATURE",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"attacksNearestCreature" : {
-						"val" : 1,
-						"type" : "ATTACKS_NEAREST_CREATURE",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0-1",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"attacksNearestCreature" : {
-						"val" : 2,
-						"type" : "ATTACKS_NEAREST_CREATURE",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "0-2",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"attacksNearestCreature" : {
-						"val" : 3,
-						"type" : "ATTACKS_NEAREST_CREATURE",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-
-		"immunity" : {
-			"MIND_IMMUNITY": true,
-			"UNDEAD": true,
-			"NON_LIVING": true
-		},
-		"flags" : {
-			"negative": true
-		}
-	},
-	"hypnotize" : {
-		"index" : 60,
-		"anim" : 21,
-		"sounds": {
-			"cast": "HYPNOTIZ"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"hypnotized" : {
-						"val" : 0,
-						"type" : "HYPNOTIZED",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"hypnotized" : {
-						"val" : 1,
-						"type" : "HYPNOTIZED",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"hypnotized" : {
-						"val" : 2,
-						"type" : "HYPNOTIZED",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"hypnotized" : {
-						"val" : 3,
-						"type" : "HYPNOTIZED",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-
-		"immunity" : {
-			"MIND_IMMUNITY": true,
-			"UNDEAD": true,
-			"NON_LIVING": true
-		},
-		"flags" : {
-			"negative": true
-		}
-	},
-	"forgetfulness" : {
-		"index" : 61,
-		"targetType" : "CREATURE", 
-		"anim" : 42,
-		"sounds": {
-			"cast": "FORGET"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"forgetfull" : {
-						"val" : 0,
-						"type" : "FORGETFULL",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"forgetfull" : {
-						"val" : 1,
-						"type" : "FORGETFULL",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"forgetfull" : {
-						"val" : 2,
-						"type" : "FORGETFULL",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "X",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"forgetfull" : {
-						"val" : 3,
-						"type" : "FORGETFULL",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"limit" : {
-			"SHOOTER": true
-		},
-		"immunity" : {
-			"MIND_IMMUNITY": true,
-			"UNDEAD": true,
-			"NON_LIVING": true
-		},
-		"flags" : {
-			"negative": true
-		}
-	},
-	"blind" : {
-		"index" : 62,
-		"anim" : 6,
-		"sounds": {
-			"cast": "BLIND"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"notActive" : {
-						"val" : 0,
-						"type" : "NOT_ACTIVE",
-						"subtype" : 62,
-						"duration" : [
-							"UNITL_BEING_ATTACKED",
-							"N_TURNS"
-						]
-					},
-					"generalAttackReduction" : {
-						"type" : "GENERAL_ATTACK_REDUCTION",
-						"duration" : [
-							"UNITL_BEING_ATTACKED",
-							"N_TURNS"
-						]
-					},
-					"noRetaliation" : {
-						"val" : 0,
-						"type" : "NO_RETALIATION",
-						"duration" : "UNITL_BEING_ATTACKED"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"notActive" : {
-						"val" : 0,
-						"type" : "NOT_ACTIVE",
-						"subtype" : 62,
-						"duration" : [
-							"UNITL_BEING_ATTACKED",
-							"N_TURNS"
-						]
-					},
-					"generalAttackReduction" : {
-						"type" : "GENERAL_ATTACK_REDUCTION",
-						"duration" : [
-							"UNITL_BEING_ATTACKED",
-							"N_TURNS"
-						]
-					},
-					"noRetaliation" : {
-						"val" : 0,
-						"type" : "NO_RETALIATION",
-						"duration" : "UNITL_BEING_ATTACKED"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"notActive" : {
-						"val" : 0,
-						"type" : "NOT_ACTIVE",
-						"subtype" : 62,
-						"duration" :[
-							"UNITL_BEING_ATTACKED",
-							"N_TURNS"
-						]
-					},
-					"generalAttackReduction" : {
-						"type" : "GENERAL_ATTACK_REDUCTION",
-						"duration" : [
-							"UNITL_BEING_ATTACKED",
-							"N_TURNS"
-						]
-					},
-					"noRetaliation" : {
-						"val" : 0,
-						"type" : "NO_RETALIATION",
-						"duration" : "UNITL_BEING_ATTACKED"
-					}
-				}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"notActive" : {
-						"val" : 0,
-						"type" : "NOT_ACTIVE",
-						"subtype" : 62,
-						"duration" : [
-							"UNITL_BEING_ATTACKED",
-							"N_TURNS"
-						]
-					},
-					"generalAttackReduction" : {
-						"type" : "GENERAL_ATTACK_REDUCTION",
-						"duration" : [
-							"UNITL_BEING_ATTACKED",
-							"N_TURNS"
-						]
-					},
-					"noRetaliation" : {
-						"val" : 0,
-						"type" : "NO_RETALIATION",
-						"duration" : "UNITL_BEING_ATTACKED"
-					}
-				}
-			}
-		},
-
-		"immunity" : {
-			"MIND_IMMUNITY": true,
-			"UNDEAD": true,
-			"NON_LIVING": true
-		},
-		"flags" : {
-			"negative": true
-		}
-	},
-	"teleport" : {
-		"index" : 63,
-		"anim" : -1,
-		"sounds": {
-			"cast": "TELPTOUT"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":false}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":false}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":false}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":false}
-			}
-		},
-		"immunity" : {
-			"SIEGE_WEAPON": true
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"removeObstacle" : {
-		"index" : 64,
-		"anim" : -1,
-		"sounds": {
-			"cast": "REMOVEOB"
-		},
-		"levels" : {
-			"none":{
-				"range" : "X"
-			},
-			"basic":{
-				"range" : "X"
-			},
-			"advanced":{
-				"range" : "X"
-			},
-			"expert":{
-				"range" : "X"
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"clone" : {
-		"index" : 65,
-		"anim" : -1,
-		"sounds": {
-			"cast": "CLONE"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true}
-			}
-		},
-		"immunity" : {
-			"SIEGE_WEAPON": true
-		},
-		"flags" : {
-			"positive": true
-		}
-	},
-	"fireElemental" : {
-		"index" : 66,
-		"anim" : -1,
-		"sounds": {
-			"cast": "SUMNELM"
-		},
-		"levels" : {
-			"none":{
-				"range" : "X"
-			},
-			"basic":{
-				"range" : "X"
-			},
-			"advanced":{
-				"range" : "X"
-			},
-			"expert":{
-				"range" : "X"
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"earthElemental" : {
-		"index" : 67,
-		"anim" : -1,
-		"sounds": {
-			"cast": "SUMNELM"
-		},
-		"levels" : {
-			"none":{
-				"range" : "X"
-			},
-			"basic":{
-				"range" : "X"
-			},
-			"advanced":{
-				"range" : "X"
-			},
-			"expert":{
-				"range" : "X"
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"waterElemental" : {
-		"index" : 68,
-		"anim" : -1,
-		"sounds": {
-			"cast": "SUMNELM"
-		},
-		"levels" : {
-			"none":{
-				"range" : "X"
-			},
-			"basic":{
-				"range" : "X"
-			},
-			"advanced":{
-				"range" : "X"
-			},
-			"expert":{
-				"range" : "X"
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"airElemental" : {
-		"index" : 69,
-		"anim" : -1,
-		"sounds": {
-			"cast": "SUMNELM"
-		},
-		"levels" : {
-			"none":{
-				"range" : "X"
-			},
-			"basic":{
-				"range" : "X"
-			},
-			"advanced":{
-				"range" : "X"
-			},
-			"expert":{
-				"range" : "X"
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"stoneGaze" : {
-		"index" : 70,
-		"anim" : 70,
-		"sounds": {
-			"cast": "PARALYZE"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"notActive" : {
-						"val" : 0,
-						"type" : "NOT_ACTIVE",
-						"subtype" : 62,
-						"duration" : [
-							"UNITL_BEING_ATTACKED",
-							"N_TURNS"
-						]
-					},
-					"noRetaliation" : {
-						"val" : 0,
-						"type" : "NO_RETALIATION",
-						"duration" : "UNITL_BEING_ATTACKED"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"notActive" : {
-						"val" : 0,
-						"type" : "NOT_ACTIVE",
-						"subtype" : 62,
-						"duration" : [
-							"UNITL_BEING_ATTACKED",
-							"N_TURNS"
-						]
-					},
-					"noRetaliation" : {
-						"val" : 0,
-						"type" : "NO_RETALIATION",
-						"duration" : "UNITL_BEING_ATTACKED"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"notActive" : {
-						"val" : 0,
-						"type" : "NOT_ACTIVE",
-						"subtype" : 62,
-						"duration" : [
-							"UNITL_BEING_ATTACKED",
-							"N_TURNS"
-						]
-					},
-					"noRetaliation" : {
-						"val" : 0,
-						"type" : "NO_RETALIATION",
-						"duration" : "UNITL_BEING_ATTACKED"
-					}
-				}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"notActive" : {
-						"val" : 0,
-						"type" : "NOT_ACTIVE",
-						"subtype" : 62,
-						"duration" : [
-							"UNITL_BEING_ATTACKED",
-							"N_TURNS"
-						]
-					},
-					"noRetaliation" : {
-						"val" : 0,
-						"type" : "NO_RETALIATION",
-						"duration" : "UNITL_BEING_ATTACKED"
-					}
-				}
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"poison" : {
-		"index" : 71,
-		"anim" : 67,
-		"sounds": {
-			"cast": "POISON"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"poison" : {
-						"val" : 30,
-						"type" : "POISON",
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					},
-					"stackHealth" : {
-						"val" : -10,
-						"type" : "STACK_HEALTH",
-						"valueType" : "PERCENT_TO_ALL",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"poison" : {
-						"val" : 30,
-						"type" : "POISON",
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					},
-					"stackHealth" : {
-						"val" : -10,
-						"type" : "STACK_HEALTH",
-						"valueType" : "PERCENT_TO_ALL",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"poison" : {
-						"val" : 30,
-						"type" : "POISON",
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					},
-					"stackHealth" : {
-						"val" : -10,
-						"type" : "STACK_HEALTH",
-						"valueType" : "PERCENT_TO_ALL",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"poison" : {
-						"val" : 30,
-						"type" : "POISON",
-						"valueType" : "INDEPENDENT_MAX",
-						"duration" : "N_TURNS"
-					},
-					"stackHealth" : {
-						"val" : -10,
-						"type" : "STACK_HEALTH",
-						"valueType" : "PERCENT_TO_ALL",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"immunity" : {
-			"UNDEAD": true,
-			"NON_LIVING": true
-		},
-		"flags" : {
-			"negative": true
-		}
-	},
-	"bind" : {
-		"index" : 72,
-		"anim" : 68,
-		"sounds": {
-			"cast": "BIND"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"effects" : {
-					"bindEffect" : {
-						"val" : 0,
-						"type" : "BIND_EFFECT",
-						"duration" : "PERMANENT",
-						"addInfo" : -1
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"effects" : {
-					"bindEffect" : {
-						"val" : 0,
-						"type" : "BIND_EFFECT",
-						"duration" : "PERMANENT",
-						"addInfo" : -1
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"effects" : {
-					"bindEffect" : {
-						"val" : 0,
-						"type" : "BIND_EFFECT",
-						"duration" : "PERMANENT",
-						"addInfo" : -1
-					}
-				}
-			},
-			"expert":{
-				"range" : "0",
-				"effects" : {
-					"bindEffect" : {
-						"val" : 0,
-						"type" : "BIND_EFFECT",
-						"duration" : "PERMANENT",
-						"addInfo" : -1
-					}
-				}
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"disease" : {
-		"index" : 73,
-		"anim" : 69,
-		"sounds": {
-			"cast": "DISEASE"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"attack" : {
-						"val" : -2,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"duration" : "N_TURNS"
-					},
-					"defence" : {
-						"val" : -2,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.defence",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"attack" : {
-						"val" : -2,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"duration" : "N_TURNS"
-					},
-					"defence" : {
-						"val" : -2,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.defence",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"attack" : {
-						"val" : -2,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"duration" : "N_TURNS"
-					},
-					"defence" : {
-						"val" : -2,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.defence",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"attack" : {
-						"val" : -2,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"duration" : "N_TURNS"
-					},
-					"defence" : {
-						"val" : -2,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.defence",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"immunity" : {
-			"UNDEAD": true,
-			"NON_LIVING": true
-		},
-		"flags" : {
-			"negative": true
-		}
-	},
-	"paralyze" : {
-		"index" : 74,
-		"anim" : 70,
-		"sounds": {
-			"cast": "PARALYZE"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"notActive" : {
-						"val" : 0,
-						"type" : "NOT_ACTIVE",
-						"subtype" : 74,
-						"duration" : [
-							"UNITL_BEING_ATTACKED",
-							"N_TURNS"
-						]
-					},
-					"noRetaliation" : {
-						"val" : 0,
-						"type" : "NO_RETALIATION",
-						"duration" : "UNITL_BEING_ATTACKED"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"notActive" : {
-						"val" : 0,
-						"type" : "NOT_ACTIVE",
-						"subtype" : 74,
-						"duration" : [
-							"UNITL_BEING_ATTACKED",
-							"N_TURNS"
-						]
-					},
-					"noRetaliation" : {
-						"val" : 0,
-						"type" : "NO_RETALIATION",
-						"duration" : "UNITL_BEING_ATTACKED"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"notActive" : {
-						"val" : 0,
-						"type" : "NOT_ACTIVE",
-						"subtype" : 74,
-						"duration" : [
-							"UNITL_BEING_ATTACKED",
-							"N_TURNS"
-						]
-					},
-					"noRetaliation" : {
-						"val" : 0,
-						"type" : "NO_RETALIATION",
-						"duration" : "UNITL_BEING_ATTACKED"
-					}
-				}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"notActive" : {
-						"val" : 0,
-						"type" : "NOT_ACTIVE",
-						"subtype" : 74,
-						"duration" : [
-							"UNITL_BEING_ATTACKED",
-							"N_TURNS"
-						]
-					},
-					"noRetaliation" : {
-						"val" : 0,
-						"type" : "NO_RETALIATION",
-						"duration" : "UNITL_BEING_ATTACKED"
-					}
-				}
-			}
-		},
-		"flags" : {
-			"negative": true
-		}
-	},
-	"age" : {
-		"index" : 75,
-		"anim" : 71,
-		"sounds": {
-			"cast": "AGE"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"stackHealth" : {
-						"val" : -50,
-						"type" : "STACK_HEALTH",
-						"valueType" : "PERCENT_TO_ALL",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"stackHealth" : {
-						"val" : -50,
-						"type" : "STACK_HEALTH",
-						"valueType" : "PERCENT_TO_ALL",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"stackHealth" : {
-						"val" : -50,
-						"type" : "STACK_HEALTH",
-						"valueType" : "PERCENT_TO_ALL",
-						"duration" : "N_TURNS"
-					}
-				}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"stackHealth" : {
-						"val" : -50,
-						"type" : "STACK_HEALTH",
-						"valueType" : "PERCENT_TO_ALL",
-						"duration" : "N_TURNS"
-					}
-				}
-			}
-		},
-		"immunity" : {
-			"UNDEAD": true,
-			"NON_LIVING": true
-		},
-		"flags" : {
-			"negative": true
-		}
-	},
-	"deathCloud" : {
-		"index" : 76,
-		"anim" : 72,
-		"sounds": {
-			"cast": "DEATHCLD"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0-1"
-			},
-			"basic":{
-				"range" : "0-1"
-			},
-			"advanced":{
-				"range" : "0-1"
-			},
-			"expert":{
-				"range" : "0-1"
-			}
-		},
-		"immunity" : {
-			"UNDEAD": true,
-			"NON_LIVING": true
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"thunderbolt" : {
-		"index" : 77,
-		"anim" : 38,
-		"sounds": {
-			"cast": "LIGHTBLT"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0"
-			},
-			"basic":{
-				"range" : "0"
-			},
-			"advanced":{
-				"range" : "0"
-			},
-			"expert":{
-				"range" : "0"
-			}
-		},
-		"flags" : {
-			"damage": true,
-			"offensive": true,
-			"negative": true
-		}
-	},
-	"dispelHelpful" : {
-		"index" : 78,
-		"anim" : 41,
-		"sounds": {
-			"cast": "DISPELL"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0"
-			},
-			"basic":{
-				"range" : "0"
-			},
-			"advanced":{
-				"range" : "0"
-			},
-			"expert":{
-				"range" : "0"
-			}
-		},
-
-		"flags" : {
-			"negative": true
-		}
-	},
-	"deathStare" : {
-		"index" : 79,
-		"anim" : 80,
-		"sounds": {
-			"cast": "DEATHSTR"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0"
-			},
-			"basic":{
-				"range" : "0"
-			},
-			"advanced":{
-				"range" : "0"
-			},
-			"expert":{
-				"range" : "0"
-			}
-		},
-		"immunity" : {
-			"UNDEAD": true,
-			"NON_LIVING": true
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"acidBreath" : {
-		"index" : 80,
-		"anim" : 81,
-		"sounds": {
-			"cast": "ACID"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : -3,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"duration" : "PERMANENT",
-						"valueType" : "ADDITIVE_VALUE"
-					}
-				}
-			},
-			"basic":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : -3,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"duration" : "PERMANENT",
-						"valueType" : "ADDITIVE_VALUE"
-					}
-				}
-			},
-			"advanced":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : -3,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"duration" : "PERMANENT",
-						"valueType" : "ADDITIVE_VALUE"
-					}
-				}
-			},
-			"expert":{
-				"range" : "0",
-				"targetModifier":{"smart":true},
-				"effects" : {
-					"primarySkill" : {
-						"val" : -3,
-						"type" : "PRIMARY_SKILL",
-						"subtype" : "primSkill.attack",
-						"duration" : "PERMANENT",
-						"valueType" : "ADDITIVE_VALUE"
-					}
-				}
-			}
-		},
-		"flags" : {
-			"indifferent": true
-		}
-	},
-	"acidBreathDamage" : {
-		"index" : 81,
-		"anim" : 81,
-		"sounds": {
-			"cast": "ACID"
-		},
-		"levels" : {
-			"none":{
-				"range" : "0"
-			},
-			"basic":{
-				"range" : "0"
-			},
-			"advanced":{
-				"range" : "0"
-			},
-			"expert":{
-				"range" : "0"
-			}
-		},
-		"flags" : {
-			"damage": true,
-			"indifferent": true
-		},
-		"immunity" : {
-			"DIRECT_DAMAGE_IMMUNITY": true
-		}
-	}
-}

+ 300 - 0
config/spells/ability.json

@@ -0,0 +1,300 @@
+{
+	"stoneGaze" : {
+		"index" : 70,
+		"anim" : 70,
+		"sounds": {
+			"cast": "PARALYZE"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"notActive" : {
+						"val" : 0,
+						"type" : "NOT_ACTIVE",
+						"subtype" : 62,
+						"duration" : [
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"noRetaliation" : {
+						"val" : 0,
+						"type" : "NO_RETALIATION",
+						"duration" : "UNITL_BEING_ATTACKED"
+					}
+				}				
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"poison" : {
+		"index" : 71,
+		"anim" : 67,
+		"sounds": {
+			"cast": "POISON"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"poison" : {
+						"val" : 30,
+						"type" : "POISON",
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					},
+					"stackHealth" : {
+						"val" : -10,
+						"type" : "STACK_HEALTH",
+						"valueType" : "PERCENT_TO_ALL",
+						"duration" : "N_TURNS"
+					}
+				}			
+			}
+		},
+		"immunity" : {
+			"UNDEAD": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"bind" : {
+		"index" : 72,
+		"anim" : 68,
+		"sounds": {
+			"cast": "BIND"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"effects" : {
+					"bindEffect" : {
+						"val" : 0,
+						"type" : "BIND_EFFECT",
+						"duration" : "PERMANENT",
+						"addInfo" : -1
+					}
+				}			
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"disease" : {
+		"index" : 73,
+		"anim" : 69,
+		"sounds": {
+			"cast": "DISEASE"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"attack" : {
+						"val" : -2,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"duration" : "N_TURNS"
+					},
+					"defence" : {
+						"val" : -2,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"duration" : "N_TURNS"
+					}
+				}		
+			}
+		},
+		"immunity" : {
+			"UNDEAD": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"paralyze" : {
+		"index" : 74,
+		"anim" : 70,
+		"sounds": {
+			"cast": "PARALYZE"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"notActive" : {
+						"val" : 0,
+						"type" : "NOT_ACTIVE",
+						"subtype" : 74,
+						"duration" : [
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"noRetaliation" : {
+						"val" : 0,
+						"type" : "NO_RETALIATION",
+						"duration" : "UNITL_BEING_ATTACKED"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"age" : {
+		"index" : 75,
+		"anim" : 71,
+		"sounds": {
+			"cast": "AGE"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"stackHealth" : {
+						"val" : -50,
+						"type" : "STACK_HEALTH",
+						"valueType" : "PERCENT_TO_ALL",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"immunity" : {
+			"UNDEAD": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"deathCloud" : {
+		"index" : 76,
+		"anim" : 72,
+		"sounds": {
+			"cast": "DEATHCLD"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0-1"
+			}
+		},
+		"immunity" : {
+			"UNDEAD": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"thunderbolt" : {
+		"index" : 77,
+		"anim" : 38,
+		"sounds": {
+			"cast": "LIGHTBLT"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		}
+	},
+	"dispelHelpful" : {
+		"index" : 78,
+		"anim" : 41,
+		"sounds": {
+			"cast": "DISPELL"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0"
+			}
+		},
+
+		"flags" : {
+			"negative": true
+		}
+	},
+	"deathStare" : {
+		"index" : 79,
+		"anim" : 80,
+		"sounds": {
+			"cast": "DEATHSTR"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0"
+			}
+		},
+		"absoluteImmunity" : {
+			"UNDEAD": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"acidBreath" : {
+		"index" : 80,
+		"anim" : 81,
+		"sounds": {
+			"cast": "ACID"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"primarySkill" : {
+						"val" : -3,
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"duration" : "PERMANENT",
+						"valueType" : "ADDITIVE_VALUE"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"acidBreathDamage" : {
+		"index" : 81,
+		"anim" : 81,
+		"sounds": {
+			"cast": "ACID"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"indifferent": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	}
+}

+ 152 - 0
config/spells/adventure.json

@@ -0,0 +1,152 @@
+{
+	"summonBoat" : {
+		"index" : 0,
+		"anim" : -1,
+		"sounds": {
+			"cast": "SUMMBOAT"
+		},
+		"levels" :{
+			"base":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"scuttleBoat" : {
+		"index" : 1,
+		"anim" : -1,
+		"sounds": {
+			"cast": "SCUTBOAT"
+		},
+		"levels" : {
+			"base":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"visions" : {
+		"index" : 2,
+		"anim" : -1,
+		"sounds": {
+			"cast": "VISIONS"
+		},
+		"levels" : {
+			"base":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"viewEarth" : {
+		"index" : 3,
+		"anim" : -1,
+		"sounds": {
+			"cast": "VIEW"
+		},
+		"levels" : {
+			"base":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"disguise" : {
+		"index" : 4,
+		"anim" : -1,
+		"sounds": {
+			"cast": "DISGUISE"
+		},
+		"levels" : {
+			"base":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"viewAir" : {
+		"index" : 5,
+		"anim" : -1,
+		"sounds": {
+			"cast": "VIEW"
+		},
+		"levels" : {
+			"base":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"fly" : {
+		"index" : 6,
+		"anim" : -1,
+		"sounds": {
+			"cast": "FLYSPELL"
+		},
+		"levels" : {
+			"base":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"waterWalk" : {
+		"index" : 7,
+		"anim" : -1,
+		"sounds": {
+			"cast": "WATRWALK"
+		},
+		"levels" : {
+			"base":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"dimensionDoor" : {
+		"index" : 8,
+		"anim" : -1,
+		"sounds": {
+			"cast": "TELPTOUT"
+		},
+		"levels" : {
+			"base":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"townPortal" : {
+		"index" : 9,
+		"anim" : -1,
+		"sounds": {
+			"cast": "TELPTOUT"
+		},
+		"levels" : {
+			"base":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	}
+}

+ 281 - 0
config/spells/offensive.json

@@ -0,0 +1,281 @@
+{
+	"magicArrow" : {
+		"index" : 15,
+		"anim" : 64,
+		"sounds": {
+			"cast": "MAGICBLT"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true}
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"iceBolt" : {
+		"index" : 16,
+		"anim" : 46,
+		"sounds": {
+			"cast": "ICERAY"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true}
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"lightningBolt" : {
+		"index" : 17,
+		"anim" : 38,
+		"sounds": {
+			"cast": "LIGHTBLT"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true}
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"implosion" : {
+		"index" : 18,
+		"anim" : 10,
+		"sounds": {
+			"cast": "DECAY"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true}
+			}
+		},
+
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"chainLightning" : {
+		"index" : 19,
+		"anim" : 38,
+		"sounds": {
+			"cast": "CHAINLTE"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true}
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		}
+	},
+	"frostRing" : {
+		"index" : 20,
+		"anim" : 45,
+		"sounds": {
+			"cast": "FROSTING"
+		},
+		"levels" : {
+			"base":{
+				"range" : "1",
+				"targetModifier":{"smart":false}
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"fireball" : {
+		"index" : 21,
+		"anim" : 53,
+		"sounds": {
+			"cast": "FIREBALL"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0,1",
+				"targetModifier":{"smart":false}
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"inferno" : {
+		"index" : 22,
+		"anim" : 9,
+		"sounds": {
+			"cast": "FIREBLST"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0-2",
+				"targetModifier":{"smart":false}
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"meteorShower" : {
+		"index" : 23,
+		"anim" : 16,
+		"sounds": {
+			"cast": "METEOR"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0,1",
+				"targetModifier":{"smart":false}
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"deathRipple" : {
+		"index" : 24,
+		"targetType" : "CREATURE",
+		"anim" : 8,
+		"sounds": {
+			"cast": "DEATHRIP"
+		},
+		"levels" : {
+			"base":{
+				"range" : "X",
+				"targetModifier":{"smart":false}
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"absoluteImmunity":{
+			"UNDEAD": true,
+		},		
+		"immunity" : {
+			"SIEGE_WEAPON": true,			
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"destroyUndead" : {
+		"index" : 25,
+		"targetType" : "CREATURE",
+		"anim" : 29,
+		"sounds": {
+			"cast": "COLDRING"
+		},
+		"levels" : {
+			"base":{
+				"range" : "X",
+				"targetModifier":{"smart":false}
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"absoluteLimit" : {
+			"UNDEAD": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"armageddon" : {
+		"index" : 26,
+		"targetType" : "CREATURE",
+		"anim" : 12,
+		"sounds": {
+			"cast": "ARMGEDN"
+		},
+		"levels" : {
+			"base":{
+				"range" : "X",
+				"targetModifier":{"smart":false}
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"titanBolt" : {
+		"index" : 57,
+		"anim" : 38,
+		"sounds": {
+			"cast": "LIGHTBLT"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true}
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"offensive": true,
+			"negative": true,
+			"special": true
+		}
+	},
+}

+ 298 - 0
config/spells/other.json

@@ -0,0 +1,298 @@
+{
+	"quicksand" : {
+		"index" : 10,
+		"anim" : -1,
+		"sounds": {
+			"cast": "QUIKSAND"
+		},
+		"levels" : {
+			"base":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"landMine" : {
+		"index" : 11,
+		"anim" : -1,
+		"sounds": {
+			"cast": ""
+		},
+		"levels" : {
+			"base":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"indifferent": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"forceField" : {
+		"index" : 12,
+		"anim" : -1,
+		"sounds": {
+			"cast": "FORCEFLD"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"fireWall" : {
+		"index" : 13,
+		"anim" : -1,
+		"sounds": {
+			"cast": "FIREWALL"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0"
+			}
+		},
+		"flags" : {
+			"damage": true,
+			"indifferent": true
+		},
+		"immunity" : {
+			"DIRECT_DAMAGE_IMMUNITY": true
+		}
+	},
+	"earthquake" : {
+		"index" : 14,
+		"anim" : -1,
+		"sounds": {
+			"cast": "ERTHQUAK"
+		},
+		"levels" : {
+			"base":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	
+	"dispel" : {
+		"index" : 35,
+		"anim" : 41,
+		"sounds": {
+			"cast": "DISPELL"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"cure" : {
+		"index" : 37,
+		"anim" : 39,
+		"sounds": {
+			"cast": "CURE"
+		},
+		"levels" : {
+			"base":{
+				"targetModifier":{"smart":true},
+				"range" : "0"
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"resurrection" : {
+		"index" : 38,
+		"anim" : 79,
+		"sounds": {
+			"cast": "RESURECT"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true}
+			}
+		},
+		"flags" : {
+			"rising": true,
+			"positive": true
+		},
+		"absoluteImmunity" : {
+			"UNDEAD": true,
+			"NON_LIVING": true
+		}
+	},
+	"animateDead" : {
+		"index" : 39,
+		"anim" : 79,
+		"sounds": {
+			"cast": "ANIMDEAD"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true}
+			}
+		},
+		"flags" : {
+			"rising": true,
+			"positive": true
+		},
+		"absoluteLimit" : {
+			"UNDEAD": true
+		}
+	},
+	"sacrifice" : {
+		"index" : 40,
+		"anim" : 79,
+		"sounds": {
+			"cast": "SACRIF1"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true}
+			}
+		},
+		"flags" : {
+			"rising": true,
+			"positive": true
+		},
+		"absoluteImmunity" : {
+			"UNDEAD": true,
+			"NON_LIVING": true
+		}
+	},
+	"teleport" : {
+		"index" : 63,
+		"anim" : -1,
+		"sounds": {
+			"cast": "TELPTOUT"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":false}
+			}
+		},
+		"immunity" : {
+			"SIEGE_WEAPON": true
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"removeObstacle" : {
+		"index" : 64,
+		"anim" : -1,
+		"sounds": {
+			"cast": "REMOVEOB"
+		},
+		"levels" : {
+			"base":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"clone" : {
+		"index" : 65,
+		"anim" : -1,
+		"sounds": {
+			"cast": "CLONE"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true}
+			}
+		},
+		"immunity" : {
+			"SIEGE_WEAPON": true
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"fireElemental" : {
+		"index" : 66,
+		"anim" : -1,
+		"sounds": {
+			"cast": "SUMNELM"
+		},
+		"levels" : {
+			"base":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"earthElemental" : {
+		"index" : 67,
+		"anim" : -1,
+		"sounds": {
+			"cast": "SUMNELM"
+		},
+		"levels" : {
+			"base":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"waterElemental" : {
+		"index" : 68,
+		"anim" : -1,
+		"sounds": {
+			"cast": "SUMNELM"
+		},
+		"levels" : {
+			"base":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	},
+	"airElemental" : {
+		"index" : 69,
+		"anim" : -1,
+		"sounds": {
+			"cast": "SUMNELM"
+		},
+		"levels" : {
+			"base":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"indifferent": true
+		}
+	}
+}

+ 1204 - 0
config/spells/timed.json

@@ -0,0 +1,1204 @@
+{
+	"shield" : {
+		"index" : 27,
+		"anim" : 27,
+		"sounds": {
+			"cast": "SHIELD"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",			
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"generalDamageReduction" : {
+						"type" : "GENERAL_DAMAGE_REDUCTION",
+						"subtype" : 0,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"airShield" : {
+		"index" : 28,
+		"anim" : 2,
+		"sounds": {
+			"cast": "AIRSHELD"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",			
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"generalDamageReduction" : {
+						"type" : "GENERAL_DAMAGE_REDUCTION",
+						"subtype" : 1,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"fireShield" : {
+		"index" : 29,
+		"anim" : 11,
+		"sounds": {
+			"cast": "FIRESHIE"
+		},
+		// It looks that fireshield has two separate sounds
+		//			"soundfile":"FIRESHLD.wav"
+		//		
+		"levels" : {
+			"base":{
+				"range" : "0",			
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"fireShield" : {
+						"type" : "FIRE_SHIELD",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"protectAir" : {
+		"index" : 30,
+		"anim" : 22,
+		"sounds": {
+			"cast": "PROTECTA"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 0,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"protectFire" : {
+		"index" : 31,
+		"anim" : 24,
+		"sounds": {
+			"cast": "PROTECTF"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 1,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"protectWater" : {
+		"index" : 32,
+		"anim" : 23,
+		"sounds": {
+			"cast": "PROTECTW"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",			
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 2,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"protectEarth" : {
+		"index" : 33,
+		"anim" : 26,
+		"sounds": {
+			"cast": "PROTECTE"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"spellDamageReduction" : {
+						"type" : "SPELL_DAMAGE_REDUCTION",
+						"subtype" : 3,
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"antiMagic" : {
+		"index" : 34,
+		"anim" : 5,
+		"sounds": {
+			"cast": "ANTIMAGK"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"levelSpellImmunity" : {
+						"val" : 3,					
+						"type" : "LEVEL_SPELL_IMMUNITY",
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"effects" : {
+					"levelSpellImmunity" : {
+						"val" : 4
+					}
+				}
+			},
+			"expert":{
+				"effects" : {
+					"levelSpellImmunity" : {
+						"val" : 5
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+
+	"magicMirror" : {
+		"index" : 36,
+		"anim" : 3,
+		"sounds": {
+			"cast": "BACKLASH"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"magicMirror" : {
+						"type" : "MAGIC_MIRROR",
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+
+	"bless" : {
+		"index" : 41,
+		"anim" : 36,
+		"sounds": {
+			"cast": "BLESS"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",			
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"alwaysMaximumDamage" : {
+						"val" : 0,
+						"type" : "ALWAYS_MAXIMUM_DAMAGE",
+						"subtype" : -1,
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"counters" : {
+			"spell.curse": true
+		},
+		"absoluteImmunity" : {
+			"UNDEAD": true
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"curse" : {
+		"index" : 42,
+		"anim" : 40,
+		"sounds": {
+			"cast": "CURSE"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",			
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"alwaysMinimumDamage" : {
+						"addInfo" : 0,
+						"val" : 0,
+						"type" : "ALWAYS_MINIMUM_DAMAGE",
+						"subtype" : -1,
+						"valueType" : "INDEPENDENT_MAX",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"expert":{
+				"range" : "X"
+			}
+		},
+		"counters" : {
+			"spell.bless": true
+		},
+		"absoluteImmunity" : {
+			"UNDEAD": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"bloodlust" : {
+		"index" : 43,
+		"anim" : 4,
+		"sounds": {
+			"cast": "BLOODLUS"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",			
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"primarySkill" : {
+						"val" : 3,					
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"effectRange" : "ONLY_MELEE_FIGHT",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"effects" : {
+					"primarySkill" : {
+						"val" : 6
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"primarySkill" : {
+						"val" : 6
+					}
+				}
+			}
+		},
+		"counters" : {
+			"spell.weakness": true
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"precision" : {
+		"index" : 44,
+		"anim" : 25,
+		"sounds": {
+			"cast": "PRECISON"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"primarySkill" : {
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"val" : 3,
+						"effectRange" : "ONLY_DISTANCE_FIGHT",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"effects" : {
+					"primarySkill" : {
+						"val" : 6
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"primarySkill" : {
+						"val" : 6
+					}
+				}
+			}
+		},
+		"absoluteLimit" : {
+			"SHOOTER": true
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"weakness" : {
+		"index" : 45,
+		"anim" : 56,
+		"sounds": {
+			"cast": "WEAKNESS"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",			
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"primarySkill" : {
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"val" : -3,						
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+
+			"advanced":{
+				"effects" : {
+					"primarySkill" : {
+						"val" : -6
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"primarySkill" : {
+						"val" : -6
+					}
+				}
+			}
+		},
+		"counters" : {
+			"spell.bloodlust": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"stoneSkin" : {
+		"index" : 46,
+		"anim" : 54,
+		"sounds": {
+			"cast": "TUFFSKIN"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",			
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"primarySkill" : {
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"val" : 3,						
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"effects" : {
+					"primarySkill" : {
+						"val" : 6
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"primarySkill" : {
+						"val" : 6
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"disruptingRay" : {
+		"index" : 47,
+		"targetType" : "CREATURE", //fix, dont remove
+		"anim" : 14,
+		"sounds": {
+			"cast": "DISRUPTR"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"primarySkill" : {
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"val" : -3,						
+						"valueType" : "ADDITIVE_VALUE",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"effects" : {
+					"primarySkill" : {
+						"val" : -4
+					}
+				}
+			},
+			"expert":{
+				"effects" : {
+					"primarySkill" : {
+						"val" : -5
+					}
+				}
+			}
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"prayer" : {
+		"index" : 48,
+		"anim" : 0,
+		"sounds": {
+			"cast": "PRAYER"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",			
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"attack" : {
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.attack",
+						"val" : 2,						
+						"duration" : "N_TURNS"
+					},
+					"defence" : {
+						"type" : "PRIMARY_SKILL",
+						"subtype" : "primSkill.defence",
+						"val" : 2,						
+						"duration" : "N_TURNS"
+					},
+					"stacksSpeed" : {
+						"addInfo" : 0,
+						"type" : "STACKS_SPEED",
+						"val" : 2,						
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"effects" : {
+					"attack" : {
+						"val" : 4
+					},
+					"defence" : {
+						"val" : 4
+					},
+					"stacksSpeed" : {
+						"val" : 4
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"attack" : {
+						"val" : 4
+					},
+					"defence" : {
+						"val" : 4
+					},
+					"stacksSpeed" : {
+						"val" : 4
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"mirth" : {
+		"index" : 49,
+		"anim" : 20,
+		"sounds": {
+			"cast": "MIRTH"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",			
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"morale" : {
+						"type" : "MORALE",
+						"val" : 1,						
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"effects" : {
+					"morale" : {
+						"val" : 2
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"morale" : {
+						"val" : 2
+					}
+				}
+			}
+		},
+		"counters" : {
+			"spell.sorrow":true
+		},
+		"absoluteImmunity":{
+			"UNDEAD": true,		
+		},
+		"immunity" : {
+			"MIND_IMMUNITY": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"sorrow" : {
+		"index" : 50,
+		"anim" : 30,
+		"sounds": {
+			"cast": "SORROW"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",			
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"morale" : {
+						"type" : "MORALE",
+						"val" : -1,						
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"effects" : {
+					"morale" : {
+						"val" : -2
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"morale" : {
+						"val" : -2
+					}
+				}
+			}
+		},
+		"counters" : {
+			"spell.mirth":true
+		},
+		"absoluteImmunity":{
+			"UNDEAD": true,		
+		},
+		"immunity" : {
+			"MIND_IMMUNITY": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"fortune" : {
+		"index" : 51,
+		"anim" : 18,
+		"sounds": {
+			"cast": "FORTUNE"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",			
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"luck" : {
+						"type" : "LUCK",
+						"val" : 1,						
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"effects" : {
+					"luck" : {
+						"val" : 2
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"luck" : {
+						"val" : 2
+					}
+				}
+			}
+		},
+		"counters" : {
+			"spell.misfortune": true
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"misfortune" : {
+		"index" : 52,
+		"anim" : 48,
+		"sounds": {
+			"cast": "MISFORT"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",			
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"luck" : {
+						"type" : "LUCK",
+						"val" : -1,						
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"effects" : {
+					"luck" : {
+						"val" : -2
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"luck" : {
+						"val" : -2
+					}
+				}
+			}
+		},
+		"counters" : {
+			"spell.fortune":true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"haste" : {
+		"index" : 53,
+		"anim" : 31,
+		"sounds": {
+			"cast": "HASTE"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",			
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"stacksSpeed" : {
+						"addInfo" : 0,
+						"type" : "STACKS_SPEED",
+						"val" : 3,						
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"effects" : {
+					"stacksSpeed" : {
+						"val" : 5
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"stacksSpeed" : {
+						"val" : 5
+					}
+				}
+			}
+		},
+		"counters" : {
+			"spell.slow": true
+		},
+		"immunity" : {
+			"SIEGE_WEAPON": true
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"slow" : {
+		"index" : 54,
+		"anim" : 19,
+		"sounds": {
+			"cast": "MUCKMIRE"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",			
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"stacksSpeed" : {
+						"addInfo" : 0,
+						"type" : "STACKS_SPEED",
+						"val" : -25,						
+						"valueType" : "PERCENT_TO_ALL",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"effects" : {
+					"stacksSpeed" : {
+						"val" : -50
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"stacksSpeed" : {
+						"val" : -50
+					}
+				}
+			}
+		},
+
+		"counters" : {
+			"spell.haste":true
+		},
+		"immunity" : {
+			"SIEGE_WEAPON": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"slayer" : {
+		"index" : 55,
+		"anim" : 28,
+		"sounds": {
+			"cast": "SLAYER"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"slayer" : {
+						"type" : "SLAYER",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"none":{
+				"effects" : {
+					"slayer" : {
+						"val" : 0
+					}
+				}
+			},
+			"basic":{
+				"effects" : {
+					"slayer" : {
+						"val" : 1
+					}
+				}
+			},
+			"advanced":{
+				"effects" : {
+					"slayer" : {
+						"val" : 2
+					}
+				}
+			},
+			"expert":{
+				"effects" : {
+					"slayer" : {
+						"val" : 3
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"frenzy" : {
+		"index" : 56,
+		"anim" : 17,
+		"sounds": {
+			"cast": "FRENZY"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"inFrenzy" : {
+						"type" : "IN_FRENZY",
+						"val" : 100,						
+						"duration" : "STACK_GETS_TURN"
+					}
+				}
+			},
+			"advanced":{
+				"effects" : {
+					"inFrenzy" : {
+						"val" : 150
+					}
+				}
+			},
+			"expert":{
+				"effects" : {
+					"inFrenzy" : {
+						"val" : 200
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+
+	"counterstrike" : {
+		"index" : 58,
+		"anim" : 7,
+		"sounds": {
+			"cast": "CNTRSTRK"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",			
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"additionalRetaliation" : {
+						"type" : "ADDITIONAL_RETALIATION",
+						"val" : 1,						
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"advanced":{
+				"effects" : {
+					"additionalRetaliation" : {
+						"val" : 2
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"additionalRetaliation" : {
+						"val" : 2
+					}
+				}
+			}
+		},
+		"flags" : {
+			"positive": true
+		}
+	},
+	"berserk" : {
+		"index" : 59,
+		"anim" : 35,
+		"sounds": {
+			"cast": "BERSERK"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"attacksNearestCreature" : {
+						"type" : "ATTACKS_NEAREST_CREATURE",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"none":{
+				"effects" : {
+					"attacksNearestCreature" : {
+						"val" : 0
+					}
+				}
+			},
+			"basic":{
+				"effects" : {
+					"attacksNearestCreature" : {
+						"val" : 1
+					}
+				}
+			},
+			"advanced":{
+				"range" : "0-1",
+				"effects" : {
+					"attacksNearestCreature" : {
+						"val" : 2
+					}
+				}
+			},
+			"expert":{
+				"range" : "0-2",
+				"effects" : {
+					"attacksNearestCreature" : {
+						"val" : 3
+					}
+				}
+			}
+		},
+
+		"immunity" : {
+			"MIND_IMMUNITY": true,
+			"UNDEAD": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"hypnotize" : {
+		"index" : 60,
+		"anim" : 21,
+		"sounds": {
+			"cast": "HYPNOTIZ"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"hypnotized" : {
+						"type" : "HYPNOTIZED",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"none":{
+				"effects" : {
+					"hypnotized" : {
+						"val" : 0
+					}
+				}
+			},
+			"basic":{
+				"effects" : {
+					"hypnotized" : {
+						"val" : 1
+					}
+				}
+			},
+			"advanced":{
+				"effects" : {
+					"hypnotized" : {
+						"val" : 2
+					}
+				}
+			},
+			"expert":{
+				"effects" : {
+					"hypnotized" : {
+						"val" : 3
+					}
+				}
+			}
+		},
+
+		"immunity" : {
+			"MIND_IMMUNITY": true,
+			"UNDEAD": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"forgetfulness" : {
+		"index" : 61,
+		"targetType" : "CREATURE", 
+		"anim" : 42,
+		"sounds": {
+			"cast": "FORGET"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",			
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"forgetful" : {
+						"type" : "FORGETFULL",
+						"duration" : "N_TURNS"
+					}
+				}
+			},
+			"none":{
+				"effects" : {
+					"forgetful" : {
+						"val" : 0
+					}
+				}
+			},
+			"basic":{
+				"effects" : {
+					"forgetful" : {
+						"val" : 1
+					}
+				}
+			},
+			"advanced":{
+				"effects" : {
+					"forgetful" : {
+						"val" : 2
+					}
+				}
+			},
+			"expert":{
+				"range" : "X",
+				"effects" : {
+					"forgetful" : {
+						"val" : 3
+					}
+				}
+			}
+		},
+		"absoluteLimit" : {
+			"SHOOTER": true
+		},
+		"immunity" : {
+			"MIND_IMMUNITY": true,
+			"UNDEAD": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	},
+	"blind" : {
+		"index" : 62,
+		"anim" : 6,
+		"sounds": {
+			"cast": "BLIND"
+		},
+		"levels" : {
+			"base":{
+				"range" : "0",
+				"targetModifier":{"smart":true},
+				"effects" : {
+					"notActive" : {
+						"val" : 0,
+						"type" : "NOT_ACTIVE",
+						"subtype" : 62,
+						"duration" : [
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"generalAttackReduction" : {
+						"type" : "GENERAL_ATTACK_REDUCTION",
+						"duration" : [
+							"UNITL_BEING_ATTACKED",
+							"N_TURNS"
+						]
+					},
+					"noRetaliation" : {
+						"val" : 0,
+						"type" : "NO_RETALIATION",
+						"duration" : "UNITL_BEING_ATTACKED"
+					}
+				}
+			}
+		},
+
+		"immunity" : {
+			"MIND_IMMUNITY": true,
+			"UNDEAD": true,
+			"NON_LIVING": true
+		},
+		"flags" : {
+			"negative": true
+		}
+	}
+}

+ 2 - 0
debian/control

@@ -5,6 +5,8 @@ Maintainer: Ivan Savenko <[email protected]>
 Build-Depends: debhelper (>= 8), cmake, libsdl-image1.2-dev, libsdl-ttf2.0-dev, libsdl-mixer1.2-dev (>= 1.2.8), zlib1g-dev, libavformat-dev, libswscale-dev, libboost-dev (>=1.48), libboost-program-options-dev (>=1.48), libboost-filesystem-dev (>=1.48), libboost-system-dev (>=1.48), libboost-locale-dev (>=1.48), libboost-thread-dev (>=1.48), qtbase5-dev
 Standards-Version: 3.9.1
 Homepage: http://vcmi.eu
+Vcs-Git: git://github.com/vcmi/vcmi.git
+Vcs-Browser: https://github.com/vcmi/vcmi
 
 Package: vcmi
 Architecture: any

+ 0 - 2
debian/docs

@@ -1,2 +0,0 @@
-NEWS
-README

+ 170 - 166
launcher/VCMI_launcher.vcxproj

@@ -1,167 +1,171 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup Label="ProjectConfigurations">
-    <ProjectConfiguration Include="Debug|Win32">
-      <Configuration>Debug</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="RD|Win32">
-      <Configuration>RD</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-  </ItemGroup>
-  <PropertyGroup Label="Globals">
-    <ProjectGuid>{5B6946C8-A24F-4223-8415-5E16A238ACED}</ProjectGuid>
-    <Keyword>Win32Proj</Keyword>
-    <RootNamespace>VCMI_launcher</RootNamespace>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <UseDebugLibraries>true</UseDebugLibraries>
-    <PlatformToolset>v120_xp</PlatformToolset>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <UseDebugLibraries>false</UseDebugLibraries>
-    <PlatformToolset>v120_xp</PlatformToolset>
-    <WholeProgramOptimization>true</WholeProgramOptimization>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
-  <ImportGroup Label="ExtensionSettings">
-  </ImportGroup>
-  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-    <Import Project="..\VCMI_global_debug.props" />
-    <Import Project="..\VCMI_global.props" />
-  </ImportGroup>
-  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-    <Import Project="..\VCMI_global_release.props" />
-    <Import Project="..\VCMI_global.props" />
-  </ImportGroup>
-  <PropertyGroup Label="UserMacros" />
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <IncludePath>.\GeneratedFiles;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QtDir)\include;$(IncludePath)</IncludePath>
-    <LibraryPath>$(QtDir)\lib;$(LibraryPath)</LibraryPath>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
-    <LinkIncremental>
-    </LinkIncremental>
-    <IncludePath>.\GeneratedFiles;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QtDir)\include;$(IncludePath)</IncludePath>
-    <CustomBuildBeforeTargets>
-    </CustomBuildBeforeTargets>
-    <LibraryPath>$(QtDir)\lib;$(LibraryPath)</LibraryPath>
-  </PropertyGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <ClCompile>
-      <PrecompiledHeader>Use</PrecompiledHeader>
-      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
-      <AdditionalOptions>/Zm150 %(AdditionalOptions)</AdditionalOptions>
-    </ClCompile>
-    <Link />
-    <Link>
-      <AdditionalDependencies>VCMI_lib.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;Qt5Networkd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
-    </Link>
-  </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
-    <ClCompile>
-      <PrecompiledHeader>Use</PrecompiledHeader>
-      <Optimization>Full</Optimization>
-      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
-      <AdditionalOptions>/Zm150 %(AdditionalOptions)</AdditionalOptions>
-    </ClCompile>
-    <Link>
-      <AdditionalDependencies>VCMI_lib.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5Network.lib;%(AdditionalDependencies)</AdditionalDependencies>
-    </Link>
-    <CustomBuildStep>
-      <Command>
-      </Command>
-    </CustomBuildStep>
-    <CustomBuildStep>
-      <Message>
-      </Message>
-    </CustomBuildStep>
-    <CustomBuildStep>
-      <Outputs>
-      </Outputs>
-    </CustomBuildStep>
-  </ItemDefinitionGroup>
-  <ItemGroup>
-    <ClCompile Include="GeneratedFiles\*.cpp">
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-      </PrecompiledHeader>
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
-      </PrecompiledHeader>
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
-      </PrecompiledHeader>
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='RD|x64'">
-      </PrecompiledHeader>
-    </ClCompile>
-    <ClCompile Include="GeneratedFiles\Debug\*.cpp">
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='RD|x64'">true</ExcludedFromBuild>
-    </ClCompile>
-    <ClCompile Include="GeneratedFiles\RD\*.cpp">
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
-    </ClCompile>
-    <ClCompile Include=".\StdInc.cpp">
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">Create</PrecompiledHeader>
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='RD|x64'">Create</PrecompiledHeader>
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
-    </ClCompile>
-    <ClCompile Include=".\**\*.cpp" Exclude=".\**\moc_*.cpp;.\StdInc.cpp" />
-  </ItemGroup>
-  <ItemGroup>
-    <ClInclude Include=".\**\*.h" Exclude=".\**\*_moc.h" />
-    <ClInclude Include="GeneratedFiles\*.h" />
-    <CustomBuild Include=".\**\*_moc.h" Exclude=".\**\ui_*.h">
-      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
-      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing %(Filename) file...</Message>
-      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
-      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fStdInc.h" "-f..\..\%(RecursiveDir)%(Filename).h"  -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg"</Command>
-      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
-      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Filename) file...</Message>
-      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
-      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fStdInc.h" "-f..\..\%(RecursiveDir)%(Filename).h"  -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg"</Command>
-      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
-      <Message Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">Moc%27ing some file...</Message>
-      <Outputs Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
-      <Command Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fStdInc.h" "-f..\..\%(RecursiveDir)%(Filename).h"  -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg"</Command>
-      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='RD|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
-      <Message Condition="'$(Configuration)|$(Platform)'=='RD|x64'">Moc%27ing %(Filename) file...</Message>
-      <Outputs Condition="'$(Configuration)|$(Platform)'=='RD|x64'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
-      <Command Condition="'$(Configuration)|$(Platform)'=='RD|x64'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fStdInc.h" "-f..\..\%(RecursiveDir)%(Filename).h"  -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg"</Command>
-    </CustomBuild>
-  </ItemGroup>
-  <ItemGroup>
-    <CustomBuild Include=".\**\*.ui">
-      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
-      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Uic%27ing %(Identity)...</Message>
-      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
-      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
-      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
-      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Uic%27ing %(Identity)...</Message>
-      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\GeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
-      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
-      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
-      <Message Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">Uic%27ing %(Identity)...</Message>
-      <Outputs Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">.\GeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
-      <Command Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">"$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
-      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='RD|x64'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
-      <Message Condition="'$(Configuration)|$(Platform)'=='RD|x64'">Uic%27ing %(Identity)...</Message>
-      <Outputs Condition="'$(Configuration)|$(Platform)'=='RD|x64'">.\GeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
-      <Command Condition="'$(Configuration)|$(Platform)'=='RD|x64'">"$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
-    </CustomBuild>
-  </ItemGroup>
-  <ItemGroup>
-    <ResourceCompile Include="VCMI_launcher.rc" />
-  </ItemGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
-  <ImportGroup Label="ExtensionTargets">
-  </ImportGroup>
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="RD|Win32">
+      <Configuration>RD</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{5B6946C8-A24F-4223-8415-5E16A238ACED}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>VCMI_launcher</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v110</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v110</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\VCMI_global_debug.props" />
+    <Import Project="..\VCMI_global.props" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\VCMI_global_release.props" />
+    <Import Project="..\VCMI_global.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <IncludePath>.\GeneratedFiles;D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtGui;D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtCore;D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtANGLE;D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtWidgets;D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include;$(IncludePath)</IncludePath>
+    <LibraryPath>D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\lib;$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
+    <LinkIncremental>
+    </LinkIncremental>
+    <IncludePath>.\GeneratedFiles;D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtGui;D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtCore;D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtANGLE;D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtWidgets;D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include;$(IncludePath)</IncludePath>
+    <CustomBuildBeforeTargets>
+    </CustomBuildBeforeTargets>
+    <LibraryPath>D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\lib;$(LibraryPath)</LibraryPath>
+    <OutDir>..</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
+      <AdditionalOptions>/Zm150 %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link />
+    <Link>
+      <AdditionalDependencies>VCMI_lib.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;Qt5Networkd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\..\libs;..</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <Optimization>Full</Optimization>
+      <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
+      <AdditionalOptions>/Zm150 %(AdditionalOptions)</AdditionalOptions>
+      <PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>VCMI_lib.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5Network.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\..\libs;..</AdditionalLibraryDirectories>
+    </Link>
+    <CustomBuildStep>
+      <Command>
+      </Command>
+    </CustomBuildStep>
+    <CustomBuildStep>
+      <Message>
+      </Message>
+    </CustomBuildStep>
+    <CustomBuildStep>
+      <Outputs>
+      </Outputs>
+    </CustomBuildStep>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="GeneratedFiles\*.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+      </PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+      </PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
+      </PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='RD|x64'">
+      </PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\Debug\*.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='RD|x64'">true</ExcludedFromBuild>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\RD\*.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+    </ClCompile>
+    <ClCompile Include=".\StdInc.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='RD|x64'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include=".\**\*.cpp" Exclude=".\**\moc_*.cpp;.\StdInc.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include=".\**\*.h" Exclude=".\**\*_moc.h" />
+    <ClInclude Include="GeneratedFiles\*.h" />
+    <CustomBuild Include=".\**\*_moc.h" Exclude=".\**\ui_*.h">
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Calling D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\moc.exe for %(Filename) file...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fStdInc.h" "-f..\..\%(RecursiveDir)%(Filename).h"  -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtCore" "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtGui" "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtWidgets" "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtSvg"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Calling D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\moc.exe for %(Filename) file...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fStdInc.h" "-f..\..\%(RecursiveDir)%(Filename).h"  -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtCore" "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtGui" "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtWidgets" "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtSvg"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">Calling D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\moc.exe for %(Filename) file...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">"D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fStdInc.h" "-f..\..\%(RecursiveDir)%(Filename).h"  -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtCore" "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtGui" "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtWidgets" "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtSvg"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='RD|x64'">D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='RD|x64'">Calling D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\moc.exe for %(Filename) file...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='RD|x64'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='RD|x64'">"D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fStdInc.h" "-f..\..\%(RecursiveDir)%(Filename).h"  -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtCore" "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtGui" "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtWidgets" "-ID:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\include\QtSvg"</Command>
+    </CustomBuild>
+  </ItemGroup>
+  <ItemGroup>
+    <CustomBuild Include=".\**\*.ui">
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Uic%27ing %(Identity)...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Uic%27ing %(Identity)...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\GeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">Uic%27ing %(Identity)...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">.\GeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">"D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='RD|x64'">D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='RD|x64'">Uic%27ing %(Identity)...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='RD|x64'">.\GeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='RD|x64'">"D:\Programowanie\Biblioteki\QT\5.1.1\msvc2012\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
+    </CustomBuild>
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="VCMI_launcher.rc" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
 </Project>

+ 2 - 36
lib/BattleState.cpp

@@ -87,7 +87,7 @@ std::pair< std::vector<BattleHex>, int > BattleInfo::getPath(BattleHex start, Ba
 }
 
 ui32 BattleInfo::calculateDmg( const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero,
-	bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg )
+	bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg, CRandomGenerator & rand )
 {
 	TDmgRange range = calculateDmgRange(attacker, defender, shooting, charge, lucky, unlucky, deathBlow, ballistaDoubleDmg);
 
@@ -97,7 +97,7 @@ ui32 BattleInfo::calculateDmg( const CStack* attacker, const CStack* defender, c
 		int howManyToAv = std::min<ui32>(10, attacker->count);
 		for (int g=0; g<howManyToAv; ++g)
 		{
-			valuesToAverage[g] = range.first  +  rand() % (range.second - range.first + 1);
+			valuesToAverage[g] = rand.nextInt(range.first, range.second);
 		}
 
 		return std::accumulate(valuesToAverage, valuesToAverage + howManyToAv, 0) / howManyToAv;
@@ -723,40 +723,6 @@ const CGHeroInstance * BattleInfo::getHero( PlayerColor player ) const
 	return nullptr;
 }
 
-std::vector<ui32> BattleInfo::calculateResistedStacks(const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::vector<const CStack*> & affectedCreatures, PlayerColor casterSideOwner, ECastingMode::ECastingMode mode, int usedSpellPower, int spellLevel) const
-{
-	std::vector<ui32> ret;
-	for(auto & affectedCreature : affectedCreatures)
-	{
-		if(battleIsImmune(caster, sp, mode, (affectedCreature)->position) != ESpellCastProblem::OK) //FIXME: immune stacks should not display resisted animation
-		{
-			ret.push_back((affectedCreature)->ID);
-			continue;
-		}
-
-		//non-negative spells should always succeed, unless immune
-		if(!sp->isNegative())// && (*it)->owner == casterSideOwner)
-			continue;
-
-		/*
-		const CGHeroInstance * bonusHero; //hero we should take bonuses from
-		if((*it)->owner == casterSideOwner)
-			bonusHero = caster;
-		else
-			bonusHero = hero2;*/
-
-		int prob = (affectedCreature)->magicResistance(); //probability of resistance in %
-
-		if(prob > 100) prob = 100;
-
-		if(rand()%100 < prob) //immunity from resistance
-			ret.push_back((affectedCreature)->ID);
-
-	}
-
-	return ret;
-}
-
 PlayerColor BattleInfo::theOtherPlayer(PlayerColor player) const
 {
 	return sides[!whatSide(player)].color;

+ 1 - 3
lib/BattleState.h

@@ -129,7 +129,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb
 	shared_ptr<CObstacleInstance> getObstacleOnTile(BattleHex tile) const;
 	std::set<BattleHex> getStoppers(bool whichSidePerspective) const;
 
-	ui32 calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg); //charge - number of hexes travelled before attack (for champion's jousting)
+	ui32 calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg, CRandomGenerator & rand); //charge - number of hexes travelled before attack (for champion's jousting)
 	void calculateCasualties(std::map<ui32,si32> *casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount)
 
 	//void getPotentiallyAttackableHexes(AttackableTiles &at, const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos); //hexes around target that could be attacked in melee
@@ -144,8 +144,6 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb
 
 	const CGHeroInstance * getHero(PlayerColor player) const; //returns fighting hero that belongs to given player
 
-	std::vector<ui32> calculateResistedStacks(const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::vector<const CStack*> & affectedCreatures, PlayerColor casterSideOwner, ECastingMode::ECastingMode mode, int usedSpellPower, int spellLevel) const;
-
 	const CStack * battleGetStack(BattleHex pos, bool onlyAlive); //returns stack at given tile
 	const CGHeroInstance * battleGetOwner(const CStack * stack) const; //returns hero that owns given stack; nullptr if none
 	void localInit();

+ 168 - 134
lib/CBattleCallback.cpp

@@ -177,29 +177,36 @@ bool CBattleInfoEssentials::battleHasNativeStack(ui8 side) const
 	return false;
 }
 
-TStacks CBattleInfoEssentials::battleGetAllStacks(bool includeTurrets /*= false*/) const /*returns all stacks, alive or dead or undead or mechanical :) */
+TStacks CBattleInfoEssentials::battleGetAllStacks(bool includeTurrets /*= false*/) const
+{
+	return battleGetStacksIf([](const CStack * s){return true;},includeTurrets);
+}
+
+TStacks CBattleInfoEssentials::battleGetStacksIf(TStackFilter predicate, bool includeTurrets /*= false*/) const
 {
 	TStacks ret;
 	RETURN_IF_NOT_BATTLE(ret);
-	boost::copy(getBattle()->stacks, std::back_inserter(ret));
-	if(!includeTurrets)
-		vstd::erase_if(ret, [](const CStack *stack) { return stack->type->idNumber == CreatureID::ARROW_TOWERS; });
-
+	
+	vstd::copy_if(getBattle()->stacks, std::back_inserter(ret), [=](const CStack * s){
+		return predicate(s) && (includeTurrets || !(s->type->idNumber == CreatureID::ARROW_TOWERS));
+	});
+	
 	return ret;
 }
 
+
 TStacks CBattleInfoEssentials::battleAliveStacks() const
 {
-	TStacks ret;
-	vstd::copy_if(battleGetAllStacks(), std::back_inserter(ret), [](const CStack *s){ return s->alive(); });
-	return ret;
+	return battleGetStacksIf([](const CStack * s){
+		return s->alive();
+	});
 }
 
 TStacks CBattleInfoEssentials::battleAliveStacks(ui8 side) const
 {
-	TStacks ret;
-	vstd::copy_if(battleGetAllStacks(), std::back_inserter(ret), [=](const CStack *s){ return s->alive()  &&  s->attackerOwned == !side; });
-	return ret;
+	return battleGetStacksIf([=](const CStack * s){
+		return s->alive() && s->attackerOwned == !side;
+	});
 }
 
 int CBattleInfoEssentials::battleGetMoatDmg() const
@@ -1562,87 +1569,33 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
 {
 	RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID);
 
-	// Get stack at destination hex -> subject of our spell.
-	const CStack * subject = battleGetStackByPos(dest, !spell->isRisingSpell()); //only alive if not rising spell
-
-	if(subject)
+	// Get all stacks at destination hex -> subject of our spell. only alive if not rising spell
+	TStacks stacks = battleGetStacksIf([=](const CStack * s){
+		return s->coversPos(dest) && (spell->isRisingSpell() || s->alive());
+	});
+	
+	if(!stacks.empty())
 	{
-		if (spell->isPositive() && subject->hasBonusOfType(Bonus::RECEPTIVE)) //accept all positive spells
-			return ESpellCastProblem::OK;
-
-		if (spell->isImmuneBy(subject)) //TODO: move all logic to spellhandler
-			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
-
-		switch (spell->id) //TODO: more general logic for new spells?
+		bool allImmune = true;
+		
+		ESpellCastProblem::ESpellCastProblem problem;		
+		
+		for(auto s : stacks)
 		{
-		case SpellID::CLONE:
+			ESpellCastProblem::ESpellCastProblem res = battleStackIsImmune(caster,spell,mode,s);
+			
+			if(res == ESpellCastProblem::OK)
 			{
-				//can't clone already cloned creature
-				if (vstd::contains(subject->state, EBattleStackState::CLONED))
-					return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
-				//TODO: how about stacks casting Clone?
-				//currently Clone casted by stack is assumed Expert level
-				ui8 schoolLevel;
-				if (caster)
-				{
-					schoolLevel = caster->getSpellSchoolLevel(spell);
-				}
-				else
-				{
-					schoolLevel = 3;
-				}
-
-				if (schoolLevel < 3)
-				{
-					int maxLevel = (std::max(schoolLevel, (ui8)1) + 4);
-					int creLevel = subject->getCreature()->level;
-					if (maxLevel < creLevel) //tier 1-5 for basic, 1-6 for advanced, any level for expert
-						return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
-				}
+				allImmune = false;
 			}
-			break;
-		case SpellID::DISPEL_HELPFUL_SPELLS:
-			{
-				TBonusListPtr spellBon = subject->getSpellBonuses();
-				bool hasPositiveSpell = false;
-				for(const Bonus * b : *spellBon)
-				{
-					if(SpellID(b->sid).toSpell()->isPositive())
-					{
-						hasPositiveSpell = true;
-						break;
-					}
-				}
-				if(!hasPositiveSpell)
-				{
-					return ESpellCastProblem::NO_SPELLS_TO_DISPEL;
-				}
-			}
-			break;
-		}
-
-		if (spell->isRisingSpell())
-		{
-			if(subject->count >= subject->baseAmount)
-				return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
-			
-			if (caster) //FIXME: Archangels can cast immune stack
+			else
 			{
-				auto maxHealth = calculateHealedHP (caster, spell, subject);
-				if (maxHealth < subject->MaxHealth()) //must be able to rise at least one full creature
-					return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+				problem = res;
 			}
 		}
-		else if(spell->id == SpellID::HYPNOTIZE && caster) //do not resist hypnotize casted after attack, for example
-		{
-			//TODO: what with other creatures casting hypnotize, Faerie Dragons style?
-			ui64 subjectHealth = (subject->count - 1) * subject->MaxHealth() + subject->firstHPleft;
-			//apply 'damage' bonus for hypnotize, including hero specialty
-			ui64 maxHealth = calculateSpellBonus (caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER)
-				* spell->power + spell->getPower(caster->getSpellSchoolLevel(spell)), spell, caster, subject);
-			if (subjectHealth > maxHealth)
-				return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
-		}
+		
+		if(allImmune)
+			return problem;
 	}
 	else //no target stack on this tile
 	{
@@ -1666,6 +1619,88 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
 	return ESpellCastProblem::OK;
 }
 
+ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleStackIsImmune(const CGHeroInstance * caster, const CSpell * spell, ECastingMode::ECastingMode mode, const CStack * subject) const
+{
+	if (spell->isPositive() && subject->hasBonusOfType(Bonus::RECEPTIVE)) //accept all positive spells
+		return ESpellCastProblem::OK;
+
+	if (spell->isImmuneBy(subject)) //TODO: move all logic to spellhandler
+		return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+
+	switch (spell->id) //TODO: more general logic for new spells?
+	{
+	case SpellID::CLONE:
+		{
+			//can't clone already cloned creature
+			if (vstd::contains(subject->state, EBattleStackState::CLONED))
+				return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+			//TODO: how about stacks casting Clone?
+			//currently Clone casted by stack is assumed Expert level
+			ui8 schoolLevel;
+			if (caster)
+			{
+				schoolLevel = caster->getSpellSchoolLevel(spell);
+			}
+			else
+			{
+				schoolLevel = 3;
+			}
+
+			if (schoolLevel < 3)
+			{
+				int maxLevel = (std::max(schoolLevel, (ui8)1) + 4);
+				int creLevel = subject->getCreature()->level;
+				if (maxLevel < creLevel) //tier 1-5 for basic, 1-6 for advanced, any level for expert
+					return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+			}
+		}
+		break;
+	case SpellID::DISPEL_HELPFUL_SPELLS:
+		{
+			TBonusListPtr spellBon = subject->getSpellBonuses();
+			bool hasPositiveSpell = false;
+			for(const Bonus * b : *spellBon)
+			{
+				if(SpellID(b->sid).toSpell()->isPositive())
+				{
+					hasPositiveSpell = true;
+					break;
+				}
+			}
+			if(!hasPositiveSpell)
+			{
+				return ESpellCastProblem::NO_SPELLS_TO_DISPEL;
+			}
+		}
+		break;
+	}
+
+	if (spell->isRisingSpell())
+	{
+		if(subject->count >= subject->baseAmount)
+			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+		
+		if (caster) //FIXME: Archangels can cast immune stack
+		{
+			auto maxHealth = calculateHealedHP (caster, spell, subject);
+			if (maxHealth < subject->MaxHealth()) //must be able to rise at least one full creature
+				return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+		}
+	}
+	else if(spell->id == SpellID::HYPNOTIZE && caster) //do not resist hypnotize casted after attack, for example
+	{
+		//TODO: what with other creatures casting hypnotize, Faerie Dragons style?
+		ui64 subjectHealth = (subject->count - 1) * subject->MaxHealth() + subject->firstHPleft;
+		//apply 'damage' bonus for hypnotize, including hero specialty
+		ui64 maxHealth = calculateSpellBonus (caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER)
+			* spell->power + spell->getPower(caster->getSpellSchoolLevel(spell)), spell, caster, subject);
+		if (subjectHealth > maxHealth)
+			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
+	}	
+		
+	return ESpellCastProblem::OK;
+}
+
 ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell( PlayerColor player, const CSpell * spell, ECastingMode::ECastingMode mode ) const
 {
 	RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID);
@@ -1708,7 +1743,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
 		auto stacks = spell->isNegative() ? battleAliveStacks(!side) : battleAliveStacks();
 		for(auto stack : stacks)
 		{
-			if(!battleIsImmune(castingHero, spell, mode, stack->position))
+			if( ESpellCastProblem::OK == battleStackIsImmune(castingHero, spell, mode, stack))
 			{
 				allStacksImmune = false;
 				break;
@@ -1750,7 +1785,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
 			bool targetExists = false;
 			for(const CStack * stack : battleGetAllStacks()) //dead stacks will be immune anyway
 			{
-				bool immune = battleIsImmune(caster, spell, mode, stack->position) != ESpellCastProblem::OK;
+				bool immune =  ESpellCastProblem::OK != battleStackIsImmune(caster, spell, mode, stack);
 				bool casterStack = stack->owner == caster->getOwner();
 				
 				if(!immune)
@@ -1805,7 +1840,7 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetPossibleTargets(PlayerColor
 			
 			for(const CStack * stack : battleAliveStacks())
 			{
-				bool immune = battleIsImmune(caster, spell, mode, stack->position) != ESpellCastProblem::OK;
+				bool immune = ESpellCastProblem::OK != battleStackIsImmune(caster, spell, mode, stack);
 				bool casterStack = stack->owner == caster->getOwner();
 				
 				if(!immune)
@@ -2018,28 +2053,14 @@ ui32 CBattleInfoCallback::calculateSpellDmg( const CSpell * sp, const CGHeroInst
 
 std::set<const CStack*> CBattleInfoCallback::getAffectedCreatures(const CSpell * spell, int skillLevel, PlayerColor attackerOwner, BattleHex destinationTile)
 {
-	std::set<const CStack*> attackedCres; /*std::set to exclude multiple occurrences of two hex creatures*/
+	std::set<const CStack*> attackedCres; //std::set to exclude multiple occurrences of two hex creatures
 
 	const ui8 attackerSide = playerToSide(attackerOwner) == 1;
 	const auto attackedHexes = spell->rangeInHexes(destinationTile, skillLevel, attackerSide);
-	const bool onlyAlive = !spell->isRisingSpell(); //when casting resurrection or animate dead we should be allow to select dead stack
-
+	
 	const CSpell::TargetInfo ti = spell->getTargetInfo(skillLevel);
 	//TODO: more generic solution for mass spells
-	if(spell->id == SpellID::DEATH_RIPPLE || spell->id == SpellID::DESTROY_UNDEAD )
-	{
-		for(const CStack *stack : battleGetAllStacks())
-		{
-			if((spell->id == SpellID::DEATH_RIPPLE && !stack->getCreature()->isUndead()) //death ripple
-				|| (spell->id == SpellID::DESTROY_UNDEAD && stack->getCreature()->isUndead()) //destroy undead
-				)
-			{
-				if(stack->isValidTarget())
-					attackedCres.insert(stack);
-			}
-		}
-	}
-	else if (spell->id == SpellID::CHAIN_LIGHTNING)
+	if (spell->id == SpellID::CHAIN_LIGHTNING)
 	{
 		std::set<BattleHex> possibleHexes;
 		for (auto stack : battleGetAllStacks())
@@ -2074,7 +2095,7 @@ std::set<const CStack*> CBattleInfoCallback::getAffectedCreatures(const CSpell *
 	{
 		for(BattleHex hex : attackedHexes)
 		{
-			if(const CStack * st = battleGetStackByPos(hex, onlyAlive))
+			if(const CStack * st = battleGetStackByPos(hex, ti.onlyAlive))
 			{
 				if (spell->id == SpellID::DEATH_CLOUD) //Death Cloud //TODO: fireball and fire immunity
 				{
@@ -2090,38 +2111,51 @@ std::set<const CStack*> CBattleInfoCallback::getAffectedCreatures(const CSpell *
 	}
 	else if(spell->getTargetType() == CSpell::CREATURE)
 	{
+		auto predicate = [=](const CStack * s){
+			const bool positiveToAlly = spell->isPositive() && s->owner == attackerOwner;
+			const bool negativeToEnemy = spell->isNegative() && s->owner != attackerOwner;
+			const bool validTarget = s->isValidTarget(!ti.onlyAlive); //todo: this should be handled by spell class
+	
+			//for single target spells select stacks covering destination tile
+			const bool rangeCovers = ti.massive || s->coversPos(destinationTile);
+			//handle smart targeting
+			const bool positivenessFlag = !ti.smart || spell->isNeutral() || positiveToAlly || negativeToEnemy;
+			
+			return rangeCovers  && positivenessFlag && validTarget;		
+		};
+		
+		TStacks stacks = battleGetStacksIf(predicate);
+		
 		if (ti.massive)
 		{
-			TStacks stacks = battleGetAllStacks();
-
-			vstd::erase_if(stacks,[&](const CStack * stack){
-				return ti.smart && spell->isNegative() && stack->owner == attackerOwner;
-			});
-
-			vstd::erase_if(stacks,[&](const CStack * stack){
-				return ti.smart && spell->isPositive() && stack->owner != attackerOwner;
-			});
-
-			vstd::erase_if(stacks,[&](const CStack * stack){
-				return !stack->isValidTarget(!onlyAlive);
-			});
-			
+			//for massive spells add all targets
 			for (auto stack : stacks)
 				attackedCres.insert(stack);
 
 		}
 		else
 		{
-			if(const CStack * st = battleGetStackByPos(destinationTile, onlyAlive))
-				attackedCres.insert(st);			
+			//for single target spells we must select one target. Alive stack is preferred (issue #1763)
+			for(auto stack : stacks)
+			{
+				if(stack->alive())
+				{
+					attackedCres.insert(stack);
+					break;
+				}				
+			}	
+			
+			if(attackedCres.empty() && !stacks.empty())
+			{
+				attackedCres.insert(stacks.front());
+			}						
 		}
-		
 	}
 	else //custom range from attackedHexes
 	{
 		for(BattleHex hex : attackedHexes)
 		{
-			if(const CStack * st = battleGetStackByPos(hex, onlyAlive))
+			if(const CStack * st = battleGetStackByPos(hex, ti.onlyAlive))
 				attackedCres.insert(st);
 		}
 	}
@@ -2251,10 +2285,14 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) co
 		}
 	}
 
-	if (possibleSpells.size())
-		return possibleSpells[rand() % possibleSpells.size()];
+	if(!possibleSpells.empty())
+	{
+		return *RandomGeneratorUtil::nextItem(possibleSpells, gs->getRandomGenerator());
+	}
 	else
+	{
 		return SpellID::NONE;
+	}
 }
 
 SpellID CBattleInfoCallback::getRandomCastedSpell(const CStack * caster) const
@@ -2269,7 +2307,7 @@ SpellID CBattleInfoCallback::getRandomCastedSpell(const CStack * caster) const
 	{
 		totalWeight += std::max(b->additionalInfo, 1); //minimal chance to cast is 1
 	}
-	int randomPos = rand() % totalWeight;
+	int randomPos = gs->getRandomGenerator().nextInt(totalWeight - 1);
 	for(Bonus * b : *bl)
 	{
 		randomPos -= std::max(b->additionalInfo, 1);
@@ -2431,22 +2469,18 @@ bool CPlayerBattleCallback::battleCanFlee() const
 
 TStacks CPlayerBattleCallback::battleGetStacks(EStackOwnership whose /*= MINE_AND_ENEMY*/, bool onlyAlive /*= true*/) const
 {
-	TStacks ret;
-	RETURN_IF_NOT_BATTLE(ret);
 	if(whose != MINE_AND_ENEMY)
 	{
 		ASSERT_IF_CALLED_WITH_PLAYER
 	}
-	vstd::copy_if(battleGetAllStacks(), std::back_inserter(ret), [=](const CStack *s) -> bool
-	{
+	
+	return battleGetStacksIf([=](const CStack * s){
 		const bool ownerMatches = (whose == MINE_AND_ENEMY)
 			|| (whose == ONLY_MINE && s->owner == player)
 			|| (whose == ONLY_ENEMY && s->owner != player);
 		const bool alivenessMatches = s->alive()  ||  !onlyAlive;
-		return ownerMatches && alivenessMatches;
+		return ownerMatches && alivenessMatches;		
 	});
-
-	return ret;
 }
 
 int CPlayerBattleCallback::battleGetSurrenderCost() const

+ 21 - 2
lib/CBattleCallback.h

@@ -31,6 +31,7 @@ namespace BattleSide
 }
 
 typedef std::vector<const CStack*> TStacks;
+typedef std::function<bool(const CStack *)> TStackFilter;
 
 class CBattleInfoEssentials;
 
@@ -168,7 +169,15 @@ public:
 	ETerrainType battleTerrainType() const;
 	BFieldType battleGetBattlefieldType() const;
 	std::vector<shared_ptr<const CObstacleInstance> > battleGetAllObstacles(boost::optional<BattlePerspective::BattlePerspective> perspective = boost::none) const; //returns all obstacles on the battlefield
-	TStacks battleGetAllStacks(bool includeTurrets = false) const; //returns all stacks, alive or dead or undead or mechanical :)
+    
+    /** @brief Main method for getting battle stacks
+     *
+     * @param predicate Functor that shall return true for desired stack
+     * @return filtered stacks
+     *
+     */                             	
+	TStacks battleGetStacksIf(TStackFilter predicate, bool includeTurrets = false) const;
+	
 	bool battleHasNativeStack(ui8 side) const;
 	int battleGetMoatDmg() const; //what dmg unit will suffer if ending turn in the moat
 	const CGTownInstance * battleGetDefendedTown() const; //returns defended town if current battle is a siege, nullptr instead
@@ -190,7 +199,12 @@ public:
 	si8 battleGetWallState(int partOfWall) const;
 
 	//helpers
+	///returns all stacks, alive or dead or undead or mechanical :)
+	TStacks battleGetAllStacks(bool includeTurrets = false) const;
+	
+	///returns all alive stacks excluding turrets
 	TStacks battleAliveStacks() const;
+	///returns all alive stacks from particular side excluding turrets
 	TStacks battleAliveStacks(ui8 side) const;
 	const CStack * battleGetStackByID(int ID, bool onlyAlive = true) const; //returns stack info by given ID
 	bool battleIsObstacleVisibleForSide(const CObstacleInstance & coi, BattlePerspective::BattlePerspective side) const;
@@ -279,7 +293,7 @@ public:
 	SpellID getRandomCastedSpell(const CStack * caster) const; //called at the beginning of turn for Faerie Dragon
 
 	//checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into acount general problems such as not having spellbook or mana points etc.
-	ESpellCastProblem::ESpellCastProblem battleIsImmune(const CGHeroInstance * caster, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const; 
+	ESpellCastProblem::ESpellCastProblem battleStackIsImmune(const CGHeroInstance * caster, const CSpell * spell, ECastingMode::ECastingMode mode, const CStack * subject) const; 
 
 
 	const CStack * getStackIf(std::function<bool(const CStack*)> pred) const;
@@ -307,6 +321,11 @@ public:
 	AccessibilityInfo getAccesibility(const std::vector<BattleHex> &accessibleHexes) const; //given hexes will be marked as accessible
 	std::pair<const CStack *, BattleHex> getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const;
 protected:
+	
+	//checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into acount general problems such as not having spellbook or mana points etc.
+	ESpellCastProblem::ESpellCastProblem battleIsImmune(const CGHeroInstance * caster, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const;
+	
+	
 	ReachabilityInfo getFlyingReachability(const ReachabilityInfo::Parameters &params) const;
 	ReachabilityInfo makeBFS(const AccessibilityInfo &accessibility, const ReachabilityInfo::Parameters &params) const;
 	ReachabilityInfo makeBFS(const CStack *stack) const; //uses default parameters -> stack position and owner's perspective

+ 736 - 0
lib/CGameInfoCallback.cpp

@@ -0,0 +1,736 @@
+/*
+ * CGameInfoCallback.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 "CGameInfoCallback.h"
+
+#include "CGameState.h" // PlayerState
+#include "CObjectHandler.h" // for CGObjectInstance
+#include "StartInfo.h" // for StartInfo
+#include "BattleState.h" // for BattleInfo
+#include "NetPacks.h" // for InfoWindow
+
+//TODO make clean
+#define ERROR_VERBOSE_OR_NOT_RET_VAL_IF(cond, verbose, txt, retVal) do {if(cond){if(verbose)logGlobal->errorStream() << BOOST_CURRENT_FUNCTION << ": " << txt; return retVal;}} while(0)
+#define ERROR_RET_IF(cond, txt) do {if(cond){logGlobal->errorStream() << BOOST_CURRENT_FUNCTION << ": " << txt; return;}} while(0)
+#define ERROR_RET_VAL_IF(cond, txt, retVal) do {if(cond){logGlobal->errorStream() << BOOST_CURRENT_FUNCTION << ": " << txt; return retVal;}} while(0)
+
+PlayerColor CGameInfoCallback::getOwner(ObjectInstanceID heroID) const
+{
+	const CGObjectInstance *obj = getObj(heroID);
+	ERROR_RET_VAL_IF(!obj, "No such object!", PlayerColor::CANNOT_DETERMINE);
+	return obj->tempOwner;
+}
+
+int CGameInfoCallback::getResource(PlayerColor Player, Res::ERes which) const
+{
+	const PlayerState *p = getPlayer(Player);
+	ERROR_RET_VAL_IF(!p, "No player info!", -1);
+	ERROR_RET_VAL_IF(p->resources.size() <= which || which < 0, "No such resource!", -1);
+	return p->resources[which];
+}
+
+const CGHeroInstance* CGameInfoCallback::getSelectedHero( PlayerColor Player ) const
+{
+	const PlayerState *p = getPlayer(Player);
+	ERROR_RET_VAL_IF(!p, "No player info!", nullptr);
+	return getHero(p->currentSelection);
+}
+
+const CGHeroInstance* CGameInfoCallback::getSelectedHero() const
+{
+	return getSelectedHero(gs->currentPlayer);
+}
+
+const PlayerSettings * CGameInfoCallback::getPlayerSettings(PlayerColor color) const
+{
+	return &gs->scenarioOps->getIthPlayersSettings(color);
+}
+
+bool CGameInfoCallback::isAllowed( int type, int id )
+{
+	switch(type)
+	{
+	case 0:
+		return gs->map->allowedSpell[id];
+	case 1:
+		return gs->map->allowedArtifact[id];
+	case 2:
+		return gs->map->allowedAbilities[id];
+	default:
+		ERROR_RET_VAL_IF(1, "Wrong type!", false);
+	}
+}
+
+const PlayerState * CGameInfoCallback::getPlayer(PlayerColor color, bool verbose) const
+{
+	ERROR_VERBOSE_OR_NOT_RET_VAL_IF(!hasAccess(color), verbose, "Cannot access player " << color << "info!", nullptr);
+	ERROR_VERBOSE_OR_NOT_RET_VAL_IF(!vstd::contains(gs->players,color), verbose, "Cannot find player " << color << "info!", nullptr);
+	return &gs->players[color];
+}
+
+const CTown * CGameInfoCallback::getNativeTown(PlayerColor color) const
+{
+	const PlayerSettings *ps = getPlayerSettings(color);
+	ERROR_RET_VAL_IF(!ps, "There is no such player!", nullptr);
+	return VLC->townh->factions[ps->castle]->town;
+}
+
+const CGObjectInstance * CGameInfoCallback::getObjByQuestIdentifier(int identifier) const
+{
+	ERROR_RET_VAL_IF(!vstd::contains(gs->map->questIdentifierToId, identifier), "There is no object with such quest identifier!", nullptr);
+	return getObj(gs->map->questIdentifierToId[identifier]);
+}
+
+/************************************************************************/
+/*                                                                      */
+/************************************************************************/
+
+const CGObjectInstance* CGameInfoCallback::getObj(ObjectInstanceID objid, bool verbose) const
+{
+	si32 oid = objid.num;
+	if(oid < 0  ||  oid >= gs->map->objects.size())
+	{
+		if(verbose)
+            logGlobal->errorStream() << "Cannot get object with id " << oid;
+		return nullptr;
+	}
+
+	const CGObjectInstance *ret = gs->map->objects[oid];
+	if(!ret)
+	{
+		if(verbose)
+            logGlobal->errorStream() << "Cannot get object with id " << oid << ". Object was removed.";
+		return nullptr;
+	}
+
+	if(!isVisible(ret, player))
+	{
+		if(verbose)
+            logGlobal->errorStream() << "Cannot get object with id " << oid << ". Object is not visible.";
+		return nullptr;
+	}
+
+	return ret;
+}
+
+const CGHeroInstance* CGameInfoCallback::getHero(ObjectInstanceID objid) const
+{
+	const CGObjectInstance *obj = getObj(objid, false);
+	if(obj)
+		return dynamic_cast<const CGHeroInstance*>(obj);
+	else
+		return nullptr;
+}
+const CGTownInstance* CGameInfoCallback::getTown(ObjectInstanceID objid) const
+{
+	const CGObjectInstance *obj = getObj(objid, false);
+	if(obj)
+		return dynamic_cast<const CGTownInstance*>(obj);
+	else
+		return nullptr;
+}
+
+void CGameInfoCallback::getUpgradeInfo(const CArmedInstance *obj, SlotID stackPos, UpgradeInfo &out) const
+{
+	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+	ERROR_RET_IF(!canGetFullInfo(obj), "Cannot get info about not owned object!");
+	ERROR_RET_IF(!obj->hasStackAtSlot(stackPos), "There is no such stack!");
+	out = gs->getUpgradeInfo(obj->getStack(stackPos));
+	//return gs->getUpgradeInfo(obj->getStack(stackPos));
+}
+
+const StartInfo * CGameInfoCallback::getStartInfo(bool beforeRandomization /*= false*/) const
+{
+	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+	if(beforeRandomization)
+		return gs->initialOpts;
+	else
+		return gs->scenarioOps;
+}
+
+int CGameInfoCallback::getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const
+{
+	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+	ERROR_RET_VAL_IF(!canGetFullInfo(caster), "Cannot get info about caster!", -1);
+	//if there is a battle
+	if(gs->curB)
+		return gs->curB->battleGetSpellCost(sp, caster);
+
+	//if there is no battle
+	return caster->getSpellCost(sp);
+}
+
+int CGameInfoCallback::estimateSpellDamage(const CSpell * sp, const CGHeroInstance * hero) const
+{
+	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+
+	ERROR_RET_VAL_IF(hero && !canGetFullInfo(hero), "Cannot get info about caster!", -1);
+	if(!gs->curB) //no battle
+	{
+		if (hero) //but we see hero's spellbook
+			return gs->curB->calculateSpellDmg(
+				sp, hero, nullptr, hero->getSpellSchoolLevel(sp), hero->getPrimSkillLevel(PrimarySkill::SPELL_POWER));
+		else
+			return 0; //mage guild
+	}
+	//gs->getHero(gs->currentPlayer)
+	//const CGHeroInstance * ourHero = gs->curB->heroes[0]->tempOwner == player ? gs->curB->heroes[0] : gs->curB->heroes[1];
+	const CGHeroInstance * ourHero = hero;
+	return gs->curB->calculateSpellDmg(
+		sp, ourHero, nullptr, ourHero->getSpellSchoolLevel(sp), ourHero->getPrimSkillLevel(PrimarySkill::SPELL_POWER));
+}
+
+void CGameInfoCallback::getThievesGuildInfo(SThievesGuildInfo & thi, const CGObjectInstance * obj)
+{
+	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+	ERROR_RET_IF(!obj, "No guild object!");
+	ERROR_RET_IF(obj->ID == Obj::TOWN && !canGetFullInfo(obj), "Cannot get info about town guild object!");
+	//TODO: advmap object -> check if they're visited by our hero
+
+	if(obj->ID == Obj::TOWN  ||  obj->ID == Obj::TAVERN)
+	{
+		gs->obtainPlayersStats(thi, gs->players[obj->tempOwner].towns.size());
+	}
+	else if(obj->ID == Obj::DEN_OF_THIEVES)
+	{
+		gs->obtainPlayersStats(thi, 20);
+	}
+}
+
+int CGameInfoCallback::howManyTowns(PlayerColor Player) const
+{
+	ERROR_RET_VAL_IF(!hasAccess(Player), "Access forbidden!", -1);
+	return gs->players[Player].towns.size();
+}
+
+bool CGameInfoCallback::getTownInfo( const CGObjectInstance *town, InfoAboutTown &dest ) const
+{
+	ERROR_RET_VAL_IF(!isVisible(town, player), "Town is not visible!", false);  //it's not a town or it's not visible for layer
+	bool detailed = hasAccess(town->tempOwner);
+
+	//TODO vision support
+	if(town->ID == Obj::TOWN)
+		dest.initFromTown(static_cast<const CGTownInstance *>(town), detailed);
+	else if(town->ID == Obj::GARRISON || town->ID == Obj::GARRISON2)
+		dest.initFromArmy(static_cast<const CArmedInstance *>(town), detailed);
+	else
+		return false;
+	return true;
+}
+
+int3 CGameInfoCallback::guardingCreaturePosition (int3 pos) const //FIXME: redundant?
+{
+	ERROR_RET_VAL_IF(!isVisible(pos), "Tile is not visible!", int3(-1,-1,-1));
+	return gs->guardingCreaturePosition(pos);
+}
+
+std::vector<const CGObjectInstance*> CGameInfoCallback::getGuardingCreatures (int3 pos) const
+{
+	ERROR_RET_VAL_IF(!isVisible(pos), "Tile is not visible!", std::vector<const CGObjectInstance*>());
+	std::vector<const CGObjectInstance*> ret;
+	for(auto cr : gs->guardingCreatures(pos))
+	{
+		ret.push_back(cr);
+	}
+	return ret;
+}
+
+bool CGameInfoCallback::getHeroInfo( const CGObjectInstance *hero, InfoAboutHero &dest ) const
+{
+	const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(hero);
+
+	ERROR_RET_VAL_IF(!h, "That's not a hero!", false);
+	ERROR_RET_VAL_IF(!isVisible(h->getPosition(false)), "That hero is not visible!", false);
+
+	//TODO vision support
+	dest.initFromHero(h, hasAccess(h->tempOwner));
+	return true;
+}
+
+int CGameInfoCallback::getDate(Date::EDateType mode) const
+{
+	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+	return gs->getDate(mode);
+}
+std::vector < std::string > CGameInfoCallback::getObjDescriptions(int3 pos) const
+{
+	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+	std::vector<std::string> ret;
+	const TerrainTile *t = getTile(pos);
+	ERROR_RET_VAL_IF(!t, "Not a valid tile given!", ret);
+
+
+	for(const CGObjectInstance * obj : t->blockingObjects)
+		ret.push_back(obj->getHoverText());
+	return ret;
+}
+
+bool CGameInfoCallback::isVisible(int3 pos, boost::optional<PlayerColor> Player) const
+{
+	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+	return gs->map->isInTheMap(pos) && (!Player || gs->isVisible(pos, *Player));
+}
+
+bool CGameInfoCallback::isVisible(int3 pos) const
+{
+	return isVisible(pos, player);
+}
+
+bool CGameInfoCallback::isVisible( const CGObjectInstance *obj, boost::optional<PlayerColor> Player ) const
+{
+	return gs->isVisible(obj, Player);
+}
+
+bool CGameInfoCallback::isVisible(const CGObjectInstance *obj) const
+{
+	return isVisible(obj, player);
+}
+// const CCreatureSet* CInfoCallback::getGarrison(const CGObjectInstance *obj) const
+// {
+// 	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+// 	if()
+// 	const CArmedInstance *armi = dynamic_cast<const CArmedInstance*>(obj);
+// 	if(!armi)
+// 		return nullptr;
+// 	else
+// 		return armi;
+// }
+
+std::vector < const CGObjectInstance * > CGameInfoCallback::getBlockingObjs( int3 pos ) const
+{
+	std::vector<const CGObjectInstance *> ret;
+	const TerrainTile *t = getTile(pos);
+	ERROR_RET_VAL_IF(!t, "Not a valid tile requested!", ret);
+
+	for(const CGObjectInstance * obj : t->blockingObjects)
+		ret.push_back(obj);
+	return ret;
+}
+
+std::vector <const CGObjectInstance * > CGameInfoCallback::getVisitableObjs(int3 pos, bool verbose /*= true*/) const
+{
+	std::vector<const CGObjectInstance *> ret;
+	const TerrainTile *t = getTile(pos, verbose);
+	ERROR_VERBOSE_OR_NOT_RET_VAL_IF(!t, verbose, pos << " is not visible!", ret);
+
+	for(const CGObjectInstance * obj : t->visitableObjects)
+	{
+		if(player < nullptr || obj->ID != Obj::EVENT) //hide events from players
+			ret.push_back(obj);
+	}
+
+	return ret;
+}
+const CGObjectInstance * CGameInfoCallback::getTopObj (int3 pos) const
+{
+	return vstd::backOrNull(getVisitableObjs(pos));
+}
+
+std::vector < const CGObjectInstance * > CGameInfoCallback::getFlaggableObjects(int3 pos) const
+{
+	std::vector<const CGObjectInstance *> ret;
+	const TerrainTile *t = getTile(pos);
+	ERROR_RET_VAL_IF(!t, "Not a valid tile requested!", ret);
+	for(const CGObjectInstance *obj : t->blockingObjects)
+		if(obj->tempOwner != PlayerColor::UNFLAGGABLE)
+			ret.push_back(obj);
+// 	const std::vector < std::pair<const CGObjectInstance*,SDL_Rect> > & objs = CGI->mh->ttiles[pos.x][pos.y][pos.z].objects;
+// 	for(size_t b=0; b<objs.size(); ++b)
+// 	{
+// 		if(objs[b].first->tempOwner!=254 && !((objs[b].first->defInfo->blockMap[pos.y - objs[b].first->pos.y + 5] >> (objs[b].first->pos.x - pos.x)) & 1))
+// 			ret.push_back(CGI->mh->ttiles[pos.x][pos.y][pos.z].objects[b].first);
+// 	}
+	return ret;
+}
+
+int3 CGameInfoCallback::getMapSize() const
+{
+	return int3(gs->map->width, gs->map->height, gs->map->twoLevel ? 2 : 1);
+}
+
+std::vector<const CGHeroInstance *> CGameInfoCallback::getAvailableHeroes(const CGObjectInstance * townOrTavern) const
+{
+	ASSERT_IF_CALLED_WITH_PLAYER
+	std::vector<const CGHeroInstance *> ret;
+	//ERROR_RET_VAL_IF(!isOwnedOrVisited(townOrTavern), "Town or tavern must be owned or visited!", ret);
+	//TODO: town needs to be owned, advmap tavern needs to be visited; to be reimplemented when visit tracking is done
+	range::copy(gs->players[*player].availableHeroes, std::back_inserter(ret));
+	vstd::erase_if(ret, [](const CGHeroInstance *h) { return h == nullptr; });
+	return ret;
+}
+
+const TerrainTile * CGameInfoCallback::getTile( int3 tile, bool verbose) const
+{
+	ERROR_VERBOSE_OR_NOT_RET_VAL_IF(!isVisible(tile), verbose, tile << " is not visible!", nullptr);
+
+	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+	return &gs->map->getTile(tile);
+}
+
+EBuildingState::EBuildingState CGameInfoCallback::canBuildStructure( const CGTownInstance *t, BuildingID ID )
+{
+	ERROR_RET_VAL_IF(!canGetFullInfo(t), "Town is not owned!", EBuildingState::TOWN_NOT_OWNED);
+
+	if(!t->town->buildings.count(ID))
+		return EBuildingState::BUILDING_ERROR;
+
+	const CBuilding * building = t->town->buildings.at(ID);
+
+
+	if(t->hasBuilt(ID))	//already built
+		return EBuildingState::ALREADY_PRESENT;
+
+	//can we build it?
+	if(vstd::contains(t->forbiddenBuildings, ID))
+		return EBuildingState::FORBIDDEN; //forbidden
+
+	if(ID == BuildingID::CAPITOL)
+	{
+		const PlayerState *ps = getPlayer(t->tempOwner);
+		if(ps)
+		{
+			for(const CGTownInstance *t : ps->towns)
+			{
+				if(t->hasBuilt(BuildingID::CAPITOL))
+				{
+					return EBuildingState::HAVE_CAPITAL; //no more than one capitol
+				}
+			}
+		}
+	}
+	else if(ID == BuildingID::SHIPYARD)
+	{
+		const TerrainTile *tile = getTile(t->bestLocation(), false);
+
+		if(!tile || tile->terType != ETerrainType::WATER)
+			return EBuildingState::NO_WATER; //lack of water
+	}
+
+	auto buildTest = [&](const BuildingID & id)
+	{
+		return t->hasBuilt(id);
+	};
+
+	if(t->builded >= VLC->modh->settings.MAX_BUILDING_PER_TURN)
+		return EBuildingState::CANT_BUILD_TODAY; //building limit
+
+	if (!building->requirements.test(buildTest))
+		return EBuildingState::PREREQUIRES;
+
+	if (building->upgrade != BuildingID::NONE && !t->hasBuilt(building->upgrade))
+		return EBuildingState::MISSING_BASE;
+
+	//checking resources
+	if(!building->resources.canBeAfforded(getPlayer(t->tempOwner)->resources))
+		return EBuildingState::NO_RESOURCES; //lack of res
+
+	return EBuildingState::ALLOWED;
+}
+
+const CMapHeader * CGameInfoCallback::getMapHeader() const
+{
+	return gs->map;
+}
+
+bool CGameInfoCallback::hasAccess(boost::optional<PlayerColor> playerId) const
+{
+	return !player || gs->getPlayerRelations( *playerId, *player ) != PlayerRelations::ENEMIES;
+}
+
+EPlayerStatus::EStatus CGameInfoCallback::getPlayerStatus(PlayerColor player, bool verbose) const
+{
+	const PlayerState *ps = gs->getPlayer(player, verbose);
+	ERROR_VERBOSE_OR_NOT_RET_VAL_IF(!ps, verbose, "No such player!", EPlayerStatus::WRONG);
+
+	return ps->status;
+}
+
+std::string CGameInfoCallback::getTavernGossip(const CGObjectInstance * townOrTavern) const
+{
+	return "GOSSIP TEST";
+}
+
+PlayerRelations::PlayerRelations CGameInfoCallback::getPlayerRelations( PlayerColor color1, PlayerColor color2 ) const
+{
+	return gs->getPlayerRelations(color1, color2);
+}
+
+bool CGameInfoCallback::canGetFullInfo(const CGObjectInstance *obj) const
+{
+	return !obj || hasAccess(obj->tempOwner);
+}
+
+int CGameInfoCallback::getHeroCount( PlayerColor player, bool includeGarrisoned ) const
+{
+	int ret = 0;
+	const PlayerState *p = gs->getPlayer(player);
+	ERROR_RET_VAL_IF(!p, "No such player!", -1);
+
+	if(includeGarrisoned)
+		return p->heroes.size();
+	else
+		for(auto & elem : p->heroes)
+			if(!elem->inTownGarrison)
+				ret++;
+	return ret;
+}
+
+bool CGameInfoCallback::isOwnedOrVisited(const CGObjectInstance *obj) const
+{
+	if(canGetFullInfo(obj))
+		return true;
+
+	const TerrainTile *t = getTile(obj->visitablePos()); //get entrance tile
+	const CGObjectInstance *visitor = t->visitableObjects.back(); //visitong hero if present or the obejct itself at last
+	return visitor->ID == Obj::HERO && canGetFullInfo(visitor); //owned or allied hero is a visitor
+}
+
+PlayerColor CGameInfoCallback::getCurrentPlayer() const
+{
+	return gs->currentPlayer;
+}
+
+CGameInfoCallback::CGameInfoCallback()
+{
+}
+
+CGameInfoCallback::CGameInfoCallback(CGameState *GS, boost::optional<PlayerColor> Player)
+{
+	gs = GS;
+	player = Player;
+}
+
+const std::vector< std::vector< std::vector<ui8> > > & CPlayerSpecificInfoCallback::getVisibilityMap() const
+{
+	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+	return gs->getPlayerTeam(*player)->fogOfWarMap;
+}
+
+int CPlayerSpecificInfoCallback::howManyTowns() const
+{
+	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+	ERROR_RET_VAL_IF(!player, "Applicable only for player callbacks", -1);
+	return CGameInfoCallback::howManyTowns(*player);
+}
+
+std::vector < const CGTownInstance *> CPlayerSpecificInfoCallback::getTownsInfo(bool onlyOur) const
+{
+	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+	std::vector < const CGTownInstance *> ret = std::vector < const CGTownInstance *>();
+	for(const auto & i : gs->players)
+	{
+		for(const auto & town : i.second.towns)
+		{
+			if (i.first==player || (isVisible(town, player) && !onlyOur))
+			{
+				ret.push_back(town);
+			}
+		}
+	} //	for ( std::map<int, PlayerState>::iterator i=gs->players.begin() ; i!=gs->players.end();i++)
+	return ret;
+}
+std::vector < const CGHeroInstance *> CPlayerSpecificInfoCallback::getHeroesInfo(bool onlyOur) const
+{
+	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+	std::vector < const CGHeroInstance *> ret;
+	for(auto hero : gs->map->heroesOnMap)
+	{
+		if( !player || (hero->tempOwner == *player) ||
+			(isVisible(hero->getPosition(false), player) && !onlyOur)	)
+		{
+			ret.push_back(hero);
+		}
+	}
+	return ret;
+}
+
+boost::optional<PlayerColor> CPlayerSpecificInfoCallback::getMyColor() const
+{
+	return player;
+}
+
+int CPlayerSpecificInfoCallback::getHeroSerial(const CGHeroInstance * hero, bool includeGarrisoned) const
+{
+	if (hero->inTownGarrison && !includeGarrisoned)
+		return -1;
+
+	size_t index = 0;
+	auto & heroes = gs->players[*player].heroes;
+
+	for (auto & heroe : heroes)
+	{
+		if (includeGarrisoned || !(heroe)->inTownGarrison)
+			index++;
+
+		if (heroe == hero)
+			return index;
+	}
+	return -1;
+}
+
+int3 CPlayerSpecificInfoCallback::getGrailPos( double &outKnownRatio )
+{
+	if (!player || CGObelisk::obeliskCount == 0)
+	{
+		outKnownRatio = 0.0;
+	}
+	else
+	{
+		outKnownRatio = static_cast<double>(CGObelisk::visited[gs->getPlayerTeam(*player)->id]) / CGObelisk::obeliskCount;
+	}
+	return gs->map->grailPos;
+}
+
+std::vector < const CGObjectInstance * > CPlayerSpecificInfoCallback::getMyObjects() const
+{
+	std::vector < const CGObjectInstance * > ret;
+	for(const CGObjectInstance * obj : gs->map->objects)
+	{
+		if(obj && obj->tempOwner == player)
+			ret.push_back(obj);
+	}
+	return ret;
+}
+
+std::vector < const CGDwelling * > CPlayerSpecificInfoCallback::getMyDwellings() const
+{
+	ASSERT_IF_CALLED_WITH_PLAYER
+	std::vector < const CGDwelling * > ret;
+	for(CGDwelling * dw : gs->getPlayer(*player)->dwellings)
+	{
+		ret.push_back(dw);
+	}
+	return ret;
+}
+
+std::vector <QuestInfo> CPlayerSpecificInfoCallback::getMyQuests() const
+{
+	std::vector <QuestInfo> ret;
+	for (auto quest : gs->getPlayer(*player)->quests)
+	{
+		ret.push_back (quest);
+	}
+	return ret;
+}
+
+int CPlayerSpecificInfoCallback::howManyHeroes(bool includeGarrisoned) const
+{
+	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+	ERROR_RET_VAL_IF(!player, "Applicable only for player callbacks", -1);
+	return getHeroCount(*player,includeGarrisoned);
+}
+
+const CGHeroInstance* CPlayerSpecificInfoCallback::getHeroBySerial(int serialId, bool includeGarrisoned) const
+{
+	ASSERT_IF_CALLED_WITH_PLAYER
+	const PlayerState *p = getPlayer(*player);
+	ERROR_RET_VAL_IF(!p, "No player info", nullptr);
+
+	if (!includeGarrisoned)
+	{
+		for(ui32 i = 0; i < p->heroes.size() && i<=serialId; i++)
+			if(p->heroes[i]->inTownGarrison)
+				serialId++;
+	}
+	ERROR_RET_VAL_IF(serialId < 0 || serialId >= p->heroes.size(), "No player info", nullptr);
+	return p->heroes[serialId];
+}
+
+const CGTownInstance* CPlayerSpecificInfoCallback::getTownBySerial(int serialId) const
+{
+	ASSERT_IF_CALLED_WITH_PLAYER
+	const PlayerState *p = getPlayer(*player);
+	ERROR_RET_VAL_IF(!p, "No player info", nullptr);
+	ERROR_RET_VAL_IF(serialId < 0 || serialId >= p->towns.size(), "No player info", nullptr);
+	return p->towns[serialId];
+}
+
+int CPlayerSpecificInfoCallback::getResourceAmount(Res::ERes type) const
+{
+	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+	ERROR_RET_VAL_IF(!player, "Applicable only for player callbacks", -1);
+	return getResource(*player, type);
+}
+
+TResources CPlayerSpecificInfoCallback::getResourceAmount() const
+{
+	//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+	ERROR_RET_VAL_IF(!player, "Applicable only for player callbacks", TResources());
+	return gs->players[*player].resources;
+}
+
+const TeamState * CGameInfoCallback::getTeam( TeamID teamID ) const
+{
+	ERROR_RET_VAL_IF(!vstd::contains(gs->teams, teamID), "Cannot find info for team " << teamID, nullptr);
+	const TeamState *ret = &gs->teams[teamID];
+	ERROR_RET_VAL_IF(!!player && !vstd::contains(ret->players, *player), "Illegal attempt to access team data!", nullptr);
+	return ret;
+}
+
+const TeamState * CGameInfoCallback::getPlayerTeam( PlayerColor color ) const
+{
+	const PlayerState * ps = getPlayer(color);
+	if (ps)
+		return getTeam(ps->team);
+	return nullptr;
+}
+
+const CGHeroInstance* CGameInfoCallback::getHeroWithSubid( int subid ) const
+{
+	for(const CGHeroInstance *h : gs->map->heroesOnMap)
+		if(h->subID == subid)
+			return h;
+
+	return nullptr;
+}
+
+PlayerColor CGameInfoCallback::getLocalPlayer() const
+{
+	return getCurrentPlayer();
+}
+
+bool CGameInfoCallback::isInTheMap(const int3 &pos) const
+{
+	return gs->map->isInTheMap(pos);
+}
+
+const CArtifactInstance * CGameInfoCallback::getArtInstance( ArtifactInstanceID aid ) const
+{
+	return gs->map->artInstances[aid.num];
+}
+
+const CGObjectInstance * CGameInfoCallback::getObjInstance( ObjectInstanceID oid ) const
+{
+	return gs->map->objects[oid.num];
+}
+
+void IGameEventRealizer::showInfoDialog( InfoWindow *iw )
+{
+	commitPackage(iw);
+}
+
+void IGameEventRealizer::showInfoDialog(const std::string &msg, PlayerColor player)
+{
+	InfoWindow iw;
+	iw.player = player;
+	iw.text << msg;
+	showInfoDialog(&iw);
+}
+
+void IGameEventRealizer::setObjProperty(ObjectInstanceID objid, int prop, si64 val)
+{
+	SetObjectProperty sob;
+	sob.id = objid;
+	sob.what = prop;
+	sob.val = static_cast<ui32>(val);
+	commitPackage(&sob);
+}
+

+ 148 - 0
lib/CGameInfoCallback.h

@@ -0,0 +1,148 @@
+#pragma once
+
+#include "ResourceSet.h" // for Res::ERes
+#include "CBattleCallback.h" //for CCallbackBase
+
+/*
+ * CGameInfoCallback.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 CGObjectInstance;
+struct InfoWindow;
+struct PlayerSettings;
+struct CPackForClient;
+struct TerrainTile;
+struct PlayerState;
+class CTown;
+struct StartInfo;
+struct InfoAboutTown;
+struct UpgradeInfo;
+struct SThievesGuildInfo;
+class CGDwelling;
+class CMapHeader;
+struct TeamState;
+struct QuestInfo;
+class int3;
+
+
+class DLL_LINKAGE CGameInfoCallback : public virtual CCallbackBase
+{
+protected:
+	CGameInfoCallback();
+	CGameInfoCallback(CGameState *GS, boost::optional<PlayerColor> Player);
+	bool hasAccess(boost::optional<PlayerColor> playerId) const;
+	bool isVisible(int3 pos, boost::optional<PlayerColor> Player) const;
+	bool isVisible(const CGObjectInstance *obj, boost::optional<PlayerColor> Player) const;
+	bool isVisible(const CGObjectInstance *obj) const;
+
+	bool canGetFullInfo(const CGObjectInstance *obj) const; //true we player owns obj or ally owns obj or privileged mode
+	bool isOwnedOrVisited(const CGObjectInstance *obj) const;
+
+public:
+	//various
+	int getDate(Date::EDateType mode=Date::DAY)const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month
+	const StartInfo * getStartInfo(bool beforeRandomization = false)const;
+	bool isAllowed(int type, int id); //type: 0 - spell; 1- artifact; 2 - secondary skill
+
+	//player
+	const PlayerState * getPlayer(PlayerColor color, bool verbose = true) const;
+	int getResource(PlayerColor Player, Res::ERes which) const;
+	bool isVisible(int3 pos) const;
+	PlayerRelations::PlayerRelations getPlayerRelations(PlayerColor color1, PlayerColor color2) const;
+	void getThievesGuildInfo(SThievesGuildInfo & thi, const CGObjectInstance * obj); //get thieves' guild info obtainable while visiting given object
+	EPlayerStatus::EStatus getPlayerStatus(PlayerColor player, bool verbose = true) const; //-1 if no such player
+	PlayerColor getCurrentPlayer() const; //player that currently makes move // TODO synchronous turns
+	virtual PlayerColor getLocalPlayer() const; //player that is currently owning given client (if not a client, then returns current player)
+	const PlayerSettings * getPlayerSettings(PlayerColor color) const;
+
+
+	//armed object
+	void getUpgradeInfo(const CArmedInstance *obj, SlotID stackPos, UpgradeInfo &out)const;
+
+	//hero
+	const CGHeroInstance* getHero(ObjectInstanceID objid) const;
+	const CGHeroInstance* getHeroWithSubid(int subid) const;
+	int getHeroCount(PlayerColor player, bool includeGarrisoned) const;
+	bool getHeroInfo(const CGObjectInstance *hero, InfoAboutHero &dest) const;
+	int getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //when called during battle, takes into account creatures' spell cost reduction
+	int estimateSpellDamage(const CSpell * sp, const CGHeroInstance * hero) const; //estimates damage of given spell; returns 0 if spell causes no dmg
+	const CGHeroInstance* getSelectedHero(PlayerColor player) const; //nullptr if no hero is selected
+	const CGHeroInstance* getSelectedHero() const; //of current (active) player
+	const CArtifactInstance * getArtInstance(ArtifactInstanceID aid) const;
+	const CGObjectInstance * getObjInstance(ObjectInstanceID oid) const;
+
+	//objects
+	const CGObjectInstance* getObj(ObjectInstanceID objid, bool verbose = true) const;
+	std::vector <const CGObjectInstance * > getBlockingObjs(int3 pos)const;
+	std::vector <const CGObjectInstance * > getVisitableObjs(int3 pos, bool verbose = true)const;
+	std::vector <const CGObjectInstance * > getFlaggableObjects(int3 pos) const;
+	const CGObjectInstance * getTopObj (int3 pos) const;
+	std::vector <std::string > getObjDescriptions(int3 pos)const; //returns descriptions of objects at pos in order from the lowest to the highest
+	PlayerColor getOwner(ObjectInstanceID heroID) const;
+	const CGObjectInstance *getObjByQuestIdentifier(int identifier) const; //nullptr if object has been removed (eg. killed)
+
+	//map
+	int3 guardingCreaturePosition (int3 pos) const;
+	std::vector<const CGObjectInstance*> getGuardingCreatures (int3 pos) const;
+	const CMapHeader * getMapHeader()const;
+	int3 getMapSize() const; //returns size of map - z is 1 for one - level map and 2 for two level map
+	const TerrainTile * getTile(int3 tile, bool verbose = true) const;
+	bool isInTheMap(const int3 &pos) const;
+
+	//town
+	const CGTownInstance* getTown(ObjectInstanceID objid) const;
+	int howManyTowns(PlayerColor Player) const;
+	const CGTownInstance * getTownInfo(int val, bool mode)const; //mode = 0 -> val = player town serial; mode = 1 -> val = object id (serial)
+	std::vector<const CGHeroInstance *> getAvailableHeroes(const CGObjectInstance * townOrTavern) const; //heroes that can be recruited
+	std::string getTavernGossip(const CGObjectInstance * townOrTavern) const; 
+	EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID);//// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
+	virtual bool getTownInfo(const CGObjectInstance *town, InfoAboutTown &dest) const;
+	const CTown *getNativeTown(PlayerColor color) const;
+
+	//from gs
+	const TeamState *getTeam(TeamID teamID) const;
+	const TeamState *getPlayerTeam(PlayerColor color) const;
+	EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID) const;// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
+};
+
+class DLL_LINKAGE CPlayerSpecificInfoCallback : public CGameInfoCallback
+{
+public:
+	int howManyTowns() const;
+	int howManyHeroes(bool includeGarrisoned = true) const;
+	int3 getGrailPos(double &outKnownRatio);
+	boost::optional<PlayerColor> getMyColor() const;
+
+	std::vector <const CGTownInstance *> getTownsInfo(bool onlyOur = true) const; //true -> only owned; false -> all visible
+	int getHeroSerial(const CGHeroInstance * hero, bool includeGarrisoned=true) const;
+	const CGTownInstance* getTownBySerial(int serialId) const; // serial id is [0, number of towns)
+	const CGHeroInstance* getHeroBySerial(int serialId, bool includeGarrisoned=true) const; // serial id is [0, number of heroes)
+	std::vector <const CGHeroInstance *> getHeroesInfo(bool onlyOur = true) const; //true -> only owned; false -> all visible
+	std::vector <const CGDwelling *> getMyDwellings() const; //returns all dwellings that belong to player
+	std::vector <const CGObjectInstance * > getMyObjects() const; //returns all objects flagged by belonging player
+	std::vector <QuestInfo> getMyQuests() const;
+
+	int getResourceAmount(Res::ERes type) const;
+	TResources getResourceAmount() const;
+	const std::vector< std::vector< std::vector<ui8> > > & getVisibilityMap()const; //returns visibility map 
+	const PlayerSettings * getPlayerSettings(PlayerColor color) const;
+};
+
+class DLL_LINKAGE IGameEventRealizer
+{
+public:
+	virtual void commitPackage(CPackForClient *pack) = 0;
+
+	virtual void showInfoDialog(InfoWindow *iw);
+	virtual void setObjProperty(ObjectInstanceID objid, int prop, si64 val);
+
+
+	virtual void showInfoDialog(const std::string &msg, PlayerColor player);
+};
+

+ 27 - 0
lib/CGameInterface.cpp

@@ -22,7 +22,17 @@
  *
  */
 
+#ifdef __ANDROID__
+// we can't use shared libraries on Android so here's a hack
+extern "C" DLL_EXPORT void VCAI_GetAiName(char* name);
+extern "C" DLL_EXPORT void VCAI_GetNewAI(shared_ptr<CGlobalAI> &out);
 
+extern "C" DLL_EXPORT void StupidAI_GetAiName(char* name);
+extern "C" DLL_EXPORT void StupidAI_GetNewBattleAI(shared_ptr<CGlobalAI> &out);
+
+extern "C" DLL_EXPORT void BattleAI_GetAiName(char* name);
+extern "C" DLL_EXPORT void BattleAI_GetNewBattleAI(shared_ptr<CBattleGameInterface> &out);
+#endif
 
 template<typename rett>
 shared_ptr<rett> createAny(std::string dllname, std::string methodName)
@@ -35,6 +45,21 @@ shared_ptr<rett> createAny(std::string dllname, std::string methodName)
 	TGetAIFun getAI = nullptr;
 	TGetNameFun getName = nullptr;
 
+#ifdef __ANDROID__
+	// this is awful but it seems using shared libraries on some devices is even worse
+	if (dllname.find("libVCAI.so") != std::string::npos) {
+		getName = (TGetNameFun)VCAI_GetAiName;
+		getAI = (TGetAIFun)VCAI_GetNewAI;
+	} else if (dllname.find("libStupidAI.so") != std::string::npos) {
+		getName = (TGetNameFun)StupidAI_GetAiName;
+		getAI = (TGetAIFun)StupidAI_GetNewBattleAI;
+	} else if (dllname.find("libBattleAI.so") != std::string::npos) {
+		getName = (TGetNameFun)BattleAI_GetAiName;
+		getAI = (TGetAIFun)BattleAI_GetNewBattleAI;
+	} else {
+		throw std::runtime_error("Don't know what to do with " + dllname + " and method " + methodName);
+	}
+#else
 
 #ifdef _WIN32
 	HINSTANCE dll = LoadLibraryA(dllname.c_str());
@@ -69,6 +94,8 @@ shared_ptr<rett> createAny(std::string dllname, std::string methodName)
 		throw std::runtime_error("Cannot find method " + methodName);
 	}
 
+#endif // __ANDROID__
+
 	getName(temp);
     logGlobal->infoStream() << "Loaded " << temp;
 

+ 26 - 36
lib/CGameState.cpp

@@ -729,11 +729,13 @@ CGameState::~CGameState()
 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;
+	ETerrainType terrain = t.terType;
 	if(t.isCoastal() && !t.isWater())
-        terrain = ETerrainType::SAND;
+		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);
 }
 
@@ -808,8 +810,8 @@ void CGameState::initNewGame()
 		CStopWatch sw;
 
 		// Gen map
-		CMapGenerator mapGenerator;
-		map = mapGenerator.generate(scenarioOps->mapGenOptions.get(), scenarioOps->seedToBeUsed).release();
+		CMapGenerator mapGenerator(scenarioOps->mapGenOptions, scenarioOps->seedToBeUsed);
+		map = mapGenerator.generate().release();
 
 		// Update starting options
 		for(int i = 0; i < map->players.size(); ++i)
@@ -1000,7 +1002,7 @@ void CGameState::initGrailPosition()
 						&& !t.visitable
 						&& t.terType != ETerrainType::WATER
 						&& t.terType != ETerrainType::ROCK
-						&& map->grailPos.dist2d(int3(i,j,k)) <= map->grailRadious)
+						&& map->grailPos.dist2dSQ(int3(i, j, k)) <= (map->grailRadious * map->grailRadious))
 						allowedPos.push_back(int3(i,j,k));
 				}
 			}
@@ -1236,21 +1238,6 @@ CGameState::CrossoverHeroesList CGameState::getCrossoverHeroesFromPreviousScenar
 		}
 	}
 
-	// Now we need to perform deep copies of all heroes
-	// The lambda below replaces pointer to a hero with a pointer to its deep copy.
-	auto replaceWithDeepCopy = [](CGHeroInstance *&hero)
-	{
-		// We cache map original hero => copy.
-		// We may be called multiple times with the same hero and should return a single copy.
-		static std::map<CGHeroInstance*, CGHeroInstance*> oldToCopy;
-		if(!oldToCopy[hero])
-			oldToCopy[hero] = CMemorySerializer::deepCopy(*hero).release();
-
-		hero = oldToCopy[hero];
-	};
-	range::for_each(crossoverHeroes.heroesFromAnyPreviousScenarios, replaceWithDeepCopy);
-	range::for_each(crossoverHeroes.heroesFromPreviousScenario,     replaceWithDeepCopy);
-
 	return crossoverHeroes;
 }
 
@@ -1877,6 +1864,8 @@ void CGameState::initMapObjects()
 		}
 	}
 	CGTeleport::postInit(); //pairing subterranean gates
+
+	map->calculateGuardingGreaturePositions(); //calculate once again when all the guards are placed and initialized
 }
 
 void CGameState::initVisitingAndGarrisonedHeroes()
@@ -1923,8 +1912,7 @@ BFieldType CGameState::battleGetBattlefieldType(int3 tile)
 	for(auto &obj : map->objects)
 	{
 		//look only for objects covering given tile
-		if( !obj || obj->pos.z != tile.z
-		  || !obj->coveringAt(tile.x - obj->pos.x, tile.y - obj->pos.y))
+		if( !obj || obj->pos.z != tile.z || !obj->coveringAt(tile.x, tile.y))
 			continue;
 
 		switch(obj->ID)
@@ -1955,27 +1943,27 @@ BFieldType CGameState::battleGetBattlefieldType(int3 tile)
 	if(!t.isWater() && t.isCoastal())
 		return BFieldType::SAND_SHORE;
 
-    switch(t.terType)
+	switch(t.terType)
 	{
-    case ETerrainType::DIRT:
+	case ETerrainType::DIRT:
 		return BFieldType(rand.nextInt(3, 5));
-    case ETerrainType::SAND:
+	case ETerrainType::SAND:
 		return BFieldType::SAND_MESAS; //TODO: coast support
-    case ETerrainType::GRASS:
+	case ETerrainType::GRASS:
 		return BFieldType(rand.nextInt(6, 7));
-    case ETerrainType::SNOW:
+	case ETerrainType::SNOW:
 		return BFieldType(rand.nextInt(10, 11));
-    case ETerrainType::SWAMP:
+	case ETerrainType::SWAMP:
 		return BFieldType::SWAMP_TREES;
-    case ETerrainType::ROUGH:
+	case ETerrainType::ROUGH:
 		return BFieldType::ROUGH;
-    case ETerrainType::SUBTERRANEAN:
+	case ETerrainType::SUBTERRANEAN:
 		return BFieldType::SUBTERRANEAN;
-    case ETerrainType::LAVA:
+	case ETerrainType::LAVA:
 		return BFieldType::LAVA;
-    case ETerrainType::WATER:
+	case ETerrainType::WATER:
 		return BFieldType::SHIP;
-    case ETerrainType::ROCK:
+	case ETerrainType::ROCK:
 		return BFieldType::ROCKLANDS;
 	default:
 		return BFieldType::NONE;
@@ -2775,7 +2763,7 @@ std::vector<CGameState::CampaignHeroReplacement> CGameState::generateCampaignHer
 				{
 					auto hero = *it;
 					crossoverHeroes.removeHeroFromBothLists(hero);
-					campaignHeroReplacements.push_back(CampaignHeroReplacement(hero, heroPlaceholder->id));
+                    campaignHeroReplacements.push_back(CampaignHeroReplacement(CMemorySerializer::deepCopy(*hero).release(), heroPlaceholder->id));
 				}
 			}
 		}
@@ -2810,7 +2798,8 @@ std::vector<CGameState::CampaignHeroReplacement> CGameState::generateCampaignHer
 		auto heroPlaceholder = heroPlaceholders[i];
 		if(crossoverHeroes.heroesFromPreviousScenario.size() > i)
 		{
-			campaignHeroReplacements.push_back(CampaignHeroReplacement(crossoverHeroes.heroesFromPreviousScenario[i], heroPlaceholder->id));
+            auto hero = crossoverHeroes.heroesFromPreviousScenario[i];
+            campaignHeroReplacements.push_back(CampaignHeroReplacement(CMemorySerializer::deepCopy(*hero).release(), heroPlaceholder->id));
 		}
 	}
 
@@ -3088,7 +3077,8 @@ void InfoAboutTown::initFromTown(const CGTownInstance *t, bool detailed)
 	{
 		//include details about hero
 		details = new Details;
-		details->goldIncome = t->dailyIncome();
+		TResources income = t->dailyIncome();
+		details->goldIncome = income[Res::GOLD];
 		details->customRes = t->hasBuilt(BuildingID::RESOURCE_SILO);
 		details->hallLevel = t->hallLevel();
 		details->garrisonedHero = t->garrisonHero;

+ 1 - 2
lib/CGameState.h

@@ -15,7 +15,6 @@
 #include "ResourceSet.h"
 #include "int3.h"
 #include "CObjectHandler.h"
-#include "IGameCallback.h"
 #include "CRandomGenerator.h"
 
 /*
@@ -459,7 +458,7 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & scenarioOps & initialOpts & currentPlayer & day & map & players & teams & hpool & globalEffects;
+		h & scenarioOps & initialOpts & currentPlayer & day & map & players & teams & hpool & globalEffects & rand;
 		BONUS_TREE_DESERIALIZATION_FIX
 	}
 

+ 5 - 3
lib/CHeroHandler.cpp

@@ -23,7 +23,7 @@
  *
  */
 
-SecondarySkill CHeroClass::chooseSecSkill(const std::set<SecondarySkill> & possibles, std::minstd_rand & distr) const //picks secondary skill out from given possibilities
+SecondarySkill CHeroClass::chooseSecSkill(const std::set<SecondarySkill> & possibles, CRandomGenerator & rand) const //picks secondary skill out from given possibilities
 {
 	int totalProb = 0;
 	for(auto & possible : possibles)
@@ -32,12 +32,14 @@ SecondarySkill CHeroClass::chooseSecSkill(const std::set<SecondarySkill> & possi
 	}
 	if (totalProb != 0) // may trigger if set contains only banned skills (0 probability)
 	{
-		int ran = distr()%totalProb;
+		auto ran = rand.nextInt(totalProb - 1);
 		for(auto & possible : possibles)
 		{
 			ran -= secSkillProbability[possible];
-			if(ran<0)
+			if(ran < 0)
+			{
 				return possible;
+			}
 		}
 	}
 	// FIXME: select randomly? How H3 handles such rare situation?

+ 2 - 1
lib/CHeroHandler.h

@@ -21,6 +21,7 @@ class CGameInfo;
 class CGHeroInstance;
 struct BattleHex;
 class JsonNode;
+class CRandomGenerator;
 
 struct SSpecialtyInfo
 {	si32 type;
@@ -132,7 +133,7 @@ public:
 	CHeroClass();
 
 	bool isMagicHero() const;
-	SecondarySkill chooseSecSkill(const std::set<SecondarySkill> & possibles, std::minstd_rand & distr) const; //picks secondary skill out from given possibilities
+	SecondarySkill chooseSecSkill(const std::set<SecondarySkill> & possibles, CRandomGenerator & rand) const; //picks secondary skill out from given possibilities
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{

+ 2 - 1
lib/CMakeLists.txt

@@ -7,6 +7,7 @@ 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
@@ -70,6 +71,7 @@ set(lib_SRCS
 		CObstacleInstance.cpp
 		CObjectConstructor.cpp
 		CObjectWithReward.cpp
+		CRandomGenerator.cpp
 		CSpellHandler.cpp
 		CThreadHelper.cpp
 		CTownHandler.cpp
@@ -93,7 +95,6 @@ set(lib_HEADERS
 		CondSh.h
 		ConstTransitivePtr.h
 		CBonusTypeHandler.h
-		CRandomGenerator.h
 		CScriptingModule.h
 		CStopWatch.h
 		FunctionList.h

+ 11 - 6
lib/CModHandler.cpp

@@ -309,6 +309,12 @@ bool CContentHandler::ContentTypeHandler::loadMod(std::string modName, bool vali
 {
 	ModInfo & modInfo = modData[modName];
 	bool result = true;
+	
+	auto performValidate = [&,this](JsonNode & data, const std::string & name){
+		handler->beforeValidate(data);
+		if (validate)
+			result &= JsonUtils::validate(data, "vcmi:" + objectName, name);	
+	};
 
 	// apply patches
 	if (!modInfo.patches.isNull())
@@ -327,8 +333,8 @@ bool CContentHandler::ContentTypeHandler::loadMod(std::string modName, bool vali
 			if (originalData.size() > index)
 			{
 				JsonUtils::merge(originalData[index], data);
-				if (validate)
-					result &= JsonUtils::validate(originalData[index], "vcmi:" + objectName, name);
+				
+				performValidate(originalData[index],name);
 				handler->loadObject(modName, name, originalData[index], index);
 
 				originalData[index].clear(); // do not use same data twice (same ID)
@@ -337,8 +343,7 @@ bool CContentHandler::ContentTypeHandler::loadMod(std::string modName, bool vali
 			}
 		}
 		// normal new object or one with index bigger that data size
-		if (validate)
-			result &= JsonUtils::validate(data, "vcmi:" + objectName, name);
+		performValidate(data,name);
 		handler->loadObject(modName, name, data);
 	}
 	return result;
@@ -524,14 +529,14 @@ void CModHandler::loadConfigFromFile (std::string name)
 {
 	settings.data = JsonUtils::assembleFromFiles("config/" + name);
 	const JsonNode & hardcodedFeatures = settings.data["hardcodedFeatures"];
-
+	settings.MAX_HEROES_AVAILABLE_PER_PLAYER = hardcodedFeatures["MAX_HEROES_AVAILABLE_PER_PLAYER"].Float();
+	settings.MAX_HEROES_ON_MAP_PER_PLAYER = hardcodedFeatures["MAX_HEROES_ON_MAP_PER_PLAYER"].Float();
 	settings.CREEP_SIZE = hardcodedFeatures["CREEP_SIZE"].Float();
 	settings.WEEKLY_GROWTH = hardcodedFeatures["WEEKLY_GROWTH_PERCENT"].Float();
 	settings.NEUTRAL_STACK_EXP = hardcodedFeatures["NEUTRAL_STACK_EXP_DAILY"].Float();
 	settings.MAX_BUILDING_PER_TURN = hardcodedFeatures["MAX_BUILDING_PER_TURN"].Float();
 	settings.DWELLINGS_ACCUMULATE_CREATURES = hardcodedFeatures["DWELLINGS_ACCUMULATE_CREATURES"].Bool();
 	settings.ALL_CREATURES_GET_DOUBLE_MONTHS = hardcodedFeatures["ALL_CREATURES_GET_DOUBLE_MONTHS"].Bool();
-
 	const JsonNode & gameModules = settings.data["modules"];
 	modules.STACK_EXP = gameModules["STACK_EXPERIENCE"].Bool();
 	modules.STACK_ARTIFACT = gameModules["STACK_ARTIFACTS"].Bool();

+ 3 - 1
lib/CModHandler.h

@@ -245,11 +245,13 @@ public:
 		int MAX_BUILDING_PER_TURN;
 		bool DWELLINGS_ACCUMULATE_CREATURES;
 		bool ALL_CREATURES_GET_DOUBLE_MONTHS;
+		int MAX_HEROES_AVAILABLE_PER_PLAYER;
+		int MAX_HEROES_ON_MAP_PER_PLAYER;
 
 		template <typename Handler> void serialize(Handler &h, const int version)
 		{
 			h & data & CREEP_SIZE & WEEKLY_GROWTH & NEUTRAL_STACK_EXP & MAX_BUILDING_PER_TURN;
-			h & DWELLINGS_ACCUMULATE_CREATURES & ALL_CREATURES_GET_DOUBLE_MONTHS;
+			h & DWELLINGS_ACCUMULATE_CREATURES & ALL_CREATURES_GET_DOUBLE_MONTHS & MAX_HEROES_AVAILABLE_PER_PLAYER & MAX_HEROES_ON_MAP_PER_PLAYER;
 		}
 	} settings;
 

+ 210 - 54
lib/CObjectHandler.cpp

@@ -350,6 +350,21 @@ std::set<int3> CGObjectInstance::getBlockedPos() const
 	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)
@@ -789,17 +804,14 @@ void CGHeroInstance::initHero()
 	}
 	assert(validTypes());
 
-	if (exp == 0xffffffff)
+	level = 1;
+	if(exp == 0xffffffff)
 	{
 		initExp();
 	}
-	else if (ID != Obj::PRISON)
-	{
-		level = VLC->heroh->level(exp);
-	}
-	else //warp hero at the beginning of next turn
+	else
 	{
-		level = 1;
+		levelUpAutomatically();
 	}
 
 	if (VLC->modh->modules.COMMANDERS && !commander)
@@ -901,7 +913,7 @@ void CGHeroInstance::onHeroVisit(const CGHeroInstance * h) const
 	{
 		int txt_id;
 
-		if(cb->getHeroCount(h->tempOwner,false) < GameConstants::MAX_HEROES_PER_PLAYER) //free hero slot
+		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
@@ -975,7 +987,7 @@ void CGHeroInstance::initObj()
 	if(!type)
 		initHero(); //TODO: set up everything for prison before specialties are configured
 
-	skillsInfo.distribution.seed(rand());
+	skillsInfo.rand.setSeed(cb->gameState()->getRandomGenerator().nextInt());
 	skillsInfo.resetMagicSchoolCounter();
 	skillsInfo.resetWisdomCounter();
 
@@ -1574,7 +1586,6 @@ EAlignment::EAlignment CGHeroInstance::getAlignment() const
 void CGHeroInstance::initExp()
 {
 	exp = cb->gameState()->getRandomGenerator().nextInt(40, 89);
-	level = 1;
 }
 
 std::string CGHeroInstance::nodeName() const
@@ -1652,7 +1663,7 @@ ArtBearer::ArtBearer CGHeroInstance::bearerType() const
 	return ArtBearer::HERO;
 }
 
-std::vector<SecondarySkill> CGHeroInstance::levelUpProposedSkills() const
+std::vector<SecondarySkill> CGHeroInstance::getLevelUpProposedSecondarySkills() const
 {
 	std::vector<SecondarySkill> obligatorySkills; //hero is offered magic school or wisdom if possible
 	if (!skillsInfo.wisdomCounter)
@@ -1665,11 +1676,7 @@ std::vector<SecondarySkill> CGHeroInstance::levelUpProposedSkills() const
 		std::vector<SecondarySkill> ss;
 		ss += SecondarySkill::FIRE_MAGIC, SecondarySkill::AIR_MAGIC, SecondarySkill::WATER_MAGIC, SecondarySkill::EARTH_MAGIC;
 
-		auto rng = [=](ui32 val) mutable -> ui32
-		{
-			return skillsInfo.distribution() % val; //must be determined
-		};
-		std::random_shuffle(ss.begin(), ss.end(), rng);
+		std::shuffle(ss.begin(), ss.end(), skillsInfo.rand.getStdGenerator());
 
 		for (auto skill : ss)
 		{
@@ -1713,12 +1720,12 @@ std::vector<SecondarySkill> CGHeroInstance::levelUpProposedSkills() const
 	}
 	else if(none.size() && canLearnSkill()) //hero have free skill slot
 	{
-		skills.push_back(type->heroClass->chooseSecSkill(none, skillsInfo.distribution)); //new skill
+		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.distribution)); //upgrade existing
+		skills.push_back(type->heroClass->chooseSecSkill(basicAndAdv, skillsInfo.rand)); //upgrade existing
 		basicAndAdv.erase(skills.back());
 	}
 
@@ -1728,7 +1735,7 @@ std::vector<SecondarySkill> CGHeroInstance::levelUpProposedSkills() const
 	//3) give any other new skill
 	if(!basicAndAdv.empty())
 	{
-		SecondarySkill s = type->heroClass->chooseSecSkill(basicAndAdv, skillsInfo.distribution);//upgrade existing
+		SecondarySkill s = type->heroClass->chooseSecSkill(basicAndAdv, skillsInfo.rand);//upgrade existing
 		skills.push_back(s);
 		basicAndAdv.erase(s);
 	}
@@ -1738,18 +1745,147 @@ std::vector<SecondarySkill> CGHeroInstance::levelUpProposedSkills() const
 	}
 	else if(none.size() && canLearnSkill())
 	{
-		skills.push_back(type->heroClass->chooseSecSkill(none, skillsInfo.distribution)); //give new skill
+		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)
@@ -2080,6 +2216,7 @@ CGTownInstance::EFortLevel CGTownInstance::fortLevel() const //0 - none, 1 - for
 
 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))
@@ -2170,20 +2307,29 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const
 	return ret;
 }
 
-int CGTownInstance::dailyIncome() const
+TResources CGTownInstance::dailyIncome() const
 {
-	int ret = 0;
-	if (hasBuilt(BuildingID::GRAIL))
-		ret+=5000;
+	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;
+		}
+	
+	}
 
-	if (hasBuilt(BuildingID::CAPITOL))
-		ret+=4000;
-	else if (hasBuilt(BuildingID::CITY_HALL))
-		ret+=2000;
-	else if (hasBuilt(BuildingID::TOWN_HALL))
-		ret+=1000;
-	else if (hasBuilt(BuildingID::VILLAGE_HALL))
-		ret+=500;
 	return ret;
 }
 bool CGTownInstance::hasFort() const
@@ -2325,13 +2471,15 @@ 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()%4+2;//bonus to random rare resource
+			int resID = rand.nextInt(2, 5); //bonus to random rare resource
 			resID = (resID==2)?1:resID;
-			int resVal = rand()%4+1;//with size 1..4
+			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);
@@ -2356,11 +2504,11 @@ void CGTownInstance::newTurn() const
 				}
 				if (nativeCrits.size())
 				{
-					SlotID pos = nativeCrits[rand() % nativeCrits.size()];
+					SlotID pos = *RandomGeneratorUtil::nextItem(nativeCrits, rand);
 					StackLocation sl(this, pos);
 
 					const CCreature *c = getCreature(pos);
-					if (rand()%100 < 90 || c->upgrades.empty()) //increase number if no upgrade available
+					if (rand.nextInt(99) < 90 || c->upgrades.empty()) //increase number if no upgrade available
 					{
 						cb->changeStackCount(sl, c->growth);
 					}
@@ -2369,9 +2517,9 @@ void CGTownInstance::newTurn() const
 						cb->changeStackType(sl, VLC->creh->creatures[*c->upgrades.begin()]);
 					}
 				}
-				if ((stacksCount() < GameConstants::ARMY_SIZE && rand()%100 < 25) || Slots().empty()) //add new stack
+				if ((stacksCount() < GameConstants::ARMY_SIZE && rand.nextInt(99) < 25) || Slots().empty()) //add new stack
 				{
-					int i = rand() % std::min (GameConstants::CREATURES_PER_TOWN, cb->getDate(Date::MONTH)<<1);
+					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];
@@ -3010,7 +3158,7 @@ void CGCreature::initObj()
 			amount = 1;
 		}
 	}
-	formation.randomFormation = rand();
+	formation.randomFormation = cb->gameState()->getRandomGenerator().nextInt();
 
 	temppower = stacks[SlotID(0)]->count * 1000;
 	refusedJoining = false;
@@ -3226,10 +3374,10 @@ void CGCreature::fight( const CGHeroInstance *h ) const
 		if (formation.randomFormation % 100 < 50) //upgrade
 		{
 			SlotID slotId = SlotID(stacks.size() / 2);
-			if(ui32 upgradesSize = getStack(slotId).type->upgrades.size())
+			const auto & upgrades = getStack(slotId).type->upgrades;
+			if(!upgrades.empty())
 			{
-				auto it = getStack(slotId).type->upgrades.cbegin(); //pick random in case there are more
-				std::advance (it, rand() % upgradesSize);
+				auto it = RandomGeneratorUtil::nextItem(upgrades, cb->gameState()->getRandomGenerator());
 				cb->changeStackType(StackLocation(this, slotId), VLC->creh->creatures[*it]);
 			}
 		}
@@ -3520,17 +3668,28 @@ void CGTeleport::onHeroVisit( const CGHeroInstance * h ) const
 	ObjectInstanceID destinationid;
 	switch(ID)
 	{
-	case Obj::MONOLITH1: //one way - find corresponding exit monolith
-		if(vstd::contains(objs,Obj::MONOLITH2) && vstd::contains(objs[Obj::MONOLITH2],subID) && objs[Obj::MONOLITH2][subID].size())
-			destinationid = objs[Obj::MONOLITH2][subID][rand()%objs[Obj::MONOLITH2][subID].size()];
+	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::MONOLITH3://two way monolith - pick any other one
+	}
+	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)
 		{
-			while ((destinationid = objs[ID][subID][rand()%objs[ID][subID].size()]) == id); //choose another exit
+			//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))
@@ -3581,9 +3740,8 @@ void CGTeleport::onHeroVisit( const CGHeroInstance * h ) const
 	if (ID == Obj::WHIRLPOOL)
 	{
 		std::set<int3> tiles = cb->getObj(destinationid)->getBlockedPos();
-		auto it = tiles.begin();
-		std::advance (it, rand() % tiles.size()); //picking random element of set is tiring
-		cb->moveHero (h->id, *it + int3(1,0,0), true);
+		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);
@@ -3627,13 +3785,13 @@ void CGTeleport::postInit() //matches subterranean gates into pairs
 		const CGObjectInstance *cur = gatesSplit[0][i];
 
 		//find nearest underground exit
-		std::pair<int,double> best(-1,150000); //pair<pos_in_vector, distance>
+		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;
-			double hlp = checked->pos.dist2d(cur->pos);
+			si32 hlp = checked->pos.dist2dSQ(cur->pos);
 			if(hlp < best.second)
 			{
 				best.first = j;
@@ -3647,9 +3805,7 @@ void CGTeleport::postInit() //matches subterranean gates into pairs
 			gatesSplit[1][best.first] = nullptr;
 		}
 		else
-		{
 			gates.push_back(std::make_pair(cur->id, ObjectInstanceID()));
-		}
 	}
 	objs.erase(Obj::SUBTERRANEAN_GATE);
 }

+ 42 - 36
lib/CObjectHandler.h

@@ -8,6 +8,7 @@
 #include "int3.h"
 #include "GameConstants.h"
 #include "ResourceSet.h"
+#include "CRandomGenerator.h"
 
 /*
  * CObjectHandler.h, part of VCMI engine
@@ -225,6 +226,7 @@ public:
 	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;
@@ -388,8 +390,8 @@ public:
 	struct DLL_LINKAGE SecondarySkillsInfo
 	{
 		//skills are determined, initialized at map start
-		//FIXME: remove mutable?
-		mutable std::minstd_rand distribution;
+		//FIXME remove mutable
+		mutable CRandomGenerator rand;
 		ui8 magicSchoolCounter;
 		ui8 wisdomCounter;
 
@@ -398,39 +400,10 @@ public:
 
 		template <typename Handler> void serialize(Handler &h, const int version)
 		{
-			h & magicSchoolCounter & wisdomCounter;
-			if (h.saving)
-			{
-				std::ostringstream stream;
-				stream << distribution;
-				std::string str = stream.str();
-				h & str;
-			}
-			else
-			{
-				std::string str;
-				h & str;
-				std::istringstream stream(str);
-				stream >> distribution;
-			}
+			h & magicSchoolCounter & wisdomCounter & rand;
 		}
 	} skillsInfo;
 
-	//////////////////////////////////////////////////////////////////////////
-
-
-	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 & visitedObjects;
-		h & visitedTown & boat;
-		h & type & specialty & commander;
-		BONUS_TREE_DESERIALIZATION_FIX
-		//visitied town pointer will be restored by map serialization method
-	}
-	//////////////////////////////////////////////////////////////////////////
 	int3 getSightCenter() const; //"center" tile from which the sight distance is calculated
 	int getSightRadious() const; //sight distance (should be used if player-owned structure)
 	//////////////////////////////////////////////////////////////////////////
@@ -452,9 +425,28 @@ public:
 	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
-	bool canLearnSkill() const; ///true if hero has free secondary skill slot
+	void levelUp(std::vector<SecondarySkill> skills);
 
 	int maxMovePoints(bool onLand) const;
 	int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false) const;
@@ -471,8 +463,6 @@ public:
 	CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const;
 	void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const;
 	ECanDig diggingStatus() const; //0 - can dig; 1 - lack of movement; 2 -
-	std::vector<SecondarySkill> levelUpProposedSkills() const;
-	bool gainsLevel() const; //true if hero has lower level than should upon his experience
 
 	//////////////////////////////////////////////////////////////////////////
 
@@ -509,6 +499,22 @@ public:
 	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
@@ -719,7 +725,7 @@ public:
 	//checks if building is constructed and town has same subID
 	bool hasBuilt(BuildingID buildingID) const;
 	bool hasBuilt(BuildingID buildingID, int townID) const;
-	int dailyIncome() const; //calculates daily income of this town
+	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;

+ 86 - 0
lib/CRandomGenerator.cpp

@@ -0,0 +1,86 @@
+
+/*
+ * CRandomGenerator.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 "CRandomGenerator.h"
+
+boost::thread_specific_ptr<CRandomGenerator> CRandomGenerator::defaultRand;
+
+CRandomGenerator::CRandomGenerator()
+{
+	resetSeed();
+}
+
+void CRandomGenerator::setSeed(int seed)
+{
+	rand.seed(seed);
+}
+
+void CRandomGenerator::resetSeed()
+{
+	boost::hash<std::string> stringHash;
+	auto threadIdHash = stringHash(boost::lexical_cast<std::string>(boost::this_thread::get_id()));
+	setSeed(threadIdHash * std::time(nullptr));
+}
+
+TRandI CRandomGenerator::getIntRange(int lower, int upper)
+{
+	return boost::bind(TIntDist(lower, upper), boost::ref(rand));
+}
+
+int CRandomGenerator::nextInt(int upper)
+{
+	return getIntRange(0, upper)();
+}
+
+int CRandomGenerator::nextInt(int lower, int upper)
+{
+	return getIntRange(lower, upper)();
+}
+
+int CRandomGenerator::nextInt()
+{
+	return TIntDist()(rand);
+}
+
+TRand CRandomGenerator::getDoubleRange(double lower, double upper)
+{
+	return boost::bind(TRealDist(lower, upper), boost::ref(rand));
+}
+
+double CRandomGenerator::nextDouble(double upper)
+{
+	return getDoubleRange(0, upper)();
+}
+
+double CRandomGenerator::nextDouble(double lower, double upper)
+{
+	return getDoubleRange(lower, upper)();
+}
+
+double CRandomGenerator::nextDouble()
+{
+	return TRealDist()(rand);
+}
+
+CRandomGenerator & CRandomGenerator::getDefault()
+{
+	if(!defaultRand.get())
+	{
+		defaultRand.reset(new CRandomGenerator());
+	}
+	return *defaultRand.get();
+}
+
+TGenerator & CRandomGenerator::getStdGenerator()
+{
+	return rand;
+}

+ 46 - 44
lib/CRandomGenerator.h

@@ -19,82 +19,84 @@ typedef std::function<double()> TRand;
 
 /// The random generator randomly generates integers and real numbers("doubles") between
 /// a given range. This is a header only class and mainly a wrapper for
-/// convenient usage of the standard random API.
-class CRandomGenerator : boost::noncopyable
+/// convenient usage of the standard random API. An instance of this RNG is not thread safe.
+class DLL_LINKAGE CRandomGenerator : boost::noncopyable
 {
 public:
-	/// Seeds the generator with the current time by default.
-	CRandomGenerator() 
-	{
-		rand.seed(static_cast<unsigned long>(std::time(nullptr)));
-	}
+	/// Seeds the generator by default with the product of the current time in milliseconds and the
+	/// current thread ID.
+	CRandomGenerator();
 
-	void setSeed(int seed)
-	{
-		rand.seed(seed);
-	}
+	void setSeed(int seed);
+
+	/// Resets the seed to the product of the current time in milliseconds and the
+	/// current thread ID.
+	void resetSeed();
 
 	/// Generate several integer numbers within the same range.
 	/// e.g.: auto a = gen.getIntRange(0,10); a(); a(); a();
 	/// requires: lower <= upper
-	TRandI getIntRange(int lower, int upper)
-	{
-		return boost::bind(TIntDist(lower, upper), boost::ref(rand));
-	}
+	TRandI getIntRange(int lower, int upper);
 	
 	/// Generates an integer between 0 and upper.
 	/// requires: 0 <= upper
-	int nextInt(int upper)
-	{
-		return getIntRange(0, upper)();
-	}
+	int nextInt(int upper);
 
 	/// requires: lower <= upper
-	int nextInt(int lower, int upper)
-	{
-		return getIntRange(lower, upper)();
-	}
+	int nextInt(int lower, int upper);
 	
 	/// Generates an integer between 0 and the maximum value it can hold.
-	int nextInt()
-	{
-		return TIntDist()(rand);
-	}
+	int nextInt();
 
 	/// Generate several double/real numbers within the same range.
 	/// e.g.: auto a = gen.getDoubleRange(4.5,10.2); a(); a(); a();
 	/// requires: lower <= upper
-	TRand getDoubleRange(double lower, double upper)
-	{
-		return boost::bind(TRealDist(lower, upper), boost::ref(rand));
-	}
+	TRand getDoubleRange(double lower, double upper);
 	
 	/// Generates a double between 0 and upper.
 	/// requires: 0 <= upper
-	double nextDouble(double upper)
-	{
-		return getDoubleRange(0, upper)();
-	}
+	double nextDouble(double upper);
 
 	/// requires: lower <= upper
-	double nextDouble(double lower, double upper)
-	{
-		return getDoubleRange(lower, upper)();
-	}
+	double nextDouble(double lower, double upper);
 
 	/// Generates a double between 0.0 and 1.0.
-	double nextDouble()
-	{
-		return TRealDist()(rand);
-	}
+	double nextDouble();
+
+	/// Gets a globally accessible RNG which will be constructed once per thread. For the
+	/// seed a combination of the thread ID and current time in milliseconds will be used.
+	static CRandomGenerator & getDefault();
+
+	/// Provide method so that this RNG can be used with legacy std:: API
+	TGenerator & getStdGenerator();
 
 private:
 	TGenerator rand;
+	static boost::thread_specific_ptr<CRandomGenerator> defaultRand;
+
+public:
+	template <typename Handler>
+	void serialize(Handler & h, const int version)
+	{
+		if(h.saving)
+		{
+			std::ostringstream stream;
+			stream << rand;
+			std::string str = stream.str();
+			h & str;
+		}
+		else
+		{
+			std::string str;
+			h & str;
+			std::istringstream stream(str);
+			stream >> rand;
+		}
+	}
 };
 
 namespace RandomGeneratorUtil
 {
-	/// Gets an iterator to an element of a nonempty container randomly. Undefined behaviour if container is empty.
 	template<typename Container>
 	auto nextItem(const Container & container, CRandomGenerator & rand) -> decltype(std::begin(container))
 	{

+ 5 - 2
lib/CScriptingModule.h

@@ -2,7 +2,6 @@
 
 
 #include "IGameEventsReceiver.h"
-#include "IGameCallback.h"
 
 /*
  * CScriptingModule.h, part of VCMI engine
@@ -14,6 +13,9 @@
  *
  */
 
+class IGameEventRealizer;
+class CPrivilagedInfoCallback;
+
 class CScriptingModule : public IGameEventsReceiver, public IBattleEventsReceiver
 {
 public:
@@ -24,4 +26,5 @@ public:
 
 	CScriptingModule(){}
 	virtual ~CScriptingModule(){}
-};
+};
+

+ 164 - 133
lib/CSpellHandler.cpp

@@ -22,7 +22,7 @@
 
 namespace SpellConfig
 {
-    static const std::string LEVEL_NAMES[] = {"none", "basic", "advanced", "expert"};
+	static const std::string LEVEL_NAMES[] = {"none", "basic", "advanced", "expert"};
 
 }
 
@@ -131,7 +131,7 @@ namespace SRSLPraserHelpers
 }
 
 CSpell::LevelInfo::LevelInfo()
-    :description(""),cost(0),power(0),AIValue(0),smartTarget(true),range("0")
+	:description(""),cost(0),power(0),AIValue(0),smartTarget(true),range("0")
 {
 
 }
@@ -152,7 +152,7 @@ CSpell::CSpell():
 	isRising(false), isDamage(false), isOffensive(false),
 	targetType(ETargetType::NO_TARGET)
 {
-    levels.resize(GameConstants::SPELL_SCHOOL_LEVELS);
+	levels.resize(GameConstants::SPELL_SCHOOL_LEVELS);
 }
 
 CSpell::~CSpell()
@@ -163,8 +163,8 @@ const CSpell::LevelInfo & CSpell::getLevelInfo(const int level) const
 {
 	if(level < 0 || level >= GameConstants::SPELL_SCHOOL_LEVELS)
 	{
-        logGlobal->errorStream() << __FUNCTION__ << " invalid school level " << level;
-        throw new std::runtime_error("Invalid school level");
+		logGlobal->errorStream() << __FUNCTION__ << " invalid school level " << level;
+		throw new std::runtime_error("Invalid school level");
 	}
 
 	return levels.at(level);
@@ -173,7 +173,7 @@ const CSpell::LevelInfo & CSpell::getLevelInfo(const int level) const
 
 std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const
 {
-    using namespace SRSLPraserHelpers;
+	using namespace SRSLPraserHelpers;
 
 	std::vector<BattleHex> ret;
 
@@ -280,15 +280,16 @@ CSpell::ETargetType CSpell::getTargetType() const
 
 const CSpell::TargetInfo CSpell::getTargetInfo(const int level) const
 {
-    TargetInfo info;
+	TargetInfo info;
 
-    auto & levelInfo = getLevelInfo(level);
+	auto & levelInfo = getLevelInfo(level);
 
-    info.type = getTargetType();
-    info.smart = levelInfo.smartTarget;
-    info.massive = levelInfo.range == "X";
+	info.type = getTargetType();
+	info.smart = levelInfo.smartTarget;
+	info.massive = levelInfo.range == "X";
+	info.onlyAlive = !isRisingSpell();
 
-    return info;
+	return info;
 }
 
 
@@ -317,6 +318,12 @@ bool CSpell::isNegative() const
 	return positiveness == NEGATIVE;
 }
 
+bool CSpell::isNeutral() const
+{
+	return positiveness == NEUTRAL;
+}
+
+
 bool CSpell::isRisingSpell() const
 {
 	return isRising;
@@ -334,7 +341,7 @@ bool CSpell::isOffensiveSpell() const
 
 bool CSpell::isSpecialSpell() const
 {
-    return isSpecial;
+	return isSpecial;
 }
 
 bool CSpell::hasEffects() const
@@ -356,12 +363,12 @@ const std::string& CSpell::getCastSound() const
 
 si32 CSpell::getCost(const int skillLevel) const
 {
-    return getLevelInfo(skillLevel).cost;
+	return getLevelInfo(skillLevel).cost;
 }
 
 si32 CSpell::getPower(const int skillLevel) const
 {
-    return getLevelInfo(skillLevel).power;
+	return getLevelInfo(skillLevel).power;
 }
 
 //si32 CSpell::calculatePower(const int skillLevel) const
@@ -371,11 +378,11 @@ si32 CSpell::getPower(const int skillLevel) const
 
 si32 CSpell::getProbability(const TFaction factionId) const
 {
-    if(!vstd::contains(probabilities,factionId))
-    {
-        return defaultProbability;
-    }
-    return probabilities.at(factionId);
+	if(!vstd::contains(probabilities,factionId))
+	{
+		return defaultProbability;
+	}
+	return probabilities.at(factionId);
 }
 
 
@@ -383,17 +390,17 @@ void CSpell::getEffects(std::vector<Bonus>& lst, const int level) const
 {
 	if(level < 0 || level >= GameConstants::SPELL_SCHOOL_LEVELS)
 	{
-        logGlobal->errorStream() << __FUNCTION__ << " invalid school level " << level;
+		logGlobal->errorStream() << __FUNCTION__ << " invalid school level " << level;
 		return;
 	}
 
-    const std::vector<Bonus> & effects = levels[level].effects;
+	const std::vector<Bonus> & effects = levels[level].effects;
 
 	if(effects.empty())
-    {
+	{
 		logGlobal->errorStream() << __FUNCTION__ << " This spell ("  + name + ") has no effects for level " << level;
 		return;
-    }
+	}
 
 	lst.reserve(lst.size() + effects.size());
 
@@ -406,26 +413,34 @@ void CSpell::getEffects(std::vector<Bonus>& lst, const int level) const
 bool CSpell::isImmuneBy(const IBonusBearer* obj) const
 {
 	//todo: use new bonus API
-	//1. Check limiters
-	for(auto b : limiters)
+	//1. Check absolute limiters
+	for(auto b : absoluteLimiters)
 	{
 		if (!obj->hasBonusOfType(b))
 			return true;
 	}
 
 	//2. Check absolute immunities
-	//todo: check config: some creatures are unaffected always, for example undead to resurrection.
-    for(auto b : absoluteImmunities)
+	for(auto b : absoluteImmunities)
 	{
 		if (obj->hasBonusOfType(b))
 			return true;
 	}
 
-    //3. Check negation
+	//3. Check negation
+	//FIXME: Orb of vulnerability mechanics is not such trivial
 	if(obj->hasBonusOfType(Bonus::NEGATE_ALL_NATURAL_IMMUNITIES)) //Orb of vulnerability
 		return false;
+		
+	//4. Check negatable limit
+	for(auto b : limiters)
+	{
+		if (!obj->hasBonusOfType(b))
+			return true;
+	}
 
-    //4. Check negatable immunities
+
+	//5. Check negatable immunities
 	for(auto b : immunities)
 	{
 		if (obj->hasBonusOfType(b))
@@ -444,7 +459,7 @@ bool CSpell::isImmuneBy(const IBonusBearer* obj) const
 		return false;
 	};
 
-    //4. Check elemental immunities
+	//6. Check elemental immunities
 	if(fire)
 	{
 		if(battleTestElementalImmunity(Bonus::FIRE_IMMUNITY))
@@ -480,23 +495,23 @@ bool CSpell::isImmuneBy(const IBonusBearer* obj) const
 
 void CSpell::setIsOffensive(const bool val)
 {
-   isOffensive = val;
+	isOffensive = val;
 
-   if(val)
-   {
-       positiveness = CSpell::NEGATIVE;
-       isDamage = true;
-   }
+	if(val)
+	{
+		positiveness = CSpell::NEGATIVE;
+		isDamage = true;
+	}
 }
 
 void CSpell::setIsRising(const bool val)
 {
-    isRising = val;
+	isRising = val;
 
-    if(val)
-    {
-        positiveness = CSpell::POSITIVE;
-    }
+	if(val)
+	{
+		positiveness = CSpell::POSITIVE;
+	}
 }
 
 
@@ -517,99 +532,99 @@ CSpellHandler::CSpellHandler()
 
 std::vector<JsonNode> CSpellHandler::loadLegacyData(size_t dataSize)
 {
-    using namespace SpellConfig;
-    std::vector<JsonNode> legacyData;
+	using namespace SpellConfig;
+	std::vector<JsonNode> legacyData;
 
 	CLegacyConfigParser parser("DATA/SPTRAITS.TXT");
 
 	auto readSchool = [&](JsonMap& schools, const std::string& name)
 	{
-        if (parser.readString() == "x")
-        {
-            schools[name].Bool() = true;
-        }
+		if (parser.readString() == "x")
+		{
+			schools[name].Bool() = true;
+		}
 	};
 
 	auto read = [&,this](bool combat, bool ability)
 	{
 		do
 		{
-		    JsonNode lineNode(JsonNode::DATA_STRUCT);
+			JsonNode lineNode(JsonNode::DATA_STRUCT);
 
-		    const si32 id = legacyData.size();
+			const si32 id = legacyData.size();
 
-            lineNode["index"].Float() = id;
-            lineNode["type"].String() = ability ? "ability" : (combat ? "combat" : "adventure");
+			lineNode["index"].Float() = id;
+			lineNode["type"].String() = ability ? "ability" : (combat ? "combat" : "adventure");
 
-            lineNode["name"].String() = parser.readString();
+			lineNode["name"].String() = parser.readString();
 
-            parser.readString(); //ignored unused abbreviated name
-            lineNode["level"].Float()      = parser.readNumber();
+			parser.readString(); //ignored unused abbreviated name
+			lineNode["level"].Float()      = parser.readNumber();
 
-            auto& schools = lineNode["school"].Struct();
+			auto& schools = lineNode["school"].Struct();
 
-            readSchool(schools, "earth");
-            readSchool(schools, "water");
-            readSchool(schools, "fire");
-            readSchool(schools, "air");
+			readSchool(schools, "earth");
+			readSchool(schools, "water");
+			readSchool(schools, "fire");
+			readSchool(schools, "air");
 
-            auto& levels = lineNode["levels"].Struct();
+			auto& levels = lineNode["levels"].Struct();
 
-            auto getLevel = [&](const size_t idx)->JsonMap&
-            {
-                assert(idx < GameConstants::SPELL_SCHOOL_LEVELS);
-                return levels[LEVEL_NAMES[idx]].Struct();
-            };
+			auto getLevel = [&](const size_t idx)->JsonMap&
+			{
+				assert(idx < GameConstants::SPELL_SCHOOL_LEVELS);
+				return levels[LEVEL_NAMES[idx]].Struct();
+			};
 
-            auto costs = parser.readNumArray<si32>(GameConstants::SPELL_SCHOOL_LEVELS);
-            lineNode["power"].Float() = parser.readNumber();
-            auto powers = parser.readNumArray<si32>(GameConstants::SPELL_SCHOOL_LEVELS);
+			auto costs = parser.readNumArray<si32>(GameConstants::SPELL_SCHOOL_LEVELS);
+			lineNode["power"].Float() = parser.readNumber();
+			auto powers = parser.readNumArray<si32>(GameConstants::SPELL_SCHOOL_LEVELS);
 
-            auto& chances = lineNode["gainChance"].Struct();
+			auto& chances = lineNode["gainChance"].Struct();
 
-            for(size_t i = 0; i < GameConstants::F_NUMBER ; i++){
-                chances[ETownType::names[i]].Float() = parser.readNumber();
-            }
+			for(size_t i = 0; i < GameConstants::F_NUMBER ; i++){
+				chances[ETownType::names[i]].Float() = parser.readNumber();
+			}
 
-            auto AIVals = parser.readNumArray<si32>(GameConstants::SPELL_SCHOOL_LEVELS);
+			auto AIVals = parser.readNumArray<si32>(GameConstants::SPELL_SCHOOL_LEVELS);
 
-            std::vector<std::string> descriptions;
-            for(size_t i = 0; i < GameConstants::SPELL_SCHOOL_LEVELS ; i++)
-                descriptions.push_back(parser.readString());
+			std::vector<std::string> descriptions;
+			for(size_t i = 0; i < GameConstants::SPELL_SCHOOL_LEVELS ; i++)
+				descriptions.push_back(parser.readString());
 
-            std::string attributes = parser.readString();
+			std::string attributes = parser.readString();
 
-            std::string targetType = "NO_TARGET";
+			std::string targetType = "NO_TARGET";
 
-            if(attributes.find("CREATURE_TARGET_1") != std::string::npos
-                || attributes.find("CREATURE_TARGET_2") != std::string::npos)
-                targetType = "CREATURE_EXPERT_MASSIVE";
-            else if(attributes.find("CREATURE_TARGET") != std::string::npos)
-                targetType = "CREATURE";
-            else if(attributes.find("OBSTACLE_TARGET") != std::string::npos)
-                targetType = "OBSTACLE";
+			if(attributes.find("CREATURE_TARGET_1") != std::string::npos
+				|| attributes.find("CREATURE_TARGET_2") != std::string::npos)
+				targetType = "CREATURE_EXPERT_MASSIVE";
+			else if(attributes.find("CREATURE_TARGET") != std::string::npos)
+				targetType = "CREATURE";
+			else if(attributes.find("OBSTACLE_TARGET") != std::string::npos)
+				targetType = "OBSTACLE";
 
-            //save parsed level specific data
-            for(size_t i = 0; i < GameConstants::SPELL_SCHOOL_LEVELS; i++)
-            {
-                auto& level = getLevel(i);
-                level["description"].String() = descriptions[i];
-                level["cost"].Float() = costs[i];
-                level["power"].Float() = powers[i];
-                level["aiValue"].Float() = AIVals[i];
-            }
+			//save parsed level specific data
+			for(size_t i = 0; i < GameConstants::SPELL_SCHOOL_LEVELS; i++)
+			{
+				auto& level = getLevel(i);
+				level["description"].String() = descriptions[i];
+				level["cost"].Float() = costs[i];
+				level["power"].Float() = powers[i];
+				level["aiValue"].Float() = AIVals[i];
+			}
 
-            if(targetType == "CREATURE_EXPERT_MASSIVE")
-            {
-                lineNode["targetType"].String() = "CREATURE";
-                getLevel(3)["range"].String() = "X";
-            }
-            else
-            {
-                lineNode["targetType"].String() = targetType;
-            }
+			if(targetType == "CREATURE_EXPERT_MASSIVE")
+			{
+				lineNode["targetType"].String() = "CREATURE";
+				getLevel(3)["range"].String() = "X";
+			}
+			else
+			{
+				lineNode["targetType"].String() = targetType;
+			}
 
-		    legacyData.push_back(lineNode);
+			legacyData.push_back(lineNode);
 
 
 		}
@@ -633,9 +648,9 @@ std::vector<JsonNode> CSpellHandler::loadLegacyData(size_t dataSize)
 	//clone Acid Breath attributes for Acid Breath damage effect
 	JsonNode temp = legacyData[SpellID::ACID_BREATH_DEFENSE];
 	temp["index"].Float() = SpellID::ACID_BREATH_DAMAGE;
-    legacyData.push_back(temp);
+	legacyData.push_back(temp);
 
-    objects.resize(legacyData.size());
+	objects.resize(legacyData.size());
 
 	return legacyData;
 }
@@ -751,23 +766,23 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json)
 		logGlobal->errorStream() << "No positiveness specified, assumed NEUTRAL";
 	}
 
-    spell->isSpecial = flags["special"].Bool();
+	spell->isSpecial = flags["special"].Bool();
 
-    auto findBonus = [&](std::string name, std::vector<Bonus::BonusType> &vec)
-    {
-        auto it = bonusNameMap.find(name);
-        if(it == bonusNameMap.end())
-        {
-            logGlobal->errorStream() << spell->name << ": invalid bonus name" << name;
-        }
-        else
-        {
-            vec.push_back((Bonus::BonusType)it->second);
-        }
-    };
+	auto findBonus = [&](std::string name, std::vector<Bonus::BonusType> &vec)
+	{
+		auto it = bonusNameMap.find(name);
+		if(it == bonusNameMap.end())
+		{
+			logGlobal->errorStream() << spell->name << ": invalid bonus name" << name;
+		}
+		else
+		{
+			vec.push_back((Bonus::BonusType)it->second);
+		}
+	};
 
-    auto readBonusStruct = [&](std::string name, std::vector<Bonus::BonusType> &vec)
-    {
+	auto readBonusStruct = [&](std::string name, std::vector<Bonus::BonusType> &vec)
+	{
 		for(auto bonusData: json[name].Struct())
 		{
 			const std::string bonusId = bonusData.first;
@@ -776,16 +791,15 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json)
 			if(flag)
 				findBonus(bonusId, vec);
 		}
-    };
-
-    readBonusStruct("immunity", spell->immunities);
-
-    readBonusStruct("absoluteImmunity", spell->absoluteImmunities);
+	};
 
-    readBonusStruct("limit", spell->limiters);
+	readBonusStruct("immunity", spell->immunities);
+	readBonusStruct("absoluteImmunity", spell->absoluteImmunities);
+	readBonusStruct("limit", spell->limiters);	
+	readBonusStruct("absoluteLimit", spell->absoluteLimiters);
 
 
-    const JsonNode & graphicsNode = json["graphics"];
+	const JsonNode & graphicsNode = json["graphics"];
 
 	spell->iconImmune = graphicsNode["iconImmune"].String();
 	spell->iconBook = graphicsNode["iconBook"].String();
@@ -800,9 +814,9 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json)
 	spell->castSound = soundsNode["cast"].String();
 
 
-    //load level attributes
+	//load level attributes
 
-    const int levelsCount = GameConstants::SPELL_SCHOOL_LEVELS;
+	const int levelsCount = GameConstants::SPELL_SCHOOL_LEVELS;
 
 	for(int levelIndex = 0; levelIndex < levelsCount; levelIndex++)
 	{
@@ -838,18 +852,35 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json)
 		
 	}
 
-    return spell;
+	return spell;
 }
 
 void CSpellHandler::afterLoadFinalization()
 {
-    //FIXME: it is a bad place for this code, should refactor loadFromJson to know object id during loading
+	//FIXME: it is a bad place for this code, should refactor loadFromJson to know object id during loading
 	for(auto spell: objects)
 		for(auto & level: spell->levels)
 			for(auto & bonus: level.effects)
 				bonus.sid = spell->id;
 }
 
+void CSpellHandler::beforeValidate(JsonNode & object)
+{
+	//handle "base" level info
+	
+	JsonNode& levels = object["levels"];
+	JsonNode& base = levels["base"];
+	
+	auto inheritNode = [&](const std::string & name){
+		JsonUtils::inherit(levels[name],base);
+	};
+	
+	inheritNode("none");
+	inheritNode("basic");
+	inheritNode("advanced");
+	inheritNode("expert");
+
+}
 
 
 CSpellHandler::~CSpellHandler()

+ 54 - 50
lib/CSpellHandler.h

@@ -23,43 +23,44 @@ struct BattleHex;
 class DLL_LINKAGE CSpell
 {
 public:
-    struct LevelInfo
-    {
-        std::string description; //descriptions of spell for skill level
-        si32 cost; //per skill level: 0 - none, 1 - basic, etc
-        si32 power; //per skill level: 0 - none, 1 - basic, etc
-        si32 AIValue; //AI values: per skill level: 0 - none, 1 - basic, etc
-
-        bool smartTarget;
-        std::string range;
-
-        std::vector<Bonus> effects;
-
-        LevelInfo();
-        ~LevelInfo();
-
-        template <typename Handler> void serialize(Handler &h, const int version)
-        {
-            h & description & cost & power & AIValue & smartTarget & range & effects;
-        }
-    };
-
-    /** \brief Low level accessor. Don`t use it if absolutely necessary
-     *
-     * \param level. spell school level
-     * \return Spell level info structure
-     *
-     */
-    const CSpell::LevelInfo& getLevelInfo(const int level) const;
+	struct LevelInfo
+	{
+		std::string description; //descriptions of spell for skill level
+		si32 cost; //per skill level: 0 - none, 1 - basic, etc
+		si32 power; //per skill level: 0 - none, 1 - basic, etc
+		si32 AIValue; //AI values: per skill level: 0 - none, 1 - basic, etc
+
+		bool smartTarget;
+		std::string range;
+
+		std::vector<Bonus> effects;
+
+		LevelInfo();
+		~LevelInfo();
+
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & description & cost & power & AIValue & smartTarget & range & effects;
+		}
+	};
+
+	/** \brief Low level accessor. Don`t use it if absolutely necessary
+	 *
+	 * \param level. spell school level
+	 * \return Spell level info structure
+	 *
+	 */
+	const CSpell::LevelInfo& getLevelInfo(const int level) const;
 public:
 	enum ETargetType {NO_TARGET, CREATURE, OBSTACLE};
 	enum ESpellPositiveness {NEGATIVE = -1, NEUTRAL = 0, POSITIVE = 1};
 
 	struct TargetInfo
 	{
-	    ETargetType type;
-	    bool smart;
-	    bool massive;
+		ETargetType type;
+		bool smart;
+		bool massive;
+		bool onlyAlive;
 	};
 
 	SpellID id;
@@ -96,6 +97,7 @@ public:
 
 	bool isPositive() const;
 	bool isNegative() const;
+	bool isNeutral() const;
 
 	bool isRisingSpell() const;
 	bool isDamageSpell() const;
@@ -110,12 +112,12 @@ public:
 
 	si32 getCost(const int skillLevel) const;
 
-    /**
-	* Returns spell level power, base power ignored
-	*/
+	/**
+	 * Returns spell level power, base power ignored
+	 */
 	si32 getPower(const int skillLevel) const;
 
-//    /**
+//	/**
 //	* Returns spell power, taking base power into account
 //	*/
 //	si32 calculatePower(const int skillLevel) const;
@@ -124,8 +126,8 @@ public:
 	si32 getProbability(const TFaction factionId) const;
 
 	/**
-	* Returns resource name of icon for SPELL_IMMUNITY bonus
-	*/
+	 * Returns resource name of icon for SPELL_IMMUNITY bonus
+	 */
 	const std::string& getIconImmune() const;
 
 	const std::string& getCastSound() const;
@@ -133,29 +135,29 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & identifier & id & name & level & earth & water & fire & air & power
-            & probabilities  & attributes & combatSpell & creatureAbility & positiveness & counteredSpells & mainEffectAnim;
+		  & probabilities  & attributes & combatSpell & creatureAbility & positiveness & counteredSpells & mainEffectAnim;
 		h & isRising & isDamage & isOffensive;
 		h & targetType;
-		h & immunities & limiters;
+		h & immunities & limiters & absoluteImmunities & absoluteLimiters;
 		h & iconImmune;
-        h & absoluteImmunities & defaultProbability;
+		h & defaultProbability;
 
-        h & isSpecial;
+		h & isSpecial;
 
-        h & castSound & iconBook & iconEffect & iconScenarioBonus & iconScroll;
+		h & castSound & iconBook & iconEffect & iconScenarioBonus & iconScroll;
 
-        h & levels;
+		h & levels;
 
 	}
 	friend class CSpellHandler;
 	friend class Graphics;
 
 private:
-    void setIsOffensive(const bool val);
-    void setIsRising(const bool val);
+	void setIsOffensive(const bool val);
+	void setIsRising(const bool val);
 
 private:
-    si32 defaultProbability;
+	si32 defaultProbability;
 
 	bool isRising;
 	bool isDamage;
@@ -169,6 +171,7 @@ private:
 	std::vector<Bonus::BonusType> immunities; //any of these grants immunity
 	std::vector<Bonus::BonusType> absoluteImmunities; //any of these grants immunity, can't be negated
 	std::vector<Bonus::BonusType> limiters; //all of them are required to be affected
+	std::vector<Bonus::BonusType> absoluteLimiters; //all of them are required to be affected, can't be negated
 
 	///graphics related stuff
 
@@ -198,10 +201,11 @@ public:
 	CSpellHandler();
 	virtual ~CSpellHandler();
 
-    ///IHandler base
+	///IHandler base
 
-    std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
-    void afterLoadFinalization() override;
+	std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
+	void afterLoadFinalization() override;
+	void beforeValidate(JsonNode & object) override;
 
 	/**
 	 * Gets a list of default allowed spells. OH3 spells are all allowed by default.
@@ -217,5 +221,5 @@ public:
 		h & objects ;
 	}
 protected:
-    CSpell * loadFromJson(const JsonNode & json) override;
+	CSpell * loadFromJson(const JsonNode & json) override;
 };

+ 58 - 0
lib/CTownHandler.cpp

@@ -105,9 +105,12 @@ JsonNode readBuilding(CLegacyConfigParser & parser)
 	for(const std::string & resID : GameConstants::RESOURCE_NAMES)
 		cost[resID].Float() = parser.readNumber();
 
+
+
 	cost.Struct().erase("mithril"); // erase mithril to avoid confusing validator
 
 	parser.endLine();
+
 	return ret;
 }
 
@@ -303,6 +306,36 @@ void CTownHandler::loadBuilding(CTown &town, const std::string & stringID, const
 	ret->name = source["name"].String();
 	ret->description = source["description"].String();
 	ret->resources = TResources(source["cost"]);
+	ret->produce =   TResources(source["produce"]);
+
+	//for compatibility with older town mods
+	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
+			{
+				ret->produce[Res::WOOD] = 1;
+				ret->produce[Res::ORE] = 1;
+			}
+		}
+		}
+	}
 
 	loadBuildingRequirements(town, *ret, source["requires"]);
 
@@ -510,6 +543,17 @@ void CTownHandler::loadClientData(CTown &town, const JsonNode & source)
 	info.guildWindow = source["guildWindow"].String();
 	info.buildingsIcons = source["buildingsIcons"].String();
 
+	//left for back compatibility - will be removed later
+	if (source["guildBackground"].String() != "")
+		info.guildBackground = source["guildBackground"].String();
+	else
+		info.guildBackground = "TPMAGE.bmp";
+	if (source["tavernVideo"].String() != "")
+	    info.tavernVideo = source["tavernVideo"].String();
+	else
+		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();
@@ -535,6 +579,8 @@ void CTownHandler::loadTown(CTown &town, const JsonNode & source)
 
 	town.moatDamage = source["moatDamage"].Float();
 
+	
+
 	town.mageLevel = source["mageGuild"].Float();
 	town.names = source["names"].convertTo<std::vector<std::string> >();
 
@@ -648,6 +694,7 @@ CFaction * CTownHandler::loadFromJson(const JsonNode &source, std::string identi
 	faction->creatureBg120 = source["creatureBackground"]["120px"].String();
 	faction->creatureBg130 = source["creatureBackground"]["130px"].String();
 
+
 	faction->nativeTerrain = ETerrainType(vstd::find_pos(GameConstants::TERRAIN_NAMES,
 		source["nativeTerrain"].String()));
 	int alignment = vstd::find_pos(EAlignment::names, source["alignment"].String());
@@ -674,6 +721,7 @@ CFaction * CTownHandler::loadFromJson(const JsonNode &source, std::string identi
 void CTownHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
 {
 	auto object = loadFromJson(data, name);
+
 	object->index = factions.size();
 	if (object->town)
 	{
@@ -766,3 +814,13 @@ std::vector<bool> CTownHandler::getDefaultAllowed() const
 	}
 	return allowedFactions;
 }
+std::set<TFaction> CTownHandler::getAllowedFactions() const
+{
+	std::set<TFaction> allowedFactions;
+	auto allowed = getDefaultAllowed();
+	for (size_t i=0; i<allowed.size(); i++)
+		if (allowed[i])
+			allowedFactions.insert(i);
+
+	return allowedFactions;
+}

+ 12 - 4
lib/CTownHandler.h

@@ -25,6 +25,9 @@ class CFaction;
 /// a typical building encountered in every castle ;]
 /// this is structure available to both client and server
 /// contains all mechanics-related data about town structures
+
+
+
 class DLL_LINKAGE CBuilding
 {
 
@@ -36,6 +39,7 @@ public:
 
 	CTown * town; // town this building belongs to
 	TResources resources;
+	TResources produce;
 	TRequired requirements;
 	std::string identifier;
 
@@ -61,7 +65,7 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & identifier & town & bid & resources & name & description & requirements & upgrade & mode;
+		h & identifier & town & bid & resources & produce & name & description & requirements & upgrade & mode;
 	}
 
 	friend class CTownHandler;
@@ -116,6 +120,8 @@ public:
 
 	std::string creatureBg120;
 	std::string creatureBg130;
+	
+	
 
 	std::vector<SPuzzleInfo> puzzleMap;
 
@@ -132,7 +138,7 @@ public:
 	~CTown();
 
 	CFaction * faction;
-
+	
 	std::vector<std::string> names; //names of the town instances
 
 	/// level -> list of creatures on this tier
@@ -170,9 +176,10 @@ public:
 		int icons[2][2];
 		std::string iconSmall[2][2]; /// icon names used during loading
 		std::string iconLarge[2][2];
-
+		std::string tavernVideo;
 		std::string musicTheme;
 		std::string townBackground;
+		std::string guildBackground;
 		std::string guildWindow;
 		std::string buildingsIcons;
 		std::string hallBackground;
@@ -193,7 +200,7 @@ public:
 
 		template <typename Handler> void serialize(Handler &h, const int version)
 		{
-			h & icons & iconSmall & iconLarge & musicTheme & townBackground & guildWindow & buildingsIcons & hallBackground;
+			h & icons & iconSmall & iconLarge & tavernVideo & musicTheme & townBackground & guildBackground & guildWindow & buildingsIcons & hallBackground;
 			h & advMapVillage & advMapCastle & advMapCapitol & hallSlots & structures;
 			h & siegePrefix & siegePositions & siegeShooter;
 		}
@@ -263,6 +270,7 @@ public:
 	void afterLoadFinalization() override;
 
 	std::vector<bool> getDefaultAllowed() const override;
+	std::set<TFaction> getAllowedFactions() const;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{

+ 1 - 1
lib/Connection.h

@@ -28,7 +28,7 @@
 #include "mapping/CCampaignHandler.h" //for CCampaignState
 #include "rmg/CMapGenerator.h" // for CMapGenOptions
 
-const ui32 version = 748;
+const ui32 version = 749;
 const ui32 minSupportedVersion = version;
 
 class CConnection;

+ 15 - 4
lib/GameConstants.h

@@ -14,7 +14,7 @@
 
 namespace GameConstants
 {
-	const std::string VCMI_VERSION = "VCMI 0.95";
+	const std::string VCMI_VERSION = "VCMI 0.95b";
 
 	const int BFIELD_WIDTH = 17;
 	const int BFIELD_HEIGHT = 11;
@@ -450,6 +450,17 @@ namespace EWallState
 	};
 }
 
+namespace ETileType
+{
+	enum ETileType
+	{
+		FREE,
+		POSSIBLE,
+		BLOCKED,
+		USED
+	};
+}
+
 class Obj
 {
 public:
@@ -496,9 +507,9 @@ public:
 		LEAN_TO = 39,
 		LIBRARY_OF_ENLIGHTENMENT = 41,
 		LIGHTHOUSE = 42,
-		MONOLITH1 = 43,
-		MONOLITH2 = 44,
-		MONOLITH3 = 45,
+		MONOLITH_ONE_WAY_ENTRANCE = 43,
+		MONOLITH_ONE_WAY_EXIT = 44,
+		MONOLITH_TWO_WAY = 45,
 		MAGIC_PLAINS1 = 46,
 		SCHOOL_OF_MAGIC = 47,
 		MAGIC_SPRING = 48,

+ 13 - 13
lib/HeroBonus.cpp

@@ -55,18 +55,18 @@ const std::map<std::string, ui16> bonusDurationMap = boost::assign::map_list_of
 const std::map<std::string, Bonus::LimitEffect> bonusLimitEffect = boost::assign::map_list_of
 	BONUS_ITEM(NO_LIMIT)
 	BONUS_ITEM(ONLY_DISTANCE_FIGHT)
-	BONUS_ITEM(ONLY_MELEE_FIGHT)
-	BONUS_ITEM(ONLY_ENEMY_ARMY);
-
-const std::map<std::string, TLimiterPtr> bonusLimiterMap = boost::assign::map_list_of
-	("SHOOTER_ONLY", make_shared<HasAnotherBonusLimiter>(Bonus::SHOOTER))
-	("DRAGON_NATURE", make_shared<HasAnotherBonusLimiter>(Bonus::DRAGON_NATURE))
-	("IS_UNDEAD", make_shared<HasAnotherBonusLimiter>(Bonus::UNDEAD));
-
-const std::map<std::string, TPropagatorPtr> bonusPropagatorMap = boost::assign::map_list_of
-	("BATTLE_WIDE", make_shared<CPropagatorNodeType>(CBonusSystemNode::BATTLE))
-	("VISITED_TOWN_AND_VISITOR", make_shared<CPropagatorNodeType>(CBonusSystemNode::TOWN_AND_VISITOR))
-	("PLAYER_PROPAGATOR", make_shared<CPropagatorNodeType>(CBonusSystemNode::PLAYER))
+	BONUS_ITEM(ONLY_MELEE_FIGHT)
+	BONUS_ITEM(ONLY_ENEMY_ARMY);
+
+const std::map<std::string, TLimiterPtr> bonusLimiterMap = boost::assign::map_list_of
+	("SHOOTER_ONLY", make_shared<HasAnotherBonusLimiter>(Bonus::SHOOTER))
+	("DRAGON_NATURE", make_shared<HasAnotherBonusLimiter>(Bonus::DRAGON_NATURE))
+	("IS_UNDEAD", make_shared<HasAnotherBonusLimiter>(Bonus::UNDEAD));
+
+const std::map<std::string, TPropagatorPtr> bonusPropagatorMap = boost::assign::map_list_of
+	("BATTLE_WIDE", make_shared<CPropagatorNodeType>(CBonusSystemNode::BATTLE))
+	("VISITED_TOWN_AND_VISITOR", make_shared<CPropagatorNodeType>(CBonusSystemNode::TOWN_AND_VISITOR))
+	("PLAYER_PROPAGATOR", make_shared<CPropagatorNodeType>(CBonusSystemNode::PLAYER))
 	("HERO", make_shared<CPropagatorNodeType>(CBonusSystemNode::HERO))
 	("TEAM_PROPAGATOR", make_shared<CPropagatorNodeType>(CBonusSystemNode::TEAM)) //untested
 	("GLOBAL_EFFECT", make_shared<CPropagatorNodeType>(CBonusSystemNode::GLOBAL_EFFECTS)); //untested
@@ -1363,7 +1363,7 @@ int CCreatureTypeLimiter::limit(const BonusLimitationContext &context) const
 	if(!c)
 		return true;
 	return c != creature   &&   (!includeUpgrades || !creature->isMyUpgrade(c));
-	//drop bonus if it's not our creature and (we dont check upgrades or its not our upgrade)
+	//drop bonus if it's not our creature and (we don`t check upgrades or its not our upgrade)
 }
 
 CCreatureTypeLimiter::CCreatureTypeLimiter(const CCreature &Creature, bool IncludeUpgrades /*= true*/)

部分文件因为文件数量过多而无法显示