Bladeren bron

Merged changes from upstream and fixed compilation caused by API changes

Ivan Savenko 11 jaren geleden
bovenliggende
commit
32b6568b65
100 gewijzigde bestanden met toevoegingen van 6013 en 6025 verwijderingen
  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*/)

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