Przeglądaj źródła

Merged bonusesFifthPart branch.
- Rewritten stacks and artifacts handling
- Partially done bonus system features (propagators still not working)
- Duel mode (only battle between two players) for automated testing of battle AIs
- New battle AI: StupidAI. Very simple and minimalistic but should be way better than GeniusAI
- Files from hch/ subfolder moved to folders of their projects

Still there are many bugs but whole thing should be stable enough to allow further development/fixing in the trunk [merging changes became too big pain]

Michał W. Urbańczyk 14 lat temu
rodzic
commit
ec601ffffd
100 zmienionych plików z 7736 dodań i 3939 usunięć
  1. 27 53
      AI/GeniusAI/BattleLogic.cpp
  2. 2 10
      AI/GeniusAI/BattleLogic.h
  3. 47 39
      AI/GeniusAI/CGeniusAI.cpp
  4. 4 4
      AI/GeniusAI/CGeniusAI.h
  5. 265 0
      AI/StupidAI/StupidAI.cpp
  6. 36 0
      AI/StupidAI/StupidAI.h
  7. 121 0
      AI/StupidAI/StupidAI.vcxproj
  8. 30 0
      AI/StupidAI/main.cpp
  9. 1 0
      AI/StupidAI/stdafx.cpp
  10. 3 0
      AI/StupidAI/stdafx.h
  11. 142 82
      CCallback.cpp
  12. 76 54
      CCallback.h
  13. 25 6
      CGameInterface.cpp
  14. 63 33
      CGameInterface.h
  15. 3 3
      StartInfo.h
  16. 6 6
      client/AdventureMapButton.cpp
  17. 43 45
      client/CAdvmapInterface.cpp
  18. 0 2
      client/CAdvmapInterface.h
  19. 1 1
      client/CAnimation.cpp
  20. 205 232
      client/CBattleInterface.cpp
  21. 34 37
      client/CBattleInterface.h
  22. 2 2
      client/CBitmapHandler.cpp
  23. 27 25
      client/CCastleInterface.cpp
  24. 5 5
      client/CCastleInterface.h
  25. 1 1
      client/CCreatureAnimation.cpp
  26. 1 1
      client/CCreatureAnimation.h
  27. 1 1
      client/CCursorHandler.cpp
  28. 2 2
      client/CDefHandler.cpp
  29. 0 0
      client/CDefHandler.h
  30. 2 2
      client/CGameInfo.cpp
  31. 29 17
      client/CGameInfo.h
  32. 191 351
      client/CHeroWindow.cpp
  33. 18 19
      client/CHeroWindow.h
  34. 31 41
      client/CKingdomInterface.cpp
  35. 3 3
      client/CKingdomInterface.h
  36. 96 73
      client/CMT.cpp
  37. 3 3
      client/CMessage.cpp
  38. 0 0
      client/CMusicBase.h
  39. 11 9
      client/CMusicHandler.cpp
  40. 5 4
      client/CMusicHandler.h
  41. 162 66
      client/CPlayerInterface.cpp
  42. 73 60
      client/CPlayerInterface.h
  43. 23 23
      client/CPreGame.cpp
  44. 7 7
      client/CSndHandler.cpp
  45. 0 0
      client/CSndHandler.h
  46. 0 0
      client/CSoundBase.h
  47. 24 24
      client/CSpellWindow.cpp
  48. 2 0
      client/CVideoHandler.cpp
  49. 0 0
      client/CVideoHandler.h
  50. 97 42
      client/Client.cpp
  51. 52 35
      client/Client.h
  52. 6 1
      client/GUIBase.cpp
  53. 3 2
      client/GUIBase.h
  54. 254 214
      client/GUIClasses.cpp
  55. 78 36
      client/GUIClasses.h
  56. 42 9
      client/Graphics.cpp
  57. 9 0
      client/Graphics.h
  58. 139 113
      client/NetPacksClient.cpp
  59. 1 1
      client/SDL_Extensions.cpp
  60. 39 34
      client/mapHandler.cpp
  61. 3 3
      client/mapHandler.h
  62. 244 1
      global.h
  63. 0 152
      hch/CArtHandler.h
  64. 12 0
      int3.h
  65. 72 0
      lib/BattleAction.cpp
  66. 16 5
      lib/BattleAction.h
  67. 2045 0
      lib/BattleState.cpp
  68. 221 0
      lib/BattleState.h
  69. 533 209
      lib/CArtHandler.cpp
  70. 234 0
      lib/CArtHandler.h
  71. 2 20
      lib/CBuildingHandler.cpp
  72. 5 4
      lib/CBuildingHandler.h
  73. 7 6
      lib/CCampaignHandler.cpp
  74. 0 0
      lib/CCampaignHandler.h
  75. 78 65
      lib/CCreatureHandler.cpp
  76. 11 8
      lib/CCreatureHandler.h
  77. 362 91
      lib/CCreatureSet.cpp
  78. 83 25
      lib/CCreatureSet.h
  79. 4 6
      lib/CDefObjInfoHandler.cpp
  80. 4 3
      lib/CDefObjInfoHandler.h
  81. 147 745
      lib/CGameState.cpp
  82. 72 176
      lib/CGameState.h
  83. 2 2
      lib/CGeneralTextHandler.cpp
  84. 1 1
      lib/CGeneralTextHandler.h
  85. 0 0
      lib/CHeroHandler.cpp
  86. 8 2
      lib/CHeroHandler.h
  87. 8 13
      lib/CLodHandler.cpp
  88. 15 8
      lib/CLodHandler.h
  89. 1 1
      lib/CMapInfo.cpp
  90. 258 327
      lib/CObjectHandler.cpp
  91. 107 30
      lib/CObjectHandler.h
  92. 31 33
      lib/CSpellHandler.cpp
  93. 2 3
      lib/CSpellHandler.h
  94. 0 0
      lib/CTownHandler.cpp
  95. 0 0
      lib/CTownHandler.h
  96. 8 8
      lib/Connection.cpp
  97. 57 8
      lib/Connection.h
  98. 66 0
      lib/ConstTransitivePtr.h
  99. 289 101
      lib/HeroBonus.cpp
  100. 155 50
      lib/HeroBonus.h

+ 27 - 53
AI/GeniusAI/BattleLogic.cpp

@@ -1,9 +1,11 @@
 #include "BattleLogic.h"
+#include "../../lib/BattleState.h"
 #include <math.h>
 #include <boost/lexical_cast.hpp>
 #include <boost/lambda/lambda.hpp>
 #include <boost/lambda/bind.hpp>
 #include <boost/lambda/if.hpp>
+#include <boost/foreach.hpp>
 
 #ifdef _WIN32
 #define WIN32_LEAN_AND_MEAN //excludes rarely used stuff from windows headers - delete this line if something is missing
@@ -72,8 +74,8 @@ void CBattleLogic::SetCurrentTurn(int turn)
 
 void CBattleLogic::MakeStatistics(int currentCreatureId)
 {
-	typedef std::map<int, CStack> map_stacks;
-	map_stacks allStacks = m_cb->battleGetStacks();
+	typedef std::vector<const CStack*> vector_stacks;
+	vector_stacks allStacks = m_cb->battleGetStacks(false);
 	const CStack *currentStack = m_cb->battleGetStackByID(currentCreatureId);
 	if(currentStack->position < 0) //turret
 	{
@@ -118,14 +120,13 @@ void CBattleLogic::MakeStatistics(int currentCreatureId)
 	int totalDamage = 0;
 	int totalHitPoints = 0;
 
-	for (map_stacks::const_iterator it = allStacks.begin(); it != allStacks.end(); ++it)
+	BOOST_FOREACH(const CStack *st, allStacks)
 	{
-		const CStack *st = &it->second;
 		const int stackHP = st->valOfBonuses(Bonus::STACK_HEALTH);
 
-		if ((it->second.attackerOwned != 0) != m_bIsAttacker)
+		if ((st->attackerOwned != 0) != m_bIsAttacker)
 		{
-			int id = it->first;
+			int id = st->ID;
 			if (st->count < 1)
 			{
 				continue;
@@ -138,7 +139,7 @@ void CBattleLogic::MakeStatistics(int currentCreatureId)
 			m_statHitPoints.push_back(std::pair<int, int>(id, hitPoints));
 			m_statMaxSpeed.push_back(std::pair<int, int>(id, stackHP));
 
-			totalEnemyDamage += (st->type->damageMax + st->type->damageMin) * st->count / 2;
+			totalEnemyDamage += (st->getCreature()->damageMax + st->getCreature()->damageMin) * st->count / 2;
 			totalEnemyHitPoints += hitPoints;
 
 			// calculate casualties
@@ -195,12 +196,12 @@ void CBattleLogic::MakeStatistics(int currentCreatureId)
 
 			m_statCasualties.push_back(std::pair<int, SCreatureCasualties>(id, cs));
 
-			if (st->type->isShooting() && st->shots > 0)
+			if (st->getCreature()->isShooting() && st->shots > 0)
 			{
 				m_statDistanceFromShooters.push_back(std::pair<int, int>(id, m_battleHelper.GetShortestDistance(currentStack->position, st->position)));
 			}
 
-			if (currentStack->hasBonusOfType(Bonus::FLYING) || (currentStack->type->isShooting() && currentStack->shots > 0))
+			if (currentStack->hasBonusOfType(Bonus::FLYING) || (currentStack->getCreature()->isShooting() && currentStack->shots > 0))
 			{
 				m_statDistance.push_back(std::pair<int, int>(id, m_battleHelper.GetShortestDistance(currentStack->position, st->position)));
 			}
@@ -259,9 +260,9 @@ void CBattleLogic::MakeStatistics(int currentCreatureId)
 BattleAction CBattleLogic::MakeDecision(int stackID)
 {
 	const CStack *currentStack = m_cb->battleGetStackByID(stackID);
-	if(currentStack->position < 0 || currentStack->type->idNumber == 147) //turret or first aid kit
+	if(currentStack->position < 0 || currentStack->getCreature()->idNumber == 147) //turret or first aid kit
 	{
-		return MakeDefend(stackID);
+		return BattleAction::makeDefend(currentStack);
 	}
 	MakeStatistics(stackID);
 
@@ -285,11 +286,11 @@ BattleAction CBattleLogic::MakeDecision(int stackID)
 	if (additionalInfo == -1 || creatures.empty())
 	{
 		// defend
-		return MakeDefend(stackID);
+		return BattleAction::makeDefend(currentStack);
 	}
 	else if (additionalInfo == -2)
 	{
-		return MakeWait(stackID);
+		return BattleAction::makeWait(currentStack);
 	}
 
 	list<int>::iterator it, eit;
@@ -472,26 +473,6 @@ std::vector<int> CBattleLogic::GetAvailableHexesForAttacker(const CStack *defend
 	return fields;
 }
 
-BattleAction CBattleLogic::MakeDefend(int stackID)
-{
-	BattleAction ba;
-	ba.side = m_side;
-	ba.actionType = action_defend;
-	ba.stackNumber = stackID;
-	ba.additionalInfo = -1;
-	return ba;
-}
-
-BattleAction CBattleLogic::MakeWait(int stackID)
-{
-	BattleAction ba;
-	ba.side = m_side;
-	ba.actionType = action_wait;
-	ba.stackNumber = stackID;
-	ba.additionalInfo = -1;
-	return ba;
-}
-
 BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
 {
 	const CStack *attackerStack = m_cb->battleGetStackByID(attackerID),
@@ -501,19 +482,12 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
 	//don't attack ourselves
 	if(destinationStack->attackerOwned == !m_side)
 	{
-		return MakeDefend(attackerID);
+		return BattleAction::makeDefend(attackerStack);
 	}
 
-	if (m_cb->battleCanShoot(attackerID, m_cb->battleGetPos(destinationID)))
+	if (m_cb->battleCanShoot(attackerStack, destinationStack->position))	// shoot
 	{
-		// shoot
-		BattleAction ba;
-		ba.side = m_side;
-		ba.additionalInfo = -1;
-		ba.actionType = action_shoot; // shoot
-		ba.stackNumber = attackerID;
-		ba.destinationTile = (ui16)m_cb->battleGetPos(destinationID);
-		return ba;
+		return BattleAction::makeShotAttack(attackerStack, destinationStack);
 	}
 	else
 	{
@@ -522,7 +496,7 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
 		std::vector<int> av_tiles = GetAvailableHexesForAttacker(m_cb->battleGetStackByID(destinationID), m_cb->battleGetStackByID(attackerID));
 		if (av_tiles.size() < 1)
 		{
-			return MakeDefend(attackerID);
+			return BattleAction::makeDefend(attackerStack);
 		}
 
 		// get the best tile - now the nearest
@@ -545,11 +519,11 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
 			}
 		}
 
-		std::vector<int> fields = m_cb->battleGetAvailableHexes(attackerID, false);
+		std::vector<THex> fields = m_cb->battleGetAvailableHexes(m_cb->battleGetStackByID(attackerID), false);
 
 		if(fields.size() == 0)
 		{
-			return MakeDefend(attackerID);
+			return BattleAction::makeDefend(attackerStack);
 		}
 
 		BattleAction ba;
@@ -559,14 +533,14 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
 		ba.destinationTile = static_cast<ui16>(dest_tile);
 		//simplified checking for possibility of attack (previous was too simplified)
 		int destStackPos = m_cb->battleGetPos(destinationID);
-		if(BattleInfo::mutualPosition(dest_tile, destStackPos) != -1)
+		if(THex::mutualPosition(dest_tile, destStackPos) != -1)
 			ba.additionalInfo = destStackPos;
-		else if(BattleInfo::mutualPosition(dest_tile, destStackPos+1) != -1)
+		else if(THex::mutualPosition(dest_tile, destStackPos+1) != -1)
 			ba.additionalInfo = destStackPos+1;
-		else if(BattleInfo::mutualPosition(dest_tile, destStackPos-1) != -1)
+		else if(THex::mutualPosition(dest_tile, destStackPos-1) != -1)
 			ba.additionalInfo = destStackPos-1;
 		else
-			MakeDefend(attackerID);
+			return BattleAction::makeDefend(attackerStack);
 
 		int nearest_dist = m_battleHelper.InfiniteDistance;
 		int nearest_pos = -1;
@@ -600,7 +574,7 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
 			}
 		}
 
-		for (std::vector<int>::const_iterator it = fields.begin(); it != fields.end(); ++it)
+		for (std::vector<THex>::const_iterator it = fields.begin(); it != fields.end(); ++it)
 		{
 			if (*it == dest_tile)
 			{
@@ -762,9 +736,9 @@ void CBattleLogic::PrintBattleAction(const BattleAction &action) // for debug pu
 		message += ", " + boost::lexical_cast<std::string>(m_battleHelper.DecodeYPosition(action.additionalInfo));
 		message += ", creature - ";
 		const CStack *c = m_cb->battleGetStackByPos(action.additionalInfo);
-		if (c && c->type)
+		if (c && c->getCreature())
 		{
-			message += c->type->nameRef;
+			message += c->getCreature()->nameRef;
 		}
 		else
 		{

+ 2 - 10
AI/GeniusAI/BattleLogic.h

@@ -7,8 +7,8 @@
 #pragma warning (disable: 4100 4251 4245 4018 4081)
 #include "../../global.h"
 #include "../../CCallback.h"
-#include "../../hch/CCreatureHandler.h"
-#include "../../hch/CObjectHandler.h"
+#include "../../lib/CCreatureHandler.h"
+#include "../../lib/CObjectHandler.h"
 #pragma warning (default: 4100 4251 4245 4018 4081)
 
 #pragma warning (disable: 4100)
@@ -106,14 +106,6 @@ private:
 	 * Helper function. It's used for performing an attack action.
 	 */
 	std::vector<int> GetAvailableHexesForAttacker(const CStack *defender, const CStack *attacker = NULL);
-	/**
-	 * Just make defend action.
-	 */
-	BattleAction MakeDefend(int stackID);
-	/**
-	 * Just make wait action.
-	 */
-	BattleAction MakeWait(int stackID);
 	/**
 	 * Make an attack action if it's possible.
 	 * If it's not possible then function returns defend action.

+ 47 - 39
AI/GeniusAI/CGeniusAI.cpp

@@ -3,8 +3,9 @@
 #include <iostream>
 #include <boost/lexical_cast.hpp>
 
-#include "../../hch/CBuildingHandler.h"
-#include "../../hch/CHeroHandler.h"
+#include "../../lib/BattleState.h"
+#include "../../lib/CBuildingHandler.h"
+#include "../../lib/CHeroHandler.h"
 #include "../../lib/VCMI_Lib.h"
 #include "../../lib/NetPacks.h"
 #include "AIPriorities.h"
@@ -60,7 +61,7 @@ CGeniusAI::HypotheticalGameState::TownModel::TownModel(
 {
 	hasBuilt = static_cast<bool>(t->builded);
 	creaturesToRecruit = t->creatures;
-	creaturesInGarrison = t->getArmy();
+	//creaturesInGarrison = t->getArmy();
 }
 
 CGeniusAI::HypotheticalGameState::HypotheticalGameState(CGeniusAI& ai)
@@ -315,10 +316,10 @@ float CGeniusAI::TownObjective::getValue() const
 				resourceCosts[i] = creature->cost[i]*howMany;
 			break;
 
-		case upgradeCreatures:
-			UpgradeInfo ui = AI->m_cb->getUpgradeInfo(whichTown->t,which);
-			ID = whichTown->creaturesInGarrison.getCreature(which)->idNumber;
-			howMany = whichTown->creaturesInGarrison.getAmount(which);
+	 	 case upgradeCreatures:
+			  UpgradeInfo ui = AI->m_cb->getUpgradeInfo(whichTown->t,which);
+			  ID = whichTown->t->getCreature(which)->idNumber;
+			  howMany = whichTown->t->getStackCount(which);
 
 			newID = ui.newID.back();
 			int upgrade_serial = ui.newID.size() - 1;
@@ -425,7 +426,7 @@ void CGeniusAI::TownObjective::print() const
 
 		  case upgradeCreatures:
 			  UpgradeInfo ui = AI->m_cb->getUpgradeInfo (whichTown->t, which);
-			  ID = whichTown->creaturesInGarrison.getCreature(which)->idNumber;
+			  ID = whichTown->t->getCreature(which)->idNumber;
 			  tlog6 << "upgrade " << VLC->creh->creatures[ID]->namePl;
 			  //ui.cost	
 		  break;
@@ -746,8 +747,7 @@ void CGeniusAI::HeroObjective::fulfill(CGeniusAI& cg, HypotheticalGameState& hgs
 	{
 	  //upgrade hero's units
 		tlog6 << "visiting town" << endl;
-		CCreatureSet hcreatures = h->h->getArmy();
-		for (TSlots::const_iterator i = hcreatures.Slots().begin(); i != hcreatures.Slots().end(); i++)
+		for (TSlots::const_iterator i = h->h->Slots().begin(); i != h->h->Slots().end(); i++)
 		{ // For each hero slot.
 			UpgradeInfo ui = cg.m_cb->getUpgradeInfo(h->h,i->first);
 
@@ -759,7 +759,7 @@ void CGeniusAI::HeroObjective::fulfill(CGeniusAI& cg, HypotheticalGameState& hgs
 				for (int ii = 0; ii < ui.cost.size(); ii++) // Can afford the upgrade?
 				{
 					for (j = ui.cost[ii].begin(); j != ui.cost[ii].end(); j++)
-						if (hgs.resourceAmounts[j->first] < j->second * i->second.count)
+						if (hgs.resourceAmounts[j->first] < j->second * i->second->count)
 							canUpgrade = false;
 				}
 			}
@@ -767,37 +767,35 @@ void CGeniusAI::HeroObjective::fulfill(CGeniusAI& cg, HypotheticalGameState& hgs
 			{
 				cg.m_cb->upgradeCreature(h->h, i->first, ui.newID.back());
 				tlog6 << "upgrading hero's "
-						<< i->second.type->namePl
+						<< i->second->type->namePl
 						<< endl;
 			}
 	  }
 
 	  // Give town's units to hero.
-	  CCreatureSet tcreatures = town->getArmy();
 	  int weakestCreatureStack;
 	  int weakestCreatureAIValue = 99999; // we will lower it in the process
 
-	  for (TSlots::const_iterator i = tcreatures.Slots().begin(); i != tcreatures.Slots().end(); i++)
+	  for (TSlots::const_iterator i = town->Slots().begin(); i != town->Slots().end(); i++)
 	  {
-		  if (i->second.type->AIValue < weakestCreatureAIValue)
+		  if (i->second->type->AIValue < weakestCreatureAIValue)
 		  {
-			  weakestCreatureAIValue  = i->second.type->AIValue;
+			  weakestCreatureAIValue  = i->second->type->AIValue;
 			  weakestCreatureStack    = i->first;
 		  }
 	  }
-	  for (TSlots::const_iterator i = tcreatures.Slots().begin(); i != tcreatures.Slots().end(); i++)\
+	  for (TSlots::const_iterator i = town->Slots().begin(); i != town->Slots().end(); i++)\
 	  { // For each town slot.
-		  hcreatures = h->h->getArmy();
-		  int hSlot = hcreatures.getSlotFor(i->second.type->idNumber);
+		  int hSlot = h->h->getSlotFor(i->second->type->idNumber);
 
 		  if (hSlot == -1)
 			continue;
-		  tlog6 << "giving hero " << i->second.type->namePl << endl;
-		  if (!hcreatures.slotEmpty(hSlot))
+		  tlog6 << "giving hero " << i->second->type->namePl << endl;
+		  if (!h->h->slotEmpty(hSlot))
 		  {
         // Can't take garrisonHero's last unit.
 			  if ( (i->first == weakestCreatureStack) && (town->garrisonHero != NULL) )
-				  cg.m_cb->splitStack(town, h->h, i->first, hSlot, i->second.count - 1);
+				  cg.m_cb->splitStack(town, h->h, i->first, hSlot, i->second->count - 1);
 			  else
           // TODO: the comment says that this code is not safe for the AI.
 				  cg.m_cb->mergeStacks(town, h->h, i->first, hSlot);
@@ -843,8 +841,8 @@ void CGeniusAI::addTownObjectives (HypotheticalGameState::TownModel& t, Hypothet
 	if (!t.hasBuilt)
 	{
     // m_cb->getCBuildingsByID(t.t);
-		std::map<int, CBuilding*> thisTownsBuildings = VLC->buildh->buildings[t.t->subID];
-		for (std::map<int, CBuilding*>::iterator i = thisTownsBuildings.begin(); i != thisTownsBuildings.end(); i++)
+		bmap<int, ConstTransitivePtr<CBuilding> > thisTownsBuildings = VLC->buildh->buildings[t.t->subID];
+		for (bmap<int, ConstTransitivePtr<CBuilding> >::iterator i = thisTownsBuildings.begin(); i != thisTownsBuildings.end(); i++)
 		{
 			if (m_cb->canBuildStructure(t.t, i->first) == 7)
 			{
@@ -878,7 +876,7 @@ void CGeniusAI::addTownObjectives (HypotheticalGameState::TownModel& t, Hypothet
 	}
 
   // Upgrade creatures.
-	for (TSlots::const_iterator i = t.creaturesInGarrison.Slots().begin(); i != t.creaturesInGarrison.Slots().end(); i++)
+	for (TSlots::const_iterator i = t.t->Slots().begin(); i != t.t->Slots().end(); i++)
 	{
 		UpgradeInfo ui = m_cb->getUpgradeInfo(t.t, i->first);
 		if (ui.newID.size())
@@ -888,7 +886,7 @@ void CGeniusAI::addTownObjectives (HypotheticalGameState::TownModel& t, Hypothet
 			int upgrade_serial = ui.newID.size() - 1;
 			for (std::set< std::pair<int, int> >::iterator j = ui.cost[upgrade_serial].begin(); j != ui.cost[upgrade_serial].end(); j++)
 			{
-				if (hgs.resourceAmounts[j->first] < j->second * i->second.count)
+				if (hgs.resourceAmounts[j->first] < j->second * i->second->count)
 					canAfford = false;
 			}
 			if (canAfford)
@@ -955,7 +953,7 @@ void CGeniusAI::TownObjective::fulfill(CGeniusAI& cg,
 
 		case upgradeCreatures:
 			UpgradeInfo ui = cg.m_cb->getUpgradeInfo(whichTown->t, which);
-			ID = whichTown->creaturesInGarrison.getCreature(which)->idNumber;
+			ID = whichTown->t->getCreature(which)->idNumber;
 			newID = ui.newID.back();
 		// TODO: reduce resources in hgs
 			cg.m_cb->upgradeCreature(whichTown->t, which, newID);
@@ -1308,7 +1306,7 @@ void CGeniusAI::battleNewRound(int round)
 /**
  *
  */
-void CGeniusAI::battleStackMoved(int ID, int dest, int distance, bool end)
+void CGeniusAI::battleStackMoved(int ID, THex dest, int distance, bool end)
 {
 	std::string message("\t\t\tCGeniusAI::battleStackMoved ID(");
 	message += boost::lexical_cast<std::string>(ID);
@@ -1341,13 +1339,13 @@ void CGeniusAI::battleSpellCast(const BattleSpellCast *sc)
 /**
  *
  */
-void CGeniusAI::battleStackMoved(int ID,
-                                 int dest,
-                                 bool startMoving,
-                                 bool endMoving)
-{
-	DbgBox("\t\t\tCGeniusAI::battleStackMoved");
-}
+// void CGeniusAI::battleStackMoved(int ID,
+//                                  THex dest,
+//                                  bool startMoving,
+//                                  bool endMoving)
+// {
+// 	DbgBox("\t\t\tCGeniusAI::battleStackMoved");
+// }
 
 
 /**
@@ -1375,15 +1373,25 @@ void CGeniusAI::battleStackIsAttacked(int ID,
 /**
  * called when it's turn of that stack
  */
-BattleAction CGeniusAI::activeStack(int stackID)
+BattleAction CGeniusAI::activeStack(const CStack * stack)
 {
 	std::string message("\t\t\tCGeniusAI::activeStack stackID(");
 
-	message += boost::lexical_cast<std::string>(stackID);
+	message += boost::lexical_cast<std::string>(stack->ID);
 	message += ")";
 	DbgBox(message.c_str());
 
-	BattleAction bact = m_battleLogic->MakeDecision(stackID);
+	BattleAction bact = m_battleLogic->MakeDecision(stack->ID);
 	assert(m_cb->battleGetStackByID(bact.stackNumber));
 	return bact;
-};
+}
+
+
+//WTF?!? why is this needed?!?!?!
+BattleAction CGlobalAI::activeStack( const CStack * stack )
+{
+	BattleAction ba; ba.actionType = BattleAction::DEFEND;
+	ba.stackNumber = stack->ID;
+	return ba;
+}
+

+ 4 - 4
AI/GeniusAI/CGeniusAI.h

@@ -61,7 +61,7 @@ private:
 			TownModel(const CGTownInstance *t);
 			const CGTownInstance *t;
 			std::vector<std::pair<ui32, std::vector<ui32> > > creaturesToRecruit;
-			CCreatureSet creaturesInGarrison;				//type, num
+			//CCreatureSet creaturesInGarrison;				//type, num
 			bool hasBuilt;
 		};
 		HypotheticalGameState(){}
@@ -205,15 +205,15 @@ public:
 	virtual void battleStacksAttacked(const std::set<BattleStackAttacked> & bsa); //called when stack receives damage (after battleAttack())
 	virtual void battleEnd(const BattleResult *br);
 	virtual void battleNewRound(int round); //called at the beggining of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
-	virtual void battleStackMoved(int ID, int dest, int distance, bool end);
+	virtual void battleStackMoved(int ID, THex dest, int distance, bool end);
 	virtual void battleSpellCast(const BattleSpellCast *sc);
 	virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side); //called by engine when battle starts; side=0 - left, side=1 - right
 	//virtual void battlefieldPrepared(int battlefieldType, std::vector<CObstacle*> obstacles); //called when battlefield is prepared, prior the battle beginning
 	//
-	virtual void battleStackMoved(int ID, int dest, bool startMoving, bool endMoving);
+	//virtual void battleStackMoved(int ID, int dest, bool startMoving, bool endMoving);
 	virtual void battleStackAttacking(int ID, int dest);
 	virtual void battleStackIsAttacked(int ID, int dmg, int killed, int IDby, bool byShooting);
-	virtual BattleAction activeStack(int stackID);
+	virtual BattleAction activeStack(const CStack * stack);
 	void battleResultsApplied();
 	friend class Priorities;
 };

+ 265 - 0
AI/StupidAI/StupidAI.cpp

@@ -0,0 +1,265 @@
+#include "stdafx.h"
+#include "StupidAI.h"
+#include "../../lib/BattleState.h"
+#include "../../CCallback.h"
+#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+#include "../../lib/CCreatureHandler.h"
+#include <algorithm>
+#include <boost/thread.hpp>
+
+IBattleCallback * cbc;
+
+CStupidAI::CStupidAI(void)
+	: side(-1), cb(NULL)
+{
+	print("created");
+}
+
+
+CStupidAI::~CStupidAI(void)
+{
+	print("destroyed");
+}
+
+void CStupidAI::init( IBattleCallback * CB )
+{
+	print("init called, saving ptr to IBattleCallback");
+	cbc = cb = CB;
+}
+
+void CStupidAI::actionFinished( const BattleAction *action )
+{
+	print("actionFinished called");
+}
+
+void CStupidAI::actionStarted( const BattleAction *action )
+{
+	print("actionStarted called");
+}
+
+struct EnemyInfo
+{
+	const CStack * s;
+	int adi, adr;
+	std::vector<THex> attackFrom; //for melee fight
+	EnemyInfo(const CStack * _s) : s(_s)
+	{}
+	void calcDmg(const CStack * ourStack)
+	{
+		TDmgRange retal, dmg = cbc->battleEstimateDamage(ourStack, s, &retal);
+		adi = (dmg.first + dmg.second) / 2;
+		adr = (retal.first + retal.second) / 2;
+	}
+
+	bool operator==(const EnemyInfo& ei) const
+	{
+		return s == ei.s;
+	}
+};
+
+bool isMoreProfitable(const EnemyInfo &ei1, const EnemyInfo& ei2) 
+{
+	return (ei1.adi-ei1.adr) < (ei2.adi - ei2.adr);
+}
+
+int distToNearestNeighbour(THex hex, const std::vector<int> & dists, THex *chosenHex = NULL)
+{
+	int ret = 1000000;
+	BOOST_FOREACH(THex n, hex.neighbouringTiles())
+	{
+		if(dists[n] >= 0 && dists[n] < ret)
+		{
+			ret = dists[n];
+			if(chosenHex)
+				*chosenHex = n;
+		}
+	}
+
+	return ret;
+}
+
+bool isCloser(const EnemyInfo & ei1, const EnemyInfo & ei2, const std::vector<int> & dists)
+{
+	return distToNearestNeighbour(ei1.s->position, dists) < distToNearestNeighbour(ei2.s->position, dists);
+}
+
+static bool willSecondHexBlockMoreEnemyShooters(const THex &h1, const THex &h2)
+{
+	int shooters[2] = {0}; //count of shooters on hexes
+
+	for(int i = 0; i < 2; i++)
+		BOOST_FOREACH(THex neighbour, (i ? h2 : h1).neighbouringTiles())
+			if(const CStack *s = cbc->battleGetStackByPos(neighbour))
+				if(s->getCreature()->isShooting())
+						shooters[i]++;
+
+	return shooters[0] < shooters[1];
+}
+
+BattleAction CStupidAI::activeStack( const CStack * stack )
+{
+	//boost::this_thread::sleep(boost::posix_time::seconds(2));
+	print("activeStack called");
+	std::vector<THex> avHexes = cb->battleGetAvailableHexes(stack, false);
+	std::vector<int> dists = cb->battleGetDistances(stack);
+	std::vector<EnemyInfo> enemiesShootable, enemiesReachable, enemiesUnreachable;
+
+	BOOST_FOREACH(const CStack *s, cb->battleGetStacks())
+	{
+		if(s->owner != stack->owner)
+		{
+			if(cb->battleCanShoot(stack, s->position))
+			{
+				enemiesShootable.push_back(s);
+			}
+			else
+			{
+				BOOST_FOREACH(THex hex, avHexes)
+				{
+					if(CStack::isMeleeAttackPossible(stack, s, hex))
+					{
+						std::vector<EnemyInfo>::iterator i = std::find(enemiesReachable.begin(), enemiesReachable.end(), s);
+						if(i == enemiesReachable.end())
+						{
+							enemiesReachable.push_back(s);
+							i = enemiesReachable.begin() + (enemiesReachable.size() - 1);
+						}
+
+						i->attackFrom.push_back(hex);
+					}
+				}
+
+				if(!vstd::contains(enemiesReachable, s))
+					enemiesUnreachable.push_back(s);
+			}
+		}
+	}
+
+	if(enemiesShootable.size())
+	{
+		const EnemyInfo &ei= *std::max_element(enemiesShootable.begin(), enemiesShootable.end(), isMoreProfitable);
+		return BattleAction::makeShotAttack(stack, ei.s);
+	}
+	else if(enemiesReachable.size())
+	{
+		const EnemyInfo &ei= *std::max_element(enemiesReachable.begin(), enemiesReachable.end(), &isMoreProfitable);
+		return BattleAction::makeMeleeAttack(stack, ei.s, *std::max_element(ei.attackFrom.begin(), ei.attackFrom.end(), &willSecondHexBlockMoreEnemyShooters));
+	}
+	else
+	{
+		const EnemyInfo &ei= *std::min_element(enemiesUnreachable.begin(), enemiesUnreachable.end(), boost::bind(isCloser, _1, _2, boost::ref(dists)));
+		if(distToNearestNeighbour(ei.s->position, dists) < BFIELD_SIZE)
+		{
+			return goTowards(stack, ei.s->position);
+		}
+	}
+
+	return BattleAction::makeDefend(stack);
+}
+
+void CStupidAI::battleAttack(const BattleAttack *ba)
+{
+	print("battleAttack called");
+}
+
+void CStupidAI::battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa)
+{
+	print("battleStacksAttacked called");
+}
+
+void CStupidAI::battleEnd(const BattleResult *br) 
+{
+	print("battleEnd called");
+}
+
+void CStupidAI::battleResultsApplied() 
+{
+	print("battleResultsApplied called");
+}
+
+void CStupidAI::battleNewRoundFirst(int round) 
+{
+	print("battleNewRoundFirst called");
+}
+
+void CStupidAI::battleNewRound(int round) 
+{
+	print("battleNewRound called");
+}
+
+void CStupidAI::battleStackMoved(const CStack * stack, THex dest, int distance, bool end) 
+{
+	print("battleStackMoved called");;
+}
+
+void CStupidAI::battleSpellCast(const BattleSpellCast *sc) 
+{
+	print("battleSpellCast called");
+}
+
+void CStupidAI::battleStacksEffectsSet(const SetStackEffect & sse) 
+{
+	print("battleStacksEffectsSet called");
+}
+
+void CStupidAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool Side) 
+{
+	print("battleStart called");
+	side = Side;
+}
+
+void CStupidAI::battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, si32 lifeDrainFrom) 
+{
+	print("battleStacksHealedRes called");
+}
+
+void CStupidAI::battleNewStackAppeared(const CStack * stack) 
+{
+	print("battleNewStackAppeared called");
+}
+
+void CStupidAI::battleObstaclesRemoved(const std::set<si32> & removedObstacles) 
+{
+	print("battleObstaclesRemoved called");
+}
+
+void CStupidAI::battleCatapultAttacked(const CatapultAttack & ca) 
+{
+	print("battleCatapultAttacked called");
+}
+
+void CStupidAI::battleStacksRemoved(const BattleStacksRemoved & bsr) 
+{
+	print("battleStacksRemoved called");
+}
+
+void CStupidAI::print(const std::string &text) const
+{
+	tlog0 << "CStupidAI [" << this <<"]: " << text << std::endl;
+}
+
+BattleAction CStupidAI::goTowards(const CStack * stack, THex hex)
+{
+	THex realDest = hex;
+	THex predecessors[BFIELD_SIZE];
+	std::vector<int> dists = cb->battleGetDistances(stack, hex);
+	if(distToNearestNeighbour(hex, dists, &realDest) > BFIELD_SIZE)
+	{
+		print("goTowards: Cannot reach");
+		return BattleAction::makeDefend(stack);
+	}
+
+	dists = cb->battleGetDistances(stack, realDest, predecessors);
+	std::vector<THex> avHexes = cb->battleGetAvailableHexes(stack, false);
+
+	while(1)
+	{
+		assert(realDest.isValid());
+		if(vstd::contains(avHexes, hex))
+			return BattleAction::makeMove(stack, hex);
+
+		hex = predecessors[hex];
+	}
+}
+

+ 36 - 0
AI/StupidAI/StupidAI.h

@@ -0,0 +1,36 @@
+#pragma once
+
+class CStupidAI : public CBattleGameInterface
+{
+	int side;
+	IBattleCallback *cb;
+
+	void print(const std::string &text) const;
+public:
+	CStupidAI(void);
+	~CStupidAI(void);
+
+	void init(IBattleCallback * CB) OVERRIDE;
+	void actionFinished(const BattleAction *action) OVERRIDE;//occurs AFTER every action taken by any stack or by the hero
+	void actionStarted(const BattleAction *action) OVERRIDE;//occurs BEFORE every action taken by any stack or by the hero
+	BattleAction activeStack(const CStack * stack) OVERRIDE; //called when it's turn of that stack
+
+	void battleAttack(const BattleAttack *ba) OVERRIDE; //called when stack is performing attack
+	void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa) OVERRIDE; //called when stack receives damage (after battleAttack())
+	void battleEnd(const BattleResult *br) OVERRIDE;
+	void battleResultsApplied() OVERRIDE; //called when all effects of last battle are applied
+	void battleNewRoundFirst(int round) OVERRIDE; //called at the beginning of each turn before changes are applied;
+	void battleNewRound(int round) OVERRIDE; //called at the beggining of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
+	void battleStackMoved(const CStack * stack, THex dest, int distance, bool end) OVERRIDE;
+	void battleSpellCast(const BattleSpellCast *sc) OVERRIDE;
+	void battleStacksEffectsSet(const SetStackEffect & sse) OVERRIDE;//called when a specific effect is set to stacks
+	void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) OVERRIDE; //called by engine when battle starts; side=0 - left, side=1 - right
+	void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, si32 lifeDrainFrom) OVERRIDE; //called when stacks are healed / resurrected first element of pair - stack id, second - healed hp
+	void battleNewStackAppeared(const CStack * stack) OVERRIDE; //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned
+	void battleObstaclesRemoved(const std::set<si32> & removedObstacles) OVERRIDE; //called when a certain set  of obstacles is removed from batlefield; IDs of them are given
+	void battleCatapultAttacked(const CatapultAttack & ca) OVERRIDE; //called when catapult makes an attack
+	void battleStacksRemoved(const BattleStacksRemoved & bsr) OVERRIDE; //called when certain stack is completely removed from battlefield
+
+	BattleAction goTowards(const CStack * stack, THex hex );
+};
+

+ 121 - 0
AI/StupidAI/StupidAI.vcxproj

@@ -0,0 +1,121 @@
+<?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>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{15DABC90-234A-4B6B-9EEB-777C4768B82B}</ProjectGuid>
+    <RootNamespace>StupidAI</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</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" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </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" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup />
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>E:\C++\Lua_includes;E:\vcmi\rep - assembla\trunk\lua src;E:\C++\boost_1_43_0;E:\C++\SDL_mixer-1.2.7\include;E:\C++\SDL_ttf-2.0.8\include;E:\C++\zlib 1.2.3 binaries\include;E:\C++\SDL-1.2.11\devlibs - visual\SDL-1.2.11\include;E:\C++\SDL_Image 1.2.5\SDL_image-1.2.5\include;%(AdditionalIncludeDirectories)options&gt;</AdditionalIncludeDirectories>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>../../../libs; ../../;E:\vcmi\rep - assembla\trunk;E:\C++\lua bin;E:\C++\boost_1_43_0\lib;E:\C++\SDL_mixer-1.2.7\lib;E:\C++\SDL_ttf-2.0.8\lib;E:\C++\zlib 1.2.3 binaries\lib;E:\C++\SDL-1.2.11\devlibs - visual\SDL-1.2.11\lib;E:\C++\SDL_Image 1.2.5\SDL_image-1.2.5\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <OutputFile>$(OutDir)StupidAI.dll</OutputFile>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <AdditionalIncludeDirectories>E:\C++\Lua_includes;E:\vcmi\rep - assembla\trunk\lua src;E:\C++\boost_1_43_0;E:\C++\SDL_mixer-1.2.7\include;E:\C++\SDL_ttf-2.0.8\include;E:\C++\zlib 1.2.3 binaries\include;E:\C++\SDL-1.2.11\devlibs - visual\SDL-1.2.11\include;E:\C++\SDL_Image 1.2.5\SDL_image-1.2.5\include;%(AdditionalIncludeDirectories)options&gt;</AdditionalIncludeDirectories>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>../../../libs; ../../;E:\vcmi\rep - assembla\trunk;E:\C++\lua bin;E:\C++\boost_1_43_0\lib;E:\C++\SDL_mixer-1.2.7\lib;E:\C++\SDL_ttf-2.0.8\lib;E:\C++\zlib 1.2.3 binaries\lib;E:\C++\SDL-1.2.11\devlibs - visual\SDL-1.2.11\lib;E:\C++\SDL_Image 1.2.5\SDL_image-1.2.5\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <AdditionalIncludeDirectories>E:\C++\Lua_includes;E:\vcmi\rep - assembla\trunk\lua src;E:\C++\boost_1_43_0;E:\C++\SDL_mixer-1.2.7\include;E:\C++\SDL_ttf-2.0.8\include;E:\C++\zlib 1.2.3 binaries\include;E:\C++\SDL-1.2.11\devlibs - visual\SDL-1.2.11\include;E:\C++\SDL_Image 1.2.5\SDL_image-1.2.5\include;%(AdditionalIncludeDirectories)options&gt;</AdditionalIncludeDirectories>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>VCMI_lib.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>../../../libs; ../../;E:\vcmi\rep - assembla\trunk;E:\C++\lua bin;E:\C++\boost_1_43_0\lib;E:\C++\SDL_mixer-1.2.7\lib;E:\C++\SDL_ttf-2.0.8\lib;E:\C++\zlib 1.2.3 binaries\lib;E:\C++\SDL-1.2.11\devlibs - visual\SDL-1.2.11\lib;E:\C++\SDL_Image 1.2.5\SDL_image-1.2.5\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <OutputFile>$(OutDir)StupidAI.dll</OutputFile>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="main.cpp" />
+    <ClCompile Include="stdafx.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">Create</PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include="StupidAI.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="stdafx.h" />
+    <ClInclude Include="StupidAI.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 30 - 0
AI/StupidAI/main.cpp

@@ -0,0 +1,30 @@
+#include "stdafx.h"
+#include "StupidAI.h"
+
+const char *g_cszAiName = "Stupid AI 0.1";
+
+extern "C" DLL_F_EXPORT int GetGlobalAiVersion()
+{
+	return AI_INTERFACE_VER;
+}
+
+extern "C" DLL_F_EXPORT void GetAiName(char* name)
+{
+	strcpy_s(name, strlen(g_cszAiName) + 1, g_cszAiName);
+}
+
+extern "C" DLL_F_EXPORT char* GetAiNameS()
+{
+	// need to be defined
+	return NULL;
+}
+
+extern "C" DLL_F_EXPORT CBattleGameInterface* GetNewBattleAI()
+{
+	return new CStupidAI();
+}
+
+extern "C" DLL_F_EXPORT void ReleaseBattleAI(CBattleGameInterface* i)
+{
+	delete (CStupidAI*)i;
+}

+ 1 - 0
AI/StupidAI/stdafx.cpp

@@ -0,0 +1 @@
+#include "stdafx.h"

+ 3 - 0
AI/StupidAI/stdafx.h

@@ -0,0 +1,3 @@
+#pragma  once
+#include <boost/lexical_cast.hpp>
+#include "../../AI_Base.h"

+ 142 - 82
CCallback.cpp

@@ -1,24 +1,25 @@
 #include "stdafx.h"
 #include "CCallback.h"
-#include "hch/CCreatureHandler.h"
+#include "lib/CCreatureHandler.h"
 #include "client/CGameInfo.h"
 #include "lib/CGameState.h"
+#include "lib/BattleState.h"
 #include "client/CPlayerInterface.h"
 #include "client/Client.h"
 #include "lib/map.h"
-#include "hch/CBuildingHandler.h"
-#include "hch/CDefObjInfoHandler.h"
-#include "hch/CGeneralTextHandler.h"
-#include "hch/CHeroHandler.h"
-#include "hch/CObjectHandler.h"
+#include "lib/CBuildingHandler.h"
+#include "lib/CDefObjInfoHandler.h"
+#include "lib/CGeneralTextHandler.h"
+#include "lib/CHeroHandler.h"
+#include "lib/CObjectHandler.h"
 #include "lib/Connection.h"
 #include "lib/NetPacks.h"
 #include "client/mapHandler.h"
 #include <boost/foreach.hpp>
 #include <boost/thread.hpp>
 #include <boost/thread/shared_mutex.hpp>
-#include "hch/CSpellHandler.h"
-#include "hch/CArtHandler.h"
+#include "lib/CSpellHandler.h"
+#include "lib/CArtHandler.h"
 #ifdef min
 #undef min
 #endif
@@ -158,7 +159,7 @@ const CGTownInstance * CCallback::getTownInfo(int val, bool mode) const //mode =
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
 	if (!mode)
 	{
-		const std::vector<CGTownInstance *> &towns = gs->players[gs->currentPlayer].towns;
+		const std::vector<ConstTransitivePtr<CGTownInstance> > &towns = gs->players[gs->currentPlayer].towns;
 		if(val < towns.size())
 			return towns[val];
 		else 
@@ -232,7 +233,7 @@ const CGHeroInstance * CCallback::getHeroInfo(int val, int mode) const //mode =
 	}
 	else //object id
 	{
-		return static_cast<const CGHeroInstance*>(gs->map->objects[val]);
+		return static_cast<const CGHeroInstance*>(gs->map->objects[val].get());
 	}
 	return NULL;
 }
@@ -463,7 +464,7 @@ bool CCallback::buildBuilding(const CGTownInstance *town, si32 buildingID)
 
 	if(town->tempOwner!=player)
 		return false;
-	CBuilding *b = CGI->buildh->buildings[t->subID][buildingID];
+	const CBuilding *b = CGI->buildh->buildings[t->subID][buildingID];
 	for(int i=0;i<b->resources.size();i++)
 		if(b->resources[i] > gs->players[player].resources[i])
 			return false; //lack of resources
@@ -473,19 +474,26 @@ bool CCallback::buildBuilding(const CGTownInstance *town, si32 buildingID)
 	return true;
 }
 
-int CCallback::battleGetBattlefieldType()
+int CBattleCallback::battleGetBattlefieldType()
 {
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
-	return gs->battleGetBattlefieldType();
+	//return gs->battleGetBattlefieldType();
+
+	if(!gs->curB)
+	{
+		tlog2<<"battleGetBattlefieldType called when there is no battle!"<<std::endl;
+		return -1;
+	}
+	return gs->curB->battlefieldType;
 }
 
-int CCallback::battleGetObstaclesAtTile(int tile) //returns bitfield 
+int CBattleCallback::battleGetObstaclesAtTile(THex tile) //returns bitfield 
 {
 	//TODO - write
 	return -1;
 }
 
-std::vector<CObstacleInstance> CCallback::battleGetAllObstacles()
+std::vector<CObstacleInstance> CBattleCallback::battleGetAllObstacles()
 {
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
 	if(gs->curB)
@@ -494,14 +502,14 @@ std::vector<CObstacleInstance> CCallback::battleGetAllObstacles()
 		return std::vector<CObstacleInstance>();
 }
 
-const CStack* CCallback::battleGetStackByID(int ID, bool onlyAlive)
+const CStack* CBattleCallback::battleGetStackByID(int ID, bool onlyAlive)
 {
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
 	if(!gs->curB) return NULL;
 	return gs->curB->getStack(ID, onlyAlive);
 }
 
-int CCallback::battleMakeAction(BattleAction* action)
+int CBattleCallback::battleMakeAction(BattleAction* action)
 {
 	assert(action->actionType == BattleAction::HERO_SPELL);
 	MakeCustomAction mca(*action);
@@ -509,45 +517,46 @@ int CCallback::battleMakeAction(BattleAction* action)
 	return 0;
 }
 
-const CStack* CCallback::battleGetStackByPos(int pos, bool onlyAlive)
+const CStack* CBattleCallback::battleGetStackByPos(THex pos, bool onlyAlive)
 {
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
-	return battleGetStackByID(gs->battleGetStack(pos, onlyAlive), onlyAlive);
+	return gs->curB->battleGetStack(pos, onlyAlive);
 }
 
-int CCallback::battleGetPos(int stack)
+THex CBattleCallback::battleGetPos(int stack)
 {
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
 	if(!gs->curB)
 	{
 		tlog2<<"battleGetPos called when there is no battle!"<<std::endl;
-		return -1;
+		return THex::INVALID;
 	}
 	for(size_t g=0; g<gs->curB->stacks.size(); ++g)
 	{
 		if(gs->curB->stacks[g]->ID == stack)
 			return gs->curB->stacks[g]->position;
 	}
-	return -1;
+	return THex::INVALID;
 }
 
-std::map<int, CStack> CCallback::battleGetStacks()
+std::vector<const CStack*> CBattleCallback::battleGetStacks(bool onlyAlive /*= true*/)
 {
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
-	std::map<int, CStack> ret;
+	std::vector<const CStack*> ret;
 	if(!gs->curB) //there is no battle
 	{
+		tlog2<<"battleGetStacks called when there is no battle!"<<std::endl;
 		return ret;
 	}
 
-	for(size_t g=0; g<gs->curB->stacks.size(); ++g)
-	{
-		ret[gs->curB->stacks[g]->ID] = *(gs->curB->stacks[g]);
-	}
+	BOOST_FOREACH(const CStack *s, gs->curB->stacks)
+		if(s->alive()  ||  !onlyAlive)
+			ret.push_back(s);
+
 	return ret;
 }
 
-void CCallback::getStackQueue( std::vector<const CStack *> &out, int howMany )
+void CBattleCallback::getStackQueue( std::vector<const CStack *> &out, int howMany )
 {
 	if(!gs->curB)
 	{
@@ -557,52 +566,52 @@ void CCallback::getStackQueue( std::vector<const CStack *> &out, int howMany )
 	gs->curB->getStackQueue(out, howMany);
 }
 
-std::vector<int> CCallback::battleGetAvailableHexes(int ID, bool addOccupiable)
+std::vector<THex> CBattleCallback::battleGetAvailableHexes(const CStack * stack, bool addOccupiable)
 {
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
 	if(!gs->curB)
 	{
 		tlog2<<"battleGetAvailableHexes called when there is no battle!"<<std::endl;
-		return std::vector<int>();
+		return std::vector<THex>();
 	}
-	return gs->curB->getAccessibility(ID, addOccupiable);
+	return gs->curB->getAccessibility(stack, addOccupiable);
 	//return gs->battleGetRange(ID);
 }
 
-bool CCallback::battleCanShoot(int ID, int dest)
+bool CBattleCallback::battleCanShoot(const CStack * stack, THex dest)
 {
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
 
 	if(!gs->curB) return false;
 
-	return gs->battleCanShoot(ID, dest);
+	return gs->curB->battleCanShoot(stack, dest);
 }
 
-bool CCallback::battleCanCastSpell()
+bool CBattleCallback::battleCanCastSpell()
 {
 	if(!gs->curB) //there is no battle
 		return false;
 
-	if(gs->curB->side1 == player)
+	if(gs->curB->sides[0] == player)
 		return gs->curB->castSpells[0] == 0 && gs->curB->heroes[0] && gs->curB->heroes[0]->getArt(17);
 	else
 		return gs->curB->castSpells[1] == 0 && gs->curB->heroes[1] && gs->curB->heroes[1]->getArt(17);
 }
 
-bool CCallback::battleCanFlee()
+bool CBattleCallback::battleCanFlee()
 {
-	return gs->battleCanFlee(player);
+	return gs->curB->battleCanFlee(player);
 }
 
-const CGTownInstance *CCallback::battleGetDefendedTown()
+const CGTownInstance *CBattleCallback::battleGetDefendedTown()
 {
-	if(!gs->curB || gs->curB->tid == -1)
+	if(!gs->curB || gs->curB->town == NULL)
 		return NULL;
 
-	return static_cast<const CGTownInstance *>(gs->map->objects[gs->curB->tid]);
+	return gs->curB->town;
 }
 
-ui8 CCallback::battleGetWallState(int partOfWall)
+ui8 CBattleCallback::battleGetWallState(int partOfWall)
 {
 	if(!gs->curB || gs->curB->siege == 0)
 	{
@@ -611,7 +620,7 @@ ui8 CCallback::battleGetWallState(int partOfWall)
 	return gs->curB->si.wallState[partOfWall];
 }
 
-int CCallback::battleGetWallUnderHex(int hex)
+int CBattleCallback::battleGetWallUnderHex(THex hex)
 {
 	if(!gs->curB || gs->curB->siege == 0)
 	{
@@ -620,14 +629,15 @@ int CCallback::battleGetWallUnderHex(int hex)
 	return gs->curB->hexToWallPart(hex);
 }
 
-std::pair<ui32, ui32> CCallback::battleEstimateDamage(int attackerID, int defenderID)
+TDmgRange CBattleCallback::battleEstimateDamage(const CStack * attacker, const CStack * defender, TDmgRange * retaliationDmg)
 {
 	if(!gs->curB)
 		return std::make_pair(0, 0);
 
 	const CGHeroInstance * attackerHero, * defenderHero;
+	bool shooting = battleCanShoot(attacker, defender->position);
 
-	if(gs->curB->side1 == player)
+	if(gs->curB->sides[0] == player)
 	{
 		attackerHero = gs->curB->heroes[0];
 		defenderHero = gs->curB->heroes[1];
@@ -638,13 +648,30 @@ std::pair<ui32, ui32> CCallback::battleEstimateDamage(int attackerID, int defend
 		defenderHero = gs->curB->heroes[0];
 	}
 
-	const CStack * attacker = gs->curB->getStack(attackerID, false),
-		* defender = gs->curB->getStack(defenderID);
+	TDmgRange ret = gs->curB->calculateDmgRange(attacker, defender, attackerHero, defenderHero, shooting, 0, false);
 
-	return gs->curB->calculateDmgRange(attacker, defender, attackerHero, defenderHero, battleCanShoot(attacker->ID, defender->position), 0, false);
+	if(retaliationDmg)
+	{
+		if(shooting)
+		{
+			retaliationDmg->first = retaliationDmg->second = 0;
+		}
+		else
+		{
+			ui32 TDmgRange::* pairElems[] = {&TDmgRange::first, &TDmgRange::second};
+			for (int i=0; i<2; ++i)
+			{
+				BattleStackAttacked bsa;
+				bsa.damageAmount = ret.*pairElems[i];
+				retaliationDmg->*pairElems[!i] = gs->curB->calculateDmgRange(defender, attacker, bsa.newAmount, attacker->count, attackerHero, defenderHero, false, false, false).*pairElems[!i];
+			}
+		}
+	}
+	
+	return ret;
 }
 
-ui8 CCallback::battleGetSiegeLevel()
+ui8 CBattleCallback::battleGetSiegeLevel()
 {
 	if(!gs->curB)
 		return 0;
@@ -652,7 +679,7 @@ ui8 CCallback::battleGetSiegeLevel()
 	return gs->curB->siege;
 }
 
-const CGHeroInstance * CCallback::battleGetFightingHero(ui8 side) const
+const CGHeroInstance * CBattleCallback::battleGetFightingHero(ui8 side) const
 {
 	if(!gs->curB)
 		return 0;
@@ -660,6 +687,19 @@ const CGHeroInstance * CCallback::battleGetFightingHero(ui8 side) const
 	return gs->curB->heroes[side];
 }
 
+template <typename T>
+void CBattleCallback::sendRequest(const T* request)
+{
+	//TODO? should be part of CClient but it would have to be very tricky cause template/serialization issues
+	if(waitTillRealize)
+		cl->waitingRequest.set(true);
+
+	*cl->serv << request;
+
+	if(waitTillRealize)
+		cl->waitingRequest.waitWhileTrue();
+}
+
 void CCallback::swapGarrisonHero( const CGTownInstance *town )
 {
 	if(town->tempOwner != player) return;
@@ -705,7 +745,7 @@ std::vector < const CGObjectInstance * > CCallback::getFlaggableObjects(int3 pos
 
 	std::vector < const CGObjectInstance * > ret;
 
-	std::vector < std::pair<const CGObjectInstance*,SDL_Rect> > & objs = CGI->mh->ttiles[pos.x][pos.y][pos.z].objects;
+	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))
@@ -822,21 +862,8 @@ void CCallback::buildBoat( const IShipyard *obj )
 	sendRequest(&bb);
 }
 
-template <typename T>
-void CCallback::sendRequest(const T* request)
-{
-	//TODO? should be part of CClient but it would have to be very tricky cause template/serialization issues
-	if(waitTillRealize)
-		cl->waitingRequest.set(true);
-
-	*cl->serv << request;
-
-	if(waitTillRealize)
-		cl->waitingRequest.waitWhileTrue();
-}
-
 CCallback::CCallback( CGameState * GS, int Player, CClient *C ) 
-	:gs(GS), cl(C), player(Player)
+	:CBattleCallback(GS, Player, C)
 {
 	waitTillRealize = false;
 }
@@ -909,19 +936,19 @@ bool CCallback::hasAccess(int playerId) const
 	return gs->getPlayerRelations( playerId, player ) ||  player < 0;
 }
 
-si8 CCallback::battleHasDistancePenalty( int stackID, int destHex )
+si8 CBattleCallback::battleHasDistancePenalty( const CStack * stack, THex destHex )
 {
-	return gs->curB->hasDistancePenalty(stackID, destHex);
+	return gs->curB->hasDistancePenalty(stack, destHex);
 }
 
-si8 CCallback::battleHasWallPenalty( int stackID, int destHex )
+si8 CBattleCallback::battleHasWallPenalty( const CStack * stack, THex destHex )
 {
-	return gs->curB->hasWallPenalty(stackID, destHex);
+	return gs->curB->hasWallPenalty(stack, destHex);
 }
 
-si8 CCallback::battleCanTeleportTo(int stackID, int destHex, int telportLevel)
+si8 CBattleCallback::battleCanTeleportTo(const CStack * stack, THex destHex, int telportLevel)
 {
-	return gs->curB->canTeleportTo(stackID, destHex, telportLevel);
+	return gs->curB->canTeleportTo(stack, destHex, telportLevel);
 }
 
 int CCallback::getPlayerStatus(int player) const
@@ -981,7 +1008,7 @@ InfoAboutTown::~InfoAboutTown()
 void InfoAboutTown::initFromTown( const CGTownInstance *t, bool detailed )
 {
 	obj = t;
-	army = t->getArmy();
+	army = ArmyDescriptor(t, detailed);
 	built = t->builded;
 	fortLevel = t->fortLevel();
 	name = t->name;
@@ -997,21 +1024,14 @@ void InfoAboutTown::initFromTown( const CGTownInstance *t, bool detailed )
 		details->hallLevel = t->hallLevel();
 		details->garrisonedHero = t->garrisonHero;
 	}
-	/*else
-	{
-		//hide info about hero stacks counts
-		for(std::map<si32,std::pair<ui32,si32> >::iterator i = slots.begin(); i != slots.end(); ++i)
-		{
-			i->second.second = 0;
-		}
-	}*/
+	//TODO: adjust undetailed info about army to our count of thieves guilds
 }
 
 void InfoAboutTown::initFromGarrison(const CGGarrison *garr, bool detailed)
 {
 	obj = garr;
 	fortLevel = 0;
-	army = garr->getArmy();
+	army = ArmyDescriptor(garr, detailed);
 	name = CGI->generaltexth->names[33]; // "Garrison"
 	owner = garr->tempOwner;
 	built = false;
@@ -1026,4 +1046,44 @@ void InfoAboutTown::initFromGarrison(const CGGarrison *garr, bool detailed)
 		details->goldIncome = -1;
 		details->hallLevel = -1;
 	}
-}
+}
+
+bool CBattleCallback::hasAccess( int playerId ) const
+{
+	return playerId == player || player < 0;
+}
+
+CBattleCallback::CBattleCallback(CGameState *GS, int Player, CClient *C )
+{
+	gs = GS;
+	player = Player;
+	cl = C;
+}
+
+std::vector<int> CBattleCallback::battleGetDistances(const CStack * stack, THex hex /*= THex::INVALID*/, THex * predecessors /*= NULL*/)
+{
+	if(!hex.isValid())
+		hex = stack->position;
+
+	std::vector<int> ret;
+	bool ac[BFIELD_SIZE];
+	THex pr[BFIELD_SIZE];
+	int dist[BFIELD_SIZE];
+	gs->curB->makeBFS(stack->position, ac, pr, dist, stack->doubleWide(), stack->attackerOwned, stack->hasBonusOfType(Bonus::FLYING), false);
+
+	for(int i=0; i<BFIELD_SIZE; ++i)
+	{
+		if(pr[i] == -1)
+			ret.push_back(-1);
+		else
+			ret.push_back(dist[i]);
+	}
+
+	if(predecessors)
+	{
+		memcpy(predecessors, pr, BFIELD_SIZE * sizeof(THex));
+	}
+
+	return ret;
+}
+

+ 76 - 54
CCallback.h

@@ -43,6 +43,7 @@ class CMapHeader;
 struct CGPathNode;
 struct CGPath;
 class CGGarrison;
+class CObstacleInstance;
 
 struct InfoAboutTown
 {
@@ -61,7 +62,7 @@ struct InfoAboutTown
 	CTown *tType;
 	bool built;
 
-	CCreatureSet army; //numbers of creatures are valid only if details
+	ArmyDescriptor army; //numbers of creatures are valid only if details
 
 	InfoAboutTown();
 	~InfoAboutTown();
@@ -69,10 +70,39 @@ struct InfoAboutTown
 	void initFromGarrison(const CGGarrison *garr, bool detailed);
 };
 
-class ICallback
+class IBattleCallback
 {
 public:
 	bool waitTillRealize; //if true, request functions will return after they are realized by server
+	//battle
+	virtual int battleGetBattlefieldType()=0; //   1. sand/shore   2. sand/mesas   3. dirt/birches   4. dirt/hills   5. dirt/pines   6. grass/hills   7. grass/pines   8. lava   9. magic plains   10. snow/mountains   11. snow/trees   12. subterranean   13. swamp/trees   14. fiery fields   15. rock lands   16. magic clouds   17. lucid pools   18. holy ground   19. clover field   20. evil fog   21. "favourable winds" text on magic plains background   22. cursed ground   23. rough   24. ship to ship   25. ship
+	virtual int battleGetObstaclesAtTile(THex tile)=0; //returns bitfield
+	virtual std::vector<CObstacleInstance> battleGetAllObstacles()=0; //returns all obstacles on the battlefield
+	virtual const CStack * battleGetStackByID(int ID, bool onlyAlive = true)=0; //returns stack info by given ID
+	virtual const CStack * battleGetStackByPos(THex pos, bool onlyAlive = true)=0; //returns stack info by given pos
+	virtual THex battleGetPos(int stack)=0; //returns position (tile ID) of stack
+	virtual int battleMakeAction(BattleAction* action)=0;//for casting spells by hero - DO NOT use it for moving active stack
+	virtual std::vector<const CStack*> battleGetStacks(bool onlyAlive = true)=0; //returns stacks on battlefield
+	virtual void getStackQueue( std::vector<const CStack *> &out, int howMany )=0; //returns vector of stack in order of their move sequence
+	virtual std::vector<THex> battleGetAvailableHexes(const CStack * stack, bool addOccupiable)=0; //returns numbers of hexes reachable by creature with id ID
+	virtual std::vector<int> battleGetDistances(const CStack * stack, THex hex = THex::INVALID, THex * predecessors = NULL)=0; //returns vector of distances to [dest hex number]
+	virtual bool battleCanShoot(const CStack * stack, THex dest)=0; //returns true if unit with id ID can shoot to dest
+	virtual bool battleCanCastSpell()=0; //returns true, if caller can cast a spell
+	virtual bool battleCanFlee()=0; //returns true if caller can flee from the battle
+	virtual const CGTownInstance * battleGetDefendedTown()=0; //returns defended town if current battle is a siege, NULL instead
+	virtual ui8 battleGetWallState(int partOfWall)=0; //for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle
+	virtual int battleGetWallUnderHex(THex hex)=0; //returns part of destructible wall / gate / keep under given hex or -1 if not found
+	virtual TDmgRange battleEstimateDamage(const CStack * attacker, const CStack * defender, TDmgRange * retaliationDmg = NULL)=0; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair <min dmg, max dmg>
+	virtual ui8 battleGetSiegeLevel()=0; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle
+	virtual const CGHeroInstance * battleGetFightingHero(ui8 side) const =0; //returns hero corresponding to given side (0 - attacker, 1 - defender)
+	virtual si8 battleHasDistancePenalty(const CStack * stack, THex destHex) =0; //checks if given stack has distance penalty
+	virtual si8 battleHasWallPenalty(const CStack * stack, THex destHex) =0; //checks if given stack has wall penalty
+	virtual si8 battleCanTeleportTo(const CStack * stack, THex destHex, int telportLevel) =0; //checks if teleportation of given stack to given position can take place
+};
+
+class ICallback : public virtual IBattleCallback
+{
+public:
 	//hero
 	virtual bool moveHero(const CGHeroInstance *h, int3 dst) =0; //dst must be free, neighbouring tile (this function can move hero only by one tile)
 	virtual bool dismissHero(const CGHeroInstance * hero)=0; //dismisses given hero; true - successfuly, false - not successfuly
@@ -156,44 +186,59 @@ public:
 	virtual int3 getMapSize() const =0; //returns size of map - z is 1 for one - level map and 2 for two level map
 	virtual const TerrainTile * getTileInfo(int3 tile) const = 0;
 	virtual int getPlayerRelations(ui8 color1, ui8 color2) const =0;// 0 = enemy, 1 = ally, 2 = same player 
-
-//battle
-	virtual int battleGetBattlefieldType()=0; //   1. sand/shore   2. sand/mesas   3. dirt/birches   4. dirt/hills   5. dirt/pines   6. grass/hills   7. grass/pines   8. lava   9. magic plains   10. snow/mountains   11. snow/trees   12. subterranean   13. swamp/trees   14. fiery fields   15. rock lands   16. magic clouds   17. lucid pools   18. holy ground   19. clover field   20. evil fog   21. "favourable winds" text on magic plains background   22. cursed ground   23. rough   24. ship to ship   25. ship
-	virtual int battleGetObstaclesAtTile(int tile)=0; //returns bitfield
-	virtual std::vector<CObstacleInstance> battleGetAllObstacles()=0; //returns all obstacles on the battlefield
-	virtual const CStack * battleGetStackByID(int ID, bool onlyAlive = true)=0; //returns stack info by given ID
-	virtual const CStack * battleGetStackByPos(int pos, bool onlyAlive = true)=0; //returns stack info by given pos
-	virtual int battleGetPos(int stack)=0; //returns position (tile ID) of stack
-	virtual int battleMakeAction(BattleAction* action)=0;//for casting spells by hero - DO NOT use it for moving active stack
-	virtual std::map<int, CStack> battleGetStacks()=0; //returns stacks on battlefield
-	virtual void getStackQueue( std::vector<const CStack *> &out, int howMany )=0; //returns vector of stack in order of their move sequence
-	virtual std::vector<int> battleGetAvailableHexes(int ID, bool addOccupiable)=0; //returns numbers of hexes reachable by creature with id ID
-	virtual bool battleCanShoot(int ID, int dest)=0; //returns true if unit with id ID can shoot to dest
-	virtual bool battleCanCastSpell()=0; //returns true, if caller can cast a spell
-	virtual bool battleCanFlee()=0; //returns true if caller can flee from the battle
-	virtual const CGTownInstance * battleGetDefendedTown()=0; //returns defended town if current battle is a siege, NULL instead
-	virtual ui8 battleGetWallState(int partOfWall)=0; //for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle
-	virtual int battleGetWallUnderHex(int hex)=0; //returns part of destructible wall / gate / keep under given hex or -1 if not found
-	virtual std::pair<ui32, ui32> battleEstimateDamage(int attackerID, int defenderID)=0; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair <min dmg, max dmg>
-	virtual ui8 battleGetSiegeLevel()=0; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle
-	virtual const CGHeroInstance * battleGetFightingHero(ui8 side) const =0; //returns hero corresponding to given side (0 - attacker, 1 - defender)
-	virtual si8 battleHasDistancePenalty(int stackID, int destHex) =0; //checks if given stack has distance penalty
-	virtual si8 battleHasWallPenalty(int stackID, int destHex) =0; //checks if given stack has wall penalty
 };
 
-class CCallback : public ICallback
+class CBattleCallback : public virtual IBattleCallback
 {
 private:
-	CCallback(CGameState * GS, int Player, CClient *C);;
 	CGameState * gs;
+	CBattleCallback(CGameState *GS, int Player, CClient *C);
+
+
+protected:
+	template <typename T> void sendRequest(const T*request);
 	CClient *cl;
+	virtual bool hasAccess(int playerId) const;
+	int player;
+
+public:
+	//battle
+	int battleGetBattlefieldType() OVERRIDE; //   1. sand/shore   2. sand/mesas   3. dirt/birches   4. dirt/hills   5. dirt/pines   6. grass/hills   7. grass/pines   8. lava   9. magic plains   10. snow/mountains   11. snow/trees   12. subterranean   13. swamp/trees   14. fiery fields   15. rock lands   16. magic clouds   17. lucid pools   18. holy ground   19. clover field   20. evil fog   21. "favourable winds" text on magic plains background   22. cursed ground   23. rough   24. ship to ship   25. ship
+	int battleGetObstaclesAtTile(THex tile) OVERRIDE; //returns bitfield
+	std::vector<CObstacleInstance> battleGetAllObstacles() OVERRIDE; //returns all obstacles on the battlefield
+	const CStack * battleGetStackByID(int ID, bool onlyAlive = true) OVERRIDE; //returns stack info by given ID
+	const CStack * battleGetStackByPos(THex pos, bool onlyAlive = true) OVERRIDE; //returns stack info by given pos
+	THex battleGetPos(int stack) OVERRIDE; //returns position (tile ID) of stack
+	int battleMakeAction(BattleAction* action) OVERRIDE;//for casting spells by hero - DO NOT use it for moving active stack
+	std::vector<const CStack*> battleGetStacks(bool onlyAlive = true) OVERRIDE; //returns stacks on battlefield
+	void getStackQueue( std::vector<const CStack *> &out, int howMany ) OVERRIDE; //returns vector of stack in order of their move sequence
+	std::vector<THex> battleGetAvailableHexes(const CStack * stack, bool addOccupiable) OVERRIDE; //reutrns numbers of hexes reachable by creature with id ID
+	std::vector<int> battleGetDistances(const CStack * stack, THex hex = THex::INVALID, THex * predecessors = NULL) OVERRIDE; //returns vector of distances to [dest hex number]; if predecessors is not null, it must point to BFIELD_SIZE * sizeof(int) of allocated memory
+	bool battleCanShoot(const CStack * stack, THex dest) OVERRIDE; //returns true if unit with id ID can shoot to dest
+	bool battleCanCastSpell() OVERRIDE; //returns true, if caller can cast a spell
+	bool battleCanFlee() OVERRIDE; //returns true if caller can flee from the battle
+	const CGTownInstance * battleGetDefendedTown() OVERRIDE; //returns defended town if current battle is a siege, NULL instead
+	ui8 battleGetWallState(int partOfWall) OVERRIDE; //for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle
+	int battleGetWallUnderHex(THex hex) OVERRIDE; //returns part of destructible wall / gate / keep under given hex or -1 if not found
+	TDmgRange battleEstimateDamage(const CStack * attacker, const CStack * defender, TDmgRange * retaliationDmg = NULL) OVERRIDE; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair <min dmg, max dmg>
+	ui8 battleGetSiegeLevel() OVERRIDE; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle
+	const CGHeroInstance * battleGetFightingHero(ui8 side) const OVERRIDE; //returns hero corresponding ot given side (0 - attacker, 1 - defender)
+	si8 battleHasDistancePenalty(const CStack * stack, THex destHex) OVERRIDE; //checks if given stack has distance penalty
+	si8 battleHasWallPenalty(const CStack * stack, THex destHex) OVERRIDE; //checks if given stack has wall penalty
+	si8 battleCanTeleportTo(const CStack * stack, THex destHex, int telportLevel) OVERRIDE; //checks if teleportation of given stack to given position can take place
+
+	friend CCallback;
+	friend CClient;
+};
+
+class CCallback : public ICallback, public CBattleCallback
+{
+private:
+	CCallback(CGameState * GS, int Player, CClient *C);
 	bool isVisible(int3 pos, int Player) const;
 	bool isVisible(const CGObjectInstance *obj, int Player) const;
-	template <typename T> void sendRequest(const T*request);
-
 protected:
-	bool hasAccess(int playerId) const;
-	int player;
+	virtual bool hasAccess(int playerId) const OVERRIDE;
 
 public:
 //commands
@@ -272,29 +317,6 @@ public:
 	int getPlayerStatus(int player) const;
 	int getPlayerRelations(ui8 color1, ui8 color2) const;// 0 = enemy, 1 = ally, 2 = same player 
 
-	//battle
-	int battleGetBattlefieldType(); //   1. sand/shore   2. sand/mesas   3. dirt/birches   4. dirt/hills   5. dirt/pines   6. grass/hills   7. grass/pines   8. lava   9. magic plains   10. snow/mountains   11. snow/trees   12. subterranean   13. swamp/trees   14. fiery fields   15. rock lands   16. magic clouds   17. lucid pools   18. holy ground   19. clover field   20. evil fog   21. "favourable winds" text on magic plains background   22. cursed ground   23. rough   24. ship to ship   25. ship
-	int battleGetObstaclesAtTile(int tile); //returns bitfield
-	std::vector<CObstacleInstance> battleGetAllObstacles(); //returns all obstacles on the battlefield
-	const CStack * battleGetStackByID(int ID, bool onlyAlive = true); //returns stack info by given ID
-	const CStack * battleGetStackByPos(int pos, bool onlyAlive = true); //returns stack info by given pos
-	int battleGetPos(int stack); //returns position (tile ID) of stack
-	int battleMakeAction(BattleAction* action);//for casting spells by hero - DO NOT use it for moving active stack
-	std::map<int, CStack> battleGetStacks(); //returns stacks on battlefield
-	void getStackQueue( std::vector<const CStack *> &out, int howMany ); //returns vector of stack in order of their move sequence
-	std::vector<int> battleGetAvailableHexes(int ID, bool addOccupiable); //reutrns numbers of hexes reachable by creature with id ID
-	bool battleCanShoot(int ID, int dest); //returns true if unit with id ID can shoot to dest
-	bool battleCanCastSpell(); //returns true, if caller can cast a spell
-	bool battleCanFlee(); //returns true if caller can flee from the battle
-	const CGTownInstance * battleGetDefendedTown(); //returns defended town if current battle is a siege, NULL instead
-	ui8 battleGetWallState(int partOfWall); //for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle
-	int battleGetWallUnderHex(int hex); //returns part of destructible wall / gate / keep under given hex or -1 if not found
-	std::pair<ui32, ui32> battleEstimateDamage(int attackerID, int defenderID); //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair <min dmg, max dmg>
-	ui8 battleGetSiegeLevel(); //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle
-	const CGHeroInstance * battleGetFightingHero(ui8 side) const; //returns hero corresponding ot given side (0 - attacker, 1 - defender)
-	si8 battleHasDistancePenalty(int stackID, int destHex); //checks if given stack has distance penalty
-	si8 battleHasWallPenalty(int stackID, int destHex); //checks if given stack has wall penalty
-	si8 battleCanTeleportTo(int stackID, int destHex, int telportLevel); //checks if teleportation of given stack to given position can take place
 
 //XXX hmmm _tmain on _GNUC_ wtf?
 //friends

+ 25 - 6
CGameInterface.cpp

@@ -1,5 +1,6 @@
 #include "stdafx.h"
 #include "CGameInterface.h"
+#include "lib/BattleState.h"
 
 #ifdef _WIN32
 	#define WIN32_LEAN_AND_MEAN //excludes rarely used stuff from windows headers - delete this line if something is missing
@@ -18,17 +19,18 @@
  *
  */
 
-CGlobalAI * CAIHandler::getNewAI(CCallback * cb, std::string dllname)
+template<typename rett>
+rett * createAnyAI(CCallback * cb, std::string dllname, std::string methodName)
 {
 	char temp[50];
-	CGlobalAI * ret=NULL;
-	CGlobalAI*(*getAI)(); 
+	rett * ret=NULL;
+	rett*(*getAI)(); 
 	void(*getName)(char*); 
 
 	std::string dllPath;
 
 #ifdef _WIN32
-	dllPath = "AI/"+dllname+".dll";
+	dllPath = LIB_DIR "/" +dllname+".dll";
 	HINSTANCE dll = LoadLibraryA(dllPath.c_str());
 	if (!dll)
 	{
@@ -37,7 +39,7 @@ CGlobalAI * CAIHandler::getNewAI(CCallback * cb, std::string dllname)
 	}
 	//int len = dllname.size()+1;
 	getName = (void(*)(char*))GetProcAddress(dll,"GetAiName");
-	getAI = (CGlobalAI*(*)())GetProcAddress(dll,"GetNewAI");
+	getAI = (rett*(*)())GetProcAddress(dll,methodName.c_str());
 #else
 	dllPath = LIB_DIR "/" + dllname + ".so";
 	void *dll = dlopen(dllPath.c_str(), RTLD_LOCAL | RTLD_LAZY);
@@ -47,7 +49,7 @@ CGlobalAI * CAIHandler::getNewAI(CCallback * cb, std::string dllname)
 		throw new std::string("Cannot open AI library");
 	}
 	getName = (void(*)(char*))dlsym(dll,"GetAiName");
-	getAI = (CGlobalAI*(*)())dlsym(dll,"GetNewAI");
+	getAI = (rett*(*)())dlsym(dll,methodName.c_str());
 #endif
 	getName(temp);
 	tlog0 << "Loaded AI named " << temp << std::endl;
@@ -59,3 +61,20 @@ CGlobalAI * CAIHandler::getNewAI(CCallback * cb, std::string dllname)
 	ret->dllName = dllname;	 
 	return ret;
 }
+
+CGlobalAI * CAIHandler::getNewAI(CCallback * cb, std::string dllname)
+{
+	return createAnyAI<CGlobalAI>(cb, dllname, "GetNewAI");
+}
+
+CBattleGameInterface * CAIHandler::getNewBattleAI( CCallback * cb, std::string dllname )
+{
+	return createAnyAI<CBattleGameInterface>(cb, dllname, "GetNewBattleAI");
+}
+
+BattleAction CGlobalAI::activeStack( const CStack * stack )
+{
+	BattleAction ba; ba.actionType = BattleAction::DEFEND;
+	ba.stackNumber = stack->ID;
+	return ba;
+}

+ 63 - 33
CGameInterface.h

@@ -18,6 +18,7 @@
 
 using namespace boost::logic;
 class CCallback;
+class IBattleCallback;
 class ICallback;
 class CGlobalAI;
 struct Component;
@@ -42,22 +43,70 @@ struct PackageApplied;
 struct SetObjectProperty;
 struct CatapultAttack;
 struct BattleStacksRemoved;
+struct StackLocation;
+class CStackInstance;
+class CStack;
+class CCreature;
 class CLoadFile;
 class CSaveFile;
+typedef si32 TQuantity;
 template <typename Serializer> class CISer;
 template <typename Serializer> class COSer;
+struct ArtifactLocation;
 
-class CGameInterface
+class CBattleGameInterface
 {
 public:
 	bool human;
 	int playerID;
 	std::string dllName;
 
-	virtual ~CGameInterface() {};
+	virtual ~CBattleGameInterface() {};
+
+	virtual void init(IBattleCallback * CB){};
+
+	//battle call-ins
+	virtual void actionFinished(const BattleAction *action){};//occurs AFTER every action taken by any stack or by the hero
+	virtual void actionStarted(const BattleAction *action){};//occurs BEFORE every action taken by any stack or by the hero
+	virtual BattleAction activeStack(const CStack * stack)=0; //called when it's turn of that stack
+	virtual void battleAttack(const BattleAttack *ba){}; //called when stack is performing attack
+	virtual void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa){}; //called when stack receives damage (after battleAttack())
+	virtual void battleEnd(const BattleResult *br){};
+	virtual void battleResultsApplied(){}; //called when all effects of last battle are applied
+	virtual void battleNewRoundFirst(int round){}; //called at the beginning of each turn before changes are applied;
+	virtual void battleNewRound(int round){}; //called at the beggining of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
+	virtual void battleStackMoved(const CStack * stack, THex dest, int distance, bool end){};
+	virtual void battleSpellCast(const BattleSpellCast *sc){};
+	virtual void battleStacksEffectsSet(const SetStackEffect & sse){};//called when a specific effect is set to stacks
+	virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side){}; //called by engine when battle starts; side=0 - left, side=1 - right
+	virtual void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, si32 lifeDrainFrom){}; //called when stacks are healed / resurrected first element of pair - stack id, second - healed hp
+	virtual void battleNewStackAppeared(const CStack * stack){}; //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned
+	virtual void battleObstaclesRemoved(const std::set<si32> & removedObstacles){}; //called when a certain set  of obstacles is removed from batlefield; IDs of them are given
+	virtual void battleCatapultAttacked(const CatapultAttack & ca){}; //called when catapult makes an attack
+	virtual void battleStacksRemoved(const BattleStacksRemoved & bsr){}; //called when certain stack is completely removed from battlefield
+};
+
+class CGameInterface : public CBattleGameInterface
+{
+public:
 	virtual void buildChanged(const CGTownInstance *town, int buildingID, int what){}; //what: 1 - built, 2 - demolished
-	virtual void garrisonChanged(const CGObjectInstance * obj){};
-	virtual void heroArtifactSetChanged(const CGHeroInstance*hero){};
+
+	//garrison operations
+	virtual void stackChagedCount(const StackLocation &location, const TQuantity &change, bool isAbsolute){}; //if absolute, change is the new count; otherwise count was modified by adding change
+	virtual void stackChangedType(const StackLocation &location, const CCreature &newType){}; //used eg. when upgrading creatures
+	virtual void stacksErased(const StackLocation &location){}; //stack removed from previously filled slot
+	virtual void stacksSwapped(const StackLocation &loc1, const StackLocation &loc2){};
+	virtual void newStackInserted(const StackLocation &location, const CStackInstance &stack){}; //new stack inserted at given (previously empty position)
+	virtual void stacksRebalanced(const StackLocation &src, const StackLocation &dst, TQuantity count){}; //moves creatures from src stack to dst slot, may be used for merging/splittint/moving stacks
+	//virtual void garrisonChanged(const CGObjectInstance * obj){};
+
+	//artifacts operations
+	virtual void artifactPut(const ArtifactLocation &al){};
+	virtual void artifactRemoved(const ArtifactLocation &al){};
+	virtual void artifactAssembled(const ArtifactLocation &al){};
+	virtual void artifactDisassembled(const ArtifactLocation &al){};
+	virtual void artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst){};
+
 	virtual void heroCreated(const CGHeroInstance*){};
 	virtual void heroGotLevel(const CGHeroInstance *hero, int pskill, std::vector<ui16> &skills, boost::function<void(ui32)> &callback)=0; //pskill is gained primary skill, interface has to choose one of given skills and call callback with selection id
 	virtual void heroInGarrisonChange(const CGTownInstance *town){};
@@ -81,8 +130,8 @@ public:
 	virtual void showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor){};
 	virtual void showTavernWindow(const CGObjectInstance *townOrTavern){};
 	virtual void advmapSpellCast(const CGHeroInstance * caster, int spellID){}; //called when a hero casts a spell
-	virtual void tileHidden(const std::set<int3> &pos){};
-	virtual void tileRevealed(const std::set<int3> &pos){};
+	virtual void tileHidden(const boost::unordered_set<int3, ShashInt3> &pos){};
+	virtual void tileRevealed(const boost::unordered_set<int3, ShashInt3> &pos){};
 	virtual void newObject(const CGObjectInstance * obj){}; //eg. ship built in shipyard
 	virtual void availableArtifactsChanged(const CGBlackMarket *bm = NULL){}; //bm may be NULL, then artifacts are changed in the global pool (used by merchants in towns)
 	virtual void yourTurn(){};
@@ -98,44 +147,25 @@ public:
 	virtual void gameOver(ui8 player, bool victory){}; //player lost or won the game
 	virtual void serialize(COSer<CSaveFile> &h, const int version){}; //saving
 	virtual void serialize(CISer<CLoadFile> &h, const int version){}; //loading
-
-	//battle call-ins
-	virtual void actionFinished(const BattleAction *action){};//occurs AFTER every action taken by any stack or by the hero
-	virtual void actionStarted(const BattleAction *action){};//occurs BEFORE every action taken by any stack or by the hero
-	virtual BattleAction activeStack(int stackID)=0; //called when it's turn of that stack
-	virtual void battleAttack(const BattleAttack *ba){}; //called when stack is performing attack
-	virtual void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa){}; //called when stack receives damage (after battleAttack())
-	virtual void battleEnd(const BattleResult *br){};
-	virtual void battleResultsApplied(){}; //called when all effects of last battle are applied
-	virtual void battleNewRoundFirst(int round){}; //called at the beginning of each turn before changes are applied;
-	virtual void battleNewRound(int round){}; //called at the beggining of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
-	virtual void battleStackMoved(int ID, int dest, int distance, bool end){};
-	virtual void battleSpellCast(const BattleSpellCast *sc){};
-	virtual void battleStacksEffectsSet(const SetStackEffect & sse){};//called when a specific effect is set to stacks
-	virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side){}; //called by engine when battle starts; side=0 - left, side=1 - right
-	//virtual void battlefieldPrepared(int battlefieldType, std::vector<CObstacle*> obstacles){}; //called when battlefield is prepared, prior the battle beginning
-	virtual void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, si32 lifeDrainFrom){}; //called when stacks are healed / resurrected first element of pair - stack id, second - healed hp
-	virtual void battleNewStackAppeared(int stackID){}; //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned
-	virtual void battleObstaclesRemoved(const std::set<si32> & removedObstacles){}; //called when a certain set  of obstacles is removed from batlefield; IDs of them are given
-	virtual void battleCatapultAttacked(const CatapultAttack & ca){}; //called when catapult makes an attack
-	virtual void battleStacksRemoved(const BattleStacksRemoved & bsr){}; //called when certain stack is completely removed from battlefield
 };
+
 class CAIHandler
 {
 public:
 	static CGlobalAI * getNewAI(CCallback * cb, std::string dllname);
+	static CBattleGameInterface * getNewBattleAI(CCallback * cb, std::string dllname);
 };
 class CGlobalAI : public CGameInterface // AI class (to derivate)
 {
 public:
 	//CGlobalAI();
-	virtual void yourTurn(){};
+	virtual void yourTurn() OVERRIDE{};
 	virtual void heroKilled(const CGHeroInstance*){};
-	virtual void heroCreated(const CGHeroInstance*){};
-	virtual void battleStackMoved(int ID, int dest, int distance){};
-	virtual void battleStackAttacking(int ID, int dest){};
-	virtual void battleStackIsAttacked(int ID, int dmg, int killed, int IDby, bool byShooting){};
-	virtual BattleAction activeStack(int stackID) {BattleAction ba; ba.actionType = 3; ba.stackNumber = stackID; return ba;};
+	virtual void heroCreated(const CGHeroInstance*) OVERRIDE{};
+	virtual void battleStackMoved(const CStack * stack, THex dest, int distance, bool end) OVERRIDE{};
+	virtual void battleStackAttacking(int ID, int dest) {};
+	virtual void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa) OVERRIDE{};
+	virtual BattleAction activeStack(const CStack * stack) OVERRIDE;
 };
 
 #endif // __CGAMEINTERFACE_H__

+ 3 - 3
StartInfo.h

@@ -52,11 +52,11 @@ struct PlayerSettings
 
 struct StartInfo
 {
-	enum EMode {NEW_GAME, LOAD_GAME, CAMPAIGN, INVALID = 255};
+	enum EMode {NEW_GAME, LOAD_GAME, CAMPAIGN, DUEL, INVALID = 255};
 
 	ui8 mode; //uses EMode enum
 	ui8 difficulty; //0=easy; 4=impossible
-	std::map<int, PlayerSettings> playerInfos; //color indexed
+	bmap<int, PlayerSettings> playerInfos; //color indexed
 	ui8 turnTime; //in minutes, 0=unlimited
 	std::string mapname;
 	ui8 whichMapInCampaign; //used only for mode CAMPAIGN
@@ -71,7 +71,7 @@ struct StartInfo
 
 	PlayerSettings *getPlayersSettings(const ui8 nameID)
 	{
-		for(std::map<int, PlayerSettings>::iterator it=playerInfos.begin(); it != playerInfos.end(); ++it)
+		for(bmap<int, PlayerSettings>::iterator it=playerInfos.begin(); it != playerInfos.end(); ++it)
 			if(it->second.human == nameID)
 				return &it->second;
 

+ 6 - 6
client/AdventureMapButton.cpp

@@ -3,16 +3,16 @@
 #include "CAdvmapInterface.h"
 #include "SDL_Extensions.h"
 #include "CGameInfo.h"
-#include "../hch/CLodHandler.h"
-#include "../hch/CGeneralTextHandler.h"
-#include "../hch/CTownHandler.h"
+#include "../lib/CLodHandler.h"
+#include "../lib/CGeneralTextHandler.h"
+#include "../lib/CTownHandler.h"
 #include "../CCallback.h"
 #include "CConfigHandler.h"
 #include "Graphics.h"
 #include "CBattleInterface.h"
 #include "CPlayerInterface.h"
 #include "CMessage.h"
-#include "../hch/CMusicHandler.h"
+#include "CMusicHandler.h"
 
 /*
  * AdventureMapButton.cpp, part of VCMI engine
@@ -142,7 +142,7 @@ void AdventureMapButton::clickLeft(tribool down, bool previousState)
 
 	if (down) 
 	{
-		CGI->soundh->playSound(soundBase::button);
+		CCS->soundh->playSound(soundBase::button);
 		state = 1;
 	} 
 	else if(hoverable && hovered)
@@ -295,7 +295,7 @@ void CHighlightableButton::clickLeft(tribool down, bool previousState)
 		return;
 	if (down) 
 	{
-		CGI->soundh->playSound(soundBase::button);
+		CCS->soundh->playSound(soundBase::button);
 		state = 1;
 	} 
 	else

+ 43 - 45
client/CAdvmapInterface.cpp

@@ -12,11 +12,11 @@
 #include "CConfigHandler.h"
 #include "CSpellWindow.h"
 #include "Graphics.h"
-#include "../hch/CDefHandler.h"
-#include "../hch/CGeneralTextHandler.h"
-#include "../hch/CHeroHandler.h"
-#include "../hch/CObjectHandler.h"
-#include "../hch/CTownHandler.h"
+#include "CDefHandler.h"
+#include "../lib/CGeneralTextHandler.h"
+#include "../lib/CHeroHandler.h"
+#include "../lib/CObjectHandler.h"
+#include "../lib/CTownHandler.h"
 #include "../lib/map.h"
 #include "mapHandler.h"
 #include "../stdafx.h"
@@ -27,8 +27,9 @@
 #include <sstream>
 #include "CPreGame.h"
 #include "../lib/VCMI_Lib.h"
-#include "../hch/CSpellHandler.h"
+#include "../lib/CSpellHandler.h"
 #include <boost/foreach.hpp>
+#include "CSoundBase.h"
 
 #ifdef _MSC_VER
 #pragma warning (disable : 4355)
@@ -491,7 +492,7 @@ void CTerrainRect::mouseMoved (const SDL_MouseMotionEvent & sEvent)
 
 	if(tHovered != pom) //tile outside the map
 	{
-		CGI->curh->changeGraphic(0, 0);
+		CCS->curh->changeGraphic(0, 0);
 		return;
 	}
 
@@ -507,7 +508,7 @@ void CTerrainRect::hover(bool on)
 	if (!on)
 	{
 		adventureInt->statusbar.clear();
-		CGI->curh->changeGraphic(0,0);
+		CCS->curh->changeGraphic(0,0);
 	}
 	//Hoverable::hover(on);
 }
@@ -1188,7 +1189,6 @@ townList(ADVOPT.tlistSize,ADVOPT.tlistX,ADVOPT.tlistY,ADVOPT.tlistAU,ADVOPT.tlis
 	//townList.init();
 	//townList.genList();
 
-	heroWindow = new CHeroWindow(LOCPLINT->playerID);
 
 	for (int g=0; g<ADVOPT.gemG.size(); ++g)
 	{
@@ -1202,7 +1202,6 @@ townList(ADVOPT.tlistSize,ADVOPT.tlistX,ADVOPT.tlistY,ADVOPT.tlistAU,ADVOPT.tlis
 CAdvMapInt::~CAdvMapInt()
 {
 	SDL_FreeSurface(bg);
-	delete heroWindow;
 
 	for(int i=0; i<gems.size(); i++)
 		delete gems[i];
@@ -1329,7 +1328,7 @@ void CAdvMapInt::deactivate()
 	deactivateMouseMove();
 	scrollingDir = 0;
 
-	CGI->curh->changeGraphic(0,0);
+	CCS->curh->changeGraphic(0,0);
 	kingOverview.deactivate();
 	underground.deactivate();
 	questlog.deactivate();
@@ -1734,7 +1733,6 @@ void CAdvMapInt::setPlayer(int Player)
 	nextHero.setPlayerColor(player);
 	endTurn.setPlayerColor(player);
 	graphics->blueToPlayersAdv(resdatabar.bg,player);
-	heroWindow->setPlayer(player);
 
 	//heroList.updateHList();
 	//townList.genList();
@@ -1858,18 +1856,18 @@ void CAdvMapInt::tileHovered(const int3 &tile)
 		{
 		case Spells::SCUTTLE_BOAT:
 			if(objAtTile && objAtTile->ID == 8)
-				CGI->curh->changeGraphic(0, 42);
+				CCS->curh->changeGraphic(0, 42);
 			else
-				CGI->curh->changeGraphic(0, 0);
+				CCS->curh->changeGraphic(0, 0);
 			return;
 		case Spells::DIMENSION_DOOR:
 			{
 				const TerrainTile *t = LOCPLINT->cb->getTileInfo(tile);
 				int3 hpos = selection->getSightCenter();
 				if((!t  ||  t->isClear(LOCPLINT->cb->getTileInfo(hpos)))   &&   isInScreenRange(hpos, tile))
-					CGI->curh->changeGraphic(0, 41);
+					CCS->curh->changeGraphic(0, 41);
 				else
-					CGI->curh->changeGraphic(0, 0);
+					CCS->curh->changeGraphic(0, 0);
 				return;
 			}
 		}
@@ -1882,12 +1880,12 @@ void CAdvMapInt::tileHovered(const int3 &tile)
 		if(objAtTile)
 		{
 			if(objAtTile->ID == TOWNI_TYPE && LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, objAtTile->tempOwner))
-				CGI->curh->changeGraphic(0, 3);
+				CCS->curh->changeGraphic(0, 3);
 			else if(objAtTile->ID == HEROI_TYPE && objAtTile->tempOwner == LOCPLINT->playerID)
-				CGI->curh->changeGraphic(0, 2);
+				CCS->curh->changeGraphic(0, 2);
 		}
 		else
-			CGI->curh->changeGraphic(0, 0);
+			CCS->curh->changeGraphic(0, 0);
 	}
 	else if(const CGHeroInstance *h = curHero())
 	{
@@ -1898,18 +1896,18 @@ void CAdvMapInt::tileHovered(const int3 &tile)
 				if(!LOCPLINT->cb->getPlayerRelations( LOCPLINT->playerID, objAtTile->tempOwner)) //enemy hero
 				{
 					if(accessible)
-						CGI->curh->changeGraphic(0, 5 + turns*6);
+						CCS->curh->changeGraphic(0, 5 + turns*6);
 					else
-						CGI->curh->changeGraphic(0, 0);
+						CCS->curh->changeGraphic(0, 0);
 				}
 				else //our or ally hero
 				{
 					if(selection == objAtTile)
-						CGI->curh->changeGraphic(0, 2);
+						CCS->curh->changeGraphic(0, 2);
 					else if(accessible)
-						CGI->curh->changeGraphic(0, 8 + turns*6);
+						CCS->curh->changeGraphic(0, 8 + turns*6);
 					else
-						CGI->curh->changeGraphic(0, 2);
+						CCS->curh->changeGraphic(0, 2);
 				}
 			}
 			else if(objAtTile->ID == TOWNI_TYPE)
@@ -1922,30 +1920,30 @@ void CAdvMapInt::tileHovered(const int3 &tile)
 
 						// Show movement cursor for unguarded enemy towns, otherwise attack cursor.
 						if (townObj && !townObj->stacksCount())
-							CGI->curh->changeGraphic(0, 9 + turns*6);
+							CCS->curh->changeGraphic(0, 9 + turns*6);
 						else
-							CGI->curh->changeGraphic(0, 5 + turns*6);
+							CCS->curh->changeGraphic(0, 5 + turns*6);
 
 					} 
 					else 
 					{
-						CGI->curh->changeGraphic(0, 0);
+						CCS->curh->changeGraphic(0, 0);
 					}
 				}
 				else //our or ally town
 				{
 					if(accessible)
-						CGI->curh->changeGraphic(0, 9 + turns*6);
+						CCS->curh->changeGraphic(0, 9 + turns*6);
 					else
-						CGI->curh->changeGraphic(0, 3);
+						CCS->curh->changeGraphic(0, 3);
 				}
 			}
 			else if(objAtTile->ID == 8) //boat
 			{
 				if(accessible)
-					CGI->curh->changeGraphic(0, 6 + turns*6);
+					CCS->curh->changeGraphic(0, 6 + turns*6);
 				else
-					CGI->curh->changeGraphic(0, 0);
+					CCS->curh->changeGraphic(0, 0);
 			}
 			else if (objAtTile->ID == 33 || objAtTile->ID == 219) // Garrison
 			{
@@ -1956,28 +1954,28 @@ void CAdvMapInt::tileHovered(const int3 &tile)
 					// Show battle cursor for guarded enemy garrisons, otherwise movement cursor.
 					if (garrObj&&  garrObj->stacksCount() 
 						&& !LOCPLINT->cb->getPlayerRelations( LOCPLINT->playerID, garrObj->tempOwner) )
-						CGI->curh->changeGraphic(0, 5 + turns*6);
+						CCS->curh->changeGraphic(0, 5 + turns*6);
 					else
-						CGI->curh->changeGraphic(0, 9 + turns*6);
+						CCS->curh->changeGraphic(0, 9 + turns*6);
 				} 
 				else 
-					CGI->curh->changeGraphic(0, 0);
+					CCS->curh->changeGraphic(0, 0);
 			}
 			else if (guardingCreature && accessible) //(objAtTile->ID == 54) //monster
 			{
-				CGI->curh->changeGraphic(0, 5 + turns*6);
+				CCS->curh->changeGraphic(0, 5 + turns*6);
 			}
 			else
 			{
 				if(accessible)
 				{
 					if(pnode->land)
-						CGI->curh->changeGraphic(0, 9 + turns*6);
+						CCS->curh->changeGraphic(0, 9 + turns*6);
 					else
-						CGI->curh->changeGraphic(0, 28 + turns);
+						CCS->curh->changeGraphic(0, 28 + turns);
 				}
 				else
-					CGI->curh->changeGraphic(0, 0);
+					CCS->curh->changeGraphic(0, 0);
 			}
 		} 
 		else //no objs 
@@ -1985,27 +1983,27 @@ void CAdvMapInt::tileHovered(const int3 &tile)
 			if(accessible && pnode->accessible != CGPathNode::FLYABLE)
 			{
 				if (guardingCreature) {
-					CGI->curh->changeGraphic(0, 5 + turns*6);
+					CCS->curh->changeGraphic(0, 5 + turns*6);
 				} else {
 					if(pnode->land)
 					{
 						if(LOCPLINT->cb->getTileInfo(h->getPosition(false))->tertype != TerrainTile::water)
-							CGI->curh->changeGraphic(0, 4 + turns*6);
+							CCS->curh->changeGraphic(0, 4 + turns*6);
 						else
-							CGI->curh->changeGraphic(0, 7 + turns*6); //anchor
+							CCS->curh->changeGraphic(0, 7 + turns*6); //anchor
 					}
 					else
-						CGI->curh->changeGraphic(0, 6 + turns*6);
+						CCS->curh->changeGraphic(0, 6 + turns*6);
 				}
 			}
 			else
-				CGI->curh->changeGraphic(0, 0);
+				CCS->curh->changeGraphic(0, 0);
 		}
 	}
 
 	if(const IShipyard *shipyard = ourInaccessibleShipyard(objAtTile))
 	{
-		CGI->curh->changeGraphic(0, 6);
+		CCS->curh->changeGraphic(0, 6);
 	}
 }
 
@@ -2080,7 +2078,7 @@ const IShipyard * CAdvMapInt::ourInaccessibleShipyard(const CGObjectInstance *ob
 {
 	const IShipyard *ret = IShipyard::castFrom(obj);
 
-	if(!ret || obj->tempOwner != player || CGI->curh->mode || (CGI->curh->number != 6 && CGI->curh->number != 0))
+	if(!ret || obj->tempOwner != player || CCS->curh->mode || (CCS->curh->number != 6 && CCS->curh->number != 0))
 		return NULL;
 
 	return ret;

+ 0 - 2
client/CAdvmapInterface.h

@@ -177,8 +177,6 @@ public:
 
 	const CSpell *spellBeingCasted; //NULL if none
 
-	CHeroWindow * heroWindow;
-
 	const CArmedInstance *selection; //currently selected town/hero
 
 	//functions bound to buttons

+ 1 - 1
client/CAnimation.cpp

@@ -9,7 +9,7 @@
 #include "CBitmapHandler.h"
 #include "CAnimation.h"
 #include "SDL_Extensions.h"
-#include "../hch/CLodHandler.h"
+#include "../lib/CLodHandler.h"
 
 /*
  * CAnimation.cpp, part of VCMI engine

Plik diff jest za duży
+ 205 - 232
client/CBattleInterface.cpp


+ 34 - 37
client/CBattleInterface.h

@@ -36,10 +36,10 @@ class CBattleInterface;
 
 struct SStackAttackedInfo
 {
-	int ID; //id of attacked stack
+	const CStack * defender; //attacked stack
 	int dmg; //damage dealt
 	int amountKilled; //how many creatures in stack has been killed
-	int IDby; //ID of attacking stack
+	const CStack * attacker; //attacking stack
 	bool byShooting; //if true, stack has been attacked by shooting
 	bool killed; //if true, stack has been killed
 };
@@ -91,7 +91,7 @@ class CSpellEffectAnim : public CBattleAnimation
 {
 private:
 	ui32 effect;
-	int destTile;
+	THex destTile;
 	std::string customAnim;
 	int x, y, dx, dy;
 	bool Vflip;
@@ -100,18 +100,18 @@ public:
 	void nextFrame();
 	void endAnim();
 
-	CSpellEffectAnim(CBattleInterface * _owner, ui32 _effect, int _destTile, int _dx = 0, int _dy = 0, bool _Vflip = false);
+	CSpellEffectAnim(CBattleInterface * _owner, ui32 _effect, THex _destTile, int _dx = 0, int _dy = 0, bool _Vflip = false);
 	CSpellEffectAnim(CBattleInterface * _owner, std::string _customAnim, int _x, int _y, int _dx = 0, int _dy = 0, bool _Vflip = false);
 };
 
 class CBattleStackAnimation : public CBattleAnimation
 {
 public:
-	int stackID; //id of stack whose animation it is
+	const CStack * stack; //id of stack whose animation it is
 
-	CBattleStackAnimation(CBattleInterface * _owner, int stack);
-	static bool isToReverseHlp(int hexFrom, int hexTo, bool curDir); //helper for isToReverse
-	static bool isToReverse(int hexFrom, int hexTo, bool curDir /*if true, creature is in attacker's direction*/, bool toDoubleWide, bool toDir); //determines if creature should be reversed (it stands on hexFrom and should 'see' hexTo)
+	CBattleStackAnimation(CBattleInterface * _owner, const CStack * _stack);
+	static bool isToReverseHlp(THex hexFrom, THex hexTo, bool curDir); //helper for isToReverse
+	static bool isToReverse(THex hexFrom, THex hexTo, bool curDir /*if true, creature is in attacker's direction*/, bool toDoubleWide, bool toDir); //determines if creature should be reversed (it stands on hexFrom and should 'see' hexTo)
 };
 
 class CReverseAnim : public CBattleStackAnimation
@@ -119,14 +119,14 @@ class CReverseAnim : public CBattleStackAnimation
 private:
 	int partOfAnim; //1 - first, 2 - second
 	bool secondPartSetup;
-	int hex;
+	THex hex;
 public:
 	bool priority; //true - high, false - low
 	bool init();
 	void nextFrame();
 	void endAnim();
 
-	CReverseAnim(CBattleInterface * _owner, int stack, int dest, bool _priority);
+	CReverseAnim(CBattleInterface * _owner, const CStack * stack, THex dest, bool _priority);
 };
 
 class CDefenceAnim : public CBattleStackAnimation
@@ -135,7 +135,7 @@ private:
 	//std::vector<SStackAttackedInfo> attackedInfos;
 	int dmg; //damage dealt
 	int amountKilled; //how many creatures in stack has been killed
-	int IDby; //ID of attacking stack
+	const CStack * attacker; //attacking stack
 	bool byShooting; //if true, stack has been attacked by shooting
 	bool killed; //if true, stack has been killed
 public:
@@ -149,7 +149,7 @@ public:
 class CBattleStackMoved : public CBattleStackAnimation
 {
 private:
-	int destHex; //destination
+	THex destHex; //destination
 	bool endMoving; //if this is end of move
 	int distance;
 	float stepX, stepY; //how far stack is moved in one frame
@@ -161,7 +161,7 @@ public:
 	void nextFrame();
 	void endAnim();
 
-	CBattleStackMoved(CBattleInterface * _owner, int _number, int _destHex, bool _endMoving, int _distance);
+	CBattleStackMoved(CBattleInterface * _owner, const CStack * _stack, THex _destHex, bool _endMoving, int _distance);
 };
 
 class CBattleMoveStart : public CBattleStackAnimation
@@ -171,27 +171,25 @@ public:
 	void nextFrame();
 	void endAnim();
 
-	CBattleMoveStart(CBattleInterface * _owner, int stack);
+	CBattleMoveStart(CBattleInterface * _owner, const CStack * _stack);
 };
 
 class CBattleMoveEnd : public CBattleStackAnimation
 {
 private:
-	int destinationTile;
+	THex destinationTile;
 public:
 	bool init();
 	void nextFrame();
 	void endAnim();
 
-	CBattleMoveEnd(CBattleInterface * _owner, int stack, int destTile);
+	CBattleMoveEnd(CBattleInterface * _owner, const CStack * _stack, THex destTile);
 };
 
 class CBattleAttack : public CBattleStackAnimation
 {
 protected:
-	int IDby; //attacked stack
-	int dest; //atacked hex
-	int posShiftDueToDist;
+	THex dest; //atacked hex
 	bool shooting;
 	int group; //if shooting is true, print this animation group
 	const CStack * attackedStack;
@@ -203,7 +201,7 @@ public:
 	bool checkInitialConditions();
 
 
-	CBattleAttack(CBattleInterface * _owner, int _stackID, int _dest, int _attackedID);
+	CBattleAttack(CBattleInterface * _owner, const CStack * attacker, THex _dest, const CStack * defender);
 };
 
 class CMeleeAttack : public CBattleAttack
@@ -213,7 +211,7 @@ public:
 	void nextFrame();
 	void endAnim();
 
-	CMeleeAttack(CBattleInterface * _owner, int attacker, int _dest, int _attackedID);
+	CMeleeAttack(CBattleInterface * _owner, const CStack * attacker, THex _dest, const CStack * _attacked);
 };
 
 class CShootingAnim : public CBattleAttack
@@ -226,7 +224,7 @@ public:
 	void nextFrame();
 	void endAnim();
 
-	CShootingAnim(CBattleInterface * _owner, int attacker, int _dest, int _attackedID, bool _catapult = false, int _catapultDmg = 0); //last param only for catapult attacks
+	CShootingAnim(CBattleInterface * _owner, const CStack * attacker, THex _dest, const CStack * _attacked, bool _catapult = false, int _catapultDmg = 0); //last param only for catapult attacks
 };
 
 //end of battle animation handlers
@@ -384,18 +382,18 @@ private:
 	CBattleConsole * console;
 	CBattleHero * attackingHero, * defendingHero; //fighting heroes
 	CStackQueue *queue;
-	CCreatureSet army1, army2; //copy of initial armies (for result window)
+	const CCreatureSet *army1, *army2; //copy of initial armies (for result window)
 	const CGHeroInstance * attackingHeroInstance, * defendingHeroInstance;
 	std::map< int, CCreatureAnimation * > creAnims; //animations of creatures from fighting armies (order by BattleInfo's stacks' ID)
 	std::map< int, CDefHandler * > idToProjectile; //projectiles of creatures (creatureID, defhandler)
 	std::map< int, CDefHandler * > idToObstacle; //obstacles located on the battlefield
 	std::map< int, bool > creDir; // <creatureID, if false reverse creature's animation>
 	unsigned char animCount;
-	int activeStack; //number of active stack; -1 - no one
-	int stackToActivate; //when animation is playing, we should wait till the end to make the next stack active; -1 of none
+	const CStack * activeStack; //number of active stack; NULL - no one
+	const CStack * stackToActivate; //when animation is playing, we should wait till the end to make the next stack active; NULL of none
 	void activateStack(); //sets activeStack to stackToActivate etc.
 	int mouseHoveredStack; //stack hovered by mouse; if -1 -> none
-	std::vector<int> shadedHexes; //hexes available for active stack
+	std::vector<THex> shadedHexes; //hexes available for active stack
 	int previouslyHoveredHex; //number of hex that was hovered by the cursor a while ago
 	int currentlyHoveredHex; //number of hex that is supposed to be hovered (for a while it may be inappropriately set, but will be renewed soon)
 	float getAnimSpeedMultiplier() const; //returns multiplier for number of frames in a group
@@ -407,10 +405,10 @@ private:
 	BattleAction * spellToCast; //spell for which player is choosing destination
 	void endCastingSpell(); //ends casting spell (eg. when spell has been cast or canceled)
 
-	void showAliveStack(int ID, const std::map<int, CStack> & stacks, SDL_Surface * to); //helper function for function show
-	void showPieceOfWall(SDL_Surface * to, int hex, const std::map<int, CStack> & stacks); //helper function for show
-	void redrawBackgroundWithHexes(int activeStack);
-	void printConsoleAttacked(int ID, int dmg, int killed, int IDby);
+	void showAliveStack(const CStack *stack, SDL_Surface * to); //helper function for function show
+	void showPieceOfWall(SDL_Surface * to, int hex, const std::vector<const CStack*> & stacks); //helper function for show
+	void redrawBackgroundWithHexes(const CStack * activeStack);
+	void printConsoleAttacked(const CStack * defender, int dmg, int killed, const CStack * attacker);
 
 	std::list<SProjectileInfo> projectiles; //projectiles flying on battlefield
 	void projectileShowHelper(SDL_Surface * to); //prints projectiles present on the battlefield
@@ -491,17 +489,16 @@ public:
 
 	//call-ins
 	void startAction(const BattleAction* action);
-	void newStack(int stackID); //new stack appeared on battlefield
-	void stackRemoved(int stackID); //stack disappeared from batlefiled
-	//void stackKilled(int ID, int dmg, int killed, int IDby, bool byShooting); //stack has been killed (but corpses remain)
-	void stackActivated(int number); //active stack has been changed
-	void stackMoved(int number, int destHex, bool endMoving, int distance); //stack with id number moved to destHex
+	void newStack(const CStack * stack); //new stack appeared on battlefield
+	void stackRemoved(const CStack * stack); //stack disappeared from batlefiled
+	void stackActivated(const CStack * stack); //active stack has been changed
+	void stackMoved(const CStack * stack, THex destHex, bool endMoving, int distance); //stack with id number moved to destHex
+	void waitForAnims();
 	void stacksAreAttacked(std::vector<SStackAttackedInfo> attackedInfos); //called when a certain amount of stacks has been attacked
-	void stackAttacking(int ID, int dest, int attackedID); //called when stack with id ID is attacking something on hex dest
+	void stackAttacking(const CStack * attacker, THex dest, const CStack * attacked, bool shooting); //called when stack with id ID is attacking something on hex dest
 	void newRoundFirst( int round );
 	void newRound(int number); //caled when round is ended; number is the number of round
 	void hexLclicked(int whichOne); //hex only call-in
-	void stackIsShooting(int ID, int dest, int attackedID); //called when stack with id ID is shooting to hex dest
 	void stackIsCatapulting(const CatapultAttack & ca); //called when a stack is attacking walls
 	void battleFinished(const BattleResult& br); //called when battle is finished - battleresult window should be printed
 	const BattleResult * bresult; //result of a battle; if non-zero then display when all animations end

+ 2 - 2
client/CBitmapHandler.cpp

@@ -2,8 +2,8 @@
 #include "SDL.h"
 #include "SDL_image.h"
 #include "CBitmapHandler.h"
-#include "../hch/CDefHandler.h"
-#include "../hch/CLodHandler.h"
+#include "CDefHandler.h"
+#include "../lib/CLodHandler.h"
 #include <sstream>
 #include <boost/thread.hpp>
 

+ 27 - 25
client/CCastleInterface.cpp

@@ -10,17 +10,17 @@
 #include "SDL_Extensions.h"
 #include "CCreatureAnimation.h"
 #include "Graphics.h"
-#include "../hch/CArtHandler.h"
-#include "../hch/CBuildingHandler.h"
-#include "../hch/CDefHandler.h"
-#include "../hch/CGeneralTextHandler.h"
-#include "../hch/CLodHandler.h"
-#include "../hch/CObjectHandler.h"
-#include "../hch/CSpellHandler.h"
-#include "../hch/CTownHandler.h"
-#include "../hch/CCreatureHandler.h"
+#include "../lib/CArtHandler.h"
+#include "../lib/CBuildingHandler.h"
+#include "CDefHandler.h"
+#include "../lib/CGeneralTextHandler.h"
+#include "../lib/CLodHandler.h"
+#include "../lib/CObjectHandler.h"
+#include "../lib/CSpellHandler.h"
+#include "../lib/CTownHandler.h"
+#include "../lib/CCreatureHandler.h"
 #include "../lib/map.h"
-#include "../hch/CMusicHandler.h"
+#include "CMusicHandler.h"
 #include <boost/algorithm/string.hpp>
 #include <boost/algorithm/string/replace.hpp>
 #include <boost/assign/std/vector.hpp>
@@ -28,6 +28,8 @@
 #include <cmath>
 #include <sstream>
 #include <boost/format.hpp>
+#include "../lib/CCreatureHandler.h"
+#include "CMusicHandler.h"
 using namespace boost::assign;
 using namespace CSDL_Ext;
 
@@ -54,7 +56,7 @@ int hordeToDwellingID(int bid)//helper, converts horde buiding ID into correspon
 	}
 }
 
-CBuildingRect::CBuildingRect(Structure *Str)
+CBuildingRect::CBuildingRect(const Structure *Str)
 	:CShowableAnim(0, 0, Str->defName, CShowableAnim::FLAG_BASE),
 	moi(false), str(Str)
 {
@@ -157,7 +159,7 @@ void CBuildingRect::clickRight(tribool down, bool previousState)
 	{
 		int bid = hordeToDwellingID(str->ID);
 
-		CBuilding *bld = CGI->buildh->buildings[str->townID].find(bid)->second;
+		const CBuilding *bld = CGI->buildh->buildings[str->townID].find(bid)->second;
 		assert(bld);
 
 		CInfoPopup *vinya = new CInfoPopup();
@@ -434,7 +436,7 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, int listPos)
 	recreateIcons();
 	cityBg = BitmapHandler::loadBitmap(graphics->townBgs[town->subID]);
 	bicons = CDefHandler::giveDefEss(graphics->buildingPics[town->subID]);
-	CGI->musich->playMusic(CGI->musich->townMusics[town->subID], -1);
+	CCS->musich->playMusic(CCS->musich->townMusics[town->subID], -1);
 }
 
 CCastleInterface::~CCastleInterface()
@@ -474,7 +476,7 @@ void CCastleInterface::close()
 	}
 	LOCPLINT->castleInt = NULL;
 	GH.popIntTotally(this);
-	CGI->musich->stopMusic(5000);
+	CCS->musich->stopMusic(5000);
 }
 
 void CCastleInterface::splitF()
@@ -509,7 +511,7 @@ void CCastleInterface::buildingClicked(int building)
 				else //both heroes present, use the visiting one
 					h = town->visitingHero;
 
-				if(h && !vstd::contains(h->artifWorn,ui16(17))) //hero doesn't have spellbok
+				if(h && !h->hasSpellbook()) //hero doesn't have spellbok
 				{
 					if(LOCPLINT->cb->getResourceAmount(6) < 500) //not enough gold to buy spellbook
 					{
@@ -770,7 +772,7 @@ void CCastleInterface::showAll( SDL_Surface * to/*=NULL*/)
 	statusbar->show(to);
 	resdatabar->draw(to);
 
-	garr->show(to);
+	garr->showAll(to);
 	//draw creatures icons and their growths
 	for(size_t i=0;i<creainfo.size();i++)
 		creainfo[i]->show(to);
@@ -902,7 +904,7 @@ void CCastleInterface::recreateBuildings()
 		{
 			if(CGI->townh->structures[town->subID].find(*i)!=CGI->townh->structures[town->subID].end()) //we have info about that structure
 			{
-				Structure * st = CGI->townh->structures[town->subID][*i];
+				const Structure * st = CGI->townh->structures[town->subID][*i];
 				if(st->group<0) //no group - just add it
 				{
 					buildings.push_back(new CBuildingRect(st));
@@ -1160,7 +1162,7 @@ void CCastleInterface::CCreaInfo::clickRight(tribool down, bool previousState)
 				ch = ci->town->visitingHero;
 			};
 			if (bl.size())
-				summ+=AddToString (CGI->arth->artifacts[bl.front().id]->Name()+" %+d", descr, bl.totalValue());
+				summ+=AddToString (CGI->arth->artifacts[bl.front()->id]->Name()+" %+d", descr, bl.totalValue());
 
 			//TODO: player bonuses
 
@@ -1249,7 +1251,7 @@ void CCastleInterface::CTownInfo::clickRight(tribool down, bool previousState)
 		CInfoPopup *mess = new CInfoPopup();
 		mess->free = true;
 		CCastleInterface * ci=LOCPLINT->castleInt;
-		CBuilding *bld = CGI->buildh->buildings[ci->town->subID][bid];
+		const CBuilding *bld = CGI->buildh->buildings[ci->town->subID][bid];
 		mess->bitmap = CMessage::drawBoxTextBitmapSub
 			(LOCPLINT->playerID,bld->Description(),
 			LOCPLINT->castleInt->bicons->ourImages[bid].bitmap,
@@ -1317,7 +1319,7 @@ void CCastleInterface::keyPressed( const SDL_KeyboardEvent & key )
 		}
 		break;
 	case SDLK_SPACE:
-		if(town->visitingHero && town->garrisonHero)
+		if(!!town->visitingHero && town->garrisonHero)
 		{
 			LOCPLINT->cb->swapGarrisonHero(town);
 		}
@@ -1329,7 +1331,7 @@ void CCastleInterface::keyPressed( const SDL_KeyboardEvent & key )
 
 void CCastleInterface::splitClicked()
 {
-	if(town->visitingHero && town->garrisonHero && (hslotdown.highlight || hslotup.highlight))
+	if(!!town->visitingHero && town->garrisonHero && (hslotdown.highlight || hslotup.highlight))
 	{
 		LOCPLINT->heroExchangeStarted(town->visitingHero->id, town->garrisonHero->id);
 	}
@@ -1444,7 +1446,7 @@ CHallInterface::CHallInterface(CCastleInterface * owner)
 	boxes.resize(5);
 	for(size_t i=0;i<5;i++) //for each row
 	{
-		std::vector< std::vector< std::vector<int> > > &boxList = CGI->buildh->hall[owner->town->subID].second;
+		const std::vector< std::vector< std::vector<int> > > &boxList = CGI->buildh->hall[owner->town->subID].second;
 
 		for(size_t j=0; j<boxList[i].size();j++) //for each box
 		{
@@ -1778,7 +1780,7 @@ void CFortScreen::draw( CCastleInterface * owner, bool first)
 	for(int i=0;i<fortSize; i++)
 	{
 		int dwelling;// ID of buiding with this creature
-		CCreature *c;
+		const CCreature *c;
 		bool present = true;
 		if ( i < CREATURES_PER_TOWN )
 		{
@@ -1898,7 +1900,7 @@ CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner)
 		{
 			if(i<owner->town->mageGuildLevel() && owner->town->spells[i].size()>j)
 			{
-				spells.push_back( new Scroll(&CGI->spellh->spells[owner->town->spells[i][j]]));
+				spells.push_back( new Scroll(CGI->spellh->spells[owner->town->spells[i][j]]));
 				spells[spells.size()-1]->pos = positions[i][j];
 				blitAt(graphics->spellscr->ourImages[owner->town->spells[i][j]].bitmap,positions[i][j],*bg);
 			}
@@ -1926,7 +1928,7 @@ void CMageGuildScreen::close()
 	GH.popIntTotally(this);
 }
 
-CMageGuildScreen::Scroll::Scroll(CSpell *Spell)
+CMageGuildScreen::Scroll::Scroll(const CSpell *Spell)
 	:spell(Spell)
 {
 	used = LCLICK | RCLICK | HOVER;

+ 5 - 5
client/CCastleInterface.h

@@ -7,7 +7,7 @@
 #include <SDL.h>
 #include "CAnimation.h"
 #include "GUIBase.h"
-#include "../hch/CMusicBase.h"
+#include "CMusicBase.h"
 //#include "boost/tuple/tuple.hpp"
 class CGTownInstance;
 class CTownHandler;
@@ -38,10 +38,10 @@ class CBuildingRect : public CShowableAnim
 {
 public:
 	bool moi; //motion interested is active
-	Structure* str;
+	const Structure* str;
 	SDL_Surface* border;
 	SDL_Surface* area;
-	CBuildingRect(Structure *Str); //c-tor
+	CBuildingRect(const Structure *Str); //c-tor
 	~CBuildingRect(); //d-tor
 	void activate();
 	void deactivate();
@@ -238,9 +238,9 @@ public:
 	class Scroll : public CIntObject
 	{
 	public:
-		CSpell *spell;
+		const CSpell *spell;
 
-		Scroll(CSpell *Spell);
+		Scroll(const  CSpell *Spell);
 		void clickLeft(tribool down, bool previousState);
 		void clickRight(tribool down, bool previousState);
 		void hover(bool on);

+ 1 - 1
client/CCreatureAnimation.cpp

@@ -1,5 +1,5 @@
 #include "CCreatureAnimation.h"
-#include "../hch/CLodHandler.h"
+#include "../lib/CLodHandler.h"
 #include "../lib/VCMI_Lib.h"
 #include <assert.h>
 #include "SDL_Extensions.h"

+ 1 - 1
client/CCreatureAnimation.h

@@ -3,7 +3,7 @@
 
 
 #include "../global.h"
-#include "../hch/CDefHandler.h"
+#include "CDefHandler.h"
 #include "GUIBase.h"
 #include "../client/CBitmapHandler.h"
 

+ 1 - 1
client/CCursorHandler.cpp

@@ -3,7 +3,7 @@
 #include "SDL.h"
 #include "SDL_Extensions.h"
 #include "CGameInfo.h"
-#include "../hch/CDefHandler.h"
+#include "CDefHandler.h"
 
 /*
  * CCursorHandler.cpp, part of VCMI engine

+ 2 - 2
hch/CDefHandler.cpp → client/CDefHandler.cpp

@@ -2,9 +2,9 @@
 #include "SDL.h"
 #include "CDefHandler.h"
 #include <sstream>
-#include "CLodHandler.h"
+#include "../lib/CLodHandler.h"
 #include "../lib/VCMI_Lib.h"
-#include "../client/CBitmapHandler.h"
+#include "CBitmapHandler.h"
 
 /*
  * CDefHandler.cpp, part of VCMI engine

+ 0 - 0
hch/CDefHandler.h → client/CDefHandler.h


+ 2 - 2
client/CGameInfo.cpp

@@ -12,12 +12,12 @@
  *
  */
 
-CGameInfo * CGI; //game info for general use
+const CGameInfo * CGI; //game info for general use
+CClientState * CCS;
 
 CGameInfo::CGameInfo()
 {
 	mh = NULL;
-	state = NULL;
 }
 
 void CGameInfo::setFromLib()

+ 29 - 17
client/CGameInfo.h

@@ -1,6 +1,7 @@
 #ifndef __CGAMEINFO_H__
 #define __CGAMEINFO_H__
 #include "../global.h"
+#include "../lib/ConstTransitivePtr.h"
 
 
 /*
@@ -30,6 +31,18 @@ class CCursorHandler;
 class CGameState;
 class CVideoPlayer;
 
+
+//a class for non-mechanical client GUI classes
+class CClientState
+{
+public:
+	CSoundHandler * soundh;
+	CMusicHandler * musich;
+	CConsoleHandler * consoleh;
+	CCursorHandler * curh;
+	CVideoPlayer * videoh;
+};
+
 struct Mapa;
 
 /*
@@ -38,23 +51,19 @@ struct Mapa;
 */
 class CGameInfo
 {
-	/*const*/ CGameState * state; //don't touch it in client's code
+	ConstTransitivePtr<CGameState> state; //don't touch it in client's code
 public:
-	/*const*/ CArtHandler * arth;
-	/*const*/ CHeroHandler * heroh;
-	/*const*/ CCreatureHandler * creh;
-	/*const*/ CSpellHandler * spellh;
-	/*const*/ CMapHandler * mh;
-	/*const*/ CBuildingHandler * buildh;
-	/*const*/ CObjectHandler * objh;
-	CSoundHandler * soundh;
-	CMusicHandler * musich;
-	/*const*/ CDefObjInfoHandler * dobjinfo;
-	/*const*/ CTownHandler * townh;
-	/*const*/ CGeneralTextHandler * generaltexth;
-	CConsoleHandler * consoleh;
-	CCursorHandler * curh;
-	CVideoPlayer * videoh;
+	ConstTransitivePtr<CArtHandler> arth;
+	ConstTransitivePtr<CHeroHandler> heroh;
+	ConstTransitivePtr<CCreatureHandler> creh;
+	ConstTransitivePtr<CSpellHandler> spellh;
+	ConstTransitivePtr<CObjectHandler> objh;
+	ConstTransitivePtr<CDefObjInfoHandler> dobjinfo;
+	CGeneralTextHandler * generaltexth;
+	CMapHandler * mh;
+	ConstTransitivePtr<CBuildingHandler> buildh;
+	CTownHandler * townh;
+	//CTownHandler * townh;
 
 	void setFromLib();
 
@@ -64,7 +73,10 @@ public:
 	CGameInfo();
 };
 
-
+//	
+// public:
+// 	
+// 	ConstTransitivePtr<CGeneralTextHandler> generaltexth;
 
 
 

+ 191 - 351
client/CHeroWindow.cpp

@@ -13,18 +13,20 @@
 #include "CSpellWindow.h"
 #include "CConfigHandler.h"
 #include "../global.h"
-#include "../hch/CArtHandler.h"
-#include "../hch/CDefHandler.h"
-#include "../hch/CGeneralTextHandler.h"
-#include "../hch/CHeroHandler.h"
-#include "../hch/CLodHandler.h"
-#include "../hch/CObjectHandler.h"
+#include "../lib/CArtHandler.h"
+#include "CDefHandler.h"
+#include "../lib/CGeneralTextHandler.h"
+#include "../lib/CHeroHandler.h"
+#include "../lib/CLodHandler.h"
+#include "../lib/CObjectHandler.h"
 #include <boost/algorithm/string/replace.hpp>
 #include <boost/assign/list_of.hpp>
 #include <boost/assign/std/vector.hpp>
 #include <cstdlib>
 #include <sstream>
 #include <boost/lexical_cast.hpp>
+#include <boost/format.hpp>
+#include <boost/foreach.hpp>
 
 #undef min
 
@@ -45,204 +47,155 @@ void CHeroSwitcher::clickLeft(tribool down, bool previousState)
 {
 	if(!down)
 	{
-		owner->deactivate();
 		const CGHeroInstance * buf = LOCPLINT->getWHero(id);
-		owner->setHero(buf);
-		owner->redrawCurBack();
-		owner->activate();
+		if(!buf)
+			return;
+		GH.popIntTotally(getOwner());
+		GH.pushInt(new CHeroWindow(buf));
 	}
 }
 
-CHeroSwitcher::CHeroSwitcher()
+CHeroWindow * CHeroSwitcher::getOwner()
 {
+	return dynamic_cast<CHeroWindow*>(parent);
+}
+
+CHeroSwitcher::CHeroSwitcher(int serial)
+{
+	pos = Rect(612, 87 + serial * 54, 48, 32)  +  pos;
+	id = serial;
 	used = LCLICK;
 }
 
-CHeroWindow::CHeroWindow(int playerColor):
-	player(playerColor)
+CHeroWindow::CHeroWindow(const CGHeroInstance *hero)
 {
-	background = BitmapHandler::loadBitmap("HeroScr4");
-	graphics->blueToPlayersAdv(background, playerColor);
-	pos.x = screen->w/2 - background->w/2 - 65;
-	pos.y = screen->h/2 - background->h/2 - 8;
-	pos.h = background->h;
-	pos.w = background->w;
-	curBack = NULL;
-	curHero = NULL;
-	char bufor[400];
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	garr = NULL;
+	curHero = hero;
+	player = LOCPLINT->playerID;//hero->tempOwner;
 
-	artifs = new CArtifactsOfHero(pos.topLeft());
-	artifs->commonInfo = new CArtifactsOfHero::SCommonPart;
-	artifs->commonInfo->participants.insert(artifs);
+	background = new CPicture("HeroScr4.BMP");
+	background->colorizeAndConvert(player);
+	pos = background->center();
 
-	garr = NULL;
-	ourBar = new CStatusBar(pos.x+72, pos.y+567, "ADROLLVR.bmp", 660);
 
-	quitButton = new AdventureMapButton(CGI->generaltexth->heroscrn[17], std::string(), boost::function<void()>(), pos.x+674, pos.y+524, "hsbtns.def", SDLK_RETURN);
-	dismissButton = new AdventureMapButton(std::string(), CGI->generaltexth->heroscrn[28], boost::bind(&CHeroWindow::dismissCurrent,this), pos.x+519, pos.y+437, "hsbtns2.def", SDLK_d);
-	questlogButton = new AdventureMapButton(CGI->generaltexth->heroscrn[0], std::string(), boost::bind(&CHeroWindow::questlog,this), pos.x+379, pos.y+437, "hsbtns4.def", SDLK_q);
+	//artifs = new CArtifactsOfHero(pos.topLeft(), true);
+	ourBar = new CGStatusBar(7, 559, "ADROLLVR.bmp", 660); // new CStatusBar(pos.x+72, pos.y+567, "ADROLLVR.bmp", 660);
 
-	formations = new CHighlightableButtonsGroup(0);
-	formations->addButton(map_list_of(0,CGI->generaltexth->heroscrn[23]),CGI->generaltexth->heroscrn[29], "hsbtns6.def", pos.x+546, pos.y+491, 0, 0, SDLK_t);
-	formations->addButton(map_list_of(0,CGI->generaltexth->heroscrn[24]),CGI->generaltexth->heroscrn[30], "hsbtns7.def", pos.x+546, pos.y+527, 1, 0, SDLK_l);
+	quitButton = new AdventureMapButton(CGI->generaltexth->heroscrn[17], std::string(),boost::bind(&CHeroWindow::quit,this), 609, 516, "hsbtns.def", SDLK_RETURN);
+	dismissButton = new AdventureMapButton(std::string(), CGI->generaltexth->heroscrn[28], boost::bind(&CHeroWindow::dismissCurrent,this), 454, 429, "hsbtns2.def", SDLK_d);
+	questlogButton = new AdventureMapButton(CGI->generaltexth->heroscrn[0], std::string(), boost::bind(&CHeroWindow::questlog,this), 314, 429, "hsbtns4.def", SDLK_q);
 
+	formations = new CHighlightableButtonsGroup(0);
+	{
+		BLOCK_CAPTURING;
+		formations->addButton(map_list_of(0,CGI->generaltexth->heroscrn[23]),CGI->generaltexth->heroscrn[29], "hsbtns6.def", pos.x+481, pos.y+483, 0, 0, SDLK_t);
+		formations->addButton(map_list_of(0,CGI->generaltexth->heroscrn[24]),CGI->generaltexth->heroscrn[30], "hsbtns7.def", pos.x+481, pos.y+519, 1, 0, SDLK_l);
+	}
 
-	gar2button = new CHighlightableButton(0, 0, map_list_of(0,CGI->generaltexth->heroscrn[26])(3,CGI->generaltexth->heroscrn[25]), CGI->generaltexth->heroscrn[31], false, "hsbtns8.def", NULL, pos.x+604, pos.y+491, SDLK_b);
+	tacticsButton = new CHighlightableButton(0, 0, map_list_of(0,CGI->generaltexth->heroscrn[26])(3,CGI->generaltexth->heroscrn[25]), CGI->generaltexth->heroscrn[31], false, "hsbtns8.def", NULL, 539, 483, SDLK_b);
 
 
 	//right list of heroes
 	for(int g=0; g<8; ++g)
-	{
-		//heroList.push_back(new AdventureMapButton<CHeroWindow>(std::string(), std::string(), &CHeroWindow::switchHero, 677, 95+g*54, "hsbtns5.def", this));
-		heroListMi.push_back(new CHeroSwitcher());
-		heroListMi[g]->pos = genRect(32, 48, pos.x+677, pos.y  +  95+g*54);
-		heroListMi[g]->owner = this;
-		heroListMi[g]->id = g;
-	}
+		heroListMi.push_back(new CHeroSwitcher(g));
+
 
 
 	flags = CDefHandler::giveDefEss("CREST58.DEF");
+
 	//areas
-	portraitArea = new LRClickableAreaWText();
-	portraitArea->pos = genRect(64, 58, pos.x+83, pos.y  +  26);
+	portraitArea = new LRClickableAreaWText(Rect(18, 18, 58, 64));
 
 	for(int v=0; v<PRIMARY_SKILLS; ++v)
 	{
-		primSkillAreas.push_back(new LRClickableAreaWTextComp());
-		primSkillAreas[v]->pos = genRect(64, 42, pos.x+95 + 70*v, pos.y  +  117);
-		primSkillAreas[v]->text = CGI->generaltexth->arraytxt[2+v];
-		primSkillAreas[v]->type = v;
-		primSkillAreas[v]->bonusValue = -1; // to be initilized when hero is being set
-		primSkillAreas[v]->baseType = 0;
-		sprintf(bufor, CGI->generaltexth->heroscrn[1].c_str(), CGI->generaltexth->primarySkillNames[v].c_str());
-		primSkillAreas[v]->hoverText = std::string(bufor);
-
+		LRClickableAreaWTextComp *area = new LRClickableAreaWTextComp(Rect(30 + 70*v, 109, 42, 64), SComponent::primskill);
+		area->text = CGI->generaltexth->arraytxt[2+v];
+		area->type = v;
+		area->hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % CGI->generaltexth->primarySkillNames[v]);
+		primSkillAreas.push_back(area);
 	}
 
-	specArea = new LRClickableAreaWText();
-	specArea->pos = genRect(42, 136, pos.x+83, pos.y  +  188);
-	specArea->hoverText = CGI->generaltexth->heroscrn[27];
-
-	expArea = new LRClickableAreaWText();
-	expArea->pos = genRect(42, 136, pos.x+83, pos.y  +  236);
-	expArea->hoverText = CGI->generaltexth->heroscrn[9];
-
-	morale = new MoraleLuckBox(true);
-	morale->pos = genRect(45,53,pos.x+240,pos.y+187);
+	specArea = new LRClickableAreaWText(Rect(18, 180, 136, 42), CGI->generaltexth->heroscrn[27]);
+	expArea = new LRClickableAreaWText(Rect(18, 228, 136, 42), CGI->generaltexth->heroscrn[9]);
+	morale = new MoraleLuckBox(true, Rect(175,179,53,45));
+	luck = new MoraleLuckBox(false, Rect(233,179,53,45));
+	spellPointsArea = new LRClickableAreaWText(Rect(162,228, 136, 42), CGI->generaltexth->heroscrn[22]);
 
-	luck = new MoraleLuckBox(false);
-	luck->pos = genRect(45,53,pos.x+298,pos.y+187);
-
-	spellPointsArea = new LRClickableAreaWText();
-	spellPointsArea->pos = genRect(42, 136, pos.x+227, pos.y  +  236);
-	spellPointsArea->hoverText = CGI->generaltexth->heroscrn[22];
-
-	for(int i=0; i<SKILL_PER_HERO; ++i)
+	for(int i = 0; i < std::min(hero->secSkills.size(), 8u); ++i)
 	{
-		secSkillAreas.push_back(new LRClickableAreaWTextComp());
-		secSkillAreas[i]->pos = genRect(42, 136, pos.x  +  ((i%2==0) ? (83) : (227)), pos.y  +  (284 + 48 * (i/2)));
-		secSkillAreas[i]->baseType = 1;
+		Rect r = Rect(i%2 == 0  ?  18  :  162,  276 + 48 * (i/2),  136,  42);
+		secSkillAreas.push_back(new LRClickableAreaWTextComp(r, SComponent::secskill));
 	}
-	pos.x += 65;
-	pos.y += 8;
+
+	//////////////////////////////////////////////////////////////////////////???????????????
+// 	pos.x += 65;
+// 	pos.y += 8;
+// 	
+	//primary skills & exp and mana
+	new CPicture(graphics->pskillsm->ourImages[0].bitmap, 32, 111, false);
+	new CPicture(graphics->pskillsm->ourImages[1].bitmap, 102, 111, false);
+	new CPicture(graphics->pskillsm->ourImages[2].bitmap, 172, 111, false);
+	new CPicture(graphics->pskillsm->ourImages[5].bitmap, 242, 111, false);
+	new CPicture(graphics->pskillsm->ourImages[4].bitmap, 20, 230, false);
+	new CPicture(graphics->pskillsm->ourImages[3].bitmap, 162, 230, false);
+
+	update(hero);
 }
 
 CHeroWindow::~CHeroWindow()
 {
-	SDL_FreeSurface(background);
-	delete quitButton;
-	delete dismissButton;
-	delete questlogButton;
-	delete formations;
-	delete gar2button;
-	//delete gar4button;
-
-	for(size_t g=0; g<heroListMi.size(); ++g)
-	{
-		delete heroListMi[g];
-	}
-
-	if(curBack)
-	{
-		SDL_FreeSurface(curBack);
-	}
-
-	delete flags;
-
-	delete garr;
-	delete ourBar;
-	delete artifs;
-
-	delete portraitArea;
-	delete expArea;
-	delete luck;
-	delete morale;
-	delete specArea;
-	delete spellPointsArea;
-	for(size_t v=0; v<primSkillAreas.size(); ++v)
-	{
-		delete primSkillAreas[v];
-	}
-	for(size_t v=0; v<secSkillAreas.size(); ++v)
-	{
-		delete secSkillAreas[v];
-	}
-}
+	delete flags;	
+	//SDL_FreeSurface(curBack);
+	//curBack = NULL;
+	curHero = NULL;
 
-void CHeroWindow::show(SDL_Surface *to)
-{
-	if(curBack)
-		blitAt(curBack,pos.x,pos.y,to);
-	quitButton->show(to);
-	dismissButton->show(to);
-	questlogButton->show(to);
-	formations->show(to);
-	gar2button->show(to);
-	//gar4button->show(to);
-
-	garr->show(to);
-	ourBar->show(to);
-
-	artifs->showAll(to);
+	//artifs->dispose();
 }
 
-void CHeroWindow::setHero(const CGHeroInstance *hero)
-{
-	char bufor[400];
-	//CGHeroInstance *hero = const_cast<CGHeroInstance*>(Hero); //but don't modify hero! - it's only for easy map reading
+void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded /*= false*/)
+{	
 	if(!hero) //something strange... no hero? it shouldn't happen
 	{
+		tlog1 << "Set NULL hero? no way...\n";
 		return;
 	}
-	curHero = hero;
 
-	artifs->updateState = true;
-	artifs->setHero(hero);
-	artifs->updateState = false;
-
-	//pos temporarily switched, restored later
-	pos.x -= 65;
-	pos.y -= 8;
+	assert(hero == curHero);
+	//assert(hero->tempOwner == LOCPLINT->playerID || hero->tempOwner == NEUTRAL_PLAYER); //for now we won't show hero windows for non-our heroes
 
 	specArea->text = CGI->generaltexth->hTxts[hero->subID].longBonus;
 
-	gar2button->callback.clear();
-	gar2button->callback2.clear();
-
-	sprintf(bufor, CGI->generaltexth->heroscrn[16].c_str(), curHero->name.c_str(), curHero->type->heroClass->name.c_str());
-	dismissButton->hoverTexts[0] = std::string(bufor);
-
-	sprintf(bufor, CGI->generaltexth->allTexts[15].c_str(), curHero->name.c_str(), curHero->type->heroClass->name.c_str());
-	portraitArea->hoverText = std::string(bufor);
+	tacticsButton->callback.clear();
+	tacticsButton->callback2.clear();
 
+	dismissButton->hoverTexts[0] = boost::str(boost::format(CGI->generaltexth->heroscrn[16]) % curHero->name % curHero->type->heroClass->name);
+	portraitArea->hoverText = boost::str(boost::format(CGI->generaltexth->allTexts[15]) % curHero->name % curHero->type->heroClass->name);
 	portraitArea->text = hero->getBiography();
 
-	delete garr;
-	garr = new CGarrisonInt(pos.x+80, pos.y+493, 8, Point(), curBack, Point(16,486), curHero);
+	
+	{
+		AdventureMapButton * split = NULL;
+		{
+			BLOCK_CAPTURING;
+			split = new AdventureMapButton(CGI->generaltexth->allTexts[256], CGI->generaltexth->heroscrn[32], boost::bind(&CGarrisonInt::splitClick,garr), 604, 527, "hsbtns9.def", false, NULL, false); //deleted by garrison destructor
+			boost::algorithm::replace_first(split->hoverTexts[0],"%s",CGI->generaltexth->allTexts[43]);
+		}
+		//delete garr;
+		OBJ_CONSTRUCTION_CAPTURING_ALL;
+		if(!garr)
+		{
+			garr = new CGarrisonInt(15, 485, 8, Point(), background->bg, Point(15,485), curHero);
+			garr->addSplitBtn(split);
+		}
+		if(!artSets.size())
+		{
+			CArtifactsOfHero *arts = new CArtifactsOfHero(Point(-65, -8), true);
+			arts->setHero(hero);
+			artSets.push_back(arts);
+		}
+	}
 
-	AdventureMapButton * split = new AdventureMapButton(CGI->generaltexth->allTexts[256], CGI->generaltexth->heroscrn[32], boost::bind(&CGarrisonInt::splitClick,garr), pos.x+604, pos.y+527, "hsbtns9.def", false, NULL, false); //deleted by garrison destructor
-	boost::algorithm::replace_first(split->hoverTexts[0],"%s",CGI->generaltexth->allTexts[43]);
-	garr->addSplitBtn(split);
 
 	//primary skills support
 	for(size_t g=0; g<primSkillAreas.size(); ++g)
@@ -251,51 +204,46 @@ void CHeroWindow::setHero(const CGHeroInstance *hero)
 	}
 
 	//secondary skills support
-	for(size_t g=0; g<std::min(secSkillAreas.size(),hero->secSkills.size()); ++g)
+	for(size_t g=0; g< secSkillAreas.size(); ++g)
 	{
-		int skill = hero->secSkills[g].first, 
-			level = hero->getSecSkillLevel(hero->secSkills[g].first);
-
+		int skill = hero->secSkills[g].first,
+			level = hero->getSecSkillLevel(static_cast<CGHeroInstance::SecondarySkill>(hero->secSkills[g].first));
 		secSkillAreas[g]->type = skill;
 		secSkillAreas[g]->bonusValue = level;
 		secSkillAreas[g]->text = CGI->generaltexth->skillInfoTexts[skill][level-1];
-
-		sprintf(bufor, CGI->generaltexth->heroscrn[21].c_str(), CGI->generaltexth->levels[level-1].c_str(), CGI->generaltexth->skillName[skill].c_str());
-		secSkillAreas[g]->hoverText = std::string(bufor);
+		secSkillAreas[g]->hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[21]) % CGI->generaltexth->levels[level-1] % CGI->generaltexth->skillName[skill]);
 	}
 
 	//printing experience - original format does not support ui64
-	expArea->text = CGI->generaltexth->allTexts[2].c_str();
+	expArea->text = CGI->generaltexth->allTexts[2];
 	boost::replace_first(expArea->text, "%d", boost::lexical_cast<std::string>(hero->level));
 	boost::replace_first(expArea->text, "%d", boost::lexical_cast<std::string>(CGI->heroh->reqExp(hero->level+1)));
 	boost::replace_first(expArea->text, "%d", boost::lexical_cast<std::string>(hero->exp));
 
 	//printing spell points
-	sprintf(bufor, CGI->generaltexth->allTexts[205].c_str(), hero->name.c_str(), hero->mana, hero->manaLimit());
-	spellPointsArea->text = std::string(bufor);
-
+	spellPointsArea->text = boost::str(boost::format(CGI->generaltexth->allTexts[205]) % hero->name % hero->mana % hero->manaLimit());
 
 	//if we have exchange window with this hero open
 	bool noDismiss=false;
-	for(std::list<IShowActivable *>::iterator it=GH.listInt.begin() ; it != GH.listInt.end(); it++)
+	BOOST_FOREACH(IShowActivable *isa, GH.listInt)
 	{
-		CExchangeWindow * cew = dynamic_cast<CExchangeWindow*>((*it));
-			if(cew)
-				for(int g=0; g<ARRAY_COUNT(cew->heroInst); ++g)
-					if(cew->heroInst[g] == hero)
-						noDismiss = true;
-		CKingdomInterface * cki = dynamic_cast<CKingdomInterface*>((*it));
-			if (cki)
-				noDismiss = true;
+		if(CExchangeWindow * cew = dynamic_cast<CExchangeWindow*>(isa))
+			for(int g=0; g < ARRAY_COUNT(cew->heroInst); ++g)
+				if(cew->heroInst[g] == hero)
+					noDismiss = true;
+
+		if (CKingdomInterface * cki = dynamic_cast<CKingdomInterface*>(isa))
+			noDismiss = true;
 	}
 	dismissButton->block(!!hero->visitedTown || noDismiss);
-	if(hero->getSecSkillLevel(19)==0)
-		gar2button->block(true);
+
+	if(hero->getSecSkillLevel(CGHeroInstance::TACTICS) == 0)
+		tacticsButton->block(true);
 	else
 	{
-		gar2button->block(false);
-		gar2button->callback = vstd::assigno(hero->tacticFormationEnabled,true);
-		gar2button->callback2 = vstd::assigno(hero->tacticFormationEnabled,false);
+		tacticsButton->block(false);
+		tacticsButton->callback = vstd::assigno(hero->tacticFormationEnabled,true);
+		tacticsButton->callback2 = vstd::assigno(hero->tacticFormationEnabled,false);
 	}
 
 	//setting formations
@@ -306,87 +254,13 @@ void CHeroWindow::setHero(const CGHeroInstance *hero)
 	morale->set(hero);
 	luck->set(hero);
 
-	//restoring pos
-	pos.x += 65;
-	pos.y += 8;
-
-	redrawCurBack();
+	if(redrawNeeded)
+		redraw();
 }
 
 void CHeroWindow::quit()
 {
-	GH.popInt(this);
-	dispose();
-}
-
-void CHeroWindow::activate()
-{
-	quitButton->activate();
-	dismissButton->activate();
-	questlogButton->activate();
-	gar2button->activate();
-	formations->activate();
-	//gar4button->activate();
-	portraitArea->activate();
-	specArea->activate();
-	expArea->activate();
-	spellPointsArea->activate();
-	morale->activate();
-	luck->activate();
-
-	garr->activate();
-	GH.statusbar = ourBar;
-
-	for(size_t v=0; v<primSkillAreas.size(); ++v)
-	{
-		primSkillAreas[v]->activate();
-	}
-	for(size_t v=0; v<std::min(secSkillAreas.size(), curHero->secSkills.size()); ++v)
-	{
-		secSkillAreas[v]->activate();
-	}
-	redrawCurBack();
-
-	artifs->activate();
-	
-	for(size_t e=0; e<heroListMi.size(); ++e)
-	{
-		heroListMi[e]->activate();
-	}
-}
-
-void CHeroWindow::deactivate()
-{
-	quitButton->deactivate();
-	dismissButton->deactivate();
-	questlogButton->deactivate();
-	gar2button->deactivate();
-	formations->deactivate();
-	specArea->deactivate();
-	//gar4button->deactivate();
-	portraitArea->deactivate();
-	expArea->deactivate();
-	spellPointsArea->deactivate();
-	morale->deactivate();
-	luck->deactivate();
-
-	garr->deactivate();
-
-	for(size_t v=0; v<primSkillAreas.size(); ++v)
-	{
-		primSkillAreas[v]->deactivate();
-	}
-	for(size_t v=0; v<std::min(secSkillAreas.size(), curHero->secSkills.size()); ++v)
-	{
-		secSkillAreas[v]->deactivate();
-	}
-
-	artifs->deactivate();
-	
-	for(size_t e=0; e<heroListMi.size(); ++e)
-	{
-		heroListMi[e]->deactivate();
-	}
+	GH.popIntTotally(this);
 }
 
 void CHeroWindow::dismissCurrent()
@@ -399,138 +273,104 @@ void CHeroWindow::dismissCurrent()
 void CHeroWindow::questlog()
 {
 }
-
-void CHeroWindow::redrawCurBack()
+void CHeroWindow::showAll(SDL_Surface * to)
 {
-	if(curBack)
-		SDL_FreeSurface(curBack);
-	curBack = SDL_DisplayFormat(background);
-
-	//primary skills & exp and mana
-	blitAt(graphics->pskillsm->ourImages[0].bitmap, 32, 111, curBack);
-	blitAt(graphics->pskillsm->ourImages[1].bitmap, 102, 111, curBack);
-	blitAt(graphics->pskillsm->ourImages[2].bitmap, 172, 111, curBack);
-	blitAt(graphics->pskillsm->ourImages[5].bitmap, 242, 111, curBack);
-	blitAt(graphics->pskillsm->ourImages[4].bitmap, 20, 230, curBack);
-	blitAt(graphics->pskillsm->ourImages[3].bitmap, 162, 230, curBack);
-
+	CIntObject::showAll(to);
 	//blitting portrait
-	blitAt(graphics->portraitLarge[curHero->portrait], 19, 19, curBack);
-
+	blitAtLoc(graphics->portraitLarge[curHero->portrait], 19, 19, to);
+	 
 	//printing hero's name
-	CSDL_Ext::printAtMiddle(curHero->name, 190, 38, FONT_BIG, tytulowy, curBack);
-
+	printAtMiddleLoc(curHero->name, 190, 38, FONT_BIG, tytulowy, to);
+	 
 	//printing hero's level
 	std::string secondLine= CGI->generaltexth->allTexts[342];
 	boost::algorithm::replace_first(secondLine,"%d",boost::lexical_cast<std::string>(curHero->level));
 	boost::algorithm::replace_first(secondLine,"%s",curHero->type->heroClass->name);
-	CSDL_Ext::printAtMiddle(secondLine, 190, 65, FONT_MEDIUM, zwykly, curBack);
-	
+	printAtMiddleLoc(secondLine, 190, 65, FONT_MEDIUM, zwykly, to);
+	 	
 	//primary skills names
-	CSDL_Ext::printAtMiddle(CGI->generaltexth->jktexts[1], 52, 99, FONT_SMALL, tytulowy, curBack);
-	CSDL_Ext::printAtMiddle(CGI->generaltexth->jktexts[2], 123, 99, FONT_SMALL, tytulowy, curBack);
-	CSDL_Ext::printAtMiddle(CGI->generaltexth->jktexts[3], 193, 99, FONT_SMALL, tytulowy, curBack);
-	CSDL_Ext::printAtMiddle(CGI->generaltexth->jktexts[4], 262, 99, FONT_SMALL, tytulowy, curBack);
-
+	printAtMiddleLoc(CGI->generaltexth->jktexts[1], 52, 99, FONT_SMALL, tytulowy, to);
+	printAtMiddleLoc(CGI->generaltexth->jktexts[2], 123, 99, FONT_SMALL, tytulowy, to);
+	printAtMiddleLoc(CGI->generaltexth->jktexts[3], 193, 99, FONT_SMALL, tytulowy, to);
+	printAtMiddleLoc(CGI->generaltexth->jktexts[4], 262, 99, FONT_SMALL, tytulowy, to);
+	 
 	//dismiss / quest log
 	std::vector<std::string> toPrin = CMessage::breakText(CGI->generaltexth->jktexts[8]);
 	if(toPrin.size()==1)
 	{
-		CSDL_Ext::printAt(toPrin[0], 372, 439, FONT_SMALL, zwykly, curBack);
+	 	printAtLoc(toPrin[0], 372, 439, FONT_SMALL, zwykly, to);
 	}
 	else
 	{
-		CSDL_Ext::printAt(toPrin[0], 372, 430, FONT_SMALL, zwykly, curBack);
-		CSDL_Ext::printAt(toPrin[1], 372, 446, FONT_SMALL, zwykly, curBack);
+	 	printAtLoc(toPrin[0], 372, 430, FONT_SMALL, zwykly, to);
+	 	printAtLoc(toPrin[1], 372, 446, FONT_SMALL, zwykly, to);
 	}
-
+	 
 	toPrin = CMessage::breakText(CGI->generaltexth->jktexts[9]);
 	if(toPrin.size()==1)
 	{
-		CSDL_Ext::printAt(toPrin[0], 512, 439, FONT_SMALL, zwykly, curBack);
+	 	printAtLoc(toPrin[0], 512, 439, FONT_SMALL, zwykly, to);
 	}
 	else
 	{
-		CSDL_Ext::printAt(toPrin[0], 512, 430, FONT_SMALL, zwykly, curBack);
-		CSDL_Ext::printAt(toPrin[1], 512, 446, FONT_SMALL, zwykly, curBack);
+	 	printAtLoc(toPrin[0], 512, 430, FONT_SMALL, zwykly, to);
+	 	printAtLoc(toPrin[1], 512, 446, FONT_SMALL, zwykly, to);
 	}
-
+	 
 	//printing primary skills' amounts
 	for(int m=0; m<4; ++m)
 	{
-		std::ostringstream primarySkill;
-		primarySkill<<curHero->getPrimSkillLevel(m);
-		CSDL_Ext::printAtMiddle(primarySkill.str(), 53 + 70 * m, 166, FONT_SMALL, zwykly, curBack);
+	 	std::ostringstream primarySkill;
+	 	primarySkill<<curHero->getPrimSkillLevel(m);
+	 	printAtMiddleLoc(primarySkill.str(), 53 + 70 * m, 166, FONT_SMALL, zwykly, to);
 	}
-
-	//morale and luck printing
-	blitAt(graphics->luck42->ourImages[curHero->LuckVal()+3].bitmap, 239, 182, curBack);
-	blitAt(graphics->morale42->ourImages[curHero->MoraleVal()+3].bitmap, 181, 182, curBack);
-
-	blitAt(flags->ourImages[player].bitmap, 606, 8, curBack);
-
+	 
+	blitAtLoc(flags->ourImages[player].bitmap, 606, 8, to);
+	 
 	//hero list blitting
-
-	for(int pos=0, g=0; g<LOCPLINT->wanderingHeroes.size(); ++g)
+	 
+	for(int slotPos=0, g=0; g<LOCPLINT->wanderingHeroes.size(); ++g)
 	{
-		const CGHeroInstance * cur = LOCPLINT->wanderingHeroes[g];
-		if (cur->inTownGarrison)
-			// Only display heroes that are not in garrison
-			continue;
-
-		blitAt(graphics->portraitSmall[cur->portrait], 611, 87+pos*54, curBack);
-		//printing yellow border
-		if(cur->name == curHero->name)
-		{
-			for(int f=0; f<graphics->portraitSmall[cur->portrait]->w; ++f)
-			{
-				for(int h=0; h<graphics->portraitSmall[cur->portrait]->h; ++h)
-					if(f==0 || h==0 || f==graphics->portraitSmall[cur->portrait]->w-1 || h==graphics->portraitSmall[cur->portrait]->h-1)
-					{
-						CSDL_Ext::SDL_PutPixelWithoutRefresh(curBack, 611+f, 87+pos*54+h, 240, 220, 120);
-					}
-			}
-		}
-
-		pos ++;
+	 	const CGHeroInstance * cur = LOCPLINT->wanderingHeroes[g];
+	 	if (cur->inTownGarrison)
+	 		// Only display heroes that are not in garrison
+	 		continue;
+	 
+	 	blitAtLoc(graphics->portraitSmall[cur->portrait], 611, 87+slotPos*54, to);
+	 	//printing yellow border
+	 	if(cur->name == curHero->name)
+	 	{
+	 		for(int f=0; f<graphics->portraitSmall[cur->portrait]->w; ++f)
+	 		{
+	 			for(int h=0; h<graphics->portraitSmall[cur->portrait]->h; ++h)
+	 				if(f==0 || h==0 || f==graphics->portraitSmall[cur->portrait]->w-1 || h==graphics->portraitSmall[cur->portrait]->h-1)
+	 					CSDL_Ext::SDL_PutPixelWithoutRefresh(to, pos.x + 611+f, pos.y + 87+slotPos*54+h, 240, 220, 120);
+	 		}
+	 	}
+	 
+	 	slotPos ++;
 	}
-
+	 
 	//secondary skills
 	for(size_t v=0; v<std::min(secSkillAreas.size(), curHero->secSkills.size()); ++v)
 	{
-		blitAt(graphics->abils44->ourImages[curHero->secSkills[v].first*3+3+curHero->secSkills[v].second-1].bitmap, v%2 ? 161 : 18, 276 + 48 * (v/2), curBack);
-		CSDL_Ext::printAt(CGI->generaltexth->levels[curHero->secSkills[v].second-1], v%2 ? 212 : 68, 280 + 48 * (v/2), FONT_SMALL, zwykly, curBack);
-		CSDL_Ext::printAt(CGI->generaltexth->skillName[curHero->secSkills[v].first], v%2 ? 212 : 68, 300 + 48 * (v/2), FONT_SMALL, zwykly, curBack);
+	 	blitAtLoc(graphics->abils44->ourImages[curHero->secSkills[v].first*3+3+curHero->secSkills[v].second-1].bitmap, v%2 ? 161 : 18, 276 + 48 * (v/2), to);
+	 	printAtLoc(CGI->generaltexth->levels[curHero->secSkills[v].second-1], v%2 ? 212 : 68, 280 + 48 * (v/2), FONT_SMALL, zwykly, to);
+	 	printAtLoc(CGI->generaltexth->skillName[curHero->secSkills[v].first], v%2 ? 212 : 68, 300 + 48 * (v/2), FONT_SMALL, zwykly, to);
 	}
-
+	 
 	//printing special ability
-	blitAt(graphics->un44->ourImages[curHero->subID].bitmap, 18, 180, curBack);
-	CSDL_Ext::printAt(CGI->generaltexth->jktexts[5].substr(1, CGI->generaltexth->jktexts[5].size()-2), 69, 183, FONT_SMALL, tytulowy, curBack);
-	CSDL_Ext::printAt(CGI->generaltexth->hTxts[curHero->subID].bonusName, 69, 205, FONT_SMALL, zwykly, curBack);
-
+	blitAtLoc(graphics->un44->ourImages[curHero->subID].bitmap, 18, 180, to);
+	printAtLoc(CGI->generaltexth->jktexts[5].substr(1, CGI->generaltexth->jktexts[5].size()-2), 69, 183, FONT_SMALL, tytulowy, to);
+	printAtLoc(CGI->generaltexth->hTxts[curHero->subID].bonusName, 69, 205, FONT_SMALL, zwykly, to);
+	 
 	//printing necessery texts
-	CSDL_Ext::printAt(CGI->generaltexth->jktexts[6].substr(1, CGI->generaltexth->jktexts[6].size()-2), 69, 232, FONT_SMALL, tytulowy, curBack);
+	printAtLoc(CGI->generaltexth->jktexts[6].substr(1, CGI->generaltexth->jktexts[6].size()-2), 69, 232, FONT_SMALL, tytulowy, to);
 	std::ostringstream expstr;
 	expstr<<curHero->exp;
-	CSDL_Ext::printAt(expstr.str(), 68, 252, FONT_SMALL, zwykly, curBack);
-	CSDL_Ext::printAt(CGI->generaltexth->jktexts[7].substr(1, CGI->generaltexth->jktexts[7].size()-2), 213, 232, FONT_SMALL, tytulowy, curBack);
+	printAtLoc(expstr.str(), 68, 252, FONT_SMALL, zwykly, to);
+	printAtLoc(CGI->generaltexth->jktexts[7].substr(1, CGI->generaltexth->jktexts[7].size()-2), 213, 232, FONT_SMALL, tytulowy, to);
 	std::ostringstream manastr;
 	manastr << curHero->mana << '/' << curHero->manaLimit();
-	CSDL_Ext::printAt(manastr.str(), 211, 252, FONT_SMALL, zwykly, curBack);
-}
-
-void CHeroWindow::dispose()
-{
-	SDL_FreeSurface(curBack);
-	curBack = NULL;
-	curHero = NULL;
-
-	artifs->dispose();
-}
-
-void CHeroWindow::setPlayer(int Player)
-{
-	player = Player;
-
-	graphics->blueToPlayersAdv(background,player);
-}
+	printAtLoc(manastr.str(), 211, 252, FONT_SMALL, zwykly, to);
+}

+ 18 - 19
client/CHeroWindow.h

@@ -29,16 +29,21 @@ class CHeroSwitcher : public CIntObject
 {
 public:
 	int id;
-	CHeroWindow * owner;
+
+	CHeroWindow * getOwner();
 	virtual void clickLeft(tribool down, bool previousState);
 
-	CHeroSwitcher();
+	CHeroSwitcher(int serial);
 };
 
-class CHeroWindow: public CWindowWithGarrison
+
+
+class CHeroWindow: public CWindowWithGarrison, public CWindowWithArtifacts
 {
-	SDL_Surface * background, * curBack;
-	CStatusBar * ourBar; //heroWindow's statusBar
+	enum ELabel {};
+	std::map<ELabel, CLabel*> labels;
+	CPicture *background;
+	CGStatusBar * ourBar; //heroWindow's statusBar
 
 	//general graphics
 	CDefEssential *flags;
@@ -47,8 +52,6 @@ class CHeroWindow: public CWindowWithGarrison
 	//AdventureMapButton * gar4button; //splitting
 	std::vector<CHeroSwitcher *> heroListMi; //new better list of heroes
 
-	CArtifactsOfHero * artifs;
-
 	//clickable areas
 	LRClickableAreaWText * portraitArea;
 	std::vector<LRClickableAreaWTextComp *> primSkillAreas;
@@ -61,23 +64,19 @@ public:
 	const CGHeroInstance * curHero;
 	AdventureMapButton * quitButton, * dismissButton, * questlogButton; //general
 		
-	CHighlightableButton *gar2button; //garrison / formation handling;
+	CHighlightableButton *tacticsButton; //garrison / formation handling;
 	CHighlightableButtonsGroup *formations;
 	int player;
-	CHeroWindow(int playerColor); //c-tor
+	CHeroWindow(const CGHeroInstance *hero); //c-tor
 	~CHeroWindow(); //d-tor
-	void setHero(const CGHeroInstance * hero); //sets main displayed hero
-	void activate(); //activates hero window;
-	void deactivate(); //activates hero window;
-	virtual void show(SDL_Surface * to); //shows hero window
-	void showAll(SDL_Surface * to){show(to);};
-	void redrawCurBack(); //redraws curBAck from scratch
-	void dispose(); //free resources not needed after closing windows and reset state
-	void quit(); //stops displaying hero window and disposes
+
+	void update(const CGHeroInstance * hero, bool redrawNeeded = false); //sets main displayed hero
+	void showAll(SDL_Surface * to); //shows and activates adv. map interface
+//		void redrawCurBack(); //redraws curBAck from scratch
+		void quit(); //stops displaying hero window and disposes
 	void dismissCurrent(); //dissmissed currently displayed hero (curHero)
 	void questlog(); //show quest log in hero window
-	void switchHero(); //changes displayed hero
-	void setPlayer(int Player);
+		void switchHero(); //changes displayed hero
 
 	//friends
 	friend void CArtPlace::clickLeft(tribool down, bool previousState);

+ 31 - 41
client/CKingdomInterface.cpp

@@ -10,15 +10,15 @@
 #include "CMessage.h"
 #include "SDL_Extensions.h"
 #include "Graphics.h"
-#include "../hch/CArtHandler.h"
-#include "../hch/CBuildingHandler.h"
-#include "../hch/CDefHandler.h"
-#include "../hch/CHeroHandler.h"
-#include "../hch/CGeneralTextHandler.h"
-#include "../hch/CObjectHandler.h"
-#include "../hch/CTownHandler.h"
-#include "../hch/CCreatureHandler.h"
-#include "../hch/CHeroHandler.h"
+#include "../lib/CArtHandler.h"
+#include "../lib/CBuildingHandler.h"
+#include "CDefHandler.h"
+#include "../lib/CHeroHandler.h"
+#include "../lib/CGeneralTextHandler.h"
+#include "../lib/CObjectHandler.h"
+#include "../lib/CTownHandler.h"
+#include "../lib/CCreatureHandler.h"
+#include "../lib/CHeroHandler.h"
 #include "../lib/map.h"
 #include "../lib/NetPacks.h"
 #include <boost/algorithm/string/replace.hpp>
@@ -164,7 +164,7 @@ CKingdomInterface::CKingdomInterface()
 	incomesVal[7] = incomesVal[6]*1000;//gold mines -> total income
 	std::vector<const CGHeroInstance*> heroes = LOCPLINT->cb->getHeroesInfo(true);
 	for(size_t i=0; i<heroes.size();i++)
-		switch(heroes[i]->getSecSkillLevel(13))//some heroes may have estates
+		switch(heroes[i]->getSecSkillLevel(CGHeroInstance::ESTATES))//some heroes may have estates
 		{
 		case 1: //basic
 			incomesVal[7] += 125;
@@ -254,7 +254,7 @@ void CKingdomInterface::showAll( SDL_Surface * to/*=NULL*/)
 		ObjList[i]->hoverText = "";
 
 	int skipCount=0, curPos=objPos<0?(-objPos):0;
-	for (std::map<int,std::pair<int, std::string*> >::iterator it=objList.begin(); it!= objList.end(); it++)
+	for (std::map<int,std::pair<int, const std::string*> >::iterator it=objList.begin(); it!= objList.end(); it++)
 	{
 		if (skipCount<objPos)//we will show only objects from objPos
 		{
@@ -708,11 +708,9 @@ CKingdomInterface::CHeroItem::CHeroItem(int num, CKingdomInterface * Owner)
 	char bufor[400];
 	for(int i=0; i<PRIMARY_SKILLS; i++)
 	{
-		primarySkills.push_back(new LRClickableAreaWTextComp());
-		primarySkills[i]->pos = genRect(45, 32, pos.x+77 + 36*i, pos.y+26);
+		primarySkills.push_back(new LRClickableAreaWTextComp(genRect(45, 32, pos.x+77 + 36*i, pos.y+26), SComponent::primskill));
 		primarySkills[i]->text = CGI->generaltexth->arraytxt[2+i];
 		primarySkills[i]->type = i;
-		primarySkills[i]->baseType = 0;
 		sprintf(bufor, CGI->generaltexth->heroscrn[1].c_str(), CGI->generaltexth->primarySkillNames[i].c_str());
 		primarySkills[i]->hoverText = std::string(bufor);
 	};
@@ -720,11 +718,8 @@ CKingdomInterface::CHeroItem::CHeroItem(int num, CKingdomInterface * Owner)
 	experience->pos = genRect(33, 49, pos.x+322, pos.y+5);
 	experience->hoverText = CGI->generaltexth->heroscrn[9];
 
-	morale = new MoraleLuckBox(true);
-	morale->pos = genRect(20,32,pos.x+221,pos.y+52);
-
-	luck = new MoraleLuckBox(false);
-	luck->pos = genRect(20,32,pos.x+221,pos.y+28);
+	morale = new MoraleLuckBox(true, genRect(20,32,pos.x+221,pos.y+52));
+	luck = new MoraleLuckBox(false, genRect(20,32,pos.x+221,pos.y+28));
 
 	spellPoints = new LRClickableAreaWText();
 	spellPoints->pos = genRect(33, 49, pos.x+270, pos.y+5);
@@ -736,25 +731,18 @@ CKingdomInterface::CHeroItem::CHeroItem(int num, CKingdomInterface * Owner)
 
 	for(int i=0; i<SKILL_PER_HERO; ++i)
 	{
-		secondarySkills.push_back(new LRClickableAreaWTextComp());
-		secondarySkills[i]->pos = genRect(32, 32, pos.x+410+i*37, pos.y+5);
-		secondarySkills[i]->baseType = 1;
-	};
+		secondarySkills.push_back(new LRClickableAreaWTextComp(genRect(32, 32, pos.x+410+i*37, pos.y+5), SComponent::secskill));
+	}
 /*
 	for (int i=0; i<18;i++)
 	{
-		artifacts.push_back(new CArtPlace(this));
-		artifacts[i]->pos = genRect(44, 44, pos.x+268+(i%9)*48, pos.y+66);
-		artifacts[i]->baseType = SComponent::artifact;
-	};
+		artifacts.push_back(new CArtPlace(this, genRect(44, 44, pos.x+268+(i%9)*48, pos.y+66)));
+	}
 
 	for (int i=0; i<8;i++)
 	{
-		backpack.push_back(new CArtPlace(this));
-		backpack[i]->pos = genRect(44, 44, pos.x+293+(i%9)*48, pos.y+66);
-		backpack[i]->baseType = SComponent::artifact;
-	};*/
-
+		backpack.push_back(new CArtPlace(this, genRect(44, 44, pos.x+293+(i%9)*48, pos.y+66)));
+	}*/
 }
 
 CKingdomInterface::CHeroItem::~CHeroItem()
@@ -778,14 +766,14 @@ void CKingdomInterface::CHeroItem::setHero(const CGHeroInstance * newHero)
 		garr = NULL;
 	}
 	char bufor[4000];
-	artLeft->block(hero->artifacts.size() <= 8);
-	artRight->block(hero->artifacts.size() <= 8);
+	artLeft->block(hero->artifactsInBackpack.size() <= 8);
+	artRight->block(hero->artifactsInBackpack.size() <= 8);
 	garr = new CGarrisonInt(pos.x+6, pos.y+78, 4, Point(), parent->slots->ourImages[parent->PicCount].bitmap,
 		Point(6,78), hero, NULL, true, true, true);
 
 	for (int i=0; i<artifacts.size(); i++)
 	{
-		artifacts[i]->type = hero->getArtAtPos(i)->id;
+		artifacts[i]->type = hero->getArtTypeId(i);
 		if (artifacts[i]->type<0 || artifacts[i]->type == 145 )
 			artifacts[i]->hoverText = CGI->generaltexth->heroscrn[11];
 		else
@@ -797,7 +785,7 @@ void CKingdomInterface::CHeroItem::setHero(const CGHeroInstance * newHero)
 
 	for (int i=0; i<backpack.size(); i++)
 	{
-		backpack[i]->type = hero->getArtAtPos(19+i)->id;
+		backpack[i]->type = hero->getArtTypeId(19+i);
 		if (backpack[i]->type<0)
 			backpack[i]->hoverText ="";
 		else
@@ -845,10 +833,10 @@ void CKingdomInterface::CHeroItem::setHero(const CGHeroInstance * newHero)
 
 void CKingdomInterface::CHeroItem::scrollArts(int move)
 {
-	backpackPos = ( backpackPos + move + hero->artifacts.size()) % hero->artifacts.size();
+	backpackPos = ( backpackPos + move + hero->artifactsInBackpack.size()) % hero->artifactsInBackpack.size();
 	for (int i=0; i<backpack.size(); i++)
 	{
-		backpack[i]->type = hero->getArtAtPos(19+(backpackPos + i)%hero->artifacts.size())->id;
+		backpack[i]->type = hero->getArtTypeId(19+(backpackPos + i)%hero->artifactsInBackpack.size());
 		if (backpack[i]->type<0)
 			backpack[i]->hoverText ="";
 		else
@@ -913,7 +901,7 @@ void CKingdomInterface::CHeroItem::showAll(SDL_Surface * to)
 		case 0://equipped arts
 			/*for (int i = iter ; i<iter+9;i++)
 			{
-				int artID = hero->getArtAtPos(i)->id;
+				int artID = hero->getArtTypeId(i);
 				if (artID>=0)
 					blitAt(graphics->artDefs->ourImages[artID].bitmap,pos.x+268+48*(i%9),pos.y+66,to);
 			}*/
@@ -921,7 +909,7 @@ void CKingdomInterface::CHeroItem::showAll(SDL_Surface * to)
 		case 2:
 			artLeft->show(to);
 			artRight->show(to);
-			int max = hero->artifacts.size();
+			int max = hero->artifactsInBackpack.size();
 			iter = std::min(8, max);
 			/*for (size_t it = 0 ; it<iter;it++)
 				blitAt(graphics->artDefs->ourImages[hero->artifacts[(it+backpackPos)%max]->id].bitmap,pos.x+293+48*it,pos.y+66,to);
@@ -1005,7 +993,8 @@ void CKingdomInterface::CHeroItem::deactivate()
 		secondarySkills[i]->deactivate();
 }
 
-CKingdomInterface::CHeroItem::CArtPlace::CArtPlace(CHeroItem * owner)
+CKingdomInterface::CHeroItem::CArtPlace::CArtPlace(CHeroItem * owner, const Rect &r)
+	: LRClickableAreaWTextComp(r, SComponent::artifact)
 {
 	parent = owner;
 	used = LCLICK | RCLICK | HOVER;
@@ -1043,6 +1032,7 @@ void CKingdomInterface::CHeroItem::CArtPlace::deactivate()
 
 
 CKingdomInterface::CTownItem::CCreaPlace::CCreaPlace()
+	: LRClickableAreaWTextComp(Rect(0,0,0,0), -1)
 {
 	town = NULL;
 	used = LCLICK | RCLICK | HOVER;

+ 3 - 3
client/CKingdomInterface.h

@@ -5,7 +5,7 @@
 #include "../global.h"
 #include "GUIBase.h"
 #include "GUIClasses.h"
-#include "../hch/CMusicBase.h"
+#include "CMusicBase.h"
 class AdventureMapButton;
 class CHighlightableButtonsGroup;
 class CResDataBar;
@@ -61,7 +61,7 @@ class CKingdomInterface : public CGarrisonHolder
 		{
 		public:
 			CHeroItem * parent;
-			CArtPlace(CHeroItem * owner); //c-tor
+			CArtPlace(CHeroItem * owner, const Rect &r = Rect(0, 0, 44, 44)); //c-tor
 			void clickLeft(tribool down, bool previousState);
 			void clickRight(tribool down, bool previousState);
 			void activate();
@@ -118,7 +118,7 @@ public:
 	//objects list
 	int objSize, objPos;
 	CDefEssential *objPics;
-	std::map<int,std::pair<int, std::string*> > objList; //dwelling ID, count, hover text
+	std::map<int,std::pair<int, const std::string*> > objList; //dwelling ID, count, hover text
 	std::vector <HoverableArea* > ObjList;//list of dwellings
 	AdventureMapButton* ObjUp, *ObjDown, *ObjTop, *ObjBottom;//buttons for dwellings list
 

+ 96 - 73
client/CMT.cpp

@@ -23,16 +23,16 @@
 #include "../CCallback.h"
 #include "CPlayerInterface.h"
 #include "CAdvmapInterface.h"
-#include "../hch/CBuildingHandler.h"
-#include "../hch/CVideoHandler.h"
-#include "../hch/CHeroHandler.h"
-#include "../hch/CCreatureHandler.h"
-#include "../hch/CSpellHandler.h"
-#include "../hch/CMusicHandler.h"
-#include "../hch/CVideoHandler.h"
-#include "../hch/CLodHandler.h"
-#include "../hch/CDefHandler.h"
-#include "../hch/CGeneralTextHandler.h"
+#include "../lib/CBuildingHandler.h"
+#include "CVideoHandler.h"
+#include "../lib/CHeroHandler.h"
+#include "../lib/CCreatureHandler.h"
+#include "../lib/CSpellHandler.h"
+#include "CMusicHandler.h"
+#include "CVideoHandler.h"
+#include "../lib/CLodHandler.h"
+#include "CDefHandler.h"
+#include "../lib/CGeneralTextHandler.h"
 #include "Graphics.h"
 #include "Client.h"
 #include "CConfigHandler.h"
@@ -42,18 +42,22 @@
 #include <cstdlib>
 #include "../lib/NetPacks.h"
 #include "CMessage.h"
-#include "../hch/CObjectHandler.h"
+#include "../lib/CObjectHandler.h"
+#include <boost/program_options.hpp>
+#include "../lib/CArtHandler.h"
 
 #ifdef _WIN32
 #include "SDL_syswm.h"
 #endif
 #include <boost/foreach.hpp>
-#include "../hch/CDefObjInfoHandler.h"
+#include "../lib/CDefObjInfoHandler.h"
 
 #if __MINGW32__
 #undef main
 #endif
 
+namespace po = boost::program_options;
+
 /*
  * CMT.cpp, part of VCMI engine
  *
@@ -89,6 +93,7 @@ void dispose();
 void playIntro();
 static void listenForEvents();
 void requestChangingResolution();
+void startGame(StartInfo * options, CConnection *serv = NULL);
 
 #ifndef _WIN32
 #ifndef _GNU_SOURCE
@@ -128,25 +133,25 @@ void init()
 	//initializing audio
 	// Note: because of interface button range, volume can only be a
 	// multiple of 11, from 0 to 99.
-	CGI->soundh = new CSoundHandler;
-	CGI->soundh->init();
-	CGI->soundh->setVolume(GDefaultOptions.soundVolume);
-	CGI->musich = new CMusicHandler;
+	CCS->soundh = new CSoundHandler;
+	CCS->soundh->init();
+	CCS->soundh->setVolume(GDefaultOptions.soundVolume);
+	CCS->musich = new CMusicHandler;
 	//CGI->musich->init();
 	//CGI->musich->setVolume(GDefaultOptions.musicVolume);
 	tlog0<<"\tInitializing sound: "<<pomtime.getDif()<<std::endl;
 	tlog0<<"Initializing screen and sound handling: "<<tmh.getDif()<<std::endl;
 
 	initDLL(::console,logfile);
-	CGI->setFromLib();
-	CGI->soundh->initCreaturesSounds(CGI->creh->creatures);
-	CGI->soundh->initSpellsSounds(CGI->spellh->spells);
+	const_cast<CGameInfo*>(CGI)->setFromLib();
+	CCS->soundh->initCreaturesSounds(CGI->creh->creatures);
+	CCS->soundh->initSpellsSounds(CGI->spellh->spells);
 	tlog0<<"Initializing VCMI_Lib: "<<tmh.getDif()<<std::endl;
 
 	pomtime.getDif();
-	CGI->curh = new CCursorHandler;
-	CGI->curh->initCursor();
-	CGI->curh->show();
+	CCS->curh = new CCursorHandler;
+	CCS->curh->initCursor();
+	CCS->curh->show();
 	tlog0<<"Screen handler: "<<pomtime.getDif()<<std::endl;
 	pomtime.getDif();
 	graphics = new Graphics();
@@ -160,7 +165,6 @@ void init()
 	//tlog0<<"Initialization CPreGame (together): "<<tmh.getDif()<<std::endl;
 }
 
-#ifndef _WIN32
 static void prog_version(void)
 {
 	printf("%s\n", NAME_VER);
@@ -180,7 +184,6 @@ static void prog_help(const char *progname)
 	printf("  -h, --help        display this help and exit\n");
 	printf("  -v, --version     display version information and exit\n");
 }
-#endif
 
 
 #ifdef _WIN32
@@ -189,50 +192,42 @@ int _tmain(int argc, _TCHAR* argv[])
 int main(int argc, char** argv)
 #endif
 {
-
-#ifndef _WIN32
-	struct option long_options[] = {
-		{ "help", 0, NULL, 'h' },
-		{ "version", 0, NULL, 'v' },
-		{ NULL, 0, NULL, 0 }
-	};
-
-	while(1) {
-		int c;
-        int option_index;
-
-        c = getopt_long (argc, argv, "hv",
-                         long_options, &option_index);
-
-		if (c == EOF)
-            break;
-        else if (c == '?')
-            return -1;
-
-		switch(c) {
-		case 'h':
-			prog_help(argv[0]);
-			return 0;
-			break;
-
-		case 'v':
-			prog_version();
-			return 0;
-			break;
+	tlog0 << "Starting... " << std::endl;      
+	po::options_description opts("Allowed options");
+	opts.add_options()
+		("help,h", "display help and exit")
+		("version,v", "display version information and exit")
+		("battle,b", po::value<std::string>(), "runs game in duel mode (battle-only");
+
+	po::variables_map vm;
+	if(argc > 1)
+	{
+		try
+		{
+			po::store(po::parse_command_line(argc, argv, opts), vm);
+		}
+		catch(std::exception &e) 
+		{
+			tlog1 << "Failure during parsing command-line options:\n" << e.what() << std::endl;
 		}
 	}
 
-	if (optind < argc) {
-		printf ("Extra arguments: %s\n", argv[optind++]);
-		return 1;
+	po::notify(vm);
+	if(vm.count("help"))
+	{
+		prog_help(0);
+		return 0;
+	}
+	if(vm.count("version"))
+	{
+		prog_version();
+		return 0;
 	}
-#endif
 
 	//Set environment vars to make window centered. Sometimes work, sometimes not. :/
 	putenv("SDL_VIDEO_WINDOW_POS");
 	putenv("SDL_VIDEO_CENTERED=1");
 
-	tlog0 << "Starting... " << std::endl;
 	timeHandler total, pomtime;
 	std::cout.flags(std::ios::unitbuf);
 	logfile = new std::ofstream("VCMI_Client_log.txt");
@@ -247,8 +242,9 @@ int main(int argc, char** argv)
 	tlog0 << NAME << std::endl;
 
 	srand ( time(NULL) );
-	//CPG=NULL;
-	CGI = new CGameInfo; //contains all global informations about game (texts, lodHandlers, map handler itp.)
+	
+	CCS = new CClientState;
+	CGI = new CGameInfo; //contains all global informations about game (texts, lodHandlers, map handler etc.)
 
 	if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_AUDIO))
 	{
@@ -261,18 +257,34 @@ int main(int argc, char** argv)
 	tlog0 <<"\tInitializing screen: "<<pomtime.getDif() << std::endl;
 
 	// Initialize video
-	CGI->videoh = new CVideoPlayer;
+	CCS->videoh = new CVideoPlayer;
 	tlog0<<"\tInitializing video: "<<pomtime.getDif()<<std::endl;
 
 	//we can properly play intro only in the main thread, so we have to move loading to the separate thread
 	boost::thread loading(init);
-	playIntro();
+
+	if(!vm.count("battle"))
+		playIntro();
+
 	SDL_FillRect(screen,NULL,0);
 	SDL_Flip(screen);
 	loading.join();
 	tlog0<<"Initialization of VCMI (together): "<<total.getDif()<<std::endl;
 
-	GH.curInt = new CGPreGame; //will set CGP pointer to itself
+	if(!vm.count("battle"))
+	{
+		CCS->musich->playMusic(musicBase::mainMenu, -1);
+		GH.curInt = new CGPreGame; //will set CGP pointer to itself
+	}
+	else
+	{
+		StartInfo *si = new StartInfo();
+		si->mode = StartInfo::DUEL;
+		si->mapname = vm["battle"].as<std::string>();
+		si->playerInfos[0].color = 0;
+		si->playerInfos[1].color = 1;
+		startGame(si);
+	}
 	mainGUIThread = new boost::thread(&CGuiHandler::run, boost::ref(GH));
 	listenForEvents();
 
@@ -296,10 +308,7 @@ void processCommand(const std::string &message)
 	readed.str(message);
 	std::string cn; //command name
 	readed >> cn;
-	int3 src, dst;
 
-//	int heronum;//TODO use me
-	int3 dest;
 
 	if(LOCPLINT && LOCPLINT->cingconsole)
 		LOCPLINT->cingconsole->print(message);
@@ -462,6 +471,19 @@ void processCommand(const std::string &message)
 				tlog4 << typeid(*obj).name() << std::endl;
 		}
 	}
+	else if(cn=="tell")
+	{
+		std::string what;
+		int id1, id2;
+		readed >> what >> id1 >> id2;
+		if(what == "hs")
+		{
+			BOOST_FOREACH(const CGHeroInstance *h, LOCPLINT->cb->getHeroesInfo())
+				if(h->type->ID == id1)
+					if(const CArtifactInstance *a = h->getArt(id2))
+						tlog4 << a->nodeName();
+		}
+	}
 	else if(client && client->serv && client->serv->connected) //send to server
 	{
 		PlayerMessage pm(LOCPLINT->playerID,message);
@@ -472,17 +494,17 @@ void processCommand(const std::string &message)
 //plays intro, ends when intro is over or button has been pressed (handles events)
 void playIntro()
 {
-	if(CGI->videoh->openAndPlayVideo("3DOLOGO.SMK", 60, 40, screen, true))
+	if(CCS->videoh->openAndPlayVideo("3DOLOGO.SMK", 60, 40, screen, true))
 	{
-		CGI->videoh->openAndPlayVideo("AZVS.SMK", 60, 80, screen, true);
+		CCS->videoh->openAndPlayVideo("AZVS.SMK", 60, 80, screen, true);
 	}
 }
 
 void dispose()
 {
-	delete logfile;
 	if (console)
 		delete console;
+	delete logfile;
 }
 
 static void setScreenRes(int w, int h, int bpp, bool fullscreen)
@@ -608,8 +630,8 @@ static void listenForEvents()
 				client = NULL;
 
 				delete CGI->dobjinfo;
-				CGI->dobjinfo = new CDefObjInfoHandler;
-				CGI->dobjinfo->load();
+				const_cast<CGameInfo*>(CGI)->dobjinfo = new CDefObjInfoHandler;
+				const_cast<CGameInfo*>(CGI)->dobjinfo->load();
 
 				GH.curInt = CGP;
 				GH.defActionsDef = 63;
@@ -664,6 +686,7 @@ void startGame(StartInfo * options, CConnection *serv/* = NULL*/)
 	{
 	case StartInfo::NEW_GAME:
 	case StartInfo::CAMPAIGN:
+	case StartInfo::DUEL:
 		client->newGame(serv, options);
 		break;
 	case StartInfo::LOAD_GAME:
@@ -673,7 +696,7 @@ void startGame(StartInfo * options, CConnection *serv/* = NULL*/)
 		break;
 	}
 
-	CGI->musich->stopMusic();
+	CCS->musich->stopMusic();
 	client->connectionHandler = new boost::thread(&CClient::run, client);
 }
 

+ 3 - 3
client/CMessage.cpp

@@ -1,15 +1,15 @@
 #include "../stdafx.h"
 #include "CMessage.h"
 #include "SDL_ttf.h"
-#include "../hch/CDefHandler.h"
+#include "CDefHandler.h"
 #include "CAnimation.h"
 #include "CGameInfo.h"
 #include "SDL_Extensions.h"
-#include "../hch/CLodHandler.h"
+#include "../lib/CLodHandler.h"
 #include <boost/algorithm/string.hpp>
 #include <boost/algorithm/string/replace.hpp>
 #include <sstream>
-#include "../hch/CGeneralTextHandler.h"
+#include "../lib/CGeneralTextHandler.h"
 #include "Graphics.h"
 #include "GUIClasses.h"
 #include "AdventureMapButton.h"

+ 0 - 0
hch/CMusicBase.h → client/CMusicBase.h


+ 11 - 9
hch/CMusicHandler.cpp → client/CMusicHandler.cpp

@@ -9,8 +9,8 @@
 
 #include "CSndHandler.h"
 #include "CMusicHandler.h"
-#include "CCreatureHandler.h"
-#include "CSpellHandler.h"
+#include "../lib/CCreatureHandler.h"
+#include "../lib/CSpellHandler.h"
 #include "../client/CGameInfo.h"
 
 /*
@@ -29,7 +29,7 @@ static boost::bimap<soundBase::soundID, std::string> sounds;
 
 // Not pretty, but there's only one music handler object in the game.
 static void musicFinishedCallbackC(void) {
-	CGI->musich->musicFinishedCallback();
+	CCS->musich->musicFinishedCallback();
 }
 
 void CAudioBase::init()
@@ -148,7 +148,7 @@ soundBase::soundID CSoundHandler::getSoundID(std::string &fileName)
 		return it->second;
 }
 
-void CSoundHandler::initCreaturesSounds(std::vector<CCreature*> &creatures)
+void CSoundHandler::initCreaturesSounds(const std::vector<ConstTransitivePtr< CCreature> > &creatures)
 {
 	tlog5 << "\t\tReading config/cr_sounds.txt" << std::endl;
 	std::ifstream ifs(DATA_DIR "/config/cr_sounds.txt");
@@ -172,7 +172,7 @@ void CSoundHandler::initCreaturesSounds(std::vector<CCreature*> &creatures)
 		{
 			int id = -1;
 
-			std::map<std::string,int>::iterator i = CGI->creh->nameToID.find(cname);
+			bmap<std::string,int>::const_iterator i = CGI->creh->nameToID.find(cname);
 			if(i != CGI->creh->nameToID.end())
 				id = i->second;
 			else
@@ -222,7 +222,7 @@ void CSoundHandler::initCreaturesSounds(std::vector<CCreature*> &creatures)
 	}*/
 }
 
-void CSoundHandler::initSpellsSounds(std::vector<CSpell> &spells)
+void CSoundHandler::initSpellsSounds(const std::vector< ConstTransitivePtr<CSpell> > &spells)
 {
 	tlog5 << "\t\tReading config/sp_sounds.txt" << std::endl;
 	std::ifstream ifs(DATA_DIR "/config/sp_sounds.txt");
@@ -238,12 +238,14 @@ void CSoundHandler::initSpellsSounds(std::vector<CSpell> &spells)
 
 		if (str.good() || (str.eof() && soundfile != ""))
 		{
-			CSpell &s = CGI->spellh->spells[spellid];
+			const CSpell *s = CGI->spellh->spells[spellid];
 
-			if (s.soundID != soundBase::invalid)
+			if (vstd::contains(spellSounds, s))
+			{
 				tlog1 << "Spell << " << spellid << " already has a sound" << std::endl;
+			}
 			
-			s.soundID = getSoundID(soundfile);
+			spellSounds[s] = getSoundID(soundfile);
 		}
 	}
 	ifs.close();

+ 5 - 4
hch/CMusicHandler.h → client/CMusicHandler.h

@@ -5,7 +5,7 @@
 
 #include "CSoundBase.h"
 #include "CMusicBase.h"
-#include "CCreatureHandler.h"
+#include "../lib/CCreatureHandler.h"
 
 
 /*
@@ -80,8 +80,8 @@ public:
 	void init();
 	void release();
 
-	void initCreaturesSounds(std::vector<CCreature*> &creatures);
-	void initSpellsSounds(std::vector<CSpell> &spells);
+	void initCreaturesSounds(const std::vector<ConstTransitivePtr<CCreature> > &creatures);
+	void initSpellsSounds(const std::vector< ConstTransitivePtr<CSpell> > &spells);
 	void setVolume(unsigned int percent);
 
 	// Sounds
@@ -89,6 +89,7 @@ public:
 	int playSoundFromSet(std::vector<soundBase::soundID> &sound_vec);
 	void stopSound(int handler);
 	std::vector <struct CreaturesBattleSounds> CBattleSounds;
+	std::map<const CSpell*, soundBase::soundID> spellSounds;
 
 	// Sets
 	std::vector<soundBase::soundID> pickupSounds;
@@ -96,7 +97,7 @@ public:
 };
 
 // Helper
-#define battle_sound(creature,what_sound) CGI->soundh->CBattleSounds[(creature)->idNumber].what_sound
+#define battle_sound(creature,what_sound) CCS->soundh->CBattleSounds[(creature)->idNumber].what_sound
 
 class CMusicHandler: public CAudioBase
 {

+ 162 - 66
client/CPlayerInterface.cpp

@@ -16,15 +16,16 @@
 #include "CConfigHandler.h"
 #include "CCreatureAnimation.h"
 #include "Graphics.h"
-#include "../hch/CArtHandler.h"
-#include "../hch/CGeneralTextHandler.h"
-#include "../hch/CHeroHandler.h"
-#include "../hch/CLodHandler.h"
-#include "../hch/CObjectHandler.h"
+#include "../lib/CArtHandler.h"
+#include "../lib/CGeneralTextHandler.h"
+#include "../lib/CHeroHandler.h"
+#include "../lib/CLodHandler.h"
+#include "../lib/CObjectHandler.h"
 #include "../lib/Connection.h"
-#include "../hch/CSpellHandler.h"
-#include "../hch/CTownHandler.h"
-#include "../hch/CMusicHandler.h"
+#include "../lib/CSpellHandler.h"
+#include "../lib/CTownHandler.h"
+#include "../lib/BattleState.h"
+#include "CMusicHandler.h"
 #include "../lib/CondSh.h"
 #include "../lib/NetPacks.h"
 #include "../lib/map.h"
@@ -44,6 +45,7 @@
 #include <sstream>
 #include <boost/filesystem.hpp>
 #include "../StartInfo.h"
+#include <boost/foreach.hpp>
 
 #ifdef min
 #undef min
@@ -94,6 +96,7 @@ struct OCM_HLP_CGIN
 
 CPlayerInterface::CPlayerInterface(int Player)
 {
+	observerInDuelMode = false;
 	howManyPeople++;
 	GH.defActionsDef = 0;
 	LOCPLINT = this;
@@ -139,6 +142,12 @@ CPlayerInterface::~CPlayerInterface()
 void CPlayerInterface::init(ICallback * CB)
 {
 	cb = dynamic_cast<CCallback*>(CB);
+	if(observerInDuelMode)
+	{
+
+		return;
+	}
+
 	if(!adventureInt)
 		adventureInt = new CAdvMapInt();
 
@@ -243,7 +252,8 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 
 	bool directlyAttackingCreature =
 		CGI->mh->map->isInTheMap(details.attackedFrom)
-		&& adventureInt->terrain.currentPath->nodes.size() == 3;
+		&& adventureInt->terrain.currentPath					//in case if movement has been canceled in the meantime and path was already erased
+		&& adventureInt->terrain.currentPath->nodes.size() == 3;//FIXME should be 2 but works nevertheless...
 
 	if(makingTurn  &&  ho->tempOwner == playerID) //we are moving our hero - we may need to update assigned path
 	{
@@ -342,6 +352,9 @@ void CPlayerInterface::heroKilled(const CGHeroInstance* hero)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	wanderingHeroes -= hero;
+	if(vstd::contains(paths, hero)) 
+		paths.erase(hero);
+
 	adventureInt->heroList.updateHList(hero);
 }
 void CPlayerInterface::heroCreated(const CGHeroInstance * hero)
@@ -444,7 +457,7 @@ void CPlayerInterface::receivedResource(int type, int val)
 void CPlayerInterface::heroGotLevel(const CGHeroInstance *hero, int pskill, std::vector<ui16>& skills, boost::function<void(ui32)> &callback)
 {
 	waitWhileDialog();
-	CGI->soundh->playSound(soundBase::heroNewLevel);
+	CCS->soundh->playSound(soundBase::heroNewLevel);
 
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	CLevelWindow *lw = new CLevelWindow(hero,pskill,skills,callback);
@@ -474,10 +487,13 @@ void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town)
 	{
 		c->garr->highlighted = NULL;
 		c->hslotup.hero = town->garrisonHero;
-		c->garr->odown = c->hslotdown.hero = town->visitingHero;
-		c->garr->set2 = town->visitingHero;
-		c->garr->ourUp = LOCPLINT->playerID==(town->garrisonHero ? town->garrisonHero->tempOwner : town->tempOwner);
-		c->garr->ourDown = (town->visitingHero ? LOCPLINT->playerID==town->visitingHero->tempOwner : false);
+		c->hslotdown.hero = town->visitingHero;
+		const CArmedInstance *upperArmy = town;
+		if(town->garrisonHero)
+			upperArmy = town->garrisonHero;
+
+		c->garr->setArmy(upperArmy, 0);
+		c->garr->setArmy(town->visitingHero, 1);
 		c->garr->recreateSlots();
 	}
 	GH.totalRedraw();
@@ -498,7 +514,7 @@ void CPlayerInterface::garrisonChanged(const CGObjectInstance * obj)
 	{
 		if((*i)->type & IShowActivable::WITH_GARRISON)
 		{
-			CGarrisonHolder *cgh = static_cast<CGarrisonHolder*>(*i);
+			CGarrisonHolder *cgh = dynamic_cast<CGarrisonHolder*>(*i);
 			cgh->updateGarrisons();
 		}
 		else if(CTradeWindow *cmw = dynamic_cast<CTradeWindow*>(*i))
@@ -528,7 +544,7 @@ void CPlayerInterface::buildChanged(const CGTownInstance *town, int buildingID,
 	switch(what)
 	{
 	case 1:
-		CGI->soundh->playSound(soundBase::newBuilding);
+		CCS->soundh->playSound(soundBase::newBuilding);
 		castleInt->addBuilding(buildingID);
 		break;
 	case 2:
@@ -548,7 +564,7 @@ void CPlayerInterface::battleStart(const CCreatureSet *army1, const CCreatureSet
 		SDL_Delay(20);
 
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-	CGI->musich->playMusicFromSet(CGI->musich->battleMusics, -1);
+	CCS->musich->playMusicFromSet(CCS->musich->battleMusics, -1);
 	GH.pushInt(battleInt);
 }
 
@@ -591,7 +607,7 @@ void CPlayerInterface::battleStacksHealedRes(const std::vector<std::pair<ui32, u
 	}
 }
 
-void CPlayerInterface::battleNewStackAppeared(int stackID)
+void CPlayerInterface::battleNewStackAppeared(const CStack * stack)
 {
 	if(LOCPLINT != this)
 	{ //another local interface should do this
@@ -599,7 +615,7 @@ void CPlayerInterface::battleNewStackAppeared(int stackID)
 	}
 
 	//changing necessary things in battle interface
-	battleInt->newStack(stackID);
+	battleInt->newStack(stack);
 }
 
 void CPlayerInterface::battleObstaclesRemoved(const std::set<si32> & removedObstacles)
@@ -643,7 +659,7 @@ void CPlayerInterface::battleStacksRemoved(const BattleStacksRemoved & bsr)
 
 	for(std::set<ui32>::const_iterator it = bsr.stackIDs.begin(); it != bsr.stackIDs.end(); ++it) //for each removed stack
 	{
-		battleInt->stackRemoved(*it);
+		battleInt->stackRemoved(LOCPLINT->cb->battleGetStackByID(*it));
 	}
 }
 
@@ -683,23 +699,22 @@ void CPlayerInterface::actionFinished(const BattleAction* action)
 	battleInt->endAction(action);
 }
 
-BattleAction CPlayerInterface::activeStack(int stackID) //called when it's turn of that stack
+BattleAction CPlayerInterface::activeStack(const CStack * stack) //called when it's turn of that stack
 {
 
 	CBattleInterface *b = battleInt;
 	{
 		boost::unique_lock<boost::recursive_mutex> un(*pim);
 
-		const CStack *stack = cb->battleGetStackByID(stackID);
 		if(vstd::contains(stack->state,MOVED)) //this stack has moved and makes second action -> high morale
 		{
 			std::string hlp = CGI->generaltexth->allTexts[33];
-			boost::algorithm::replace_first(hlp,"%s",(stack->count != 1) ? stack->type->namePl : stack->type->nameSing);
+			boost::algorithm::replace_first(hlp,"%s",(stack->count != 1) ? stack->getCreature()->namePl : stack->getCreature()->nameSing);
 			battleInt->displayEffect(20,stack->position);
 			battleInt->console->addText(hlp);
 		}
 
-		b->stackActivated(stackID);
+		b->stackActivated(stack);
 	}
 	//wait till BattleInterface sets its command
 	boost::unique_lock<boost::mutex> lock(b->givenCommand->mx);
@@ -726,7 +741,7 @@ void CPlayerInterface::battleEnd(const BattleResult *br)
 	battleInt->battleFinished(*br);
 }
 
-void CPlayerInterface::battleStackMoved(int ID, int dest, int distance, bool end)
+void CPlayerInterface::battleStackMoved(const CStack * stack, THex dest, int distance, bool end)
 {
 	if(LOCPLINT != this)
 	{ //another local interface should do this
@@ -734,7 +749,7 @@ void CPlayerInterface::battleStackMoved(int ID, int dest, int distance, bool end
 	}
 
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-	battleInt->stackMoved(ID, dest, end, distance);
+	battleInt->stackMoved(stack, dest, end, distance);
 }
 void CPlayerInterface::battleSpellCast( const BattleSpellCast *sc )
 {
@@ -771,13 +786,14 @@ void CPlayerInterface::battleStacksAttacked(const std::vector<BattleStackAttacke
 	std::vector<SStackAttackedInfo> arg;
 	for(std::vector<BattleStackAttacked>::const_iterator i = bsa.begin(); i != bsa.end(); i++)
 	{
+		const CStack *defender = cb->battleGetStackByID(i->stackAttacked, false);
+		const CStack *attacker = cb->battleGetStackByID(i->attackerID, false);
 		if(i->isEffect() && i->effect != 12) //and not armageddon
 		{
-			const CStack *stack = cb->battleGetStackByID(i->stackAttacked, false);
-			if (stack != NULL)
-				battleInt->displayEffect(i->effect, stack->position);
+			if (defender != NULL)
+				battleInt->displayEffect(i->effect, defender->position);
 		}
-		SStackAttackedInfo to_put = {i->stackAttacked, i->damageAmount, i->killedAmount, i->attackerID, LOCPLINT->curAction->actionType==7, i->killed()};
+		SStackAttackedInfo to_put = {defender, i->damageAmount, i->killedAmount, attacker, LOCPLINT->curAction->actionType==7, i->killed()};
 		arg.push_back(to_put);
 
 	}
@@ -804,32 +820,37 @@ void CPlayerInterface::battleAttack(const BattleAttack *ba)
 	{
 		const CStack *stack = cb->battleGetStackByID(ba->stackAttacking);
 		std::string hlp = CGI->generaltexth->allTexts[45];
-		boost::algorithm::replace_first(hlp,"%s",(stack->count != 1) ? stack->type->namePl.c_str() : stack->type->nameSing.c_str());
+		boost::algorithm::replace_first(hlp,"%s",(stack->count != 1) ? stack->getCreature()->namePl.c_str() : stack->getCreature()->nameSing.c_str());
 		battleInt->console->addText(hlp);
 		battleInt->displayEffect(18,stack->position);
 	}
 	//TODO: bad luck?
 
+	const CStack * attacker = cb->battleGetStackByID(ba->stackAttacking);
+
 	if(ba->shot())
 	{
 		for(std::vector<BattleStackAttacked>::const_iterator i = ba->bsa.begin(); i != ba->bsa.end(); i++)
-			battleInt->stackIsShooting(ba->stackAttacking,cb->battleGetPos(i->stackAttacked), i->stackAttacked);
+		{
+			const CStack * attacked = cb->battleGetStackByID(i->stackAttacked);
+			battleInt->stackAttacking(attacker, cb->battleGetPos(i->stackAttacked), attacked, true);
+		}
 	}
 	else
 	{//WARNING: does not support multiple attacked creatures
-		const CStack * attacker = cb->battleGetStackByID(ba->stackAttacking);
 		int shift = 0;
-		if(ba->counter() && BattleInfo::mutualPosition(curAction->destinationTile, attacker->position) < 0)
+		if(ba->counter() && THex::mutualPosition(curAction->destinationTile, attacker->position) < 0)
 		{
-			int distp = BattleInfo::getDistance(curAction->destinationTile + 1, attacker->position);
-			int distm = BattleInfo::getDistance(curAction->destinationTile - 1, attacker->position);
+			int distp = THex::getDistance(curAction->destinationTile + 1, attacker->position);
+			int distm = THex::getDistance(curAction->destinationTile - 1, attacker->position);
 
 			if( distp < distm )
 				shift = 1;
 			else
 				shift = -1;
 		}
-		battleInt->stackAttacking( ba->stackAttacking, ba->counter() ? curAction->destinationTile + shift : curAction->additionalInfo, ba->bsa.begin()->stackAttacked );
+		const CStack * attacked = cb->battleGetStackByID(ba->bsa.begin()->stackAttacked);
+		battleInt->stackAttacking( attacker, ba->counter() ? curAction->destinationTile + shift : curAction->additionalInfo, attacked, false);
 	}
 }
 
@@ -837,7 +858,7 @@ void CPlayerInterface::showComp(SComponent comp)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
 
-	CGI->soundh->playSoundFromSet(CGI->soundh->pickupSounds);
+	CCS->soundh->playSoundFromSet(CCS->soundh->pickupSounds);
 
 	adventureInt->infoBar.showComp(&comp,4000);
 }
@@ -860,7 +881,7 @@ void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector
 	temp->setDelComps(delComps);
 	if(makingTurn && GH.listInt.size() && LOCPLINT == this)
 	{
-		CGI->soundh->playSound(static_cast<soundBase::soundID>(soundID));
+		CCS->soundh->playSound(static_cast<soundBase::soundID>(soundID));
 		showingDialog->set(true);
 		GH.pushInt(temp);
 	}
@@ -885,7 +906,7 @@ void CPlayerInterface::showBlockingDialog( const std::string &text, const std::v
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	
 	stopMovement();
-	CGI->soundh->playSound(static_cast<soundBase::soundID>(soundID));
+	CCS->soundh->playSound(static_cast<soundBase::soundID>(soundID));
 
 	if(!selection && cancel) //simple yes/no dialog
 	{
@@ -918,19 +939,19 @@ void CPlayerInterface::showBlockingDialog( const std::string &text, const std::v
 
 }
 
-void CPlayerInterface::tileRevealed(const std::set<int3> &pos)
+void CPlayerInterface::tileRevealed(const boost::unordered_set<int3, ShashInt3> &pos)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-	for(std::set<int3>::const_iterator i=pos.begin(); i!=pos.end();i++)
+	for(boost::unordered_set<int3, ShashInt3>::const_iterator i=pos.begin(); i!=pos.end();i++)
 		adventureInt->minimap.showTile(*i);
 	if(pos.size())
 		GH.totalRedraw();
 }
 
-void CPlayerInterface::tileHidden(const std::set<int3> &pos)
+void CPlayerInterface::tileHidden(const boost::unordered_set<int3, ShashInt3> &pos)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-	for(std::set<int3>::const_iterator i=pos.begin(); i!=pos.end();i++)
+	for(boost::unordered_set<int3, ShashInt3>::const_iterator i=pos.begin(); i!=pos.end();i++)
 		adventureInt->minimap.hideTile(*i);
 	if(pos.size())
 		GH.totalRedraw();
@@ -939,11 +960,9 @@ void CPlayerInterface::tileHidden(const std::set<int3> &pos)
 void CPlayerInterface::openHeroWindow(const CGHeroInstance *hero)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-	adventureInt->heroWindow->setHero(hero);
-	adventureInt->heroWindow->quitButton->callback = boost::bind(&CHeroWindow::quit,adventureInt->heroWindow);
-	GH.pushInt(adventureInt->heroWindow);
+	GH.pushInt(new CHeroWindow(hero));
 }
-
+/*
 void CPlayerInterface::heroArtifactSetChanged(const CGHeroInstance*hero)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
@@ -982,7 +1001,7 @@ void CPlayerInterface::heroArtifactSetChanged(const CGHeroInstance*hero)
 	}
 
 	updateInfo(hero);
-}
+}*/
 
 void CPlayerInterface::availableCreaturesChanged( const CGDwelling *town )
 {
@@ -1065,14 +1084,14 @@ bool CPlayerInterface::moveHero( const CGHeroInstance *h, CGPath path )
 #if 0
 			// TODO
 			if (hero is flying && sh == -1)
-				sh = CGI->soundh->playSound(soundBase::horseFlying, -1);
+				sh = CCS->soundh->playSound(soundBase::horseFlying, -1);
 #endif
 			{
 				newTerrain = cb->getTileInfo(CGHeroInstance::convertPosition(path.nodes[i].coord, false))->tertype;
 
 				if (newTerrain != currentTerrain) {
-					CGI->soundh->stopSound(sh);
-					sh = CGI->soundh->playSound(CGI->soundh->horseSounds[newTerrain], -1);
+					CCS->soundh->stopSound(sh);
+					sh = CCS->soundh->playSound(CCS->soundh->horseSounds[newTerrain], -1);
 					currentTerrain = newTerrain;
 				}
 			}
@@ -1093,7 +1112,7 @@ bool CPlayerInterface::moveHero( const CGHeroInstance *h, CGPath path )
 				break;
 		}
 
-		CGI->soundh->stopSound(sh);
+		CCS->soundh->stopSound(sh);
 		cb->recalculatePaths();
 	}
 
@@ -1263,7 +1282,7 @@ void CPlayerInterface::newObject( const CGObjectInstance * obj )
 		&& LOCPLINT->castleInt
 		&&  obj->pos-obj->getVisitableOffset() == LOCPLINT->castleInt->town->bestLocation())
 	{
-		CGI->soundh->playSound(soundBase::newBuilding);
+		CCS->soundh->playSound(soundBase::newBuilding);
 		LOCPLINT->castleInt->recreateBuildings();
 	}
 }
@@ -1316,20 +1335,20 @@ void CPlayerInterface::update()
 	}
 
 	//in some conditions we may receive calls before selection is initialized - we must ignore them
-	if(!adventureInt->selection && GH.topInt() == adventureInt)
+	if(adventureInt && !adventureInt->selection && GH.topInt() == adventureInt)
 		return;
 
 	GH.updateTime();
 	GH.handleEvents();
 
-	if(!adventureInt->isActive() && adventureInt->scrollingDir) //player forces map scrolling though interface is disabled
+	if(adventureInt && !adventureInt->isActive() && adventureInt->scrollingDir) //player forces map scrolling though interface is disabled
 		GH.totalRedraw();
 	else
 		GH.simpleRedraw();
 
-	CGI->curh->draw1();
+	CCS->curh->draw1();
 	CSDL_Ext::update(screen);
-	CGI->curh->draw2();
+	CCS->curh->draw2();
 
 	screenLTmax = Point(conf.cc.resx - screen->w, conf.cc.resy - screen->h);
 
@@ -1860,14 +1879,14 @@ void CPlayerInterface::advmapSpellCast(const CGHeroInstance * caster, int spellI
 void SystemOptions::setMusicVolume( int newVolume )
 {
 	musicVolume = newVolume;
-	CGI->musich->setVolume(newVolume);
+	CCS->musich->setVolume(newVolume);
 	settingsChanged();
 }
 
 void SystemOptions::setSoundVolume( int newVolume )
 {
 	soundVolume = newVolume;
-	CGI->soundh->setVolume(newVolume);
+	CCS->soundh->setVolume(newVolume);
 	settingsChanged();
 }
 
@@ -1895,10 +1914,10 @@ void SystemOptions::settingsChanged()
 
 void SystemOptions::apply()
 {
-	if(CGI->musich->getVolume() != musicVolume)
-		CGI->musich->setVolume(musicVolume);
-	if(CGI->soundh->getVolume() != soundVolume)
-		CGI->soundh->setVolume(soundVolume);
+	if(CCS->musich->getVolume() != musicVolume)
+		CCS->musich->setVolume(musicVolume);
+	if(CCS->soundh->getVolume() != soundVolume)
+		CCS->soundh->setVolume(soundVolume);
 
 	settingsChanged();
 }
@@ -1978,9 +1997,9 @@ void CPlayerInterface::acceptTurn()
 	 * NEWDAY. And we don't play NEWMONTH. */
 	int day = cb->getDate(1);
 	if (day != 1)
-		CGI->soundh->playSound(soundBase::newDay);
+		CCS->soundh->playSound(soundBase::newDay);
 	else
-		CGI->soundh->playSound(soundBase::newWeek);
+		CCS->soundh->playSound(soundBase::newWeek);
 
 	adventureInt->infoBar.newDay(day);
 
@@ -2104,6 +2123,83 @@ void CPlayerInterface::sendCustomEvent( int code )
 	SDL_PushEvent(&event);
 }
 
+void CPlayerInterface::stackChagedCount(const StackLocation &location, const TQuantity &change, bool isAbsolute)
+{
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
+	garrisonChanged(location.army);
+}
+
+void CPlayerInterface::stackChangedType(const StackLocation &location, const CCreature &newType)
+{
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
+	garrisonChanged(location.army);
+}
+
+void CPlayerInterface::stacksErased(const StackLocation &location)
+{
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
+	garrisonChanged(location.army);
+}
+
+void CPlayerInterface::stacksSwapped(const StackLocation &loc1, const StackLocation &loc2)
+{
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
+	garrisonChanged(loc1.army);
+	if(loc2.army != loc1.army)
+		garrisonChanged(loc2.army);
+}
+
+void CPlayerInterface::newStackInserted(const StackLocation &location, const CStackInstance &stack)
+{
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
+	garrisonChanged(location.army);
+}
+
+void CPlayerInterface::stacksRebalanced(const StackLocation &src, const StackLocation &dst, TQuantity count)
+{
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
+	garrisonChanged(src.army);
+	if(dst.army != src.army)
+		garrisonChanged(dst.army);
+}
+
+void CPlayerInterface::artifactPut(const ArtifactLocation &al)
+{
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
+}
+
+void CPlayerInterface::artifactRemoved(const ArtifactLocation &al)
+{
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
+}
+
+void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst)
+{
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
+	BOOST_FOREACH(IShowActivable *isa, GH.listInt)
+		if(isa->type & IShowActivable::WITH_ARTIFACTS)
+			BOOST_FOREACH(CArtifactsOfHero *aoh, (dynamic_cast<CWindowWithArtifacts*>(isa))->artSets)
+				aoh->artifactMoved(src, dst);
+}
+
+void CPlayerInterface::artifactAssembled(const ArtifactLocation &al)
+{
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
+	BOOST_FOREACH(IShowActivable *isa, GH.listInt)
+		if(isa->type & IShowActivable::WITH_ARTIFACTS)
+			BOOST_FOREACH(CArtifactsOfHero *aoh, (dynamic_cast<CWindowWithArtifacts*>(isa))->artSets)
+				aoh->artifactAssembled(al);
+}
+
+void CPlayerInterface::artifactDisassembled(const ArtifactLocation &al)
+{
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
+	BOOST_FOREACH(IShowActivable *isa, GH.listInt)
+		if(isa->type & IShowActivable::WITH_ARTIFACTS)
+			BOOST_FOREACH(CArtifactsOfHero *aoh, (dynamic_cast<CWindowWithArtifacts*>(isa))->artSets)
+				aoh->artifactDisassembled(al);
+}
+
 CPlayerInterface::SpellbookLastSetting::SpellbookLastSetting()
 {
 	spellbookLastPageBattle = spellbokLastPageAdvmap = 0;

+ 73 - 60
client/CPlayerInterface.h

@@ -112,6 +112,8 @@ extern SystemOptions GDefaultOptions; //defined and inited in CMT.cpp, stores de
 class CPlayerInterface : public CGameInterface, public IUpdateable
 {
 public:
+	bool observerInDuelMode;
+
 	//minor interfaces
 	CondSh<bool> *showingDialog; //indicates if dialog box is displayed
 
@@ -156,70 +158,81 @@ public:
 	const CGHeroInstance *getWHero(int pos); //returns NULL if position is not valid
 	int getLastIndex(std::string namePrefix);
 
-	//overloaded funcs from CGameInterface
-	void buildChanged(const CGTownInstance *town, int buildingID, int what); //what: 1 - built, 2 - demolished
-	void garrisonChanged(const CGObjectInstance * obj);
-	void heroArtifactSetChanged(const CGHeroInstance* hero);
-	void heroCreated(const CGHeroInstance* hero);
-	void heroGotLevel(const CGHeroInstance *hero, int pskill, std::vector<ui16> &skills, boost::function<void(ui32)> &callback);
-	void heroInGarrisonChange(const CGTownInstance *town);
-	void heroMoved(const TryMoveHero & details);
-	void heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val);
-	void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val);
-	void heroManaPointsChanged(const CGHeroInstance * hero);
-	void heroMovePointsChanged(const CGHeroInstance * hero);
-	void heroVisitsTown(const CGHeroInstance* hero, const CGTownInstance * town);
-	void receivedResource(int type, int val);
-	void showInfoDialog(const std::string &text, const std::vector<Component*> &components, int soundID);
-	void showRecruitmentDialog(const CGDwelling *dwelling, const CArmedInstance *dst, int level);
-	void showShipyardDialog(const IShipyard *obj); //obj may be town or shipyard; 
-	void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, int soundID, bool selection, bool cancel); //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
-	void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, boost::function<void()> &onEnd);
-	void showArtifactAssemblyDialog(ui32 artifactID, ui32 assembleTo, bool assemble, CFunctionList<void()> onYes, CFunctionList<void()> onNo);
-	void showPuzzleMap();
-	void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor);
-	void showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor);
-	void showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor);
-	void showTavernWindow(const CGObjectInstance *townOrTavern);
-	void advmapSpellCast(const CGHeroInstance * caster, int spellID); //called when a hero casts a spell
-	void tileHidden(const std::set<int3> &pos); //called when given tiles become hidden under fog of war
-	void tileRevealed(const std::set<int3> &pos); //called when fog of war disappears from given tiles
-	void newObject(const CGObjectInstance * obj);
-	void availableArtifactsChanged(const CGBlackMarket *bm = NULL); //bm may be NULL, then artifacts are changed in the global pool (used by merchants in towns)
-	void yourTurn();
-	void availableCreaturesChanged(const CGDwelling *town);
-	void heroBonusChanged(const CGHeroInstance *hero, const Bonus &bonus, bool gain);//if gain hero received bonus, else he lost it
-	void playerBonusChanged(const Bonus &bonus, bool gain);
-	void requestRealized(PackageApplied *pa);
-	void heroExchangeStarted(si32 hero1, si32 hero2);
-	void centerView (int3 pos, int focusTime);
-	void objectPropertyChanged(const SetObjectProperty * sop);
-	void objectRemoved(const CGObjectInstance *obj);
-	void gameOver(ui8 player, bool victory);
-	void serialize(COSer<CSaveFile> &h, const int version); //saving
-	void serialize(CISer<CLoadFile> &h, const int version); //loading
+	//overridden funcs from CGameInterface
+	void buildChanged(const CGTownInstance *town, int buildingID, int what) OVERRIDE; //what: 1 - built, 2 - demolished
+	void stackChagedCount(const StackLocation &location, const TQuantity &change, bool isAbsolute) OVERRIDE; //if absolute, change is the new count; otherwise count was modified by adding change
+	void stackChangedType(const StackLocation &location, const CCreature &newType) OVERRIDE; //used eg. when upgrading creatures
+	void stacksErased(const StackLocation &location) OVERRIDE; //stack removed from previously filled slot
+	void stacksSwapped(const StackLocation &loc1, const StackLocation &loc2) OVERRIDE;
+	void newStackInserted(const StackLocation &location, const CStackInstance &stack) OVERRIDE; //new stack inserted at given (previously empty position)
+	void stacksRebalanced(const StackLocation &src, const StackLocation &dst, TQuantity count) OVERRIDE; //moves creatures from src stack to dst slot, may be used for merging/splittint/moving stacks
+
+	void artifactPut(const ArtifactLocation &al);
+	void artifactRemoved(const ArtifactLocation &al);
+	void artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst);
+	void artifactAssembled(const ArtifactLocation &al);
+	void artifactDisassembled(const ArtifactLocation &al);
+
+	void heroCreated(const CGHeroInstance* hero) OVERRIDE;
+	void heroGotLevel(const CGHeroInstance *hero, int pskill, std::vector<ui16> &skills, boost::function<void(ui32)> &callback) OVERRIDE;
+	void heroInGarrisonChange(const CGTownInstance *town) OVERRIDE;
+	void heroMoved(const TryMoveHero & details) OVERRIDE;
+	void heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val) OVERRIDE;
+	void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) OVERRIDE;
+	void heroManaPointsChanged(const CGHeroInstance * hero) OVERRIDE;
+	void heroMovePointsChanged(const CGHeroInstance * hero) OVERRIDE;
+	void heroVisitsTown(const CGHeroInstance* hero, const CGTownInstance * town) OVERRIDE;
+	void receivedResource(int type, int val) OVERRIDE;
+	void showInfoDialog(const std::string &text, const std::vector<Component*> &components, int soundID) OVERRIDE;
+	void showRecruitmentDialog(const CGDwelling *dwelling, const CArmedInstance *dst, int level) OVERRIDE;
+	void showShipyardDialog(const IShipyard *obj) OVERRIDE; //obj may be town or shipyard; 
+	void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, int soundID, bool selection, bool cancel) OVERRIDE; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
+	void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, boost::function<void()> &onEnd) OVERRIDE;
+	void showPuzzleMap() OVERRIDE;
+	void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor) OVERRIDE;
+	void showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor) OVERRIDE;
+	void showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor) OVERRIDE;
+	void showTavernWindow(const CGObjectInstance *townOrTavern) OVERRIDE;
+	void advmapSpellCast(const CGHeroInstance * caster, int spellID) OVERRIDE; //called when a hero casts a spell
+	void tileHidden(const boost::unordered_set<int3, ShashInt3> &pos) OVERRIDE; //called when given tiles become hidden under fog of war
+	void tileRevealed(const boost::unordered_set<int3, ShashInt3> &pos) OVERRIDE; //called when fog of war disappears from given tiles
+	void newObject(const CGObjectInstance * obj) OVERRIDE;
+	void availableArtifactsChanged(const CGBlackMarket *bm = NULL) OVERRIDE; //bm may be NULL, then artifacts are changed in the global pool (used by merchants in towns)
+	void yourTurn() OVERRIDE;
+	void availableCreaturesChanged(const CGDwelling *town) OVERRIDE;
+	void heroBonusChanged(const CGHeroInstance *hero, const Bonus &bonus, bool gain) OVERRIDE;//if gain hero received bonus, else he lost it
+	void playerBonusChanged(const Bonus &bonus, bool gain) OVERRIDE;
+	void requestRealized(PackageApplied *pa) OVERRIDE;
+	void heroExchangeStarted(si32 hero1, si32 hero2) OVERRIDE;
+	void centerView (int3 pos, int focusTime) OVERRIDE;
+	void objectPropertyChanged(const SetObjectProperty * sop) OVERRIDE;
+	void objectRemoved(const CGObjectInstance *obj) OVERRIDE;
+	void gameOver(ui8 player, bool victory) OVERRIDE;
+	void serialize(COSer<CSaveFile> &h, const int version) OVERRIDE; //saving
+	void serialize(CISer<CLoadFile> &h, const int version) OVERRIDE; //loading
 
 	//for battles
-	void actionFinished(const BattleAction* action);//occurs AFTER action taken by active stack or by the hero
-	void actionStarted(const BattleAction* action);//occurs BEFORE action taken by active stack or by the hero
-	BattleAction activeStack(int stackID); //called when it's turn of that stack
-	void battleAttack(const BattleAttack *ba); //stack performs attack
-	void battleEnd(const BattleResult *br); //end of battle
-	//void battleResultQuited();
-	void battleNewRoundFirst(int round); //called at the beginning of each turn before changes are applied; used for HP regen handling
-	void battleNewRound(int round); //called at the beggining of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
-	void battleStackMoved(int ID, int dest, int distance, bool end);
-	void battleSpellCast(const BattleSpellCast *sc);
-	void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks
-	void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa);
-	void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side); //called by engine when battle starts; side=0 - left, side=1 - right
-	void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, si32 lifeDrainFrom); //called when stacks are healed / resurrected
-	void battleNewStackAppeared(int stackID); //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned
-	void battleObstaclesRemoved(const std::set<si32> & removedObstacles); //called when a certain set  of obstacles is removed from batlefield; IDs of them are given
-	void battleCatapultAttacked(const CatapultAttack & ca); //called when catapult makes an attack
-	void battleStacksRemoved(const BattleStacksRemoved & bsr); //called when certain stack is completely removed from battlefield
+	void actionFinished(const BattleAction* action) OVERRIDE;//occurs AFTER action taken by active stack or by the hero
+	void actionStarted(const BattleAction* action) OVERRIDE;//occurs BEFORE action taken by active stack or by the hero
+	BattleAction activeStack(const CStack * stack) OVERRIDE; //called when it's turn of that stack
+	void battleAttack(const BattleAttack *ba) OVERRIDE; //stack performs attack
+	void battleEnd(const BattleResult *br) OVERRIDE; //end of battle
+	void battleNewRoundFirst(int round) OVERRIDE; //called at the beginning of each turn before changes are applied; used for HP regen handling
+	void battleNewRound(int round) OVERRIDE; //called at the beggining of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
+	void battleStackMoved(const CStack * stack, THex dest, int distance, bool end) OVERRIDE;
+	void battleSpellCast(const BattleSpellCast *sc) OVERRIDE;
+	void battleStacksEffectsSet(const SetStackEffect & sse) OVERRIDE; //called when a specific effect is set to stacks
+	void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa) OVERRIDE;
+	void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) OVERRIDE; //called by engine when battle starts; side=0 - left, side=1 - right
+	void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, si32 lifeDrainFrom) OVERRIDE; //called when stacks are healed / resurrected
+	void battleNewStackAppeared(const CStack * stack) OVERRIDE; //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned
+	void battleObstaclesRemoved(const std::set<si32> & removedObstacles) OVERRIDE; //called when a certain set  of obstacles is removed from batlefield; IDs of them are given
+	void battleCatapultAttacked(const CatapultAttack & ca) OVERRIDE; //called when catapult makes an attack
+	void battleStacksRemoved(const BattleStacksRemoved & bsr) OVERRIDE; //called when certain stack is completely removed from battlefield
 
 	//-------------//
+	void showArtifactAssemblyDialog(ui32 artifactID, ui32 assembleTo, bool assemble, CFunctionList<void()> onYes, CFunctionList<void()> onNo);
+	void garrisonChanged(const CGObjectInstance * obj);
 	void heroKilled(const CGHeroInstance* hero);
 	void waitWhileDialog();
 	bool shiftPressed() const; //determines if shift key is pressed (left or right or both)

+ 23 - 23
client/CPreGame.cpp

@@ -10,17 +10,17 @@
 #include "CGameInfo.h"
 #include "CCursorHandler.h"
 #include "CAnimation.h"
-#include "../hch/CDefHandler.h"
-#include "../hch/CDefObjInfoHandler.h"
-#include "../hch/CGeneralTextHandler.h"
-#include "../hch/CLodHandler.h"
-#include "../hch/CTownHandler.h"
-#include "../hch/CHeroHandler.h"
-#include "../hch/CObjectHandler.h"
-#include "../hch/CCampaignHandler.h"
-#include "../hch/CCreatureHandler.h"
-#include "../hch/CMusicHandler.h"
-#include "../hch/CVideoHandler.h"
+#include "CDefHandler.h"
+#include "../lib/CDefObjInfoHandler.h"
+#include "../lib/CGeneralTextHandler.h"
+#include "../lib/CLodHandler.h"
+#include "../lib/CTownHandler.h"
+#include "../lib/CHeroHandler.h"
+#include "../lib/CObjectHandler.h"
+#include "../lib/CCampaignHandler.h"
+#include "../lib/CCreatureHandler.h"
+#include "CMusicHandler.h"
+#include "CVideoHandler.h"
 #include <cmath>
 #include "Graphics.h"
 //#include <boost/thread.hpp>
@@ -36,9 +36,9 @@
 #include <boost/lexical_cast.hpp>
 #include <cstdlib>
 #include "CMessage.h"
-#include "../hch/CSpellHandler.h" /*for campaign bonuses*/
-#include "../hch/CArtHandler.h" /*for campaign bonuses*/
-#include "../hch/CBuildingHandler.h" /*for campaign bonuses*/
+#include "../lib/CSpellHandler.h" /*for campaign bonuses*/
+#include "../lib/CArtHandler.h" /*for campaign bonuses*/
+#include "../lib/CBuildingHandler.h" /*for campaign bonuses*/
 #include "CBitmapHandler.h"
 #include "Client.h"
 #include "../lib/NetPacks.h"
@@ -271,7 +271,7 @@ void CMenuScreen::showAll( SDL_Surface * to )
 void CMenuScreen::show( SDL_Surface * to )
 {
 	CIntObject::show(to);
-	CGI->videoh->update(pos.x + 8, pos.y + 105, screen, true, false);
+	CCS->videoh->update(pos.x + 8, pos.y + 105, screen, true, false);
 }
 
 void CMenuScreen::moveTo( CMenuScreen *next )
@@ -328,11 +328,11 @@ void CGPreGame::update()
 {
 	if (GH.listInt.size() == 0)
 	{
-		CGI->musich->playMusic(musicBase::mainMenu, -1);
+		CCS->musich->playMusic(musicBase::mainMenu, -1);
 	#ifdef _WIN32
-		CGI->videoh->open("ACREDIT.SMK");
+		CCS->videoh->open("ACREDIT.SMK");
 	#else
-		CGI->videoh->open("ACREDIT.SMK", true, false);
+		CCS->videoh->open("ACREDIT.SMK", true, false);
 	#endif
 		GH.pushInt(scrs[CMenuScreen::mainMenu]);
 	}
@@ -340,9 +340,9 @@ void CGPreGame::update()
 	if(SEL)
 		SEL->update();
 
-	CGI->curh->draw1();
+	CCS->curh->draw1();
 	SDL_Flip(screen);
-	CGI->curh->draw2();
+	CCS->curh->draw2();
 	screenLTmax = Point(800 - screen->w, 600 - screen->h);
 	GH.topInt()->show(screen);
 	GH.updateTime();
@@ -2857,7 +2857,7 @@ void CBonusSelection::updateBonusSelection()
 			case 0: //spell
 				surfToDuplicate = de->ourImages[bonDescs[i].info2].bitmap;
 				desc = CGI->generaltexth->allTexts[715];
-				boost::algorithm::replace_first(desc, "%s", CGI->spellh->spells[bonDescs[i].info2].name);
+				boost::algorithm::replace_first(desc, "%s", CGI->spellh->spells[bonDescs[i].info2]->name);
 				break;
 			case 1: //monster
 				surfToDuplicate = de->ourImages[bonDescs[i].info2 + 2].bitmap;
@@ -2880,7 +2880,7 @@ void CBonusSelection::updateBonusSelection()
 					}
 					assert(faction != -1);
 
-					std::string bldgBitmapName = CGI->buildh->ERMUtoPicture[faction][CBuildingHandler::campToERMU(bonDescs[i].info1, faction, std::set<si32>())];
+					std::string bldgBitmapName = graphics->ERMUtoPicture[faction][CBuildingHandler::campToERMU(bonDescs[i].info1, faction, std::set<si32>())];
 					surfToDuplicate = BitmapHandler::loadBitmap(bldgBitmapName);
 
 					freeDuplicatedSurface = true;
@@ -2894,7 +2894,7 @@ void CBonusSelection::updateBonusSelection()
 			case 4: //spell scroll
 				surfToDuplicate = de->ourImages[bonDescs[i].info2].bitmap;
 				desc = CGI->generaltexth->allTexts[716];
-				boost::algorithm::replace_first(desc, "%s", CGI->spellh->spells[bonDescs[i].info2].name);
+				boost::algorithm::replace_first(desc, "%s", CGI->spellh->spells[bonDescs[i].info2]->name);
 				break;
 			case 5: //primary skill
 				{

+ 7 - 7
hch/CSndHandler.cpp → client/CSndHandler.cpp

@@ -108,7 +108,7 @@ const char * CMediaHandler::extract (std::string srcName, int &size)
 {
 	int index;
 	srcName.erase(srcName.find_last_of('.'));
-	
+
 	std::map<std::string, int>::iterator fit;
 	if ((fit = fimap.find(srcName)) != fimap.end())
 	{
@@ -128,15 +128,15 @@ CSndHandler::CSndHandler(std::string fname) : CMediaHandler(fname)
 	for (unsigned int i=0; i<numFiles; i++, se++)
 	{
 		Entry entry;
-//		char *p;
+		//		char *p;
 
 		// Reassemble the filename, drop extension
 		entry.name = se->filename;
-//		entry.name += '.';
-//		p = se->filename;
-//		while(*p) p++;
-//		p++;
-//		entry.name += p;
+		//		entry.name += '.';
+		//		p = se->filename;
+		//		while(*p) p++;
+		//		p++;
+		//		entry.name += p;
 
 		entry.offset = SDL_SwapLE32(se->offset);
 		entry.size = SDL_SwapLE32(se->size);

+ 0 - 0
hch/CSndHandler.h → client/CSndHandler.h


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


+ 24 - 24
client/CSpellWindow.cpp

@@ -1,10 +1,10 @@
 #include "CSpellWindow.h"
 #include "Graphics.h"
-#include "../hch/CDefHandler.h"
-#include "../hch/CObjectHandler.h"
-#include "../hch/CSpellHandler.h"
-#include "../hch/CGeneralTextHandler.h"
-#include "../hch/CVideoHandler.h"
+#include "CDefHandler.h"
+#include "../lib/CObjectHandler.h"
+#include "../lib/CSpellHandler.h"
+#include "../lib/CGeneralTextHandler.h"
+#include "CVideoHandler.h"
 #include "CAdvmapInterface.h"
 #include "CBattleInterface.h"
 #include "CGameInfo.h"
@@ -79,7 +79,7 @@ CSpellWindow::CSpellWindow(const SDL_Rect & myRect, const CGHeroInstance * _myHe
 	//initializing castable spells
 	for(ui32 v=0; v<CGI->spellh->spells.size(); ++v)
 	{
-		if( !CGI->spellh->spells[v].creatureAbility && myHero->canCastThisSpell(&CGI->spellh->spells[v]) )
+		if( !CGI->spellh->spells[v]->creatureAbility && myHero->canCastThisSpell(CGI->spellh->spells[v]) )
 			mySpells.insert(v);
 	}
 
@@ -91,7 +91,7 @@ CSpellWindow::CSpellWindow(const SDL_Rect & myRect, const CGHeroInstance * _myHe
 
 	for(std::set<ui32>::const_iterator g = mySpells.begin(); g!=mySpells.end(); ++g)
 	{
-		const CSpell &s = CGI->spellh->spells[*g];
+		const CSpell &s = *CGI->spellh->spells[*g];
 		Uint8 *sitesPerOurTab = s.combatSpell ? sitesPerTabBattle : sitesPerTabAdv;
 
 		++sitesPerOurTab[4];
@@ -355,8 +355,8 @@ class SpellbookSpellSorter
 public:
 	bool operator()(const int & a, const int & b)
 	{
-		CSpell A = CGI->spellh->spells[a];
-		CSpell B = CGI->spellh->spells[b];
+		const CSpell & A = *CGI->spellh->spells[a];
+		const CSpell & B = *CGI->spellh->spells[b];
 		if(A.level<B.level)
 			return true;
 		if(A.level>B.level)
@@ -386,11 +386,11 @@ void CSpellWindow::computeSpellsPerArea()
 	std::vector<ui32> spellsCurSite;
 	for(std::set<ui32>::const_iterator it = mySpells.begin(); it != mySpells.end(); ++it)
 	{
-		if(CGI->spellh->spells[*it].combatSpell ^ !battleSpellsOnly
-			&& ((CGI->spellh->spells[*it].air && selectedTab == 0) || 
-				(CGI->spellh->spells[*it].fire && selectedTab == 1) ||
-				(CGI->spellh->spells[*it].water && selectedTab == 2) ||
-				(CGI->spellh->spells[*it].earth && selectedTab == 3) ||
+		if(CGI->spellh->spells[*it]->combatSpell ^ !battleSpellsOnly
+			&& ((CGI->spellh->spells[*it]->air && selectedTab == 0) || 
+				(CGI->spellh->spells[*it]->fire && selectedTab == 1) ||
+				(CGI->spellh->spells[*it]->water && selectedTab == 2) ||
+				(CGI->spellh->spells[*it]->earth && selectedTab == 3) ||
 				selectedTab == 4 )
 			)
 		{
@@ -505,12 +505,12 @@ void CSpellWindow::deactivate()
 
 void CSpellWindow::turnPageLeft()
 {
-	CGI->videoh->openAndPlayVideo("PGTRNLFT.SMK", pos.x+13, pos.y+15, screen);
+	CCS->videoh->openAndPlayVideo("PGTRNLFT.SMK", pos.x+13, pos.y+15, screen);
 }
 
 void CSpellWindow::turnPageRight()
 {
-	CGI->videoh->openAndPlayVideo("PGTRNRGH.SMK", pos.x+13, pos.y+15, screen);
+	CCS->videoh->openAndPlayVideo("PGTRNRGH.SMK", pos.x+13, pos.y+15, screen);
 }
 
 void CSpellWindow::keyPressed(const SDL_KeyboardEvent & key)
@@ -594,7 +594,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 {
 	if(!down && mySpell!=-1)
 	{
-		const CSpell *sp = &CGI->spellh->spells[mySpell];
+		const CSpell *sp = CGI->spellh->spells[mySpell];
 
 		int spellCost = owner->myInt->cb->getSpellCost(sp, owner->myHero);
 		if(spellCost > owner->myHero->mana) //insufficient mana
@@ -661,7 +661,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 						return;
 					}
 
-					if (h->getSpellSchoolLevel(&CGI->spellh->spells[spell]) < 2) //not advanced or expert - teleport to nearest available city
+					if (h->getSpellSchoolLevel(CGI->spellh->spells[spell]) < 2) //not advanced or expert - teleport to nearest available city
 					{
 						int nearest = -1; //nearest town's ID
 						double dist = -1;
@@ -720,7 +720,7 @@ void CSpellWindow::SpellArea::clickRight(tribool down, bool previousState)
 	{
 		std::string dmgInfo;
 		const CGHeroInstance * hero = owner->myHero;
-		int causedDmg = owner->myInt->cb->estimateSpellDamage( &CGI->spellh->spells[mySpell], (hero ? hero : NULL));
+		int causedDmg = owner->myInt->cb->estimateSpellDamage( CGI->spellh->spells[mySpell], (hero ? hero : NULL));
 		if(causedDmg == 0)
 			dmgInfo = "";
 		else
@@ -731,8 +731,8 @@ void CSpellWindow::SpellArea::clickRight(tribool down, bool previousState)
 
 		SDL_Surface *spellBox = CMessage::drawBoxTextBitmapSub(
 			owner->myInt->playerID,
-			CGI->spellh->spells[mySpell].descriptions[schoolLevel] + dmgInfo, this->owner->spells->ourImages[mySpell].bitmap,
-			CGI->spellh->spells[mySpell].name,30,30);
+			CGI->spellh->spells[mySpell]->descriptions[schoolLevel] + dmgInfo, this->owner->spells->ourImages[mySpell].bitmap,
+			CGI->spellh->spells[mySpell]->name,30,30);
 		CInfoPopup *vinya = new CInfoPopup(spellBox, true);
 		GH.pushInt(vinya);
 	}
@@ -746,7 +746,7 @@ void CSpellWindow::SpellArea::hover(bool on)
 		if(on)
 		{
 			std::ostringstream ss;
-			ss<<CGI->spellh->spells[mySpell].name<<" ("<<CGI->generaltexth->allTexts[171+CGI->spellh->spells[mySpell].level]<<")";
+			ss<<CGI->spellh->spells[mySpell]->name<<" ("<<CGI->generaltexth->allTexts[171+CGI->spellh->spells[mySpell]->level]<<")";
 			owner->statusBar->print(ss.str());
 		}
 		else
@@ -761,7 +761,7 @@ void CSpellWindow::SpellArea::showAll(SDL_Surface *to)
 	if(mySpell < 0)
 		return;
 
-	const CSpell * spell = &CGI->spellh->spells[mySpell];
+	const CSpell * spell = CGI->spellh->spells[mySpell];
 
 	blitAt(owner->spells->ourImages[mySpell].bitmap, pos.x, pos.y, to);
 	blitAt(owner->schoolBorders[owner->selectedTab >= 4 ? whichSchool : owner->selectedTab]->ourImages[schoolLevel].bitmap, pos.x, pos.y, to); //printing border (indicates level of magic school)
@@ -794,7 +794,7 @@ void CSpellWindow::SpellArea::setSpell(int spellID)
 	if(mySpell < 0)
 		return;
 
-	const CSpell * spell = &CGI->spellh->spells[mySpell];
+	const CSpell * spell = CGI->spellh->spells[mySpell];
 	schoolLevel = owner->myHero->getSpellSchoolLevel(spell, &whichSchool);
 	spellCost = owner->myInt->cb->getSpellCost(spell, owner->myHero);
 }

+ 2 - 0
hch/CVideoHandler.cpp → client/CVideoHandler.cpp

@@ -941,3 +941,5 @@ CVideoPlayer::~CVideoPlayer()
 }
 
 #endif
+
+ 	  	 

+ 0 - 0
hch/CVideoHandler.h → client/CVideoHandler.h


+ 97 - 42
client/Client.cpp

@@ -1,19 +1,20 @@
-#include "../hch/CMusicHandler.h"
-#include "../hch/CCampaignHandler.h"
+#include "CMusicHandler.h"
+#include "../lib/CCampaignHandler.h"
 #include "../CCallback.h"
 #include "../CConsoleHandler.h"
 #include "CGameInfo.h"
 #include "../lib/CGameState.h"
 #include "CPlayerInterface.h"
 #include "../StartInfo.h"
-#include "../hch/CArtHandler.h"
-#include "../hch/CDefObjInfoHandler.h"
-#include "../hch/CGeneralTextHandler.h"
-#include "../hch/CHeroHandler.h"
-#include "../hch/CTownHandler.h"
-#include "../hch/CObjectHandler.h"
-#include "../hch/CBuildingHandler.h"
-#include "../hch/CSpellHandler.h"
+#include "../lib/BattleState.h"
+#include "../lib/CArtHandler.h"
+#include "../lib/CDefObjInfoHandler.h"
+#include "../lib/CGeneralTextHandler.h"
+#include "../lib/CHeroHandler.h"
+#include "../lib/CTownHandler.h"
+#include "../lib/CObjectHandler.h"
+#include "../lib/CBuildingHandler.h"
+#include "../lib/CSpellHandler.h"
 #include "../lib/Connection.h"
 #include "../lib/Interprocess.h"
 #include "../lib/NetPacks.h"
@@ -33,6 +34,7 @@
 
 #define NOT_LIB
 #include "../lib/RegisterTypes.cpp"
+#include "CBattleInterface.h"
 extern std::string NAME;
 namespace intpr = boost::interprocess;
 
@@ -115,7 +117,8 @@ void CClient::waitForMoveAndSend(int color)
 {
 	try
 	{
-		BattleAction ba = playerint[color]->activeStack(gs->curB->activeStack);
+		assert(vstd::contains(battleints, color));
+		BattleAction ba = battleints[color]->activeStack(gs->curB->getStack(gs->curB->activeStack, false));
 		*serv << &MakeAction(ba);
 		return;
 	}HANDLE_EXCEPTION
@@ -173,7 +176,7 @@ void initVillagesCapitols(Mapa * map)
 	ifs>>ccc;
 	for(int i=0;i<ccc*2;i++)
 	{
-		CGDefInfo *n;
+		const CGDefInfo *n;
 		if(i<ccc)
 		{
 			n = CGI->state->villages[i];
@@ -182,11 +185,11 @@ void initVillagesCapitols(Mapa * map)
 		else 
 			n = CGI->state->capitols[i%ccc];
 
-		ifs >> n->name;
+		ifs >> const_cast<CGDefInfo*>(n)->name;
 		if(!n)
 			tlog1 << "*HUGE* Warning - missing town def for " << i << std::endl;
 		else
-			map->defy.push_back(n);
+			map->defy.push_back(const_cast<CGDefInfo*>(n));
 	}
 }
 
@@ -207,10 +210,9 @@ void CClient::endGame( bool closeConnection /*= true*/ )
 	tlog0 << "Removed GUI." << std::endl;
 
 	delete CGI->mh;
-	CGI->mh = NULL;
+	const_cast<CGameInfo*>(CGI)->mh = NULL;
 
-	delete CGI->state;
-	CGI->state = NULL;
+	const_cast<CGameInfo*>(CGI)->state.dellNull();
 	tlog0 << "Deleted mapHandler and gameState." << std::endl;
 
 	CPlayerInterface * oldInt = LOCPLINT;
@@ -246,7 +248,7 @@ void CClient::loadGame( const std::string & fname )
 	{
 		char sig[8];
 		CMapHeader dum;
-		CGI->mh = new CMapHandler();
+		const_cast<CGameInfo*>(CGI)->mh = new CMapHandler();
 		StartInfo *si;
 
 		CLoadFile lf(fname + ".vlgm1");
@@ -254,14 +256,14 @@ void CClient::loadGame( const std::string & fname )
 		tlog0 <<"Reading save signature: "<<tmh.getDif()<<std::endl;
 		
 		lf >> *VLC;
-		CGI->setFromLib();
+		const_cast<CGameInfo*>(CGI)->setFromLib();
 		tlog0 <<"Reading handlers: "<<tmh.getDif()<<std::endl;
 
 		lf >> gs;
 		tlog0 <<"Reading gamestate: "<<tmh.getDif()<<std::endl;
 
-		CGI->state = gs;
-		CGI->mh->map = gs->map;
+		const_cast<CGameInfo*>(CGI)->state = gs;
+		const_cast<CGameInfo*>(CGI)->mh->map = gs->map;
 		pathInfo = new CPathsInfo(int3(gs->map->width, gs->map->height, gs->map->twoLevel+1));
 		CGI->mh->init();
 		initVillagesCapitols(gs->map);
@@ -341,7 +343,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 
 
 	timeHandler tmh;
-	CGI->state = new CGameState();
+	const_cast<CGameInfo*>(CGI)->state = new CGameState();
 	tlog0 <<"\tGamestate: "<<tmh.getDif()<<std::endl;
 	CConnection &c(*serv);
 	////////////////////////////////////////////////////
@@ -366,46 +368,75 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 	tlog0 <<"\tSending/Getting info to/from the server: "<<tmh.getDif()<<std::endl;
 	tlog0 << "\tUsing random seed: "<<seed << std::endl;
 
-	gs = CGI->state;
+	gs = const_cast<CGameInfo*>(CGI)->state;
 	gs->scenarioOps = si;
 	gs->init(si, sum, seed);
-
-	CGI->mh = new CMapHandler();
 	tlog0 <<"Initializing GameState (together): "<<tmh.getDif()<<std::endl;
-	CGI->mh->map = gs->map;
-	tlog0 <<"Creating mapHandler: "<<tmh.getDif()<<std::endl;
-	CGI->mh->init();
-	initVillagesCapitols(gs->map);
-	pathInfo = new CPathsInfo(int3(gs->map->width, gs->map->height, gs->map->twoLevel+1));
-	tlog0 <<"Initializing mapHandler (together): "<<tmh.getDif()<<std::endl;
+
+	if(gs->map)
+	{
+		const_cast<CGameInfo*>(CGI)->mh = new CMapHandler();
+		CGI->mh->map = gs->map;
+		tlog0 <<"Creating mapHandler: "<<tmh.getDif()<<std::endl;
+		CGI->mh->init();
+		initVillagesCapitols(gs->map);
+		pathInfo = new CPathsInfo(int3(gs->map->width, gs->map->height, gs->map->twoLevel+1));
+		tlog0 <<"Initializing mapHandler (together): "<<tmh.getDif()<<std::endl;
+	}
 
 	int humanPlayers = 0;
 	for(std::map<int, PlayerSettings>::iterator it = gs->scenarioOps->playerInfos.begin(); 
 		it != gs->scenarioOps->playerInfos.end(); ++it)//initializing interfaces for players
 	{ 
 		ui8 color = it->first;
+		gs->currentPlayer = color;
 		if(!vstd::contains(myPlayers, color))
 			continue;
 
-		CCallback *cb = new CCallback(gs,color,this);
-		if(!it->second.human) 
+		if(si->mode != StartInfo::DUEL)
 		{
-			playerint[color] = static_cast<CGameInterface*>(CAIHandler::getNewAI(cb,conf.cc.defaultAI));
+			CCallback *cb = new CCallback(gs,color,this);
+			if(!it->second.human) 
+			{
+				playerint[color] = static_cast<CGameInterface*>(CAIHandler::getNewAI(cb,conf.cc.defaultAI));
+			}
+			else 
+			{
+				playerint[color] = new CPlayerInterface(color);
+				humanPlayers++;
+			}
+			battleints[color] = playerint[color];
+
+			playerint[color]->init(cb);
 		}
-		else 
+		else
 		{
-			playerint[color] = new CPlayerInterface(color);
-			humanPlayers++;
+			CBattleCallback * cbc = new CBattleCallback(gs, color, this);
+			battleints[color] = CAIHandler::getNewBattleAI(cb,"StupidAI");
+			battleints[color]->init(cbc);
 		}
-		gs->currentPlayer = color;
-		playerint[color]->init(cb);
 	}
 
-	serv->addStdVecItems(CGI->state);
+	if(si->mode == StartInfo::DUEL)
+	{
+		CPlayerInterface *p = new CPlayerInterface(-1);
+		p->observerInDuelMode = true;
+		battleints[254] = playerint[254] = p;
+		GH.curInt = p;
+		p->init(new CCallback(gs, -1, this));
+		battleStarted(gs->curB);
+	}
+	else
+	{
+		playerint[255] =  CAIHandler::getNewAI(cb,conf.cc.defaultAI);
+		playerint[255]->init(new CCallback(gs,255,this));
+		battleints[255] = playerint[255];
+	}
+
+	serv->addStdVecItems(const_cast<CGameInfo*>(CGI)->state);
 	hotSeat = (humanPlayers > 1);
 
-	playerint[255] =  CAIHandler::getNewAI(cb,conf.cc.defaultAI);
-	playerint[255]->init(new CCallback(gs,255,this));
+
 }
 
 template <typename Handler>
@@ -519,6 +550,30 @@ void CClient::stopConnection()
 	}
 }
 
+void CClient::battleStarted(const BattleInfo * info)
+{
+	CPlayerInterface * att, * def;
+	if(vstd::contains(playerint, info->sides[0]) && playerint[info->sides[0]]->human)
+		att = static_cast<CPlayerInterface*>( playerint[info->sides[0]] );
+	else
+		att = NULL;
+
+	if(vstd::contains(playerint, info->sides[1]) && playerint[info->sides[1]]->human)
+		def = static_cast<CPlayerInterface*>( playerint[info->sides[1]] );
+	else
+		def = NULL;
+
+
+	new CBattleInterface(info->belligerents[0], info->belligerents[1], info->heroes[0], info->heroes[1], Rect((conf.cc.resx - 800)/2, (conf.cc.resy - 600)/2, 800, 600), att, def);
+
+	if(vstd::contains(battleints,info->sides[0]))
+		battleints[info->sides[0]]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 0);
+	if(vstd::contains(battleints,info->sides[1]))
+		battleints[info->sides[1]]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 1);
+	if(vstd::contains(battleints,254))
+		battleints[254]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 1);
+}
+
 template void CClient::serialize( CISer<CLoadFile> &h, const int version );
 template void CClient::serialize( COSer<CSaveFile> &h, const int version );
 

+ 52 - 35
client/Client.h

@@ -18,6 +18,7 @@
  *
  */
 
+class CBattleGameInterface;
 struct StartInfo;
 class CGameState;
 class CGameInterface;
@@ -61,6 +62,7 @@ public:
 	CCallback *cb;
 	std::set<CCallback*> callbacks; //callbacks given to player interfaces
 	std::map<ui8,CGameInterface *> playerint;
+	std::map<ui8,CBattleGameInterface *> battleints;
 	bool hotSeat;
 	CConnection *serv;
 	BattleAction *curbaction;
@@ -96,41 +98,55 @@ public:
 	int getSelectedHero();
 
 	//not working yet, will be implement somewhen later with support for local-sim-based gameplay
-	void changeSpells(int hid, bool give, const std::set<ui32> &spells){};
-	bool removeObject(int objid){return false;};
-	void setBlockVis(int objid, bool bv){};
-	void setOwner(int objid, ui8 owner){};
-	void setHoverName(int objid, MetaString * name){};
-	void setObjProperty(int objid, int prop, si64 val){};
-	void changePrimSkill(int ID, int which, si64 val, bool abs=false){};
-	void changeSecSkill(int ID, int which, int val, bool abs=false){}; 
-	void showInfoDialog(InfoWindow *iw){};
-	void showBlockingDialog(BlockingDialog *iw, const CFunctionList<void(ui32)> &callback){};
-	ui32 showBlockingDialog(BlockingDialog *iw){return 0;}; //synchronous version of above
-	void showGarrisonDialog(int upobj, int hid, bool removableUnits, const boost::function<void()> &cb){};
-	void showThievesGuildWindow(int requestingObjId){};
-	void giveResource(int player, int which, int val){};
-	void giveCreatures (int objid, const CGHeroInstance * h, CCreatureSet creatures, bool remove) {};
-	void takeCreatures (int objid, TSlots creatures){};
-	void changeCreatureType (int objid, TSlot slot, TCreature creature){};
-	void showCompInfo(ShowInInfobox * comp){};
-	void heroVisitCastle(int obj, int heroID){};
-	void stopHeroVisitCastle(int obj, int heroID){};
-	void giveHeroArtifact(int artid, int hid, int position){}; //pos==-1 - first free slot in backpack=0; pos==-2 - default if available or backpack
-	void giveNewArtifact(int hid, int position){};
-	bool removeArtifact(const CArtifact* art, int hid){return false;};
-	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, boost::function<void(BattleResult*)> cb = 0, const CGTownInstance *town = NULL){}; //use hero=NULL for no hero
-	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false){}; //if any of armies is hero, hero will be used
-	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false){}; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
-	void setAmount(int objid, ui32 val){};
-	bool moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker = 255){return false;};
-	void giveHeroBonus(GiveBonus * bonus){};
-	void setMovePoints(SetMovePoints * smp){};
-	void setManaPoints(int hid, int val){};
-	void giveHero(int id, int player){};
-	void changeObjPos(int objid, int3 newPos, ui8 flags){};
-	void sendAndApply(CPackForClient * info){};
-	void heroExchange(si32 hero1, si32 hero2){};
+	void changeSpells(int hid, bool give, const std::set<ui32> &spells) OVERRIDE {};
+	bool removeObject(int objid) OVERRIDE {return false;};
+	void setBlockVis(int objid, bool bv) OVERRIDE {};
+	void setOwner(int objid, ui8 owner) OVERRIDE {};
+	void setHoverName(int objid, MetaString * name) OVERRIDE {};
+	void setObjProperty(int objid, int prop, si64 val) OVERRIDE {};
+	void changePrimSkill(int ID, int which, si64 val, bool abs=false) OVERRIDE {};
+	void changeSecSkill(int ID, int which, int val, bool abs=false) OVERRIDE {}; 
+	void showInfoDialog(InfoWindow *iw) OVERRIDE {};
+	void showBlockingDialog(BlockingDialog *iw, const CFunctionList<void(ui32)> &callback) OVERRIDE {};
+	ui32 showBlockingDialog(BlockingDialog *iw) OVERRIDE {return 0;}; //synchronous version of above
+	void showGarrisonDialog(int upobj, int hid, bool removableUnits, const boost::function<void()> &cb) OVERRIDE {};
+	void showThievesGuildWindow(int requestingObjId) OVERRIDE {};
+	void giveResource(int player, int which, int val) OVERRIDE {};
+
+	void giveCreatures (const CArmedInstance * objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) OVERRIDE {};
+	void takeCreatures (int objid, std::vector<CStackBasicDescriptor> creatures) OVERRIDE {};
+	bool changeStackType(const StackLocation &sl, CCreature *c) OVERRIDE {return false;};
+	bool changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue = false) OVERRIDE {return false;};
+	bool insertNewStack(const StackLocation &sl, const CCreature *c, TQuantity count) OVERRIDE {return false;};
+	bool eraseStack(const StackLocation &sl, bool forceRemoval = false){return false;};
+	bool swapStacks(const StackLocation &sl1, const StackLocation &sl2) OVERRIDE {return false;}
+	bool addToSlot(const StackLocation &sl, const CCreature *c, TQuantity count) OVERRIDE {return false;}
+	void tryJoiningArmy(const CArmedInstance *src, const CArmedInstance *dst, bool removeObjWhenFinished, bool allowMerging) OVERRIDE {}
+	bool moveStack(const StackLocation &src, const StackLocation &dst, TQuantity count = -1) OVERRIDE {return false;}
+
+	void giveHeroNewArtifact(const CGHeroInstance *h, const CArtifact *artType, int pos) OVERRIDE {};
+	void giveHeroArtifact(const CGHeroInstance *h, const CArtifactInstance *a, int pos) OVERRIDE {};
+	void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) OVERRIDE {}; 
+	void removeArtifact(const ArtifactLocation &al) OVERRIDE {};
+	void moveArtifact(const ArtifactLocation &al1, const ArtifactLocation &al2) OVERRIDE {};
+
+	void showCompInfo(ShowInInfobox * comp) OVERRIDE {};
+	void heroVisitCastle(int obj, int heroID) OVERRIDE {};
+	void stopHeroVisitCastle(int obj, int heroID) OVERRIDE {};
+	//void giveHeroArtifact(int artid, int hid, int position){}; 
+	//void giveNewArtifact(int hid, int position){};
+	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, boost::function<void(BattleResult*)> cb = 0, const CGTownInstance *town = NULL) OVERRIDE {}; //use hero=NULL for no hero
+	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false) OVERRIDE {}; //if any of armies is hero, hero will be used
+	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false) OVERRIDE {}; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
+	void setAmount(int objid, ui32 val) OVERRIDE {};
+	bool moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker = 255) OVERRIDE {return false;};
+	void giveHeroBonus(GiveBonus * bonus) OVERRIDE {};
+	void setMovePoints(SetMovePoints * smp) OVERRIDE {};
+	void setManaPoints(int hid, int val) OVERRIDE {};
+	void giveHero(int id, int player) OVERRIDE {};
+	void changeObjPos(int objid, int3 newPos, ui8 flags) OVERRIDE {};
+	void sendAndApply(CPackForClient * info) OVERRIDE {};
+	void heroExchange(si32 hero1, si32 hero2) OVERRIDE {};
 
 	//////////////////////////////////////////////////////////////////////////
 	friend class CCallback; //handling players actions
@@ -138,6 +154,7 @@ public:
 	
 	void handlePack( CPack * pack ); //applies the given pack and deletes it
 	void updatePaths();
+	void battleStarted(const BattleInfo * info);
 
 	//////////////////////////////////////////////////////////////////////////
 

+ 6 - 1
client/GUIBase.cpp

@@ -188,7 +188,7 @@ void CGuiHandler::handleEvent(SDL_Event *sEvent)
 	}
 	else if(sEvent->type==SDL_MOUSEMOTION)
 	{
-		CGI->curh->cursorMove(sEvent->motion.x, sEvent->motion.y);
+		CCS->curh->cursorMove(sEvent->motion.x, sEvent->motion.y);
 		HLP::adjustMousePos(sEvent);
 		handleMouseMotion(sEvent);
 	}
@@ -1023,4 +1023,9 @@ Rect Rect::createCentered( int w, int h )
 Rect Rect::around(const Rect &r, int width /*= 1*/) /*creates rect around another */
 {
 	return Rect(r.x - width, r.y - width, r.w + width * 2, r.h + width * 2);
+}
+
+Rect Rect::centerIn(const Rect &r)
+{
+	return Rect(r.x + (r.w - w) / 2, r.y + (r.h - h) / 2, w, h);
 }

+ 3 - 2
client/GUIBase.h

@@ -149,6 +149,7 @@ struct Rect : public SDL_Rect
 		h = surf->h;
 	}
 
+	Rect centerIn(const Rect &r);
 	static Rect createCentered(int w, int h);
 	static Rect around(const Rect &r, int width = 1); //creates rect around another
 
@@ -300,7 +301,7 @@ public:
 class IShowActivable : public IShowable, public IActivable
 {
 public:
-	enum {WITH_GARRISON = 1, BLOCK_ADV_HOTKEYS = 2};
+	enum {WITH_GARRISON = 1, BLOCK_ADV_HOTKEYS = 2, WITH_ARTIFACTS = 4};
 	int type; //bin flags using etype
 	IShowActivable();
 	virtual ~IShowActivable(){}; //d-tor
@@ -435,7 +436,7 @@ public:
 	virtual void keyPressed(const SDL_KeyboardEvent & key); //call-in
 };
 
-class CGarrisonHolder : public CIntObject// to unify updating garrisons via PlayerInterface
+class CGarrisonHolder : public virtual CIntObject// to unify updating garrisons via PlayerInterface
 {
 public:
 	CGarrisonHolder();

Plik diff jest za duży
+ 254 - 214
client/GUIClasses.cpp


+ 78 - 36
client/GUIClasses.h

@@ -25,6 +25,8 @@
  *
  */
 
+struct ArtifactLocation;
+class CStackBasicDescriptor;
 class CBonusSystemNode;
 class CArtifact;
 class CDefEssential;
@@ -67,6 +69,7 @@ class CGGarrison;
 class CStackInstance;
 class IMarket;
 class CTextBox;
+class CArtifactInstance;
 
 extern SDL_Color tytulowy, tlo, zwykly ;
 
@@ -127,6 +130,7 @@ public:
 	bool delInner;
 
 	void show(SDL_Surface * to);
+	void showAll(SDL_Surface * to);
 	CRClickPopupInt(IShowActivable *our, bool deleteInt); //c-tor
 	virtual ~CRClickPopupInt(); //d-tor
 };
@@ -167,7 +171,7 @@ public:
 	void init(Etype Type, int Subtype, int Val);
 	SComponent(Etype Type, int Subtype, int Val, SDL_Surface *sur=NULL, bool freeSur=false); //c-tor
 	SComponent(const Component &c); //c-tor
-	SComponent(){}; //c-tor
+	SComponent();; //c-tor
 	virtual ~SComponent(); //d-tor
 
 	void clickRight(tribool down, bool previousState); //call-in
@@ -212,7 +216,7 @@ public:
 	void clickLeft(tribool down, bool previousState);
 	void activate();
 	void deactivate();
-	void show(SDL_Surface * to);
+	void showAll(SDL_Surface * to);
 	CGarrisonSlot(CGarrisonInt *Owner, int x, int y, int IID, int Upg=0, const CStackInstance * Creature=NULL);
 	~CGarrisonSlot(); //d-tor
 };
@@ -233,14 +237,16 @@ public:
 	     smallIcons, //true - 32x32 imgs, false - 58x64
 	     removableUnits,//player can remove units from up
 	     twoRows,//slots will be placed in 2 rows
-		 ourUp,ourDown;//player owns up or down army
+		 owned[2];//player owns up or down army [0] upper, [1] lower
 
-	const CCreatureSet *set1; //top set of creatures
-	const CCreatureSet *set2; //bottom set of creatures
+// 	const CCreatureSet *set1; //top set of creatures
+// 	const CCreatureSet *set2; //bottom set of creatures
 
 	std::vector<CGarrisonSlot*> slotsUp, slotsDown; //slots of upper and lower garrison
-	const CArmedInstance *oup, *odown; //upper and lower garrisons (heroes or towns)
+	const CArmedInstance *armedObjs[2]; //[0] is upper, [1] is down
+	//const CArmedInstance *oup, *odown; //upper and lower garrisons (heroes or towns)
 
+	void setArmy(const CArmedInstance *army, bool bottomGarrison);
 	void addSplitBtn(AdventureMapButton * button);
 	void createSet(std::vector<CGarrisonSlot*> &ret, const CCreatureSet * set, int posX, int distance, int posY, int Upg );
 	
@@ -577,12 +583,21 @@ public:
 	void showAll(SDL_Surface * to);
 };
 
-class CTradeWindow : public CIntObject //base for markets and altar of sacrifice
+class CWindowWithArtifacts : public virtual CIntObject
+{
+public:
+	std::vector<CArtifactsOfHero *> artSets;
+
+	CWindowWithArtifacts();
+	~CWindowWithArtifacts();
+};
+
+class CTradeWindow : public CWindowWithArtifacts //base for markets and altar of sacrifice
 {
 public:
 	enum EType
 	{
-		RESOURCE, PLAYER, ARTIFACT, CREATURE, CREATURE_PLACEHOLDER,ARTIFACT_PLACEHOLDER
+		RESOURCE, PLAYER, ARTIFACT_TYPE, CREATURE, CREATURE_PLACEHOLDER, ARTIFACT_PLACEHOLDER, ARTIFACT_INSTANCE
 	};
 	class CTradeableItem : public CIntObject
 	{
@@ -593,6 +608,12 @@ public:
 		bool left;
 		std::string subtitle; //empty if default
 
+		const CArtifactInstance *hlp; //holds ptr to artifact instance id type artifact 
+		const CArtifactInstance *getArtInstance() const;
+// 		const CArtifact *getArt() const;
+// 		void setArtInstance(const CArtifactInstance *art) const;
+// 		void setArt(const CArtifact *artT) const;
+
 		CFunctionList<void()> callback;
 		bool downSelection;
 
@@ -691,7 +712,7 @@ public:
 	void SacrificeBackpack();
 
 	void putOnAltar(int backpackIndex);
-	bool putOnAltar(CTradeableItem* altarSlot, int artID);
+	bool putOnAltar(CTradeableItem* altarSlot, const CArtifactInstance *art);
 	void makeDeal();
 	void showAll(SDL_Surface * to);
 
@@ -710,7 +731,7 @@ public:
 
 	void artifactPicked();
 	int firstFreeSlot();
-	void moveFromSlotToAltar(int slotID, CTradeableItem* altarSlot, int artID);
+	void moveFromSlotToAltar(int slotID, CTradeableItem* altarSlot, const CArtifactInstance *art);
 };
 
 class CSystemOptionsWindow : public CIntObject
@@ -815,7 +836,9 @@ public:
 	std::string text;
 
 	LRClickableAreaWText();
+	LRClickableAreaWText(const Rect &Pos, const std::string &HoverText = "", const std::string &ClickText = "");
 	virtual ~LRClickableAreaWText();
+	void init();
 
 	virtual void clickLeft(tribool down, bool previousState);
 	virtual void clickRight(tribool down, bool previousState);
@@ -827,6 +850,8 @@ public:
 	int baseType;
 	int bonusValue, type;
 	virtual void clickLeft(tribool down, bool previousState);
+
+	LRClickableAreaWTextComp(const Rect &Pos = Rect(0,0,0,0), int BaseType = -1);
 };
 
 class MoraleLuckBox : public LRClickableAreaWTextComp
@@ -837,7 +862,7 @@ public:
 	void set(const CBonusSystemNode *node);
 	void showAll(SDL_Surface * to);
 
-	MoraleLuckBox(bool Morale);
+	MoraleLuckBox(bool Morale, const Rect &r);
 	~MoraleLuckBox();
 };
 
@@ -861,6 +886,7 @@ public:
 	const CGTownInstance * town;
 	void clickLeft(tribool down, bool previousState);
 	void clickRight(tribool down, bool previousState);
+	LRClickableAreaOpenTown();
 };
 
 class CCreInfoWindow : public CIntObject
@@ -880,8 +906,9 @@ public:
 
 	AdventureMapButton *dismiss, *upgrade, *ok;
 	CCreInfoWindow(const CStackInstance &st, int Type = 0, boost::function<void()> Upg = 0, boost::function<void()> Dsm = 0, UpgradeInfo *ui = NULL); //c-tor
+	CCreInfoWindow(const CStack &st, int Type = 0); //c-tor
 	CCreInfoWindow(int Cid, int Type, int creatureCount); //c-tor
-	void init(const CCreature *cre, const CStackInstance *stack, int creatureCount);
+	void init(const CCreature *cre, const CBonusSystemNode *stackNode, const CGHeroInstance *heroOwner, int creatureCount);
 	void printLine(int nr, const std::string &text, int baseVal, int val=-1, bool range=false);
 	~CCreInfoWindow(); //d-tor
 	void activate();
@@ -896,13 +923,15 @@ public:
 class CArtPlace: public LRClickableAreaWTextComp
 {
 public:
-	ui16 slotID; //0   	head	1 	shoulders		2 	neck		3 	right hand		4 	left hand		5 	torso		6 	right ring		7 	left ring		8 	feet		9 	misc. slot 1		10 	misc. slot 2		11 	misc. slot 3		12 	misc. slot 4		13 	ballista (war machine 1)		14 	ammo cart (war machine 2)		15 	first aid tent (war machine 3)		16 	catapult		17 	spell book		18 	misc. slot 5		19+ 	backpack slots
+	int slotID; //0   	head	1 	shoulders		2 	neck		3 	right hand		4 	left hand		5 	torso		6 	right ring		7 	left ring		8 	feet		9 	misc. slot 1		10 	misc. slot 2		11 	misc. slot 3		12 	misc. slot 4		13 	ballista (war machine 1)		14 	ammo cart (war machine 2)		15 	first aid tent (war machine 3)		16 	catapult		17 	spell book		18 	misc. slot 5		19+ 	backpack slots
 
+	bool picked;
 	bool marked;
-	bool selectedNo;
+	bool locked;
 	CArtifactsOfHero * ourOwner;
-	const CArtifact * ourArt;
-	CArtPlace(const CArtifact * Art); //c-tor
+	const CArtifactInstance * ourArt;
+
+	CArtPlace(const CArtifactInstance * Art); //c-tor
 	void clickLeft(tribool down, bool previousState);
 	void clickRight(tribool down, bool previousState);
 	void select ();
@@ -910,15 +939,17 @@ public:
 	void activate();
 	void deactivate();
 	void showAll(SDL_Surface * to);
-	bool fitsHere (const CArtifact * art) const; //returns true if given artifact can be placed here
-	bool locked () const;
-	void userSelectedNo ();
+	bool fitsHere (const CArtifactInstance * art) const; //returns true if given artifact can be placed here
+
+	void setMeAsDest(bool backpackAsVoid = true);
+	void setArtifact(const CArtifactInstance *art);
+
 	~CArtPlace(); //d-tor
 };
 
 class CArtifactsOfHero : public CIntObject
 {
-	CGHeroInstance * curHero; //local copy of hero on which we operate
+	const CGHeroInstance * curHero; //local copy of hero on which we operate
 
 	std::vector<CArtPlace *> artWorn; // 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5
 	std::vector<CArtPlace *> backpack; //hero's visible backpack (only 5 elements!)
@@ -927,13 +958,20 @@ class CArtifactsOfHero : public CIntObject
 public:
 	struct SCommonPart
 	{
+		struct Artpos
+		{
+			int slotID;
+			const CArtifactsOfHero * AOH;
+			const CArtifactInstance *art;
+
+			Artpos();
+			void clear();
+			void setTo(const CArtPlace *place, bool dontTakeBackpack);
+			bool valid();
+			bool operator==(const ArtifactLocation &al) const;
+		} src, dst;
+
 		std::set<CArtifactsOfHero *> participants; // Needed to mark slots.
-		const CArtifact * srcArtifact;    // Held artifact.
-		const CArtifactsOfHero * srcAOH;  // Following two needed to uniquely identify the source.
-		int srcSlotID;                    //
-		const CArtifactsOfHero * destAOH; // For swapping. (i.e. changing what is held)
-		int destSlotID;	                  // Needed to determine what kind of action was last taken in setHero
-		const CArtifact * destArtifact;   // For swapping.
 
 		void reset();
 	} * commonInfo; //when we have more than one CArtifactsOfHero in one window with exchange possibility, we use this (eg. in exchange window); to be provided externally
@@ -942,19 +980,26 @@ public:
 
 	AdventureMapButton * leftArtRoll, * rightArtRoll;
 	bool allowedAssembling;
-	std::multiset<int> artifactsOnAltar; //artifacts id that are technically present in backpack but in GUI are moved to the altar - they'll be ommited in backpack slots
+	std::multiset<const CArtifactInstance*> artifactsOnAltar; //artifacts id that are technically present in backpack but in GUI are moved to the altar - they'll be ommited in backpack slots
+
+	void realizeCurrentTransaction(); //calls callback with parameters stored in commonInfo
+	void artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst);
+	void artifactAssembled(const ArtifactLocation &al);
+	void artifactDisassembled(const ArtifactLocation &al);
+	CArtPlace *getArtPlace(int slot);
 
 	void setHero(const CGHeroInstance * hero);
 	void dispose(); //free resources not needed after closing windows and reset state
 	void scrollBackpack(int dir); //dir==-1 => to left; dir==1 => to right
 
 	void safeRedraw();
-	void markPossibleSlots (const CArtifact* art);
+	void markPossibleSlots(const CArtifactInstance* art);
 	void unmarkSlots(bool withRedraw = true);
 	void setSlotData (CArtPlace* artPlace, int slotID);
+	void updateWornSlots ();
 	void eraseSlotData (CArtPlace* artPlace, int slotID);
 
-	CArtifactsOfHero(const Point& position); //c-tor
+	CArtifactsOfHero(const Point& position, bool createCommonPart = false); //c-tor
 	~CArtifactsOfHero(); //d-tor
 	void updateParentWindow();
 	friend class CArtPlace;
@@ -963,19 +1008,16 @@ public:
 class CGarrisonWindow : public CWindowWithGarrison
 {
 public:
-	SDL_Surface *bg; //background surface
+	CPicture *bg; //background surface
 	AdventureMapButton *quit;
 
 	void close();
-	void activate();
-	void deactivate();
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to){show(to);};
+	void showAll(SDL_Surface * to);
 	CGarrisonWindow(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits); //c-tor
 	~CGarrisonWindow(); //d-tor
 };
 
-class CExchangeWindow : public CWindowWithGarrison
+class CExchangeWindow : public CWindowWithGarrison, public CWindowWithArtifacts
 {
 	CStatusBar * ourBar; //internal statusbar
 
@@ -1033,7 +1075,7 @@ private:
 	AdventureMapButton * quitb;
 	CResDataBar * resdatabar;
 
-	std::vector<std::pair<SDL_Surface *, SPuzzleInfo *> > puzzlesToPullBack;
+	std::vector<std::pair<SDL_Surface *, const SPuzzleInfo *> > puzzlesToPullBack;
 	ui8 animCount;
 
 public:

+ 42 - 9
client/Graphics.cpp

@@ -1,6 +1,6 @@
 #include "../stdafx.h"
 #include "Graphics.h"
-#include "../hch/CDefHandler.h"
+#include "CDefHandler.h"
 #include "SDL_Extensions.h"
 #include <SDL_ttf.h>
 #include <boost/assign/std/vector.hpp> 
@@ -12,14 +12,16 @@
 #include <boost/assign/std/vector.hpp>
 #include "../CThreadHelper.h"
 #include "CGameInfo.h"
-#include "../hch/CLodHandler.h"
+#include "../lib/CLodHandler.h"
 #include "../lib/VCMI_Lib.h"
 #include "../CCallback.h"
-#include "../hch/CTownHandler.h"
-#include "../hch/CObjectHandler.h"
-#include "../hch/CGeneralTextHandler.h"
-#include "../hch/CCreatureHandler.h"
+#include "../lib/CTownHandler.h"
+#include "../lib/CObjectHandler.h"
+#include "../lib/CGeneralTextHandler.h"
+#include "../lib/CCreatureHandler.h"
 #include "CBitmapHandler.h"
+#include "../lib/CObjectHandler.h"
+#include "../lib/CDefObjInfoHandler.h"
 
 using namespace boost::assign;
 using namespace CSDL_Ext;
@@ -53,9 +55,9 @@ SDL_Surface * Graphics::drawHeroInfoWin(const InfoAboutHero &curh)
 	blitAt(graphics->portraitLarge[curh.portrait],11,12,ret); //portrait
 
 	//army
-	for (TSlots::const_iterator i = curh.army.Slots().begin(); i!=curh.army.Slots().end();i++)
+	for (ArmyDescriptor::const_iterator i = curh.army.begin(); i!=curh.army.end();i++)
 	{
-		blitAt(graphics->smallImgs[(*i).second.type->idNumber],slotsPos[(*i).first].first+1,slotsPos[(*i).first].second+1,ret);
+		blitAt(graphics->smallImgs[i->second.type->idNumber],slotsPos[(*i).first].first+1,slotsPos[(*i).first].second+1,ret);
 		if(curh.details)
 		{
 			SDL_itoa((*i).second.count,buf,10);
@@ -110,7 +112,7 @@ SDL_Surface * Graphics::drawTownInfoWin( const InfoAboutTown & curh )
 	int pom = curh.fortLevel - 1; if(pom<0) pom = 3; //fort pic id
 	blitAt(forts->ourImages[pom].bitmap,115,42,ret); //fort
 
-	for (TSlots::const_iterator i=curh.army.Slots().begin(); i!=curh.army.Slots().end();i++)
+	for (ArmyDescriptor::const_iterator i=curh.army.begin(); i!=curh.army.end();i++)
 	{
 		//if(!i->second.second)
 		//	continue;
@@ -286,6 +288,7 @@ Graphics::Graphics()
 	tasks += boost::bind(&Graphics::loadHeroPortraits,this);
 	tasks += boost::bind(&Graphics::initializeBattleGraphics,this);
 	tasks += boost::bind(&Graphics::loadWallPositions,this);
+	tasks += boost::bind(&Graphics::loadErmuToPicture,this);
 	tasks += GET_SURFACE(hInfo,"HEROQVBK.bmp");
 	tasks += GET_SURFACE(tInfo,"TOWNQVBK.bmp");
 	tasks += GET_SURFACE(heroInGarrison,"TOWNQKGH.bmp");
@@ -720,6 +723,36 @@ void Graphics::loadFonts()
 		fonts[i] = loadFont(fontnames[i]);
 }
 
+CDefEssential * Graphics::getDef( const CGObjectInstance * obj )
+{
+	return advmapobjGraphics[obj->defInfo->id][obj->defInfo->subid];
+}
+
+CDefEssential * Graphics::getDef( const CGDefInfo * info )
+{
+	return advmapobjGraphics[info->id][info->subid];
+}
+
+void Graphics::loadErmuToPicture()
+{
+	//loading ERMU to picture
+	std::ifstream etp(DATA_DIR "/config/ERMU_to_picture.txt");
+
+	assert(etp.is_open());
+
+	for(int g=0; g<44; ++g)
+	{
+		for (int b=0; b<ARRAY_COUNT(ERMUtoPicture); ++b)
+		{
+			std::string buf;
+			etp >> buf;
+			ERMUtoPicture[b][g] = buf;
+		}
+	}
+
+	etp.close();
+}
+
 Font::Font(unsigned char *Data)
 {
 	data = Data;

+ 9 - 0
client/Graphics.h

@@ -24,6 +24,9 @@ class CHeroClass;
 struct SDL_Color;
 struct InfoAboutHero;
 struct InfoAboutTown;
+class CGObjectInstance;
+class CGDefInfo;
+
 typedef struct _TTF_Font TTF_Font; //from SDL_ttf.h
 
 class Graphics
@@ -62,6 +65,10 @@ public:
 	std::map<std::string, CDefEssential*> mapObjectDefs; //pointers to loaded defs (key is filename, uppercase)
 	CDefHandler * FoWfullHide; //for Fog of War
 	CDefHandler * FoWpartialHide; //for For of War
+
+	std::map<int, std::map<int, CDefEssential *> > advmapobjGraphics;
+	CDefEssential * getDef(const CGObjectInstance * obj);
+	CDefEssential * getDef(const CGDefInfo * info);
 	//creatures
 	std::map<int,SDL_Surface*> smallImgs; //creature ID -> small 32x32 img of creature; //ID=-2 is for blank (black) img; -1 for the border
 	std::map<int,SDL_Surface*> bigImgs; //creature ID -> big 58x64 img of creature; //ID=-2 is for blank (black) img; -1 for the border
@@ -71,6 +78,7 @@ public:
 	std::vector< std::string > buildingPics;//filenames of def with building images (used rarely, too big to keep them loaded)
 	std::vector< std::string > townBgs;//backgrounds of town
 	std::vector< std::string > guildBgs;// name of bitmaps with imgs for mage guild screen
+	std::map<int, std::string> ERMUtoPicture[F_NUMBER]; //maps building ID to it's picture's name for each town type
 	//for battles
 	std::vector< std::vector< std::string > > battleBacks; //battleBacks[terType] - vector of possible names for certain terrain type
 	std::vector< std::string > battleHeroes; //battleHeroes[hero type] - name of def that has hero animation for battle
@@ -91,6 +99,7 @@ public:
 	void loadHeroAnim(const std::string &name, const std::vector<std::pair<int,int> > &rotations, std::vector<CDefEssential *> Graphics::*dst);
 	void loadHeroPortraits();
 	void loadWallPositions();
+	void loadErmuToPicture();
 	SDL_Surface * drawHeroInfoWin(const InfoAboutHero &curh);
 	SDL_Surface * drawHeroInfoWin(const CGHeroInstance * curh);
 	SDL_Surface * drawTownInfoWin(const InfoAboutTown & curh);

+ 139 - 113
client/NetPacksClient.cpp

@@ -4,15 +4,15 @@
 #include "CPlayerInterface.h"
 #include "CGameInfo.h"
 #include "../lib/Connection.h"
-#include "../hch/CGeneralTextHandler.h"
-#include "../hch/CDefObjInfoHandler.h"
-#include "../hch/CHeroHandler.h"
-#include "../hch/CObjectHandler.h"
+#include "../lib/CGeneralTextHandler.h"
+#include "../lib/CDefObjInfoHandler.h"
+#include "../lib/CHeroHandler.h"
+#include "../lib/CObjectHandler.h"
 #include "../lib/VCMI_Lib.h"
 #include "../lib/map.h"
 #include "../lib/VCMIDirs.h"
-#include "../hch/CSpellHandler.h"
-#include "../hch/CSoundBase.h"
+#include "../lib/CSpellHandler.h"
+#include "CSoundBase.h"
 #include "mapHandler.h"
 #include "GUIClasses.h"
 #include <boost/bind.hpp>
@@ -22,13 +22,24 @@
 #include "CConfigHandler.h"
 #include "SDL_Extensions.h"
 #include "CBattleInterface.h"
-#include "../hch/CCampaignHandler.h"
+#include "../lib/CCampaignHandler.h"
+#include "../lib/CGameState.h"
+#include "../lib/BattleState.h"
+
 
 //macro to avoid code duplication - calls given method with given arguments if interface for specific player is present
 #define INTERFACE_CALL_IF_PRESENT(player,function,...) 	\
 		if(vstd::contains(cl->playerint,player))		\
 			cl->playerint[player]->function(__VA_ARGS__);
 
+#define BATTLE_INTERFACE_CALL_IF_PRESENT(player,function,...) 	\
+		if(vstd::contains(cl->battleints,player))		\
+			cl->battleints[player]->function(__VA_ARGS__);
+
+#define BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(function,...) 	\
+	BATTLE_INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->sides[0], function, __VA_ARGS__) \
+	BATTLE_INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->sides[1], function, __VA_ARGS__) \
+	BATTLE_INTERFACE_CALL_IF_PRESENT(254, function, __VA_ARGS__)
 /*
  * NetPacksClient.cpp, part of VCMI engine
  *
@@ -73,7 +84,7 @@ void SetSecSkill::applyCl( CClient *cl )
 
 void HeroVisitCastle::applyCl( CClient *cl )
 {
-	if(start() && !garrison() && vstd::contains(cl->playerint,GS(cl)->getHero(hid)->tempOwner))
+	if(start() && vstd::contains(cl->playerint,GS(cl)->getHero(hid)->tempOwner))
 	{
 		cl->playerint[GS(cl)->getHero(hid)->tempOwner]->heroVisitsTown(GS(cl)->getHero(hid),GS(cl)->getTown(tid));
 	}
@@ -122,6 +133,67 @@ void SetAvailableHeroes::applyCl( CClient *cl )
 	//TODO: inform interface?
 }
 
+void ChangeStackCount::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(sl.army->tempOwner,stackChagedCount, sl, count, absoluteValue);
+}
+
+void SetStackType::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(sl.army->tempOwner,stackChangedType,sl, *type);
+}
+
+void EraseStack::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(sl.army->tempOwner,stacksErased,sl);
+}
+
+void SwapStacks::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(sl1.army->tempOwner,stacksSwapped, sl1, sl2);
+	if(sl1.army->tempOwner != sl2.army->tempOwner)
+		INTERFACE_CALL_IF_PRESENT(sl2.army->tempOwner,stacksSwapped, sl1, sl2);
+}
+
+void InsertNewStack::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(sl.army->tempOwner,newStackInserted,sl, *sl.getStack());
+}
+
+void RebalanceStacks::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(src.army->tempOwner, stacksRebalanced, src, dst, count);
+	if(src.army->tempOwner != dst.army->tempOwner)
+		INTERFACE_CALL_IF_PRESENT(dst.army->tempOwner,stacksRebalanced, src, dst, count);
+}
+
+void PutArtifact::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(al.hero->tempOwner, artifactPut, al);
+}
+
+void EraseArtifact::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(al.hero->tempOwner, artifactRemoved, al);
+}
+
+void MoveArtifact::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(src.hero->tempOwner, artifactMoved, src, dst);
+	if(src.hero->tempOwner != dst.hero->tempOwner)
+		INTERFACE_CALL_IF_PRESENT(src.hero->tempOwner, artifactMoved, src, dst);
+}
+
+void AssembledArtifact::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(al.hero->tempOwner, artifactAssembled, al);
+}
+
+void DisassembledArtifact::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(al.hero->tempOwner, artifactDisassembled, al);
+}
+
 void GiveBonus::applyCl( CClient *cl )
 {
 	switch(who)
@@ -129,13 +201,13 @@ void GiveBonus::applyCl( CClient *cl )
 	case HERO:
 		{
 			const CGHeroInstance *h = GS(cl)->getHero(id);
-			INTERFACE_CALL_IF_PRESENT(h->tempOwner, heroBonusChanged, h, h->bonuses.back(),true);
+			INTERFACE_CALL_IF_PRESENT(h->tempOwner, heroBonusChanged, h, *h->bonuses.back(),true);
 		}
 		break;
 	case PLAYER:
 		{
 			const PlayerState *p = GS(cl)->getPlayer(id);
-			INTERFACE_CALL_IF_PRESENT(id, playerBonusChanged, p->bonuses.back(), true);
+			INTERFACE_CALL_IF_PRESENT(id, playerBonusChanged, *p->bonuses.back(), true);
 		}
 		break;
 	}
@@ -274,12 +346,12 @@ void TryMoveHero::applyCl( CClient *cl )
 	}
 }
 
-void SetGarrisons::applyCl( CClient *cl )
-{
-	for(std::map<ui32,CCreatureSet>::iterator i = garrs.begin(); i!=garrs.end(); i++)
-		if(vstd::contains(cl->playerint,cl->getOwner(i->first)))
-			cl->playerint[cl->getOwner(i->first)]->garrisonChanged(cl->getObj(i->first));
-}
+// void SetGarrisons::applyCl( CClient *cl )
+// {
+// 	for(std::map<ui32,CCreatureSet>::iterator i = garrs.begin(); i!=garrs.end(); i++)
+// 		if(vstd::contains(cl->playerint,cl->getOwner(i->first)))
+// 			cl->playerint[cl->getOwner(i->first)]->garrisonChanged(cl->getObj(i->first));
+// }
 
 void NewStructures::applyCl( CClient *cl )
 {
@@ -334,25 +406,27 @@ void SetHeroesInTown::applyCl( CClient *cl )
 		cl->playerint[t->tempOwner]->heroInGarrisonChange(t);
 }
 
-void SetHeroArtifacts::applyCl( CClient *cl )
-{
-	CGHeroInstance *h = GS(cl)->getHero(hid);
-	CGameInterface *player = (vstd::contains(cl->playerint,h->tempOwner) ? cl->playerint[h->tempOwner] : NULL);
-	if(!player)
-		return;
-
-	//h->recreateArtBonuses();
-	player->heroArtifactSetChanged(h);
-
-// 	BOOST_FOREACH(Bonus bonus, gained)
-// 	{
-// 		player->heroBonusChanged(h,bonus,true);
-// 	}
-// 	BOOST_FOREACH(Bonus bonus, lost)
-// 	{
-// 		player->heroBonusChanged(h,bonus,false);
-// 	}
-}
+// void SetHeroArtifacts::applyCl( CClient *cl )
+// {
+// 	tlog1 << "SetHeroArtifacts :(\n";
+// // 
+// // 	CGHeroInstance *h = GS(cl)->getHero(hid);
+// // 	CGameInterface *player = (vstd::contains(cl->playerint,h->tempOwner) ? cl->playerint[h->tempOwner] : NULL);
+// // 	if(!player)
+// // 		return;
+// 
+// 	//h->recreateArtBonuses();
+// 	//player->heroArtifactSetChanged(h);
+// 
+// // 	BOOST_FOREACH(Bonus bonus, gained)
+// // 	{
+// // 		player->heroBonusChanged(h,bonus,true);
+// // 	}
+// // 	BOOST_FOREACH(Bonus bonus, lost)
+// // 	{
+// // 		player->heroBonusChanged(h,bonus,false);
+// // 	}
+// }
 
 void HeroRecruited::applyCl( CClient *cl )
 {
@@ -447,41 +521,17 @@ void GarrisonDialog::applyCl(CClient *cl)
 
 void BattleStart::applyCl( CClient *cl )
 {
-	CPlayerInterface * att, * def;
-	if(vstd::contains(cl->playerint, info->side1) && cl->playerint[info->side1]->human)
-		att = static_cast<CPlayerInterface*>( cl->playerint[info->side1] );
-	else
-		att = NULL;
-
-	if(vstd::contains(cl->playerint, info->side2) && cl->playerint[info->side2]->human)
-		def = static_cast<CPlayerInterface*>( cl->playerint[info->side2] );
-	else
-		def = NULL;
-
-
-	new CBattleInterface(info->belligerents[0], info->belligerents[1], info->heroes[0], info->heroes[1], genRect(600, 800, (conf.cc.resx - 800)/2, (conf.cc.resy - 600)/2), att, def);
-
-	if(vstd::contains(cl->playerint,info->side1))
-		cl->playerint[info->side1]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 0);
-
-	if(vstd::contains(cl->playerint,info->side2))
-		cl->playerint[info->side2]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 1);
+	cl->battleStarted(info);
 }
 
 void BattleNextRound::applyFirstCl(CClient *cl)
 {
-	if(cl->playerint.find(GS(cl)->curB->side1) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side1]->battleNewRoundFirst(round);
-	if(cl->playerint.find(GS(cl)->curB->side2) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side2]->battleNewRoundFirst(round);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleNewRoundFirst,round);
 }
 
 void BattleNextRound::applyCl( CClient *cl )
 {
-	if(cl->playerint.find(GS(cl)->curB->side1) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side1]->battleNewRound(round);
-	if(cl->playerint.find(GS(cl)->curB->side2) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side2]->battleNewRound(round);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleNewRound,round);
 }
 
 void BattleSetActiveStack::applyCl( CClient *cl )
@@ -490,28 +540,25 @@ void BattleSetActiveStack::applyCl( CClient *cl )
 	int playerToCall = -1; //player that will move activated stack
 	if( activated->hasBonusOfType(Bonus::HYPNOTIZED) )
 	{
-		playerToCall = ( GS(cl)->curB->side1 == activated->owner ? GS(cl)->curB->side2 : GS(cl)->curB->side1 );
+		playerToCall = ( GS(cl)->curB->sides[0] == activated->owner ? GS(cl)->curB->sides[1] : GS(cl)->curB->sides[0] );
 	}
 	else
 	{
 		playerToCall = activated->owner;
 	}
-	if( vstd::contains(cl->playerint, playerToCall) )
+	if( vstd::contains(cl->battleints, playerToCall) )
 		boost::thread( boost::bind(&CClient::waitForMoveAndSend, cl, playerToCall) );
 }
 
 void BattleResult::applyFirstCl( CClient *cl )
 {
-	if(cl->playerint.find(GS(cl)->curB->side1) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side1]->battleEnd(this);
-	if(cl->playerint.find(GS(cl)->curB->side2) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side2]->battleEnd(this);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleEnd,this);
 }
 
 void BattleStackMoved::applyFirstCl( CClient *cl )
 {
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1,battleStackMoved,stack,tile,distance,ending);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2,battleStackMoved,stack,tile,distance,ending);
+	const CStack * movedStack = GS(cl)->curB->getStack(stack);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStackMoved,movedStack,tile,distance,ending);
 }
 
 void BattleStackAttacked::applyCl( CClient *cl )
@@ -519,16 +566,12 @@ void BattleStackAttacked::applyCl( CClient *cl )
 	std::vector<BattleStackAttacked> bsa;
 	bsa.push_back(*this);
 
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1,battleStacksAttacked,bsa);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2,battleStacksAttacked,bsa);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStacksAttacked,bsa);
 }
 
 void BattleAttack::applyFirstCl( CClient *cl )
 {
-	if(cl->playerint.find(GS(cl)->curB->side1) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side1]->battleAttack(this);
-	if(cl->playerint.find(GS(cl)->curB->side2) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side2]->battleAttack(this);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleAttack,this);
 	for (int g=0; g<bsa.size(); ++g)
 	{
 		for (int z=0; z<bsa[g].healedStacks.size(); ++z)
@@ -540,59 +583,47 @@ void BattleAttack::applyFirstCl( CClient *cl )
 
 void BattleAttack::applyCl( CClient *cl )
 {
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1,battleStacksAttacked,bsa);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2,battleStacksAttacked,bsa);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStacksAttacked,bsa);
 }
 
 void StartAction::applyFirstCl( CClient *cl )
 {
 	cl->curbaction = new BattleAction(ba);
-	if(cl->playerint.find(GS(cl)->curB->side1) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side1]->actionStarted(&ba);
-	if(cl->playerint.find(GS(cl)->curB->side2) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side2]->actionStarted(&ba);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(actionStarted, &ba);
 }
 
 void BattleSpellCast::applyCl( CClient *cl )
 {
-	if(cl->playerint.find(GS(cl)->curB->side1) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side1]->battleSpellCast(this);
-	if(cl->playerint.find(GS(cl)->curB->side2) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side2]->battleSpellCast(this);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleSpellCast,this);
 
 	if(id >= 66 && id <= 69) //elemental summoning
 	{
-		if(cl->playerint.find(GS(cl)->curB->side1) != cl->playerint.end())
-			cl->playerint[GS(cl)->curB->side1]->battleNewStackAppeared(GS(cl)->curB->stacks.size() - 1);
-		if(cl->playerint.find(GS(cl)->curB->side2) != cl->playerint.end())
-			cl->playerint[GS(cl)->curB->side2]->battleNewStackAppeared(GS(cl)->curB->stacks.size() - 1);
+		BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleNewStackAppeared,GS(cl)->curB->stacks.back());
 	}
 }
 
 void SetStackEffect::applyCl( CClient *cl )
 {
-	BattleSpellCast sc;
-	sc.id = effect.id;
-	sc.side = 3; //doesn't matter
-	sc.skill = effect.val;
-
 	//informing about effects
-	if(cl->playerint.find(GS(cl)->curB->side1) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side1]->battleStacksEffectsSet(*this);
-	if(cl->playerint.find(GS(cl)->curB->side2) != cl->playerint.end())
-		cl->playerint[GS(cl)->curB->side2]->battleStacksEffectsSet(*this);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStacksEffectsSet,*this);
 }
 
 void StacksInjured::applyCl( CClient *cl )
 {
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1,battleStacksAttacked,stacks);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2,battleStacksAttacked,stacks);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStacksAttacked,stacks);
 }
 
 void BattleResultsApplied::applyCl( CClient *cl )
 {
-	INTERFACE_CALL_IF_PRESENT(player1,battleResultsApplied);
-	INTERFACE_CALL_IF_PRESENT(player2,battleResultsApplied);
+	INTERFACE_CALL_IF_PRESENT(player1, battleResultsApplied);
+	INTERFACE_CALL_IF_PRESENT(player2, battleResultsApplied);
+	INTERFACE_CALL_IF_PRESENT(254, battleResultsApplied);
+	if(GS(cl)->initialOpts->mode == StartInfo::DUEL)
+	{
+		cl->terminate = true;
+		CloseServer cs;
+		*cl->serv << &cs;
+	}
 }
 
 void StacksHealedOrResurrected::applyCl( CClient *cl )
@@ -602,29 +633,25 @@ void StacksHealedOrResurrected::applyCl( CClient *cl )
 	{
 		shiftedHealed.push_back(std::make_pair(healedStacks[v].stackID, healedStacks[v].healedHP));
 	}
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1, battleStacksHealedRes, shiftedHealed, lifeDrain, drainedFrom);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2, battleStacksHealedRes, shiftedHealed, lifeDrain, drainedFrom);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStacksHealedRes, shiftedHealed, lifeDrain, drainedFrom);
 }
 
 void ObstaclesRemoved::applyCl( CClient *cl )
 {
 	//inform interfaces about removed obstacles
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1, battleObstaclesRemoved, obstacles);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2, battleObstaclesRemoved, obstacles);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleObstaclesRemoved, obstacles);
 }
 
 void CatapultAttack::applyCl( CClient *cl )
 {
 	//inform interfaces about catapult attack
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1, battleCatapultAttacked, *this);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2, battleCatapultAttacked, *this);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleCatapultAttacked, *this);
 }
 
 void BattleStacksRemoved::applyCl( CClient *cl )
 {
 	//inform interfaces about removed stacks
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1, battleStacksRemoved, *this);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2, battleStacksRemoved, *this);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStacksRemoved, *this);
 }
 
 CGameState* CPackForClient::GS( CClient *cl )
@@ -634,8 +661,7 @@ CGameState* CPackForClient::GS( CClient *cl )
 
 void EndAction::applyCl( CClient *cl )
 {
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1,actionFinished,cl->curbaction);
-	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2,actionFinished,cl->curbaction);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(actionFinished, cl->curbaction);
 
 	delete cl->curbaction;
 	cl->curbaction = NULL;

+ 1 - 1
client/SDL_Extensions.cpp

@@ -7,7 +7,7 @@
 #include <algorithm>
 #include "CMessage.h"
 #include <boost/algorithm/string.hpp>
-#include "../hch/CDefHandler.h"
+#include "CDefHandler.h"
 #include <map>
 #include "Graphics.h"
 #include "GUIBase.h"

+ 39 - 34
client/mapHandler.cpp

@@ -3,21 +3,21 @@
 #include "SDL_Extensions.h"
 #include "CGameInfo.h"
 #include <cstdlib>
-#include "../hch/CLodHandler.h"
-#include "../hch/CDefObjInfoHandler.h"
+#include "../lib/CLodHandler.h"
+#include "../lib/CDefObjInfoHandler.h"
 #include <algorithm>
 #include "../lib/CGameState.h"
-#include "../hch/CHeroHandler.h"
-#include "../hch/CTownHandler.h"
+#include "../lib/CHeroHandler.h"
+#include "../lib/CTownHandler.h"
 #include "Graphics.h"
 #include <iomanip>
 #include <sstream>
-#include "../hch/CObjectHandler.h"
+#include "../lib/CObjectHandler.h"
 #include "../lib/map.h"
-#include "../hch/CDefHandler.h"
+#include "CDefHandler.h"
 #include "CConfigHandler.h"
 #include <boost/assign/list_of.hpp>
-#include "../hch/CGeneralTextHandler.h"
+#include "../lib/CGeneralTextHandler.h"
 
 /*
  * mapHandler.cpp, part of VCMI engine
@@ -294,55 +294,57 @@ void CMapHandler::initObjectRects()
 		}
 	}
 }
-static void processDef (CGDefInfo* def)
+static void processDef (const CGDefInfo* def)
 {
 	if(def->id == EVENTI_TYPE)
+	{
 		return;
+	}
 
-	if(!def->handler) //if object has already set handler (eg. heroes) it should not be overwritten
+	if(!def->handler) //if object has already set handler (eg. heroes) it should not be overwritten 
 	{
 		if(def->name.size())
 		{
 			if(vstd::contains(graphics->mapObjectDefs, def->name))
 			{
-				def->handler = graphics->mapObjectDefs[def->name];
+				const_cast<CGDefInfo*>(def)->handler = graphics->mapObjectDefs[def->name];
 			}
 			else
 			{
-				graphics->mapObjectDefs[def->name] = def->handler = CDefHandler::giveDefEss(def->name);
+				graphics->mapObjectDefs[def->name] = const_cast<CGDefInfo*>(def)->handler = CDefHandler::giveDefEss(def->name);
 			}
 		}
 		else
 		{
 			tlog2 << "No def name for " << def->id << "  " << def->subid << std::endl;
-			def->handler = NULL;
+			const_cast<CGDefInfo*>(def)->handler = NULL;
 			return;
 		}
 
-// 		def->width = def->handler->ourImages[0].bitmap->w/32;
-// 		def->height = def->handler->ourImages[0].bitmap->h/32;
-	}
-
-	CGDefInfo* pom = CGI->dobjinfo->gobjs[def->id][def->subid];
-	if(pom && def->id!=TOWNI_TYPE)
-	{
-		pom->handler = def->handler;
-		pom->width = pom->handler->ourImages[0].bitmap->w/32;
-		pom->height = pom->handler->ourImages[0].bitmap->h/32;
+ 		const_cast<CGDefInfo*>(def)->width = def->handler->ourImages[0].bitmap->w/32;
+ 		const_cast<CGDefInfo*>(def)->height = def->handler->ourImages[0].bitmap->h/32;
 	}
-	else if(def->id != HEROI_TYPE && def->id != TOWNI_TYPE)
-		tlog3 << "\t\tMinor warning: lacking def info for " << def->id << " " << def->subid <<" " << def->name << std::endl;
-
+	
+	CGDefInfo* pom = const_cast<CGameInfo*>(CGI)->dobjinfo->gobjs[def->id][def->subid]; 
+	if(pom && def->id!=TOWNI_TYPE) 
+	{ 
+		pom->handler = def->handler; 
+		pom->width = pom->handler->ourImages[0].bitmap->w/32; 
+		pom->height = pom->handler->ourImages[0].bitmap->h/32; 
+	} 
+	else if(def->id != HEROI_TYPE && def->id != TOWNI_TYPE) 
+		tlog3 << "\t\tMinor warning: lacking def info for " << def->id << " " << def->subid <<" " << def->name << std::endl; 
+	
 	//alpha transformation
 	for(size_t yy=0; yy < def->handler->ourImages.size(); ++yy)
 	{
 		CSDL_Ext::alphaTransform(def->handler->ourImages[yy].bitmap);
 	}
 }
-void CMapHandler::initHeroDef(CGHeroInstance * h)
+void CMapHandler::initHeroDef(const CGHeroInstance * h)
 {
-	h->defInfo->handler = graphics->flags1[0];
-	h->defInfo->width = h->defInfo->handler->ourImages[0].bitmap->w/32;
+	h->defInfo->handler = graphics->flags1[0]; 
+	h->defInfo->width = h->defInfo->handler->ourImages[0].bitmap->w/32; 
 	h->defInfo->height = h->defInfo->handler->ourImages[0].bitmap->h/32;
 }
 void CMapHandler::init()
@@ -350,9 +352,9 @@ void CMapHandler::init()
 	timeHandler th;
 	th.getDif();
 
-	CGI->dobjinfo->gobjs[8][0]->handler = graphics->boatAnims[0];
-	CGI->dobjinfo->gobjs[8][1]->handler = graphics->boatAnims[1];
-	CGI->dobjinfo->gobjs[8][2]->handler = graphics->boatAnims[2];
+	const_cast<CGameInfo*>(CGI)->dobjinfo->gobjs[8][0]->handler = graphics->boatAnims[0]; 
+	const_cast<CGameInfo*>(CGI)->dobjinfo->gobjs[8][1]->handler = graphics->boatAnims[1]; 
+	const_cast<CGameInfo*>(CGI)->dobjinfo->gobjs[8][2]->handler = graphics->boatAnims[2];
 
 	// Size of visible terrain.
 	int mapW = conf.go()->ac.advmapW;
@@ -385,7 +387,7 @@ void CMapHandler::init()
 
 	for(int i=0;i<map->heroes.size();i++)
 	{
-		if(!map->heroes[i]->defInfo->handler)
+		if( !map->heroes[i]->defInfo->handler )
 		{
 			initHeroDef(map->heroes[i]);
 		}
@@ -419,7 +421,10 @@ void CMapHandler::init()
 // top_tile top left tile to draw. Not necessarily visible.
 // extRect, extRect = map window on screen
 // moveX, moveY: when a hero is in movement indicates how to shift the map. Range is -31 to + 31.
-void CMapHandler::terrainRect(int3 top_tile, unsigned char anim, const std::vector< std::vector< std::vector<unsigned char> > > * visibilityMap, bool otherHeroAnim, unsigned char heroAnim, SDL_Surface * extSurf, const SDL_Rect * extRect, int moveX, int moveY, bool puzzleMode)
+void CMapHandler::terrainRect(int3 top_tile, unsigned char anim,
+	const std::vector< std::vector< std::vector<unsigned char> > > * visibilityMap,
+	bool otherHeroAnim, unsigned char heroAnim, SDL_Surface * extSurf, const SDL_Rect * extRect,
+	int moveX, int moveY, bool puzzleMode) const
 {
 	// Width and height of the portion of the map to process. Units in tiles.
 	unsigned int dx = tilesW;
@@ -778,7 +783,7 @@ void CMapHandler::terrainRect(int3 top_tile, unsigned char anim, const std::vect
 	SDL_SetClipRect(extSurf, &prevClip); //restoring clip_rect
 }
 
-std::pair<SDL_Surface *, bool> CMapHandler::getVisBitmap( const int3 & pos, const std::vector< std::vector< std::vector<unsigned char> > > & visibilityMap )
+std::pair<SDL_Surface *, bool> CMapHandler::getVisBitmap( const int3 & pos, const std::vector< std::vector< std::vector<unsigned char> > > & visibilityMap ) const
 {
 	static const int visBitmaps[256] = {-1, 34, -1, 4, 22, 22, 4, 4, 36, 36, 38, 38, 47, 47, 38, 38, 3, 25, 12, 12, 3, 25, 12, 12,
 		9, 9, 6, 6, 9, 9, 6, 6, 35, 34, 4, 4, 22, 22, 4, 4, 36, 36, 38, 38, 47, 47, 38, 38, 26, 49, 28, 28, 26, 49, 28,

+ 3 - 3
client/mapHandler.h

@@ -102,7 +102,7 @@ public:
 	CMapHandler(); //c-tor
 	~CMapHandler(); //d-tor
 
-	std::pair<SDL_Surface *, bool> getVisBitmap(const int3 & pos, const std::vector< std::vector< std::vector<unsigned char> > > & visibilityMap); //returns appropriate bitmap and info if alpha blitting is necessary
+	std::pair<SDL_Surface *, bool> getVisBitmap(const int3 & pos, const std::vector< std::vector< std::vector<unsigned char> > > & visibilityMap) const; //returns appropriate bitmap and info if alpha blitting is necessary
 
 	std::vector< std::string > getObjDescriptions(int3 pos); //returns desriptions of objects blocking given position
 	void getTerrainDescr(const int3 &pos, std::string & out, bool terName); //if tername == false => empty string when tile is clear
@@ -110,7 +110,7 @@ public:
 	bool printObject(const CGObjectInstance * obj); //puts appropriate things to ttiles, so obj will be visible on map
 	bool hideObject(const CGObjectInstance * obj); //removes appropriate things from ttiles, so obj will be no longer visible on map (but still will exist)
 	bool removeObject(CGObjectInstance * obj); //removes object from each place in VCMI (I hope)
-	void initHeroDef(CGHeroInstance * h);
+	void initHeroDef(const CGHeroInstance * h);
 	void init();
 	void calculateBlockedPos();
 	void initObjectRects();
@@ -118,7 +118,7 @@ public:
 	void roadsRiverTerrainInit();
 	void prepareFOWDefs();
 
-	void terrainRect(int3 top_tile, unsigned char anim, const std::vector< std::vector< std::vector<unsigned char> > > * visibilityMap, bool otherHeroAnim, unsigned char heroAnim, SDL_Surface * extSurf, const SDL_Rect * extRect, int moveX, int moveY, bool puzzleMode);
+	void terrainRect(int3 top_tile, unsigned char anim, const std::vector< std::vector< std::vector<unsigned char> > > * visibilityMap, bool otherHeroAnim, unsigned char heroAnim, SDL_Surface * extSurf, const SDL_Rect * extRect, int moveX, int moveY, bool puzzleMode) const;
 	void updateWater();
 	unsigned char getHeroFrameNum(unsigned char dir, bool isMoving) const; //terrainRect helper function
 	void validateRectTerr(SDL_Rect * val, const SDL_Rect * ext); //terrainRect helper

+ 244 - 1
global.h

@@ -1,11 +1,14 @@
+#pragma once
 #ifndef __GLOBAL_H__
 #define __GLOBAL_H__
 #include <iostream>
 #include <algorithm> //std::find
 #include <string> //std::find
 #include <boost/logic/tribool.hpp>
+#include <boost/unordered_set.hpp>
 using boost::logic::tribool;
 #include <boost/cstdint.hpp>
+#include <assert.h>
 typedef boost::uint64_t ui64; //unsigned int 64 bits (8 bytes)
 typedef boost::uint32_t ui32;  //unsigned int 32 bits (4 bytes)
 typedef boost::uint16_t ui16; //unsigned int 16 bits (2 bytes)
@@ -16,6 +19,7 @@ typedef boost::int16_t si16; //signed int 16 bits (2 bytes)
 typedef boost::int8_t si8; //signed int 8 bits (1 byte)
 typedef si64 expType;
 typedef ui16 spelltype;
+typedef std::pair<ui32, ui32> TDmgRange;
 typedef ui8 TBonusType;
 typedef si32 TBonusSubtype;
 #include "int3.h"
@@ -43,6 +47,7 @@ extern std::string NAME_AFFIX; //client / server
 #define DATA_DIR "."
 #define USER_DIR  "."
 #define BIN_DIR  "."
+#define LIB_DIR "AI"
 #define SERVER_NAME "VCMI_server.exe"
 #else
 #ifndef DATA_DIR
@@ -80,14 +85,22 @@ enum ElossCon {lossCastle, lossHero, timeExpires, lossStandard=255};
 enum ECombatInfo{ALIVE = 180, SUMMONED, CLONED, HAD_MORALE, WAITING, MOVED, DEFENDING};
 
 class CGameInfo;
-extern CGameInfo* CGI; //game info for general use
+extern const CGameInfo* CGI; //game info for general use
+class CClientState;
+extern CClientState * CCS;
 
+//a few typedefs for CCreatureSet
+typedef si32 TSlot;
+typedef si32 TQuantity; 
+typedef ui32 TCreature; //creature id
+const int ARMY_SIZE = 7;
 
 const int HEROI_TYPE = 34, 
 	TOWNI_TYPE = 98,
 	CREI_TYPE = 54,
 	EVENTI_TYPE = 26;
 
+const int CRE_LEVELS = 10;
 const int F_NUMBER = 9; //factions (town types) quantity
 const int PLAYER_LIMIT = 8; //player limit per map
 const int ALL_PLAYERS = 255; //bitfield
@@ -116,6 +129,176 @@ const int BFIELD_WIDTH = 17;
 const int BFIELD_HEIGHT = 11;
 const int BFIELD_SIZE = BFIELD_WIDTH * BFIELD_HEIGHT;
 
+const int SPELLBOOK_GOLD_COST = 500;
+
+
+//for battle stacks' positions
+struct THex
+{
+	static const si16 INVALID = -1;
+	enum EDir{RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT, LEFT, TOP_LEFT, TOP_RIGHT};
+
+	si16 hex;
+
+	THex() : hex(INVALID) {}
+	THex(si16 _hex) : hex(_hex)
+	{
+		//assert(isValid());
+	}
+	operator si16() const
+	{
+		return hex;
+	}
+
+	bool isValid() const
+	{
+		return hex >= 0 && hex < BFIELD_SIZE;
+	}
+
+	template<typename inttype>
+	THex(inttype x, inttype y)
+	{
+		setXY(x, y);
+	}
+
+	template<typename inttype>
+	THex(std::pair<inttype, inttype> xy)
+	{
+		setXY(xy);
+	}
+
+	template<typename inttype>
+	void setX(inttype x)
+	{
+		setXY(x, getY());
+	}
+
+	template<typename inttype>
+	void setY(inttype y)
+	{
+		setXY(getX(), y);
+	}
+
+	void setXY(si16 x, si16 y)
+	{
+		assert(x >= 0 && x < BFIELD_WIDTH && y >= 0  && y < BFIELD_HEIGHT);
+		hex = x + y * BFIELD_WIDTH;
+	}
+
+	template<typename inttype>
+	void setXY(std::pair<inttype, inttype> xy)
+	{
+		setXY(xy.first, xy.second);
+	}
+
+	si16 getY() const
+	{
+		return hex/BFIELD_WIDTH;
+	}
+
+	si16 getX() const
+	{
+		int pos = hex - getY() * BFIELD_WIDTH;
+		return pos;
+	}
+
+	std::pair<si16, si16> getXY() const
+	{
+		return std::make_pair(getX(), getY());
+	}
+
+	//moving to direction
+	void operator+=(EDir dir)
+	{
+		si16 x = getX(),
+			y = getY();
+
+		switch(dir)
+		{
+		case TOP_LEFT:
+			setXY(y%2 ? x-1 : x, y-1);
+			break;
+		case TOP_RIGHT:
+			setXY(y%2 ? x : x+1, y-1);
+			break;
+		case RIGHT:
+			setXY(x+1, y);
+			break;
+		case BOTTOM_RIGHT:
+			setXY(y%2 ? x : x+1, y+1);
+			break;
+		case BOTTOM_LEFT:
+			setXY(y%2 ? x-1 : x, y+1);
+			break;
+		case LEFT:
+			setXY(x-1, y);
+			break;
+		default:
+			throw std::string("Disaster: wrong direction in THex::operator+=!\n");
+			break;
+		}
+	}
+
+	//generates new THex moved by given dir
+	THex operator+(EDir dir) const
+	{
+		THex ret(*this);
+		ret += dir;
+		return ret;
+	}
+
+	std::vector<THex> neighbouringTiles() const
+	{
+		std::vector<THex> ret;
+		const int WN = BFIELD_WIDTH;
+		checkAndPush(hex - ( (hex/WN)%2 ? WN+1 : WN ), ret);
+		checkAndPush(hex - ( (hex/WN)%2 ? WN : WN-1 ), ret);
+		checkAndPush(hex - 1, ret);
+		checkAndPush(hex + 1, ret);
+		checkAndPush(hex + ( (hex/WN)%2 ? WN-1 : WN ), ret);
+		checkAndPush(hex + ( (hex/WN)%2 ? WN : WN+1 ), ret);
+
+		return ret;
+	}
+
+	//returns info about mutual position of given hexes (-1 - they're distant, 0 - left top, 1 - right top, 2 - right, 3 - right bottom, 4 - left bottom, 5 - left)
+	static signed char mutualPosition(THex hex1, THex hex2)
+	{
+		if(hex2 == hex1 - ( (hex1/17)%2 ? 18 : 17 )) //top left
+			return 0;
+		if(hex2 == hex1 - ( (hex1/17)%2 ? 17 : 16 )) //top right
+			return 1;
+		if(hex2 == hex1 - 1 && hex1%17 != 0) //left
+			return 5;
+		if(hex2 == hex1 + 1 && hex1%17 != 16) //right
+			return 2;
+		if(hex2 == hex1 + ( (hex1/17)%2 ? 16 : 17 )) //bottom left
+			return 4;
+		if(hex2 == hex1 + ( (hex1/17)%2 ? 17 : 18 )) //bottom right
+			return 3;
+		return -1;
+	}
+	//returns distance between given hexes
+	static si8 getDistance(THex hex1, THex hex2)
+	{
+		int xDst = std::abs(hex1 % BFIELD_WIDTH - hex2 % BFIELD_WIDTH),
+			yDst = std::abs(hex1 / BFIELD_WIDTH - hex2 / BFIELD_WIDTH);
+		return std::max(xDst, yDst) + std::min(xDst, yDst) - (yDst + 1)/2;
+	}
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & hex;
+	}
+private:
+	static void checkAndPush(int tile, std::vector<THex> & ret)
+	{
+		if( tile>=0 && tile<BFIELD_SIZE && (tile%BFIELD_WIDTH != (BFIELD_WIDTH - 1)) && (tile%BFIELD_WIDTH != 0) )
+			ret.push_back(THex(tile));
+	}
+
+};
+
 enum EMarketMode
 {
 	RESOURCE_RESOURCE, RESOURCE_PLAYER, CREATURE_RESOURCE, RESOURCE_ARTIFACT, 
@@ -123,6 +306,27 @@ enum EMarketMode
 	MARTKET_AFTER_LAST_PLACEHOLDER
 };
 
+namespace Res
+{
+	enum ERes 
+	{
+		WOOD = 0, MERCURY, ORE, SULFUR, CRYSTAL, GEMS, GOLD, MITHRIL
+	};
+}
+
+namespace Arts
+{
+	enum EPos
+	{
+		PRE_FIRST = -1, 
+		HEAD, SHOULDERS, NECK, RIGHT_HAND, LEFT_HAND, TORSO, RIGHT_RING, LEFT_RING, FEET, MISC1, MISC2, MISC3, MISC4,
+		MACH1, MACH2, MACH3, MACH4, SPELLBOOK, MISC5, 
+		AFTER_LAST
+	};
+	const ui16 BACKPACK_START = 19;
+	const int ID_CATAPULT = 3, ID_LOCK = 145;
+}
+
 enum EAlignment
 {
 	GOOD, EVIL, NEUTRAL
@@ -161,9 +365,32 @@ enum EAlignment
 	#define DLL_EXPORT DLL_F_IMPORT
 #endif
 
+
 template<typename T, size_t N> char (&_ArrayCountObj(const T (&)[N]))[N];  
 #define ARRAY_COUNT(arr)    (sizeof(_ArrayCountObj(arr)))
 
+
+
+//a normal std::map with consted operator[] for sanity
+template<typename KeyT, typename ValT>
+class bmap : public std::map<KeyT, ValT>
+{
+public:
+	const ValT & operator[](KeyT key) const
+	{
+		const_iterator it = find(key);
+		return it->second;
+	}
+	ValT & operator[](KeyT key) 
+	{
+		return static_cast<std::map<KeyT, ValT> &>(*this)[key];
+	}	
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<std::map<KeyT, ValT> &>(*this);
+	}
+};
+
 namespace vstd
 {
 	template <typename Container, typename Item>
@@ -176,6 +403,16 @@ namespace vstd
 	{
 		return c.find(i)!=c.end();
 	}
+	template <typename V, typename Item, typename Item2>
+	bool contains(const bmap<Item,V> & c, const Item2 &i) //returns true if bmap c contains item i
+	{
+		return c.find(i)!=c.end();
+	}
+	template <typename Item>
+	bool contains(const boost::unordered_set<Item> & c, const Item &i) //returns true if unordered set c contains item i
+	{
+		return c.find(i)!=c.end();
+	}
 	template <typename Container1, typename Container2>
 	typename Container2::iterator findFirstNot(Container1 &c1, Container2 &c2)//returns first element of c2 not present in c1
 	{
@@ -312,6 +549,12 @@ t1 & abetw(t1 &a, const t2 &b, const t3 &c) //makes a to fit the range <b, c>
 	return a;
 }
 
+template <typename t1, typename t2, typename t3>
+bool isbetw(const t1 &a, const t2 &b, const t3 &c) //checks if a is between b and c
+{
+	return a > b && a < c;
+}
+
 template <typename T> 
 void delNull(T* &ptr) //deleted pointer and sets it to NULL
 {

+ 0 - 152
hch/CArtHandler.h

@@ -1,152 +0,0 @@
-#ifndef __CARTHANDLER_H__
-#define __CARTHANDLER_H__
-#include "../global.h"
-#include "../lib/HeroBonus.h"
-#include <set>
-#include <list>
-#include <string>
-#include <vector>
-
-/*
- * CArtHandler.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 CDefHandler;
-class CArtifact;
-
-class DLL_EXPORT CArtifact : public CBonusSystemNode //container for artifacts
-{
-protected:
-	std::string name, description; //set if custom
-public:
-	enum EartClass {ART_SPECIAL=1, ART_TREASURE=2, ART_MINOR=4, ART_MAJOR=8, ART_RELIC=16}; //artifact classes
-	const std::string &Name() const; //getter
-	const std::string &Description() const; //getter
-	bool isBig () const;
-	bool isModable () const;
-	bool fitsAt (const std::map<ui16, const CArtifact*> &artifWorn, ui16 slot) const;
-	bool canBeAssembledTo (const std::map<ui16, const CArtifact*> &artifWorn, ui32 artifactID) const;
-	void addBonusesTo (BonusList *otherBonuses) const;
-	void removeBonusesFrom (BonusList *otherBonuses) const;
-	virtual void SetProperty (int mod){};
-	virtual void Init(){};
-	int getArtClassSerial() const; //0 - treasure, 1 - minor, 2 - major, 3 - relic, 4 - spell scroll, 5 - other
-
-	ui32 price;
-	std::vector<ui16> possibleSlots; //ids of slots where artifact can be placed
-	std::vector<ui32> * constituents; // Artifacts IDs a combined artifact consists of, or NULL.
-	std::vector<ui32> * constituentOf; // Reverse map of constituents.
-	EartClass aClass;
-	si32 id;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CBonusSystemNode&>(*this);;
-		h & name & description & price & possibleSlots & constituents & constituentOf & aClass & id;
-	}
-
-	CArtifact();
-	~CArtifact();
-
-	//override
-	void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const;
-};
-
-class DLL_EXPORT IModableArt : public CArtifact //artifact which can have different properties, such as scroll or banner
-{ //used only for dynamic cast :P
-public:
-	si32 ID; //used for smart serialization
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CArtifact&>(*this);
-		h & ID;
-	}
-};
-
-class DLL_EXPORT CScroll : public IModableArt // Spell Scroll
-{
-public:
-	CScroll(){spellid=0;};
-	CScroll(spelltype sid){spellid = sid;};
-	spelltype spellid;
-	void Init();
-	void SetProperty (int mod){spellid = mod;};
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<IModableArt&>(*this);
-		h & spellid;
-	}
-};
-
-class DLL_EXPORT CCustomizableArt : public IModableArt // Warlord's Banner with multiple options
-{
-public:
-	ui8 mode;
-	CCustomizableArt(){mode=0;};
-	void Init(){};
-	void SetProperty (int mod){};
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<IModableArt&>(*this);
-		h & mode;
-	}
-};
-
-class DLL_EXPORT CCommanderArt : public IModableArt // Growing with time
-{
-public:
-	ui32 level;
-	CCommanderArt(){level = 0;};
-	void Init(){};
-	void SetProperty (int mod){level = mod;};
-	void Upgrade(){level++;};
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<IModableArt&>(*this);
-		h & level;
-	}
-};
-
-class DLL_EXPORT CArtHandler //handles artifacts
-{
-	void giveArtBonus(int aid, Bonus::BonusType type, int val, int subtype = -1, int valType = Bonus::BASE_NUMBER, ILimiter * limiter = NULL);
-public:
-	std::vector<CArtifact*> treasures, minors, majors, relics;
-	std::vector<CArtifact *> artifacts;
-	std::vector<CArtifact *> allowedArtifacts;
-	std::set<ui32> bigArtifacts; // Artifacts that cannot be moved to backpack, e.g. war machines.
-	std::map<ui32, ui8> modableArtifacts; //1-scroll, 2-banner, 3-commander art with progressive bonus
-
-	void loadArtifacts(bool onlyTxt);
-	void sortArts();
-	void addBonuses();
-	void clear();
-	void clearHlpLists();
-	ui16 getRandomArt (int flags);
-	ui16 getArtSync (ui32 rand, int flags);
-	void getAllowedArts(std::vector<CArtifact*> &out, std::vector<CArtifact*> *arts, int flag);
-	void getAllowed(std::vector<CArtifact*> &out, int flags);
-	void erasePickedArt (si32 id);
-	bool isBigArtifact (ui32 artID) {return bigArtifacts.find(artID) != bigArtifacts.end();}
-	void equipArtifact (std::map<ui16, const CArtifact*> &artifWorn, ui16 slotID, const CArtifact* art);
-	void unequipArtifact (std::map<ui16, const CArtifact*> &artifWorn, ui16 slotID);
-	void initAllowedArtifactsList(const std::vector<ui8> &allowed); //allowed[art_id] -> 0 if not allowed, 1 if allowed
-	static int convertMachineID(int id, bool creToArt);
-	CArtHandler();
-	~CArtHandler();
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & artifacts & allowedArtifacts & treasures & minors & majors & relics;
-		//if(!h.saving) sortArts();
-	}
-};
-
-
-#endif // __CARTHANDLER_H__

+ 12 - 0
int3.h

@@ -82,6 +82,7 @@ public:
 	{
 		h & x & y & z;
 	}
+	
 };
 inline std::istream & operator>>(std::istream & str, int3 & dest)
 {
@@ -93,4 +94,15 @@ inline std::ostream & operator<<(std::ostream & str, const int3 & sth)
 	return str<<sth.x<<' '<<sth.y<<' '<<sth.z;
 }
 
+struct ShashInt3
+{
+	size_t operator()(int3 const& pos) const
+	{
+		size_t ret = ::boost::hash<int>()(pos.x);
+		boost::hash_combine(ret, pos.y);
+		boost::hash_combine(ret, pos.z);
+		return ret;
+	}
+};
+
 #endif // __INT3_H__

+ 72 - 0
lib/BattleAction.cpp

@@ -0,0 +1,72 @@
+#define VCMI_DLL
+#include "BattleAction.h"
+#include "BattleState.h"
+
+/*
+ * BattleAction.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
+ *
+ */
+
+BattleAction::BattleAction()
+{
+	side = -1;
+	stackNumber = -1;
+	actionType = INVALID;
+	destinationTile = -1;
+	additionalInfo = -1;
+}
+
+BattleAction BattleAction::makeDefend(const CStack *stack)
+{
+	BattleAction ba;
+	ba.side = !stack->attackerOwned;
+	ba.actionType = DEFEND;
+	ba.stackNumber = stack->ID;
+	return ba;
+}
+
+
+BattleAction BattleAction::makeMeleeAttack(const CStack *stack, const CStack * attacked, THex attackFrom /*= THex::INVALID*/)
+{
+	BattleAction ba;
+	ba.side = !stack->attackerOwned;
+	ba.actionType = WALK_AND_ATTACK;
+	ba.stackNumber = stack->ID;
+	ba.destinationTile = attackFrom;
+	ba.additionalInfo = attacked->position;
+	return ba;
+
+}
+BattleAction BattleAction::makeWait(const CStack *stack)
+{
+	BattleAction ba;
+	ba.side = !stack->attackerOwned;
+	ba.actionType = WAIT;
+	ba.stackNumber = stack->ID;
+	return ba;
+}
+
+BattleAction BattleAction::makeShotAttack(const CStack *shooter, const CStack *target)
+{
+	BattleAction ba;
+	ba.side = !shooter->attackerOwned;
+	ba.actionType = SHOOT;
+	ba.stackNumber = shooter->ID;
+	ba.destinationTile = target->position;
+	return ba;
+}
+
+BattleAction BattleAction::makeMove(const CStack *stack, THex dest)
+{
+	BattleAction ba;
+	ba.side = !stack->attackerOwned;
+	ba.actionType = WALK;
+	ba.stackNumber = stack->ID;
+	ba.destinationTile = dest;
+	return ba;
+}

+ 16 - 5
lib/BattleAction.h

@@ -1,6 +1,8 @@
+#pragma once;
 #ifndef __BATTLEACTION_H__
 #define __BATTLEACTION_H__
 
+#include "../global.h"
 /*
  * BattleAction.h, part of VCMI engine
  *
@@ -11,22 +13,31 @@
  *
  */
 
-struct BattleAction
+class CStack;
+
+struct DLL_EXPORT BattleAction
 {
 	ui8 side; //who made this action: false - left, true - right player
 	ui32 stackNumber;//stack ID, -1 left hero, -2 right hero,
 	enum ActionType
 	{
-		NO_ACTION = 0, HERO_SPELL, WALK, DEFEND, RETREAT, SURRENDER, WALK_AND_ATTACK, SHOOT, WAIT, CATAPULT, MONSTER_SPELL, BAD_MORALE, STACK_HEAL
+		INVALID = -1, NO_ACTION = 0, HERO_SPELL, WALK, DEFEND, RETREAT, SURRENDER, WALK_AND_ATTACK, SHOOT, WAIT, CATAPULT, MONSTER_SPELL, BAD_MORALE, STACK_HEAL
 	};
-	ui8 actionType; //    0 = No action;   1 = Hero cast a spell   2 = Walk   3 = Defend   4 = Retreat from the battle
-		//5 = Surrender   6 = Walk and Attack   7 = Shoot    8 = Wait   9 = Catapult
+	ui8 actionType; //use ActionType enum for values
 		//10 = Monster casts a spell (i.e. Faerie Dragons)	11 - Bad morale freeze	12 - stacks heals another stack
-	ui16 destinationTile;
+	THex destinationTile;
 	si32 additionalInfo; // e.g. spell number if type is 1 || 10; tile to attack if type is 6
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & side & stackNumber & actionType & destinationTile & additionalInfo;
 	}
+
+	BattleAction();
+
+	static BattleAction makeDefend(const CStack *stack);
+	static BattleAction makeWait(const CStack *stack);
+	static BattleAction makeMeleeAttack(const CStack *stack, const CStack * attacked, THex attackFrom = THex::INVALID);
+	static BattleAction makeShotAttack(const CStack *shooter, const CStack *target);
+	static BattleAction makeMove(const CStack *stack, THex dest);
 };
 #endif // __BATTLEACTION_H__

+ 2045 - 0
lib/BattleState.cpp

@@ -0,0 +1,2045 @@
+#define VCMI_DLL
+#include "BattleState.h"
+#include <fstream>
+#include <queue>
+#include <algorithm>
+#include <numeric>
+#include <sstream>
+#include <boost/foreach.hpp>
+#include <boost/assign/list_of.hpp>
+
+#include "VCMI_Lib.h"
+#include "CObjectHandler.h"
+#include "CHeroHandler.h"
+#include "CCreatureHandler.h"
+#include "CSpellHandler.h"
+#include "CTownHandler.h"
+#include "NetPacks.h"
+
+/*
+ * BattleState.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
+ *
+ */
+
+
+const CStack * BattleInfo::getNextStack() const
+{
+	std::vector<const CStack *> hlp;
+	getStackQueue(hlp, 1, -1);
+
+	if(hlp.size())
+		return hlp[0];
+	else
+		return NULL;
+}
+
+static const CStack *takeStack(std::vector<const CStack *> &st, int &curside, int turn)
+{
+	const CStack *ret = NULL;
+	unsigned i, //fastest stack
+		j; //fastest stack of the other side
+	for(i = 0; i < st.size(); i++)
+		if(st[i])
+			break;
+
+	//no stacks left
+	if(i == st.size())
+		return NULL;
+
+	const CStack *fastest = st[i], *other = NULL;
+	int bestSpeed = fastest->Speed(turn);
+
+	if(fastest->attackerOwned != curside)
+	{
+		ret = fastest;
+	}
+	else
+	{
+		for(j = i + 1; j < st.size(); j++)
+		{
+			if(!st[j]) continue;
+			if(st[j]->attackerOwned != curside || st[j]->Speed(turn) != bestSpeed)
+				break;
+		}
+
+		if(j >= st.size())
+		{
+			ret = fastest;
+		}
+		else
+		{
+			other = st[j];
+			if(other->Speed(turn) != bestSpeed)
+				ret = fastest;
+			else
+				ret = other;
+		}
+	}
+
+	assert(ret);
+	if(ret == fastest)
+		st[i] = NULL;
+	else
+		st[j] = NULL;
+
+	curside = ret->attackerOwned;
+	return ret;
+}
+
+
+
+CStack * BattleInfo::getStack(int stackID, bool onlyAlive)
+{
+	for(unsigned int g=0; g<stacks.size(); ++g)
+	{
+		if(stacks[g]->ID == stackID && (!onlyAlive || stacks[g]->alive()))
+			return stacks[g];
+	}
+	return NULL;
+}
+
+const CStack * BattleInfo::getStack(int stackID, bool onlyAlive) const
+{
+	return const_cast<BattleInfo * const>(this)->getStack(stackID, onlyAlive);
+}
+
+CStack * BattleInfo::getStackT(THex tileID, bool onlyAlive)
+{
+	for(unsigned int g=0; g<stacks.size(); ++g)
+	{
+		if(stacks[g]->position == tileID 
+			|| (stacks[g]->doubleWide() && stacks[g]->attackerOwned && stacks[g]->position-1 == tileID)
+			|| (stacks[g]->doubleWide() && !stacks[g]->attackerOwned && stacks[g]->position+1 == tileID))
+		{
+			if(!onlyAlive || stacks[g]->alive())
+			{
+				return stacks[g];
+			}
+		}
+	}
+	return NULL;
+}
+
+const CStack * BattleInfo::getStackT(THex tileID, bool onlyAlive) const
+{
+	return const_cast<BattleInfo * const>(this)->getStackT(tileID, onlyAlive);
+}
+
+void BattleInfo::getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<THex> & occupyable, bool flying, const CStack * stackToOmmit) const
+{
+	memset(accessibility, 1, BFIELD_SIZE); //initialize array with trues
+
+	//removing accessibility for side columns of hexes
+	for(int v = 0; v < BFIELD_SIZE; ++v)
+	{
+		if( v % BFIELD_WIDTH == 0 || v % BFIELD_WIDTH == (BFIELD_WIDTH - 1) )
+			accessibility[v] = false;
+	}
+
+	for(unsigned int g=0; g<stacks.size(); ++g)
+	{
+		if(!stacks[g]->alive() || stacks[g]->ID==stackToOmmit->ID || stacks[g]->position < 0) //we don't want to lock position of this stack (eg. if it's a turret)
+			continue;
+
+		accessibility[stacks[g]->position] = false;
+		if(stacks[g]->doubleWide()) //if it's a double hex creature
+		{
+			if(stacks[g]->attackerOwned)
+				accessibility[stacks[g]->position-1] = false;
+			else
+				accessibility[stacks[g]->position+1] = false;
+		}
+	}
+	//obstacles
+	for(unsigned int b=0; b<obstacles.size(); ++b)
+	{
+		std::vector<int> blocked = VLC->heroh->obstacles[obstacles[b].ID].getBlocked(obstacles[b].pos);
+		for(unsigned int c=0; c<blocked.size(); ++c)
+		{
+			if(blocked[c] >=0 && blocked[c] < BFIELD_SIZE)
+				accessibility[blocked[c]] = false;
+		}
+	}
+
+	//walls
+	if(siege > 0)
+	{
+		static const int permanentlyLocked[] = {12, 45, 78, 112, 147, 165};
+		for(int b=0; b<ARRAY_COUNT(permanentlyLocked); ++b)
+		{
+			accessibility[permanentlyLocked[b]] = false;
+		}
+
+		static const std::pair<int, THex> lockedIfNotDestroyed[] = //(which part of wall, which hex is blocked if this part of wall is not destroyed
+			{std::make_pair(2, THex(182)), std::make_pair(3, THex(130)),
+			std::make_pair(4, THex(62)), std::make_pair(5, THex(29))};
+		for(int b=0; b<ARRAY_COUNT(lockedIfNotDestroyed); ++b)
+		{
+			if(si.wallState[lockedIfNotDestroyed[b].first] < 3)
+			{
+				accessibility[lockedIfNotDestroyed[b].second] = false;
+			}
+		}
+
+		//gate
+		if(attackerOwned && si.wallState[7] < 3) //if it attacker's unit and gate is not destroyed
+		{
+			accessibility[95] = accessibility[96] = false; //block gate's hexes
+		}
+	}
+
+	//occupyability
+	if(addOccupiable && twoHex)
+	{
+		std::set<THex> rem; //tiles to unlock
+		for(int h=0; h<BFIELD_HEIGHT; ++h)
+		{
+			for(int w=1; w<BFIELD_WIDTH-1; ++w)
+			{
+				THex hex(w, h);
+				if(!isAccessible(hex, accessibility, twoHex, attackerOwned, flying, true)
+					&& (attackerOwned ? isAccessible(hex+1, accessibility, twoHex, attackerOwned, flying, true) : isAccessible(hex-1, accessibility, twoHex, attackerOwned, flying, true) )
+					)
+					rem.insert(hex);
+			}
+		}
+		occupyable = rem;
+		/*for(std::set<int>::const_iterator it = rem.begin(); it != rem.end(); ++it)
+		{
+			accessibility[*it] = true;
+		}*/
+	}
+}
+
+bool BattleInfo::isAccessible(THex hex, bool * accessibility, bool twoHex, bool attackerOwned, bool flying, bool lastPos)
+{
+	if(flying && !lastPos)
+		return true;
+
+	if(twoHex)
+	{
+		//if given hex is accessible and appropriate adjacent one is free too
+		return accessibility[hex] && accessibility[hex + (attackerOwned ? -1 : 1 )];
+	}
+	else
+	{
+		return accessibility[hex];
+	}
+}
+
+void BattleInfo::makeBFS(THex start, bool *accessibility, THex *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying, bool fillPredecessors) const //both pointers must point to the at least 187-elements int arrays
+{
+	//inits
+	for(int b=0; b<BFIELD_SIZE; ++b)
+		predecessor[b] = -1;
+	for(int g=0; g<BFIELD_SIZE; ++g)
+		dists[g] = 100000000;	
+	
+	std::queue< std::pair<THex, bool> > hexq; //bfs queue <hex, accessible> (second filed used only if fillPredecessors is true)
+	hexq.push(std::make_pair(start, true));
+	dists[hexq.front().first] = 0;
+	int curNext = -1; //for bfs loop only (helper var)
+	while(!hexq.empty()) //bfs loop
+	{
+		std::pair<THex, bool> curHex = hexq.front();
+		std::vector<THex> neighbours = curHex.first.neighbouringTiles();
+		hexq.pop();
+		for(unsigned int nr=0; nr<neighbours.size(); nr++)
+		{
+			curNext = neighbours[nr]; //if(!accessibility[curNext] || (dists[curHex]+1)>=dists[curNext])
+			bool accessible = isAccessible(curNext, accessibility, twoHex, attackerOwned, flying, dists[curHex.first]+1 == dists[curNext]);
+			if( dists[curHex.first]+1 >= dists[curNext] )
+				continue;
+			if(accessible && curHex.second)
+			{
+				hexq.push(std::make_pair(curNext, true));
+				dists[curNext] = dists[curHex.first] + 1;
+			}
+			else if(fillPredecessors && !(accessible && !curHex.second))
+			{
+				hexq.push(std::make_pair(curNext, false));
+				dists[curNext] = dists[curHex.first] + 1;
+			}
+			predecessor[curNext] = curHex.first;
+		}
+	}
+};
+
+std::vector<THex> BattleInfo::getAccessibility(const CStack * stack, bool addOccupiable) const
+{
+	std::vector<THex> ret;
+	bool ac[BFIELD_SIZE];
+
+	if(stack->position < 0) //turrets
+		return std::vector<THex>();
+
+	std::set<THex> occupyable;
+
+	getAccessibilityMap(ac, stack->doubleWide(), stack->attackerOwned, addOccupiable, occupyable, stack->hasBonusOfType(Bonus::FLYING), stack);
+
+	THex pr[BFIELD_SIZE];
+	int dist[BFIELD_SIZE];
+	makeBFS(stack->position, ac, pr, dist, stack->doubleWide(), stack->attackerOwned, stack->hasBonusOfType(Bonus::FLYING), false);
+
+	if(stack->doubleWide())
+	{
+		if(!addOccupiable)
+		{
+			std::vector<THex> rem;
+			for(int b=0; b<BFIELD_SIZE; ++b)
+			{
+				//don't take into account most left and most right columns of hexes
+				if( b % BFIELD_WIDTH == 0 || b % BFIELD_WIDTH == BFIELD_WIDTH - 1 )
+					continue;
+
+				if( ac[b] && !(stack->attackerOwned ? ac[b-1] : ac[b+1]) )
+				{
+					rem.push_back(b);
+				}
+			}
+
+			for(unsigned int g=0; g<rem.size(); ++g)
+			{
+				ac[rem[g]] = false;
+			}
+
+			//removing accessibility for side hexes
+			for(int v=0; v<BFIELD_SIZE; ++v)
+				if(stack->attackerOwned ? (v%BFIELD_WIDTH)==1 : (v%BFIELD_WIDTH)==(BFIELD_WIDTH - 2))
+					ac[v] = false;
+		}
+	}
+	
+	for (int i=0; i < BFIELD_SIZE ; ++i) {
+		if(
+			( ( !addOccupiable && dist[i] <= stack->Speed() && ac[i] ) || ( addOccupiable && dist[i] <= stack->Speed() && isAccessible(i, ac, stack->doubleWide(), stack->attackerOwned, stack->hasBonusOfType(Bonus::FLYING), true) ) )//we can reach it
+			|| (vstd::contains(occupyable, i) && ( dist[ i + (stack->attackerOwned ? 1 : -1 ) ] <= stack->Speed() ) &&
+				ac[i + (stack->attackerOwned ? 1 : -1 )] ) //it's occupyable and we can reach adjacent hex
+			)
+		{
+			ret.push_back(i);
+		}
+	}
+
+	return ret;
+}
+bool BattleInfo::isStackBlocked(const CStack * stack) const
+{
+	if(stack->hasBonusOfType(Bonus::SIEGE_WEAPON)) //siege weapons cannot be blocked
+		return false;
+
+	for(unsigned int i=0; i<stacks.size();i++)
+	{
+		if( !stacks[i]->alive()
+			|| stacks[i]->owner==stack->owner
+		  )
+			continue; //we omit dead and allied stacks
+		if(stacks[i]->doubleWide())
+		{
+			if( THex::mutualPosition(stacks[i]->position, stack->position) >= 0  
+			  || THex::mutualPosition(stacks[i]->position + (stacks[i]->attackerOwned ? -1 : 1), stack->position) >= 0)
+				return true;
+		}
+		else
+		{
+			if( THex::mutualPosition(stacks[i]->position, stack->position) >= 0 )
+				return true;
+		}
+	}
+	return false;
+}
+
+
+std::pair< std::vector<THex>, int > BattleInfo::getPath(THex start, THex dest, bool*accessibility, bool flyingCreature, bool twoHex, bool attackerOwned)
+{
+	THex predecessor[BFIELD_SIZE]; //for getting the Path
+	int dist[BFIELD_SIZE]; //calculated distances
+
+	makeBFS(start, accessibility, predecessor, dist, twoHex, attackerOwned, flyingCreature, false);
+	
+	if(predecessor[dest] == -1) //cannot reach destination
+	{
+		return std::make_pair(std::vector<THex>(), 0);
+	}
+
+	//making the Path
+	std::vector<THex> path;
+	THex curElem = dest;
+	while(curElem != start)
+	{
+		path.push_back(curElem);
+		curElem = predecessor[curElem];
+	}
+
+	return std::make_pair(path, dist[dest]);
+}
+
+TDmgRange BattleInfo::calculateDmgRange( const CStack* attacker, const CStack* defender, TQuantity attackerCount, TQuantity defenderCount, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky ) const
+{
+	float additiveBonus=1.0f, multBonus=1.0f,
+		minDmg = attacker->getMinDamage() * attackerCount, 
+		maxDmg = attacker->getMaxDamage() * attackerCount;
+
+	if(attacker->getCreature()->idNumber == 149) //arrow turret
+	{
+		switch(attacker->position)
+		{
+		case -2: //keep
+			minDmg = 15;
+			maxDmg = 15;
+			break;
+		case -3: case -4: //turrets
+			minDmg = 7.5f;
+			maxDmg = 7.5f;
+			break;
+		}
+	}
+
+	if(attacker->hasBonusOfType(Bonus::SIEGE_WEAPON) && attacker->getCreature()->idNumber != 149) //any siege weapon, but only ballista can attack (second condition - not arrow turret)
+	{ //minDmg and maxDmg are multiplied by hero attack + 1
+		minDmg *= attackerHero->getPrimSkillLevel(0) + 1; 
+		maxDmg *= attackerHero->getPrimSkillLevel(0) + 1; 
+	}
+
+	int attackDefenceDifference = 0;
+	if(attacker->hasBonusOfType(Bonus::GENERAL_ATTACK_REDUCTION))
+	{
+		float multAttackReduction = attacker->valOfBonuses(Bonus::GENERAL_ATTACK_REDUCTION, -1024) / 100.0f;
+		attackDefenceDifference = attacker->Attack() * multAttackReduction;
+	}
+	else
+	{
+		attackDefenceDifference = attacker->Attack();
+	}
+
+	if(attacker->hasBonusOfType(Bonus::ENEMY_DEFENCE_REDUCTION))
+	{
+		float multDefenceReduction = (100.0f - attacker->valOfBonuses(Bonus::ENEMY_DEFENCE_REDUCTION, -1024)) / 100.0f;
+		attackDefenceDifference -= defender->Defense() * multDefenceReduction;
+	}
+	else
+	{
+		attackDefenceDifference -= defender->Defense();
+	}
+
+	//calculating total attack/defense skills modifier
+
+	if(shooting) //precision handling (etc.)
+		attackDefenceDifference += attacker->getBonuses(Selector::typeSybtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK), Selector::effectRange(Bonus::ONLY_DISTANCE_FIGHT)).totalValue();
+	else //bloodlust handling (etc.)
+		attackDefenceDifference += attacker->getBonuses(Selector::typeSybtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK), Selector::effectRange(Bonus::ONLY_MELEE_FIGHT)).totalValue();
+
+
+	if(attacker->getEffect(55)) //slayer handling
+	{
+		std::vector<int> affectedIds;
+		int spLevel = attacker->getEffect(55)->val;
+
+		for(int g = 0; g < VLC->creh->creatures.size(); ++g)
+		{
+			BOOST_FOREACH(const Bonus *b, VLC->creh->creatures[g]->bonuses)
+			{
+				if ( (b->type == Bonus::KING3 && spLevel >= 3) || //expert
+					(b->type == Bonus::KING2 && spLevel >= 2) || //adv +
+					(b->type == Bonus::KING1 && spLevel >= 0) ) //none or basic +
+				{
+					affectedIds.push_back(g);
+					break;
+				}
+			}
+		}
+
+		for(unsigned int g=0; g<affectedIds.size(); ++g)
+		{
+			if(defender->getCreature()->idNumber == affectedIds[g])
+			{
+				attackDefenceDifference += VLC->spellh->spells[55]->powers[attacker->getEffect(55)->val];
+				break;
+			}
+		}
+	}
+
+	//bonus from attack/defense skills
+	if(attackDefenceDifference < 0) //decreasing dmg
+	{
+		float dec = 0.025f * (-attackDefenceDifference);
+		if(dec > 0.7f)
+		{
+			multBonus *= 0.3f; //1.0 - 0.7
+		}
+		else
+		{
+			multBonus *= 1.0f - dec;
+		}
+	}
+	else //increasing dmg
+	{
+		float inc = 0.05f * attackDefenceDifference;
+		if(inc > 4.0f)
+		{
+			additiveBonus += 4.0f;
+		}
+		else
+		{
+			additiveBonus += inc;
+		}
+	}
+
+
+	//applying jousting bonus
+	if( attacker->hasBonusOfType(Bonus::JOUSTING) && !defender->hasBonusOfType(Bonus::CHARGE_IMMUNITY) )
+		additiveBonus += charge * 0.05f;
+
+
+	//handling secondary abilities and artifacts giving premies to them
+	if(attackerHero)
+	{
+		if(shooting)
+		{
+			additiveBonus += attackerHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 1) / 100.0f;
+		}
+		else
+		{
+			additiveBonus += attackerHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 22) / 100.0f;
+		}
+	}
+
+	if(defendingHero)
+	{
+		multBonus *= (std::max(0, 100-defendingHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 23))) / 100.0f;
+	}
+
+	//handling hate effect
+	if( attacker->hasBonusOfType(Bonus::HATE, defender->getCreature()->idNumber) )
+		additiveBonus += 0.5f;
+
+	//luck bonus
+	if (lucky)
+	{
+		additiveBonus += 1.0f;
+	}
+
+	//handling spell effects
+	if(!shooting && defender->hasBonusOfType(Bonus::GENERAL_DAMAGE_REDUCTION, 0)) //eg. shield
+	{
+		multBonus *= float(defender->valOfBonuses(Bonus::GENERAL_DAMAGE_REDUCTION, 0)) / 100.0f;
+	}
+	else if(shooting && defender->hasBonusOfType(Bonus::GENERAL_DAMAGE_REDUCTION, 1)) //eg. air shield
+	{
+		multBonus *= float(defender->valOfBonuses(Bonus::GENERAL_DAMAGE_REDUCTION, 1)) / 100.0f;
+	}
+	if(attacker->getEffect(42)) //curse handling (partial, the rest is below)
+	{
+		multBonus *= 0.8f * float(VLC->spellh->spells[42]->powers[attacker->getEffect(42)->val]); //the second factor is 1 or 0
+	}
+
+	class HLP
+	{
+	public:
+		static bool hasAdvancedAirShield(const CStack * stack)
+		{
+
+			BOOST_FOREACH(const Bonus *it, stack->bonuses)
+			{
+				if (it->source == Bonus::SPELL_EFFECT && it->id == 28 && it->val >= 2)
+				{
+					return true;
+				}
+			}
+			return false;
+		}
+	};
+
+	//wall / distance penalty + advanced air shield
+	if (shooting && !NBonus::hasOfType(attackerHero, Bonus::NO_SHOTING_PENALTY) && (
+		hasDistancePenalty(attacker, defender->position) || hasWallPenalty(attacker, defender->position) ||
+		HLP::hasAdvancedAirShield(defender) )
+		)
+	{
+		multBonus *= 0.5;
+	}
+	if (!shooting && attacker->hasBonusOfType(Bonus::SHOOTER) && !attacker->hasBonusOfType(Bonus::NO_MELEE_PENALTY))
+	{
+		multBonus *= 0.5;
+	}
+
+	minDmg *= additiveBonus * multBonus;
+	maxDmg *= additiveBonus * multBonus;
+
+	TDmgRange returnedVal;
+
+	if(attacker->getEffect(42)) //curse handling (rest)
+	{
+		minDmg -= VLC->spellh->spells[42]->powers[attacker->getEffect(42)->val];
+		returnedVal = std::make_pair(int(minDmg), int(minDmg));
+	}
+	else if(attacker->getEffect(41)) //bless handling
+	{
+		maxDmg += VLC->spellh->spells[41]->powers[attacker->getEffect(41)->val];
+		returnedVal =  std::make_pair(int(maxDmg), int(maxDmg));
+	}
+	else
+	{
+		returnedVal =  std::make_pair(int(minDmg), int(maxDmg));
+	}
+
+	//damage cannot be less than 1
+	amax(returnedVal.first, 1);
+	amax(returnedVal.second, 1);
+
+	return returnedVal;
+}
+
+TDmgRange BattleInfo::calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky) const
+{
+	return calculateDmgRange(attacker, defender, attacker->count, defender->count, attackerHero, defendingHero, shooting, charge, lucky);
+}
+
+ui32 BattleInfo::calculateDmg( const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky )
+{
+	TDmgRange range = calculateDmgRange(attacker, defender, attackerHero, defendingHero, shooting, charge, lucky);
+
+	if(range.first != range.second)
+	{
+		int valuesToAverage[10];
+		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);
+		}
+
+		return std::accumulate(valuesToAverage, valuesToAverage + howManyToAv, 0) / howManyToAv;
+	}
+	else
+		return range.first;
+}
+
+void BattleInfo::calculateCasualties( std::map<ui32,si32> *casualties ) const
+{
+	for(unsigned int i=0; i<stacks.size();i++)//setting casualties
+	{
+		const CStack * const st = stacks[i];
+		si32 killed = (st->alive() ? st->baseAmount - st->count : st->baseAmount);
+		amax(killed, 0);
+		if(killed)
+			casualties[!st->attackerOwned][st->getCreature()->idNumber] += killed;
+	}
+}
+
+std::set<CStack*> BattleInfo::getAttackedCreatures( const CSpell * s, int skillLevel, ui8 attackerOwner, THex destinationTile )
+{
+	std::set<ui16> attackedHexes = s->rangeInHexes(destinationTile, skillLevel);
+	std::set<CStack*> attackedCres; /*std::set to exclude multiple occurrences of two hex creatures*/
+
+	bool onlyAlive = s->id != 38 && s->id != 39; //when casting resurrection or animate dead we should be allow to select dead stack
+
+	if(s->id == 24 || s->id == 25 || s->id == 26) //death ripple, destroy undead and Armageddon
+	{
+		for(int it=0; it<stacks.size(); ++it)
+		{
+			if((s->id == 24 && !stacks[it]->getCreature()->isUndead()) //death ripple
+				|| (s->id == 25 && stacks[it]->getCreature()->isUndead()) //destroy undead
+				|| (s->id == 26) //Armageddon
+				)
+			{
+				if(stacks[it]->alive())
+					attackedCres.insert(stacks[it]);
+			}
+		}
+	}
+	else if (s->range[skillLevel].size() > 1) //custom many-hex range
+	{
+		for(std::set<ui16>::iterator it = attackedHexes.begin(); it != attackedHexes.end(); ++it)
+		{
+			CStack * st = getStackT(*it, onlyAlive);
+			if(st)
+				attackedCres.insert(st);
+		}
+	}
+	else if(VLC->spellh->spells[s->id]->attributes.find("CREATURE_TARGET_1") != std::string::npos
+		|| VLC->spellh->spells[s->id]->attributes.find("CREATURE_TARGET_2") != std::string::npos) //spell to be cast on a specific creature but massive on expert
+	{
+		if(skillLevel < 3)  /*not expert */
+		{
+			CStack * st = getStackT(destinationTile, onlyAlive);
+			if(st)
+				attackedCres.insert(st);
+		}
+		else
+		{
+			for(int it=0; it<stacks.size(); ++it)
+			{
+				/*if it's non negative spell and our unit or non positive spell and hostile unit */
+				if((VLC->spellh->spells[s->id]->positiveness >= 0 && stacks[it]->owner == attackerOwner)
+					||(VLC->spellh->spells[s->id]->positiveness <= 0 && stacks[it]->owner != attackerOwner )
+					)
+				{
+					if(!onlyAlive || stacks[it]->alive())
+						attackedCres.insert(stacks[it]);
+				}
+			}
+		} //if(caster->getSpellSchoolLevel(s) < 3)
+	}
+	else if(VLC->spellh->spells[s->id]->attributes.find("CREATURE_TARGET") != std::string::npos) //spell to be cast on one specific creature
+	{
+		CStack * st = getStackT(destinationTile, onlyAlive);
+		if(st)
+			attackedCres.insert(st);
+	}
+	else //custom range from attackedHexes
+	{
+		for(std::set<ui16>::iterator it = attackedHexes.begin(); it != attackedHexes.end(); ++it)
+		{
+			CStack * st = getStackT(*it, onlyAlive);
+			if(st)
+				attackedCres.insert(st);
+		}
+	}
+	return attackedCres;
+}
+
+int BattleInfo::calculateSpellDuration( const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower )
+{
+	if(!caster) //TODO: something better
+		return std::max(5, usedSpellPower);
+	switch(spell->id)
+	{
+	case 56: //frenzy
+		return 1;
+	default: //other spells
+		return caster->getPrimSkillLevel(2) + caster->valOfBonuses(Bonus::SPELL_DURATION);
+	}
+}
+
+CStack * BattleInfo::generateNewStack(const CStackInstance &base, int stackID, bool attackerOwned, int slot, THex position) const
+{
+	int owner = attackerOwned ? sides[0] : sides[1];
+	assert(owner >= PLAYER_LIMIT  ||  base.armyObj && base.armyObj->tempOwner == owner);
+
+	CStack * ret = new CStack(&base, owner, stackID, attackerOwned, slot);
+	ret->position = position;
+	return ret;
+}
+
+CStack * BattleInfo::generateNewStack(const CStackBasicDescriptor &base, int stackID, bool attackerOwned, int slot, THex position) const
+{
+	int owner = attackerOwned ? sides[0] : sides[1];
+	CStack * ret = new CStack(&base, owner, stackID, attackerOwned, slot);
+	ret->position = position;
+	return ret;
+}
+
+ui32 BattleInfo::getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const
+{
+	ui32 ret = caster->getSpellCost(sp);
+
+	//checking for friendly stacks reducing cost of the spell and
+	//enemy stacks increasing it
+	si32 manaReduction = 0;
+	si32 manaIncrease = 0;
+	for(int g=0; g<stacks.size(); ++g)
+	{
+		if( stacks[g]->owner == caster->tempOwner && stacks[g]->hasBonusOfType(Bonus::CHANGES_SPELL_COST_FOR_ALLY) )
+		{
+			amin(manaReduction, stacks[g]->valOfBonuses(Bonus::CHANGES_SPELL_COST_FOR_ALLY));
+		}
+		if( stacks[g]->owner != caster->tempOwner && stacks[g]->hasBonusOfType(Bonus::CHANGES_SPELL_COST_FOR_ENEMY) )
+		{
+			amax(manaIncrease, stacks[g]->valOfBonuses(Bonus::CHANGES_SPELL_COST_FOR_ENEMY));
+		}
+	}
+
+	return ret + manaReduction + manaIncrease;
+}
+
+int BattleInfo::hexToWallPart(THex hex) const
+{
+	if(siege == 0) //there is no battle!
+		return -1;
+
+	static const std::pair<int, int> attackable[] = //potentially attackable parts of wall
+	{std::make_pair(50, 0), std::make_pair(183, 1), std::make_pair(182, 2), std::make_pair(130, 3),
+	std::make_pair(62, 4), std::make_pair(29, 5), std::make_pair(12, 6), std::make_pair(95, 7), std::make_pair(96, 7)};
+
+	for(int g = 0; g < ARRAY_COUNT(attackable); ++g)
+	{
+		if(attackable[g].first == hex)
+			return attackable[g].second;
+	}
+
+	return -1; //not found!
+}
+
+int BattleInfo::lineToWallHex( int line ) const
+{
+	static const int lineToHex[] = {12, 29, 45, 62, 78, 95, 112, 130, 147, 165, 182};
+
+	return lineToHex[line];
+}
+
+std::pair<const CStack *, THex> BattleInfo::getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const
+{	
+	bool ac[BFIELD_SIZE];
+	std::set<THex> occupyable;
+
+	getAccessibilityMap(ac, closest->doubleWide(), closest->attackerOwned, false, occupyable, closest->hasBonusOfType(Bonus::FLYING), closest);
+
+	THex predecessor[BFIELD_SIZE];
+	int dist[BFIELD_SIZE];
+	makeBFS(closest->position, ac, predecessor, dist, closest->doubleWide(), closest->attackerOwned, closest->hasBonusOfType(Bonus::FLYING), true);
+
+	std::vector< std::pair< std::pair<int, int>, const CStack *> > stackPairs; //pairs <<distance, hex>, stack>
+	for(int g=0; g<BFIELD_SIZE; ++g)
+	{
+		const CStack * atG = getStackT(g);
+		if(!atG || atG->ID == closest->ID) //if there is not stack or we are the closest one
+			continue;
+		if(boost::logic::indeterminate(attackerOwned) || atG->attackerOwned == attackerOwned)
+		{
+			if(predecessor[g] == -1) //TODO: is it really the best solution?
+				continue;
+			stackPairs.push_back( std::make_pair( std::make_pair(dist[predecessor[g]], g), atG) );
+		}
+	}
+
+	if(stackPairs.size() > 0)
+	{
+		std::vector< std::pair< std::pair<int, int>, const CStack *> > minimalPairs;
+		minimalPairs.push_back(stackPairs[0]);
+
+		for(int b=1; b<stackPairs.size(); ++b)
+		{
+			if(stackPairs[b].first.first < minimalPairs[0].first.first)
+			{
+				minimalPairs.clear();
+				minimalPairs.push_back(stackPairs[b]);
+			}
+			else if(stackPairs[b].first.first == minimalPairs[0].first.first)
+			{
+				minimalPairs.push_back(stackPairs[b]);
+			}
+		}
+
+		std::pair< std::pair<int, int>, const CStack *> minPair = minimalPairs[minimalPairs.size()/2];
+
+		return std::make_pair(minPair.second, predecessor[minPair.first.second]);
+	}
+
+	return std::make_pair<const CStack * , THex>(NULL, THex::INVALID);
+}
+ui32 BattleInfo::calculateSpellBonus(ui32 baseDamage, const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature) const
+{
+	ui32 ret = baseDamage;
+	//applying sorcery secondary skill
+	if(caster)
+	{
+		ret *= (100.f + caster->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 25)) / 100.0f; //sorcery
+		ret *= (100.f + caster->valOfBonuses(Bonus::SPELL_DAMAGE) + caster->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, sp->id)) / 100.0f;
+
+		if(sp->air)
+			ret *= (100.0f + caster->valOfBonuses(Bonus::AIR_SPELL_DMG_PREMY)) / 100.0f;
+		else if(sp->fire) //only one type of bonus for Magic Arrow
+			ret *= (100.0f + caster->valOfBonuses(Bonus::FIRE_SPELL_DMG_PREMY)) / 100.0f;
+		else if(sp->water)
+			ret *= (100.0f + caster->valOfBonuses(Bonus::WATER_SPELL_DMG_PREMY)) / 100.0f;
+		else if(sp->earth)
+			ret *= (100.0f + caster->valOfBonuses(Bonus::EARTH_SPELL_DMG_PREMY)) / 100.0f;
+
+		if (affectedCreature) //Hero specials like Solmyr, Deemer
+			ret *= (100.f + ((caster->valOfBonuses(Bonus::SPECIAL_SPELL_LEV, sp->id) * caster->level) / affectedCreature->getCreature()->level)) / 100.0f;
+	}
+	return ret;
+}
+
+ui32 BattleInfo::calculateSpellDmg( const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower ) const
+{
+	ui32 ret = 0; //value to return
+
+	//15 - magic arrows, 16 - ice bolt, 17 - lightning bolt, 18 - implosion, 20 - frost ring, 21 - fireball, 22 - inferno, 23 - meteor shower,
+	//24 - death ripple, 25 - destroy undead, 26 - armageddon, 77 - thunderbolt
+	
+	//FIXME: what point of dmgMultipliers map? all damage multipliers are already present in CSpell::power 
+	static std::map <int, int> dmgMultipliers = boost::assign::map_list_of(15, 10)(16, 20)(17, 25)(18, 75)(20, 10)(21, 10)(22, 10)(23, 10)(24, 5)(25, 10)(26, 50)(77, 10);
+
+	//check if spell really does damage - if not, return 0
+	if(dmgMultipliers.find(sp->id) == dmgMultipliers.end())
+		return 0;
+
+	ret = usedSpellPower * dmgMultipliers[sp->id];
+	ret += sp->powers[spellSchoolLevel];
+
+	//affected creature-specific part
+	if(affectedCreature)
+	{
+		//applying protections - when spell has more then one elements, only one protection should be applied (I think)
+		if(sp->air && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, 0)) //air spell & protection from air
+		{
+			ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, 0);
+			ret /= 100;
+		}
+		else if(sp->fire && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, 1)) //fire spell & protection from fire
+		{
+			ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, 1);
+			ret /= 100;
+		}
+		else if(sp->water && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, 2)) //water spell & protection from water
+		{
+			ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, 2);
+			ret /= 100;
+		}
+		else if (sp->earth && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, 3)) //earth spell & protection from earth
+		{
+			ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, 3);
+			ret /= 100;
+		}
+		//general spell dmg reduction
+		if(sp->air && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, -1)) //air spell & protection from air
+		{
+			ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, -1);
+			ret /= 100;
+		}
+		//dmg increasing
+		if( affectedCreature->hasBonusOfType(Bonus::MORE_DAMAGE_FROM_SPELL, sp->id) )
+		{
+			ret *= 100 + affectedCreature->valOfBonuses(Bonus::MORE_DAMAGE_FROM_SPELL, sp->id);
+			ret /= 100;
+		}
+	}
+	ret = calculateSpellBonus(ret, sp, caster, affectedCreature);
+	return ret;
+}
+
+ui32 BattleInfo::calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack) const
+{
+	int powerPerLevel;
+	bool resurrect;
+	switch(spell->id)
+	{
+	case 37: //cure
+		{
+			powerPerLevel = 5;
+			resurrect = false;
+			break;
+		}
+	case 38: //resurrection
+	case 39: //animate dead
+		{
+			powerPerLevel = 50;
+			resurrect = true;
+			break;
+		}
+	}
+	int healedHealth = caster->getPrimSkillLevel(2) * powerPerLevel + spell->powers[caster->getSpellSchoolLevel(spell)];
+	healedHealth = calculateSpellBonus(healedHealth, spell, caster, stack);
+	return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + (resurrect ? stack->baseAmount * stack->MaxHealth() : 0));
+}
+
+void BattleInfo::getStackQueue( std::vector<const CStack *> &out, int howMany, int turn /*= 0*/, int lastMoved /*= -1*/ ) const
+{
+	//we'll split creatures with remaining movement to 4 parts
+	std::vector<const CStack *> phase[4]; //0 - turrets/catapult, 1 - normal (unmoved) creatures, other war machines, 2 - waited cres that had morale, 3 - rest of waited cres
+	int toMove = 0; //how many stacks still has move
+	const CStack *active = getStack(activeStack);
+
+	//active stack hasn't taken any action yet - must be placed at the beginning of queue, no matter what
+	if(!turn && active && active->willMove() && !vstd::contains(active->state, WAITING))
+	{
+		out.push_back(active);
+		if(out.size() == howMany)
+			return;
+	}
+
+
+	for(unsigned int i=0; i<stacks.size(); ++i)
+	{
+		const CStack * const s = stacks[i];
+		if(turn <= 0 && !s->willMove() //we are considering current round and stack won't move
+			|| turn > 0 && !s->canMove(turn) //stack won't be able to move in later rounds
+			|| turn <= 0 && s == active && out.size() && s == out.front()) //it's active stack already added at the beginning of queue
+		{
+			continue;
+		}
+
+		int p = -1; //in which phase this tack will move?
+		if(turn <= 0 && vstd::contains(s->state, WAITING)) //consider waiting state only for ongoing round
+		{
+			if(vstd::contains(s->state, HAD_MORALE))
+				p = 2;
+			else
+				p = 3;
+		}
+		else if(s->getCreature()->idNumber == 145  ||  s->getCreature()->idNumber == 149) //catapult and turrets are first
+		{
+			p = 0;
+		}
+		else
+		{
+			p = 1;
+		}
+
+		phase[p].push_back(s);
+		toMove++;
+	}
+
+	for(int i = 0; i < 4; i++)
+		std::sort(phase[i].begin(), phase[i].end(), CMP_stack(i, turn > 0 ? turn : 0));
+
+	for(size_t i = 0; i < phase[0].size() && i < howMany; i++)
+		out.push_back(phase[0][i]);
+
+	if(out.size() == howMany)
+		return;
+
+	if(lastMoved == -1)
+	{
+		if(active)
+		{
+			if(out.size() && out.front() == active)
+				lastMoved = active->attackerOwned;
+			else
+				lastMoved = active->attackerOwned;
+		}
+		else
+		{
+			lastMoved = 0;
+		}
+	}
+
+	int pi = 1;
+	while(out.size() < howMany)
+	{
+		const CStack *hlp = takeStack(phase[pi], lastMoved, turn);
+		if(!hlp)
+		{
+			pi++;
+			if(pi > 3)
+			{
+				//if(turn != 2)
+				getStackQueue(out, howMany, turn + 1, lastMoved);
+				return;
+			}
+		}
+		else
+		{
+			out.push_back(hlp);
+		}
+	}
+}
+
+si8 BattleInfo::hasDistancePenalty( const CStack * stack, THex destHex ) const
+{
+	struct HLP
+	{
+		static bool lowerAnalyze(const CStack * stack, THex hex)
+		{
+			int distance = THex::getDistance(hex, stack->position);
+
+			//I hope it's approximately correct
+			return distance > 10 && !stack->hasBonusOfType(Bonus::NO_DISTANCE_PENALTY);
+		}
+	};
+	const CStack * dstStack = getStackT(destHex, false);
+
+	if (dstStack->doubleWide())
+		return HLP::lowerAnalyze(stack, destHex) && HLP::lowerAnalyze(stack, dstStack->occupiedHex());
+	else
+		return HLP::lowerAnalyze(stack, destHex);
+
+}
+
+si8 BattleInfo::sameSideOfWall(int pos1, int pos2) const
+{
+	int wallInStackLine = lineToWallHex(pos1/BFIELD_WIDTH);
+	int wallInDestLine = lineToWallHex(pos2/BFIELD_WIDTH);
+
+	bool stackLeft = pos1 < wallInStackLine;
+	bool destLeft = pos2 < wallInDestLine;
+
+	return stackLeft != destLeft;
+}
+
+si8 BattleInfo::hasWallPenalty( const CStack* stack, THex destHex ) const
+{
+	if (siege == 0)
+	{
+		return false;
+	}
+	if (stack->hasBonusOfType(Bonus::NO_WALL_PENALTY))
+	{
+		return false;
+	}
+
+	return !sameSideOfWall(stack->position, destHex);
+}
+
+si8 BattleInfo::canTeleportTo(const CStack * stack, THex destHex, int telportLevel) const
+{
+	bool ac[BFIELD_SIZE];
+
+	std::set<THex> occupyable;
+
+	getAccessibilityMap(ac, stack->doubleWide(), stack->attackerOwned, false, occupyable, stack->hasBonusOfType(Bonus::FLYING), stack);
+
+	if (siege && telportLevel < 2) //check for wall
+	{
+		return ac[destHex] && sameSideOfWall(stack->position, destHex);
+	}
+	else
+	{
+		return ac[destHex];
+	}
+
+}
+
+// void BattleInfo::getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root /*= NULL*/) const
+// {
+// 	CBonusSystemNode::getBonuses(out, selector, root);
+// 
+// 	const CStack *dest = dynamic_cast<const CStack*>(root);
+// 	if (!dest)
+// 		return;
+// 
+// 	//TODO: make it in clean way
+// 	if(Selector::matchesType(selector, Bonus::MORALE) || Selector::matchesType(selector, Bonus::LUCK))
+// 	{
+// 		BOOST_FOREACH(const CStack *s, stacks)
+// 		{
+// 			if(s->owner == dest->owner)
+// 				s->getBonuses(out, selector, Selector::effectRange(Bonus::ONLY_ALLIED_ARMY), this);
+// 			else
+// 				s->getBonuses(out, selector, Selector::effectRange(Bonus::ONLY_ENEMY_ARMY), this);
+// 		}
+// 	}
+// }
+
+bool BattleInfo::battleCanShoot(const CStack * stack, THex dest) const
+{
+	const CStack *dst = getStackT(dest);
+
+	if(!stack || !dst) return false;
+
+	const CGHeroInstance * stackHero = battleGetOwner(stack);
+
+	if(stack->hasBonusOfType(Bonus::FORGETFULL)) //forgetfulness
+		return false;
+
+	if(stack->getCreature()->idNumber == 145 && dst) //catapult cannot attack creatures
+		return false;
+
+	if(stack->hasBonusOfType(Bonus::SHOOTER)//it's shooter
+		&& stack->owner != dst->owner
+		&& dst->alive()
+		&& (!isStackBlocked(stack)  ||  NBonus::hasOfType(stackHero, Bonus::FREE_SHOOTING))
+		&& stack->shots
+		)
+		return true;
+	return false;
+}
+
+bool BattleInfo::battleCanFlee(int player) const
+{
+	if (player == sides[0])
+	{
+		if (!heroes[0])
+			return false;//current player have no hero
+	}
+	else
+	{
+		if (!heroes[1])
+			return false;
+	}
+
+	if( ( heroes[0] && heroes[0]->hasBonusOfType(Bonus::ENEMY_CANT_ESCAPE) ) //eg. one of heroes is wearing shakles of war
+		|| ( heroes[1] && heroes[1]->hasBonusOfType(Bonus::ENEMY_CANT_ESCAPE)))
+		return false;
+
+	if (player == sides[1] && siege //defender in siege
+		&& !(town->subID == 6 && vstd::contains(town->builtBuildings, 17)))//without escape tunnel
+		return false;
+
+	return true;
+}
+
+const CStack * BattleInfo::battleGetStack(THex pos, bool onlyAlive)
+{
+	for(unsigned int g=0; g<stacks.size(); ++g)
+	{
+		if((stacks[g]->position == pos 
+			|| (stacks[g]->doubleWide() 
+			&&( (stacks[g]->attackerOwned && stacks[g]->position-1 == pos) 
+			||	(!stacks[g]->attackerOwned && stacks[g]->position+1 == pos)	)
+			))
+			&& (!onlyAlive || stacks[g]->alive())
+			)
+			return stacks[g];
+	}
+	return NULL;
+}
+
+const CGHeroInstance * BattleInfo::battleGetOwner(const CStack * stack) const
+{
+	return heroes[!stack->attackerOwned];
+}
+
+si8 BattleInfo::battleMaxSpellLevel() const
+{
+// 	if(!curB) //there is not battle
+// 	{
+// 		tlog1 << "si8 CGameState::maxSpellLevel() call when there is no battle!" << std::endl;
+// 		throw "si8 CGameState::maxSpellLevel() call when there is no battle!";
+// 	}
+
+	si8 levelLimit = SPELL_LEVELS;
+
+	const CGHeroInstance *h1 =  heroes[0];
+	if(h1)
+	{
+		BOOST_FOREACH(const Bonus *i, h1->bonuses)
+			if(i->type == Bonus::BLOCK_SPELLS_ABOVE_LEVEL)
+				amin(levelLimit, i->val);
+	}
+
+	const CGHeroInstance *h2 = heroes[1];
+	if(h2)
+	{
+
+		BOOST_FOREACH(const Bonus *i, h2->bonuses)
+			if(i->type == Bonus::BLOCK_SPELLS_ABOVE_LEVEL)
+				amin(levelLimit, i->val);
+	}
+
+	return levelLimit;
+}
+
+void BattleInfo::localInit()
+{
+	belligerents[0]->battle = belligerents[1]->battle = this;
+	//TODO: attach battle to belligerents
+
+	BOOST_FOREACH(CStack *s, stacks)
+	{
+		if(s->base) //stack originating from "real" stack in garrison -> attach to it
+		{
+			s->attachTo(const_cast<CStackInstance*>(s->base));
+		}
+		else //attach directly to obj to which stack belongs and creature type
+		{
+			CArmedInstance *army = belligerents[!s->attackerOwned];
+			s->attachTo(army);
+			assert(s->type);
+			s->attachTo(const_cast<CCreature*>(s->type));
+		}
+		s->postInit();
+	}
+}
+
+namespace CGH
+{
+	using namespace std;
+	static void readItTo(ifstream & input, vector< vector<int> > & dest) //reads 7 lines, i-th one containing i integers, and puts it to dest
+	{
+		for(int j=0; j<7; ++j)
+		{
+			std::vector<int> pom;
+			for(int g=0; g<j+1; ++g)
+			{
+				int hlp; input>>hlp;
+				pom.push_back(hlp);
+			}
+			dest.push_back(pom);
+		}
+	}
+}
+
+BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town )
+{
+	CMP_stack cmpst;
+	BattleInfo *curB = new BattleInfo;
+	curB->sides[0] = armies[0]->tempOwner;
+	curB->sides[1] = armies[1]->tempOwner;
+	if(curB->sides[1] == 254) 
+		curB->sides[1] = 255;
+
+	std::vector<CStack*> & stacks = (curB->stacks);
+
+	curB->tile = tile;
+	curB->battlefieldType = terType;
+	curB->belligerents[0] = const_cast<CArmedInstance*>(armies[0]);
+	curB->belligerents[1] = const_cast<CArmedInstance*>(armies[1]);
+	curB->heroes[0] = const_cast<CGHeroInstance*>(heroes[0]);
+	curB->heroes[1] = const_cast<CGHeroInstance*>(heroes[1]);
+	curB->round = -2;
+	curB->activeStack = -1;
+
+	if(town)
+	{
+		curB->town = town;
+		curB->siege = town->fortLevel();
+	}
+	else
+	{
+		curB->town = NULL;
+		curB->siege = 0;
+	}
+
+	//reading battleStartpos
+	std::ifstream positions;
+	positions.open(DATA_DIR "/config/battleStartpos.txt", std::ios_base::in|std::ios_base::binary);
+	if(!positions.is_open())
+	{
+		tlog1<<"Unable to open battleStartpos.txt!"<<std::endl;
+	}
+	std::string dump;
+	positions>>dump; positions>>dump;
+	std::vector< std::vector<int> > attackerLoose, defenderLoose, attackerTight, defenderTight, attackerCreBank, defenderCreBank;
+	CGH::readItTo(positions, attackerLoose);
+	positions>>dump;
+	CGH::readItTo(positions, defenderLoose);
+	positions>>dump;
+	positions>>dump;
+	CGH::readItTo(positions, attackerTight);
+	positions>>dump;
+	CGH::readItTo(positions, defenderTight);
+	positions>>dump;
+	positions>>dump;
+	CGH::readItTo(positions, attackerCreBank);
+	positions>>dump;
+	CGH::readItTo(positions, defenderCreBank);
+	positions.close();
+	//battleStartpos read
+
+	int k = 0; //stack serial 
+	for(TSlots::const_iterator i = armies[0]->Slots().begin(); i!=armies[0]->Slots().end(); i++, k++)
+	{
+		int pos;
+		if(creatureBank)
+			pos = attackerCreBank[armies[0]->stacksCount()-1][k];
+		else if(armies[0]->formation)
+			pos = attackerTight[armies[0]->stacksCount()-1][k];
+		else
+			pos = attackerLoose[armies[0]->stacksCount()-1][k];
+
+		CStack * stack = curB->generateNewStack(*i->second, stacks.size(), true, i->first, pos);
+		stacks.push_back(stack);
+	}
+
+	k = 0;
+	for(TSlots::const_iterator i = armies[1]->Slots().begin(); i!=armies[1]->Slots().end(); i++, k++)
+	{
+		int pos;
+		if(creatureBank)
+			pos = defenderCreBank[armies[1]->stacksCount()-1][k];
+		else if(armies[1]->formation)
+			pos = defenderTight[armies[1]->stacksCount()-1][k];
+		else
+			pos = defenderLoose[armies[1]->stacksCount()-1][k];
+
+		CStack * stack = curB->generateNewStack(*i->second, stacks.size(), false, i->first, pos);
+		stacks.push_back(stack);
+	}
+
+	for(unsigned g=0; g<stacks.size(); ++g) //shifting positions of two-hex creatures
+	{
+		//we should do that for creature bank too
+		if(stacks[g]->doubleWide() && stacks[g]->attackerOwned)
+		{
+			stacks[g]->position += THex::RIGHT;
+		}
+		else if(stacks[g]->doubleWide() && !stacks[g]->attackerOwned)
+		{
+			stacks[g]->position += THex::LEFT;
+		}
+	}
+
+	//adding war machines
+	if(heroes[0])
+	{
+		if(heroes[0]->getArt(13)) //ballista
+		{
+			CStack * stack = curB->generateNewStack(CStackBasicDescriptor(146, 1), stacks.size(), true, 255, 52);
+			stacks.push_back(stack);
+		}
+		if(heroes[0]->getArt(14)) //ammo cart
+		{
+			CStack * stack = curB->generateNewStack(CStackBasicDescriptor(148, 1), stacks.size(), true, 255, 18);
+			stacks.push_back(stack);
+		}
+		if(heroes[0]->getArt(15)) //first aid tent
+		{
+			CStack * stack = curB->generateNewStack(CStackBasicDescriptor(147, 1), stacks.size(), true, 255, 154);
+			stacks.push_back(stack);
+		}
+	}
+	if(heroes[1])
+	{
+		//defending hero shouldn't receive ballista (bug #551)
+		if(heroes[1]->getArt(13) && !town) //ballista
+		{
+			CStack * stack = curB->generateNewStack(CStackBasicDescriptor(146, 1),  stacks.size(), false, 255, 66);
+			stacks.push_back(stack);
+		}
+		if(heroes[1]->getArt(14)) //ammo cart
+		{
+			CStack * stack = curB->generateNewStack(CStackBasicDescriptor(148, 1), stacks.size(), false, 255, 32);
+			stacks.push_back(stack);
+		}
+		if(heroes[1]->getArt(15)) //first aid tent
+		{
+			CStack * stack = curB->generateNewStack(CStackBasicDescriptor(147, 1), stacks.size(), false, 255, 168);
+			stacks.push_back(stack);
+		}
+	}
+	if(town && heroes[0] && town->hasFort()) //catapult
+	{
+		CStack * stack = curB->generateNewStack(CStackBasicDescriptor(145, 1), stacks.size(), true, 255, 120);
+		stacks.push_back(stack);
+	}
+	//war machines added
+
+	switch(curB->siege) //adding towers
+	{
+
+	case 3: //castle
+		{//lower tower / upper tower
+			CStack * stack = curB->generateNewStack(CStackBasicDescriptor(149, 1), stacks.size(), false, 255, -4);
+			stacks.push_back(stack);
+			stack = curB->generateNewStack(CStackBasicDescriptor(149, 1), stacks.size(), false, 255, -3);
+			stacks.push_back(stack);
+		}
+	case 2: //citadel
+		{//main tower
+			CStack * stack = curB->generateNewStack(CStackBasicDescriptor(149, 1), stacks.size(), false, 255, -2);
+			stacks.push_back(stack);
+		}
+	}
+
+	std::stable_sort(stacks.begin(),stacks.end(),cmpst);
+
+	//seting up siege
+	if(town && town->hasFort())
+	{
+		for(int b=0; b<ARRAY_COUNT(curB->si.wallState); ++b)
+		{
+			curB->si.wallState[b] = 1;
+		}
+	}
+
+	//randomize obstacles
+	if(town == NULL && !creatureBank) //do it only when it's not siege and not creature bank
+	{
+		bool obAv[BFIELD_SIZE]; //availability of hexes for obstacles;
+		std::vector<int> possibleObstacles;
+
+		for(int i=0; i<BFIELD_SIZE; ++i)
+		{
+			if(i%17 < 4 || i%17 > 12)
+			{
+				obAv[i] = false;
+			}
+			else
+			{
+				obAv[i] = true;
+			}
+		}
+
+		for(std::map<int, CObstacleInfo>::const_iterator g=VLC->heroh->obstacles.begin(); g!=VLC->heroh->obstacles.end(); ++g)
+		{
+			if(g->second.allowedTerrains[terType-1] == '1') //we need to take terType with -1 because terrain ids start from 1 and allowedTerrains array is indexed from 0
+			{
+				possibleObstacles.push_back(g->first);
+			}
+		}
+
+		srand(time(NULL));
+		if(possibleObstacles.size() > 0) //we cannot place any obstacles when we don't have them
+		{
+			int toBlock = rand()%6 + 6; //how many hexes should be blocked by obstacles
+			while(toBlock>0)
+			{
+				CObstacleInstance coi;
+				coi.uniqueID = curB->obstacles.size();
+				coi.ID = possibleObstacles[rand()%possibleObstacles.size()];
+				coi.pos = rand()%BFIELD_SIZE;
+				std::vector<int> block = VLC->heroh->obstacles[coi.ID].getBlocked(coi.pos);
+				bool badObstacle = false;
+				for(int b=0; b<block.size(); ++b)
+				{
+					if(block[b] < 0 || block[b] >= BFIELD_SIZE || !obAv[block[b]])
+					{
+						badObstacle = true;
+						break;
+					}
+				}
+				if(badObstacle) continue;
+				//obstacle can be placed
+				curB->obstacles.push_back(coi);
+				for(int b=0; b<block.size(); ++b)
+				{
+					if(block[b] >= 0 && block[b] < BFIELD_SIZE)
+						obAv[block[b]] = false;
+				}
+				toBlock -= block.size();
+			}
+		}
+	}
+
+	// 	//giving building bonuses, if siege and we have harrisoned hero
+	// 	if (town)
+	// 	{
+	// 		if (heroes[1])
+	// 		{
+	// 			for (int i=0; i<4; i++)
+	// 			{
+	// 				int val = town->defenceBonus(i);
+	// 				if (val)
+	// 				{
+	// 					GiveBonus gs;
+	// 					gs.bonus = Bonus(Bonus::ONE_BATTLE, Bonus::PRIMARY_SKILL, Bonus::OBJECT, val, -1, "", i);
+	// 					gs.id = heroes[1]->id;
+	// 					sendAndApply(&gs);
+	// 				}
+	// 			}
+	// 		}
+	// 		else//if we don't have hero - apply separately, if hero present - will be taken from hero bonuses
+	// 		{
+	// 			if(town->subID == 0  &&  vstd::contains(town->builtBuildings,22)) //castle, brotherhood of sword built
+	// 				for(int g=0; g<stacks.size(); ++g)
+	// 					stacks[g]->addNewBonus(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, 2, Bonus::TOWN_STRUCTURE));
+	// 
+	// 			else if(vstd::contains(town->builtBuildings,5)) //tavern is built
+	// 				for(int g=0; g<stacks.size(); ++g)
+	// 					stacks[g]->addNewBonus(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, 1, Bonus::TOWN_STRUCTURE));
+	// 
+	// 			if(town->subID == 1  &&  vstd::contains(town->builtBuildings,21)) //rampart, fountain of fortune is present
+	// 				for(int g=0; g<stacks.size(); ++g)
+	// 					stacks[g]->addNewBonus(makeFeature(Bonus::LUCK, Bonus::ONE_BATTLE, 0, 2, Bonus::TOWN_STRUCTURE));
+	// 		}
+	// 	}
+
+	//giving terrain overalay premies
+	int bonusSubtype = -1;
+	switch(terType)
+	{
+	case 9: //magic plains
+		{
+			bonusSubtype = 0;
+		}
+	case 14: //fiery fields
+		{
+			if(bonusSubtype == -1) bonusSubtype = 1;
+		}
+	case 15: //rock lands
+		{
+			if(bonusSubtype == -1) bonusSubtype = 8;
+		}
+	case 16: //magic clouds
+		{
+			if(bonusSubtype == -1) bonusSubtype = 2;
+		}
+	case 17: //lucid pools
+		{
+			if(bonusSubtype == -1) bonusSubtype = 4;
+		}
+
+		{ //common part for cases 9, 14, 15, 16, 17
+			curB->addNewBonus(new Bonus(Bonus::ONE_BATTLE, Bonus::MAGIC_SCHOOL_SKILL, Bonus::TERRAIN_OVERLAY, 3, -1, "", bonusSubtype));
+			break;
+		}
+
+	case 18: //holy ground
+		{
+			curB->addNewBonus(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, +1, Bonus::TERRAIN_OVERLAY)->addLimiter(new CreatureAlignmentLimiter(GOOD)));
+			curB->addNewBonus(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, -1, Bonus::TERRAIN_OVERLAY)->addLimiter(new CreatureAlignmentLimiter(EVIL)));
+			break;
+		}
+	case 19: //clover field
+		{ //+2 luck bonus for neutral creatures
+			curB->addNewBonus(makeFeature(Bonus::LUCK, Bonus::ONE_BATTLE, 0, +2, Bonus::TERRAIN_OVERLAY)->addLimiter(new CreatureFactionLimiter(-1)));
+			break;
+		}
+	case 20: //evil fog
+		{
+			curB->addNewBonus(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, -1, Bonus::TERRAIN_OVERLAY)->addLimiter(new CreatureAlignmentLimiter(GOOD)));
+			curB->addNewBonus(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, +1, Bonus::TERRAIN_OVERLAY)->addLimiter(new CreatureAlignmentLimiter(EVIL)));
+			break;
+		}
+	case 22: //cursed ground
+		{
+			curB->addNewBonus(makeFeature(Bonus::NO_MORALE, Bonus::ONE_BATTLE, 0, 0, Bonus::TERRAIN_OVERLAY));
+			curB->addNewBonus(makeFeature(Bonus::NO_LUCK, Bonus::ONE_BATTLE, 0, 0, Bonus::TERRAIN_OVERLAY));
+			curB->addNewBonus(makeFeature(Bonus::BLOCK_SPELLS_ABOVE_LEVEL, Bonus::ONE_BATTLE, 0, 1, Bonus::TERRAIN_OVERLAY));
+			break;
+		}
+	}
+	//overlay premies given
+
+	//native terrain bonuses
+	if(town) //during siege always take premies for native terrain of faction
+		terrain = VLC->heroh->nativeTerrains[town->town->typeID];
+
+	ILimiter *nativeTerrain = new CreatureNativeTerrainLimiter(terrain);
+	curB->addNewBonus(makeFeature(Bonus::STACKS_SPEED, Bonus::ONE_BATTLE, 0, 1, Bonus::TERRAIN_NATIVE)->addLimiter(nativeTerrain));
+	curB->addNewBonus(makeFeature(Bonus::PRIMARY_SKILL, Bonus::ONE_BATTLE, PrimarySkill::ATTACK, 1, Bonus::TERRAIN_NATIVE)->addLimiter(nativeTerrain));
+	curB->addNewBonus(makeFeature(Bonus::PRIMARY_SKILL, Bonus::ONE_BATTLE, PrimarySkill::DEFENSE, 1, Bonus::TERRAIN_NATIVE)->addLimiter(nativeTerrain));
+	//////////////////////////////////////////////////////////////////////////
+
+	return curB;
+}
+
+
+
+
+
+CStack::CStack(const CStackInstance *Base, int O, int I, bool AO, int S)
+	: base(Base), ID(I), owner(O), slot(S), attackerOwned(AO),   
+	counterAttacks(1)
+{
+	assert(base);
+	type = base->type;
+	count = baseAmount = base->count;
+}
+
+CStack::CStack()
+{
+	init();
+}
+
+CStack::CStack(const CStackBasicDescriptor *stack, int O, int I, bool AO, int S)
+	: base(NULL), ID(I), owner(O), slot(S), attackerOwned(AO), counterAttacks(1)
+{
+	type = stack->type;
+	count = baseAmount = stack->count;
+}
+
+void CStack::init()
+{
+	base = NULL;
+	type = NULL;
+	ID = -1;
+	count = baseAmount = -1;
+	firstHPleft = -1;
+	owner = 255;
+	slot = 255;
+	attackerOwned = false;
+	position = THex();
+	counterAttacks = -1;
+}
+
+void CStack::postInit()
+{
+	assert(type);
+	assert(parents.size());
+
+	firstHPleft = valOfBonuses(Bonus::STACK_HEALTH);
+	shots = getCreature()->shots;
+	counterAttacks = 1 + valOfBonuses(Bonus::ADDITIONAL_RETALIATION);
+	state.insert(ALIVE);  //alive state indication
+
+
+	assert(firstHPleft > 0);
+}
+
+ui32 CStack::Speed( int turn /*= 0*/ ) const
+{
+	if(hasBonus(Selector::type(Bonus::SIEGE_WEAPON) && Selector::turns(turn))) //war machines cannot move
+		return 0;
+
+	int speed = valOfBonuses(Selector::type(Bonus::STACKS_SPEED) && Selector::turns(turn));
+
+	int percentBonus = 0;
+	BOOST_FOREACH(const Bonus *b, bonuses)
+	{
+		if(b->type == Bonus::STACKS_SPEED)
+		{
+			percentBonus += b->additionalInfo;
+		}
+	}
+
+	speed = ((100 + percentBonus) * speed)/100;
+
+	//bind effect check
+	if(getEffect(72)) 
+	{
+		return 0;
+	}
+
+	return speed;
+}
+
+const Bonus * CStack::getEffect( ui16 id, int turn /*= 0*/ ) const
+{
+	BOOST_FOREACH(Bonus *it, bonuses)
+	{
+		if(it->source == Bonus::SPELL_EFFECT && it->id == id)
+		{
+			if(!turn || it->turnsRemain > turn)
+				return &(*it);
+		}
+	}
+	return NULL;
+}
+
+void CStack::stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse)
+{
+	si32 power = VLC->spellh->spells[sse.id]->powers[sse.val];
+	//why, why, WHY this code is here?!?
+// 	Bonus * bonus = getBonus(Selector::typeSybtype(Bonus::SPECIAL_PECULIAR_ENCHANT, sse.id));
+// 	if (bonus)
+// 	{
+// 	 	switch(bonus->additionalInfo)
+// 	 	{
+// 	 		case 0: //normal
+// 	 			switch(type->level)
+// 	 			{
+// 	 				case 1: case 2:
+// 	 					power += 3; //it doesn't necessarily make sense for some spells, use it wisely
+// 	 				break;
+// 	 				case 3: case 4:
+// 	 					power += 2;
+// 	 				break;
+// 	 				case 5: case 6:
+// 	 					power += 1;
+// 	 				break;
+// 	 			}
+// 	 		break;
+// 	 		case 1: //only Coronius as yet
+// 	 			power = std::max(5 - type->level, 0);
+// 	 		break;
+// 	 	}
+// 	}
+	 
+	switch(sse.id)
+	{
+	case 27: //shield 
+	 	sf.push_back(featureGenerator(Bonus::GENERAL_DAMAGE_REDUCTION, 0, power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 28: //air shield
+	 	sf.push_back(featureGenerator(Bonus::GENERAL_DAMAGE_REDUCTION, 1, power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 29: //fire shield
+	 	sf.push_back(featureGenerator(Bonus::FIRE_SHIELD, 0, power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 30: //protection from air
+	 	sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 0, power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 31: //protection from fire
+	 	sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 1, power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 32: //protection from water
+	 	sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 2, power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 33: //protection from earth
+	 	sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 3, power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 34: //anti-magic
+	 	sf.push_back(featureGenerator(Bonus::LEVEL_SPELL_IMMUNITY, 0, power - 1, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 41: //bless
+// 	 	if (hasBonusOfType(Bonus::SPECIAL_BLESS_DAMAGE, 41)) //TODO: better handling of bonus percentages
+// 	 	{
+// 	 		int damagePercent = dynamic_cast<const CGHeroInstance*>(armyObj)->level * valOfBonuses(Bonus::SPECIAL_BLESS_DAMAGE, 41) / type->level;
+// 	 		sf.push_back(featureGenerator(Bonus::CREATURE_DAMAGE, 0, damagePercent, sse.turnsRemain));
+// 	 		sf.back().id = sse.id;
+// 	 		sf.back().valType = Bonus::PERCENT_TO_ALL;
+// 	 	}
+	 	sf.push_back(featureGenerator(Bonus::ALWAYS_MAXIMUM_DAMAGE, -1, power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 42: //curse
+	 	sf.push_back(featureGenerator(Bonus::ALWAYS_MINIMUM_DAMAGE, -1, -1 * power, sse.turnsRemain, sse.val >= 2 ? 20 : 0));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 43: //bloodlust
+	 	sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, sse.turnsRemain, 0, Bonus::ONLY_MELEE_FIGHT));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 44: //precision
+	 	sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, sse.turnsRemain, 0, Bonus::ONLY_DISTANCE_FIGHT));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 45: //weakness
+	 	sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, -1 * power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 46: //stone skin
+	 	sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 47: //disrupting ray
+	 	sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, -1 * power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 48: //prayer
+	 	sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	sf.push_back(featureGenerator(Bonus::STACKS_SPEED, 0, power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 49: //mirth
+	 	sf.push_back(featureGenerator(Bonus::MORALE, 0, power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 50: //sorrow
+	 	sf.push_back(featureGenerator(Bonus::MORALE, 0, -1 * power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 51: //fortune
+	 	sf.push_back(featureGenerator(Bonus::LUCK, 0, power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 52: //misfortune
+	 	sf.push_back(featureGenerator(Bonus::LUCK, 0, -1 * power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 53: //haste
+	 	sf.push_back(featureGenerator(Bonus::STACKS_SPEED, 0, power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 54: //slow
+	 	sf.push_back(featureGeneratorVT(Bonus::STACKS_SPEED, 0, -1 * ( 100 - power ), sse.turnsRemain, Bonus::PERCENT_TO_ALL));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 55: //slayer
+// 	 	if (bonus) //Coronius
+// 	 	{
+// 	 		sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, sse.turnsRemain));
+// 	 		sf.back().id = sse.id;
+// 	 	}
+	 	sf.push_back(featureGenerator(Bonus::SLAYER, 0, sse.val, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 56: //frenzy
+	 	sf.push_back(featureGenerator(Bonus::IN_FRENZY, 0, VLC->spellh->spells[56]->powers[sse.val]/100.0, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 58: //counterstrike
+	 	sf.push_back(featureGenerator(Bonus::ADDITIONAL_RETALIATION, 0, power, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 59: //bersek
+	 	sf.push_back(featureGenerator(Bonus::ATTACKS_NEAREST_CREATURE, 0, sse.val, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 60: //hypnotize
+	 	sf.push_back(featureGenerator(Bonus::HYPNOTIZED, 0, sse.val, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 61: //forgetfulness
+	 	sf.push_back(featureGenerator(Bonus::FORGETFULL, 0, sse.val, sse.turnsRemain));
+	 	sf.back().id = sse.id;
+	 	break;
+	case 62: //blind
+		sf.push_back(makeFeatureVal(Bonus::NOT_ACTIVE, Bonus::UNITL_BEING_ATTACKED | Bonus::N_TURNS, 0, 0, Bonus::SPELL_EFFECT, sse.turnsRemain));
+		sf.back().id = sse.id;
+		sf.push_back(makeFeatureVal(Bonus::GENERAL_ATTACK_REDUCTION, Bonus::UNTIL_ATTACK | Bonus::N_TURNS, 0, power, Bonus::SPELL_EFFECT, sse.turnsRemain));
+		sf.back().id = sse.id;
+	 	break;
+	}
+}
+
+ui8 CStack::howManyEffectsSet(ui16 id) const
+{
+	ui8 ret = 0;
+	BOOST_FOREACH(const Bonus *it, bonuses)
+		if(it->source == Bonus::SPELL_EFFECT && it->id == id) //effect found
+		{
+			++ret;
+		}
+		return ret;
+}
+
+bool CStack::willMove(int turn /*= 0*/) const
+{
+	return ( turn ? true : !vstd::contains(state, DEFENDING) )
+		&& !moved(turn)
+		&& canMove(turn);
+}
+
+bool CStack::canMove( int turn /*= 0*/ ) const
+{
+	return alive()
+		&& !hasBonus(Selector::type(Bonus::NOT_ACTIVE) && Selector::turns(turn)); //eg. Ammo Cart or blinded creature
+}
+
+bool CStack::moved( int turn /*= 0*/ ) const
+{
+	if(!turn)
+		return vstd::contains(state, MOVED);
+	else
+		return false;
+}
+
+bool CStack::doubleWide() const
+{
+	return getCreature()->doubleWide;
+}
+
+THex CStack::occupiedHex() const
+{
+	if (doubleWide())
+	{
+		if (attackerOwned)
+			return position - 1;
+		else
+			return position + 1;
+	} 
+	else
+	{
+		return THex::INVALID;
+	}
+}
+BonusList CStack::getSpellBonuses() const
+{
+	return getBonuses(Selector::sourceTypeSel(Bonus::SPELL_EFFECT));
+}
+
+std::vector<si32> CStack::activeSpells() const
+{
+	std::vector<si32> ret;
+
+	BonusList spellEffects = getSpellBonuses();
+	BOOST_FOREACH(const Bonus *it, spellEffects)
+	{
+		if (!vstd::contains(ret, it->id)) //do not duplicate spells with multiple effects
+			ret.push_back(it->id);
+	}
+
+	return ret;
+}
+
+CStack::~CStack()
+{
+	detachFromAll();
+	if(vstd::contains(state, SUMMONED))
+		delNull(base);
+}
+
+const CGHeroInstance * CStack::getMyHero() const
+{
+	if(base)
+		return dynamic_cast<const CGHeroInstance *>(base->armyObj);
+	else //we are attached directly?
+		BOOST_FOREACH(const CBonusSystemNode *n, parents)
+		if(n->nodeType == HERO)
+			dynamic_cast<const CGHeroInstance *>(n);
+
+	return NULL;
+}
+
+std::string CStack::nodeName() const
+{
+	std::ostringstream oss;
+	oss << "Battle stack [" << ID << "]: " << count << " creatures of ";
+	if(type)
+		oss << type->namePl;
+	else
+		oss << "[UNDEFINED TYPE]";
+
+	oss << " from slot " << (int)slot;
+	if(base && base->armyObj)
+		oss << " of armyobj=" << base->armyObj->id;
+	return oss.str();
+}
+
+void CStack::prepareAttacked(BattleStackAttacked &bsa) const
+{
+	bsa.killedAmount = bsa.damageAmount / MaxHealth();
+	unsigned damageFirst = bsa.damageAmount % MaxHealth();
+
+	if( firstHPleft <= damageFirst )
+	{
+		bsa.killedAmount++;
+		bsa.newHP = firstHPleft + MaxHealth() - damageFirst;
+	}
+	else
+	{
+		bsa.newHP = firstHPleft - damageFirst;
+	}
+
+	if(count <= bsa.killedAmount) //stack killed
+	{
+		bsa.newAmount = 0;
+		bsa.flags |= 1;
+		bsa.killedAmount = count; //we cannot kill more creatures than we have
+	}
+	else
+	{
+		bsa.newAmount = count - bsa.killedAmount;
+	}
+}
+
+bool CStack::isMeleeAttackPossible(const CStack * attacker, const CStack * defender, THex attackerPos /*= THex::INVALID*/, THex defenderPos /*= THex::INVALID*/)
+{
+	if (!attackerPos.isValid())
+	{
+		attackerPos = attacker->position;
+	}
+	if (!defenderPos.isValid())
+	{
+		defenderPos = defender->position;
+	}
+
+	return
+		(THex::mutualPosition(attackerPos, defenderPos) >= 0)						//front <=> front
+		|| (attacker->doubleWide()									//back <=> front
+		&& THex::mutualPosition(attackerPos + (attacker->attackerOwned ? -1 : 1), defenderPos) >= 0)
+		|| (defender->doubleWide()									//front <=> back
+		&& THex::mutualPosition(attackerPos, defenderPos + (defender->attackerOwned ? -1 : 1)) >= 0)
+		|| (defender->doubleWide() && attacker->doubleWide()//back <=> back
+		&& THex::mutualPosition(attackerPos + (attacker->attackerOwned ? -1 : 1), defenderPos + (defender->attackerOwned ? -1 : 1)) >= 0);
+		
+}
+
+bool CMP_stack::operator()( const CStack* a, const CStack* b )
+{
+	switch(phase)
+	{
+	case 0: //catapult moves after turrets
+		return a->getCreature()->idNumber < b->getCreature()->idNumber; //catapult is 145 and turrets are 149
+		//TODO? turrets order
+	case 1: //fastest first, upper slot first
+		{
+			int as = a->Speed(turn), bs = b->Speed(turn);
+			if(as != bs)
+				return as > bs;
+			else
+				return a->slot < b->slot;
+		}
+	case 2: //fastest last, upper slot first
+		//TODO: should be replaced with order of receiving morale!
+	case 3: //fastest last, upper slot first
+		{
+			int as = a->Speed(turn), bs = b->Speed(turn);
+			if(as != bs)
+				return as < bs;
+			else
+				return a->slot < b->slot;
+		}
+	default:
+		assert(0);
+		return false;
+	}
+
+}
+
+CMP_stack::CMP_stack( int Phase /*= 1*/, int Turn )
+{
+	phase = Phase;
+	turn = Turn;
+}
+

+ 221 - 0
lib/BattleState.h

@@ -0,0 +1,221 @@
+#pragma once
+
+#include "../global.h"
+#include "HeroBonus.h"
+#include "CCreatureSet.h"
+
+#include "ConstTransitivePtr.h"
+
+/*
+ * BattleState.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 CGHeroInstance;
+class CStack;
+class CArmedInstance;
+class CGTownInstance;
+class CStackInstance;
+struct BattleStackAttacked;
+
+struct DLL_EXPORT CObstacleInstance
+{
+	int uniqueID;
+	int ID; //ID of obstacle (defines type of it)
+	int pos; //position on battlefield
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & ID & pos & uniqueID;
+	}
+};
+
+//only for use in BattleInfo
+struct DLL_EXPORT SiegeInfo
+{
+	ui8 wallState[8]; //[0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; 1 - intact, 2 - damaged, 3 - destroyed
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & wallState;
+	}
+};
+
+struct DLL_EXPORT BattleInfo : public CBonusSystemNode
+{
+	ui8 sides[2]; //sides[0] - attacker, sides[1] - defender
+	si32 round, activeStack;
+	ui8 siege; //    = 0 ordinary battle    = 1 a siege with a Fort    = 2 a siege with a Citadel    = 3 a siege with a Castle
+	const CGTownInstance * town; //used during town siege - id of attacked town; -1 if not town defence
+	int3 tile; //for background and bonuses
+	CGHeroInstance *heroes[2];
+	CArmedInstance *belligerents[2]; //may be same as heroes
+	std::vector<CStack*> stacks;
+	std::vector<CObstacleInstance> obstacles;
+	ui8 castSpells[2]; //[0] - attacker, [1] - defender
+	SiegeInfo si;
+	si32 battlefieldType;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & sides & round & activeStack & siege & town & tile & stacks & belligerents & obstacles
+			& castSpells & si & battlefieldType;
+		h & heroes;
+		h & static_cast<CBonusSystemNode&>(*this);
+	}
+
+	//////////////////////////////////////////////////////////////////////////
+	//void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;
+	//////////////////////////////////////////////////////////////////////////
+
+	const CStack * getNextStack() const; //which stack will have turn after current one
+	void getStackQueue(std::vector<const CStack *> &out, int howMany, int turn = 0, int lastMoved = -1) const; //returns stack in order of their movement action
+	CStack * getStack(int stackID, bool onlyAlive = true);
+	const CStack * getStack(int stackID, bool onlyAlive = true) const;
+	CStack * getStackT(THex tileID, bool onlyAlive = true);
+	const CStack * getStackT(THex tileID, bool onlyAlive = true) const;
+	void getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<THex> & occupyable, bool flying, const CStack* stackToOmmit = NULL) const; //send pointer to at least 187 allocated bytes
+	static bool isAccessible(THex hex, bool * accessibility, bool twoHex, bool attackerOwned, bool flying, bool lastPos); //helper for makeBFS
+	void makeBFS(THex start, bool*accessibility, THex *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying, bool fillPredecessors) const; //*accessibility must be prepared bool[187] array; last two pointers must point to the at least 187-elements int arrays - there is written result
+	std::pair< std::vector<THex>, int > getPath(THex start, THex dest, bool*accessibility, bool flyingCreature, bool twoHex, bool attackerOwned); //returned value: pair<path, length>; length may be different than number of elements in path since flying vreatures jump between distant hexes
+	std::vector<THex> getAccessibility(const CStack * stack, bool addOccupiable) const; //returns vector of accessible tiles (taking into account the creature range)
+
+	bool isStackBlocked(const CStack * stack) const; //returns true if there is neighboring enemy stack
+
+	ui32 calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky); //charge - number of hexes travelled before attack (for champion's jousting)
+	TDmgRange calculateDmgRange( const CStack* attacker, const CStack* defender, TQuantity attackerCount, TQuantity defenderCount, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
+	TDmgRange calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
+	void calculateCasualties(std::map<ui32,si32> *casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount)
+	std::set<CStack*> getAttackedCreatures(const CSpell * s, int skillLevel, ui8 attackerOwner, THex destinationTile); //calculates stack affected by given spell
+	static int calculateSpellDuration(const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower);
+	CStack * generateNewStack(const CStackInstance &base, int stackID, bool attackerOwned, int slot, THex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
+	CStack * generateNewStack(const CStackBasicDescriptor &base, int stackID, bool attackerOwned, int slot, THex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
+	ui32 getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //returns cost of given spell
+	int hexToWallPart(THex hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found
+	int lineToWallHex(int line) const; //returns hex with wall in given line
+	std::pair<const CStack *, THex> getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const; //if attackerOwned is indetermnate, returened stack is of any owner; hex is the number of hex we should be looking from; returns (nerarest creature, predecessorHex)
+	ui32 calculateSpellBonus(ui32 baseDamage, const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature) const;
+	ui32 calculateSpellDmg(const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const; //calculates damage inflicted by spell
+	ui32 calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack) const;
+	si8 hasDistancePenalty(const CStack * stackID, THex destHex) const; //determines if given stack has distance penalty shooting given pos
+	si8 sameSideOfWall(int pos1, int pos2) const; //determines if given positions are on the same side of wall
+	si8 hasWallPenalty(const CStack * stack, THex destHex) const; //determines if given stack has wall penalty shooting given pos
+	si8 canTeleportTo(const CStack * stack, THex destHex, int telportLevel) const; //determines if given stack can teleport to given place
+	bool battleCanShoot(const CStack * stack, THex dest) const; //determines if stack with given ID shoot at the selected destination
+
+	bool battleCanFlee(int player) const; //returns true if player can flee from the battle
+	const CStack * battleGetStack(THex pos, bool onlyAlive); //returns stack at given tile
+	const CGHeroInstance * battleGetOwner(const CStack * stack) const; //returns hero that owns given stack; NULL if none
+	si8 battleMaxSpellLevel() const; //calculates maximum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, SPELL_LEVELS is returned
+	void localInit();
+	static BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town );
+};
+
+class DLL_EXPORT CStack : public CBonusSystemNode, public CStackBasicDescriptor
+{ 
+public:
+	const CStackInstance *base;
+
+	ui32 ID; //unique ID of stack
+	ui32 baseAmount;
+	ui32 firstHPleft; //HP of first creature in stack
+	ui8 owner, slot;  //owner - player colour (255 for neutrals), slot - position in garrison (may be 255 for neutrals/called creatures)
+	ui8 attackerOwned; //if true, this stack is owned by attakcer (this one from left hand side of battle)
+	THex position; //position on battlefield; -2 - keep, -3 - lower tower, -4 - upper tower
+	ui8 counterAttacks; //how many counter attacks can be performed more in this turn (by default set at the beginning of the round to 1)
+	si16 shots; //how many shots left
+
+	std::set<ECombatInfo> state;
+	//overrides
+	const CCreature* getCreature() const {return type;}
+
+	CStack(const CStackInstance *base, int O, int I, bool AO, int S); //c-tor
+	CStack(const CStackBasicDescriptor *stack, int O, int I, bool AO, int S = 255); //c-tor
+	CStack(); //c-tor
+	~CStack();
+	std::string nodeName() const OVERRIDE;
+
+	void init(); //set initial (invalid) values
+	void postInit(); //used to finish initialization when inheriting creature parameters is working
+	const Bonus * getEffect(ui16 id, int turn = 0) const; //effect id (SP)
+	ui8 howManyEffectsSet(ui16 id) const; //returns amount of effects with given id set for this stack
+	bool willMove(int turn = 0) const; //if stack has remaining move this turn
+	bool moved(int turn = 0) const; //if stack was already moved this turn
+	bool canMove(int turn = 0) const; //if stack can move
+	ui32 Speed(int turn = 0) const; //get speed of creature with all modificators
+	BonusList getSpellBonuses() const;
+	static void stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse);
+	std::vector<si32> activeSpells() const; //returns vector of active spell IDs sorted by time of cast
+	const CGHeroInstance *getMyHero() const; //if stack belongs to hero (directly or was by him summoned) returns hero, NULL otherwise
+
+	static inline Bonus featureGenerator(Bonus::BonusType type, si16 subtype, si32 value, ui16 turnsRemain, si32 additionalInfo = 0, si32 limit = Bonus::NO_LIMIT)
+	{
+		Bonus hb = makeFeatureVal(type, Bonus::N_TURNS, subtype, value, Bonus::SPELL_EFFECT, turnsRemain, additionalInfo);
+		hb.effectRange = limit;
+		hb.source = Bonus::SPELL_EFFECT;
+		return hb;
+	}
+
+	static inline Bonus featureGeneratorVT(Bonus::BonusType type, si16 subtype, si32 value, ui16 turnsRemain, ui8 valType)
+	{
+		Bonus ret = makeFeatureVal(type, Bonus::N_TURNS, subtype, value, Bonus::SPELL_EFFECT, turnsRemain);
+		ret.valType = valType;
+		ret.source = Bonus::SPELL_EFFECT;
+		return ret;
+	}
+
+	static bool isMeleeAttackPossible(const CStack * attacker, const CStack * defender, THex attackerPos = THex::INVALID, THex defenderPos = THex::INVALID);
+
+	bool doubleWide() const;
+	THex occupiedHex() const; //returns number of occupied hex (not the position) if stack is double wide; otherwise -1
+
+	void prepareAttacked(BattleStackAttacked &bsa) const; //requires bsa.damageAmout filled
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		assert(isIndependentNode());
+		h & static_cast<CBonusSystemNode&>(*this);
+		h & static_cast<CStackBasicDescriptor&>(*this);
+		h & ID & baseAmount & firstHPleft & owner & slot & attackerOwned & position & state & counterAttacks
+			& shots & count;
+
+		TSlot slot = (base ? base->armyObj->findStack(base) : -1);
+		const CArmedInstance *army = (base ? base->armyObj : NULL);
+		if(h.saving)
+		{
+			h & army & slot;
+		}
+		else
+		{
+			h & army & slot;
+			if(!army || slot == -1 || !army->hasStackAtSlot(slot))
+			{
+				base = NULL;
+				tlog3 << type->nameSing << " don't have a base stack!\n";
+			}
+			else
+			{
+				base = &army->getStack(slot);
+			}
+		}
+
+	}
+	bool alive() const //determines if stack is alive
+	{
+		return vstd::contains(state,ALIVE);
+	}
+};
+
+class DLL_EXPORT CMP_stack
+{
+	int phase; //rules of which phase will be used
+	int turn;
+public:
+
+	bool operator ()(const CStack* a, const CStack* b);
+	CMP_stack(int Phase = 1, int Turn = 0);
+};

+ 533 - 209
hch/CArtHandler.cpp → lib/CArtHandler.cpp

@@ -12,6 +12,10 @@
 #include <boost/random/linear_congruential.hpp>
 #include <boost/algorithm/string/replace.hpp>
 #include "../lib/VCMI_Lib.h"
+#include "CSpellHandler.h"
+#include "CObjectHandler.h"
+#include "NetPacks.h"
+
 extern CLodHandler *bitmaph;
 using namespace boost::assign;
 
@@ -47,99 +51,99 @@ bool CArtifact::isBig () const
 {
 	return VLC->arth->isBigArtifact(id);
 }
-
-bool CArtifact::isModable () const
-{
-	return (bool)dynamic_cast<const IModableArt *>(this);
-}
-
-/**
- * Checks whether the artifact fits at a given slot.
- * @param artifWorn A hero's set of worn artifacts.
- */
-bool CArtifact::fitsAt (const std::map<ui16, const CArtifact*> &artifWorn, ui16 slotID) const
-{
-	if (!vstd::contains(possibleSlots, slotID))
-		return false;
-
-	// Can't put an artifact in a locked slot.
-	std::map<ui16, const CArtifact*>::const_iterator it = artifWorn.find(slotID);
-	if (it != artifWorn.end() && it->second->id == 145)
-		return false;
-
-	// Check if a combination artifact fits.
-	// TODO: Might want a more general algorithm?
-	//       Assumes that misc & rings fits only in their slots, and others in only one slot and no duplicates.
-	if (constituents != NULL)
-	{
-		std::map<ui16, const CArtifact*> tempArtifWorn = artifWorn;
-		const ui16 ringSlots[] = {6, 7};
-		const ui16 miscSlots[] = {9, 10, 11, 12, 18};
-		int rings = 0;
-		int misc = 0;
-
-		VLC->arth->unequipArtifact(tempArtifWorn, slotID);
-
-		BOOST_FOREACH(ui32 constituentID, *constituents) 
-		{
-			const CArtifact& constituent = *VLC->arth->artifacts[constituentID];
-			const int slot = constituent.possibleSlots[0];
-
-			if (slot == 6 || slot == 7)
-				rings++;
-			else if ((slot >= 9 && slot <= 12) || slot == 18)
-				misc++;
-			else if (tempArtifWorn.find(slot) != tempArtifWorn.end())
-				return false;
-		}
-
-		// Ensure enough ring slots are free
-		for (int i = 0; i < sizeof(ringSlots)/sizeof(*ringSlots); i++) 
-		{
-			if (tempArtifWorn.find(ringSlots[i]) == tempArtifWorn.end() || ringSlots[i] == slotID)
-				rings--;
-		}
-		if (rings > 0)
-			return false;
-
-		// Ensure enough misc slots are free.
-		for (int i = 0; i < sizeof(miscSlots)/sizeof(*miscSlots); i++) 
-		{
-			if (tempArtifWorn.find(miscSlots[i]) == tempArtifWorn.end() || miscSlots[i] == slotID)
-				misc--;
-		}
-		if (misc > 0)
-			return false;
-	}
-
-	return true;
-}
-
-bool CArtifact::canBeAssembledTo (const std::map<ui16, const CArtifact*> &artifWorn, ui32 artifactID) const
-{
-	if (constituentOf == NULL || !vstd::contains(*constituentOf, artifactID))
-		return false;
-
-	const CArtifact &artifact = *VLC->arth->artifacts[artifactID];
-	assert(artifact.constituents);
-
-	BOOST_FOREACH(ui32 constituentID, *artifact.constituents) 
-	{
-		bool found = false;
-		for (std::map<ui16, const CArtifact*>::const_iterator it = artifWorn.begin(); it != artifWorn.end(); ++it) 
-		{
-			if (it->second->id == constituentID) 
-			{
-				found = true;
-				break;
-			}
-		}
-		if (!found)
-			return false;
-	}
-
-	return true;
-}
+// 
+// bool CArtifact::isModable () const
+// {
+// 	return (bool)dynamic_cast<const IModableArt *>(this);
+// }
+
+// /**
+//  * Checks whether the artifact fits at a given slot.
+//  * @param artifWorn A hero's set of worn artifacts.
+//  */
+// bool CArtifact::fitsAt (const std::map<ui16, const CArtifact*> &artifWorn, ui16 slotID) const
+// {
+// 	if (!vstd::contains(possibleSlots, slotID))
+// 		return false;
+// 
+// 	// Can't put an artifact in a locked slot.
+// 	std::map<ui16, const CArtifact*>::const_iterator it = artifWorn.find(slotID);
+// 	if (it != artifWorn.end() && it->second->id == 145)
+// 		return false;
+// 
+// 	// Check if a combination artifact fits.
+// 	// TODO: Might want a more general algorithm?
+// 	//       Assumes that misc & rings fits only in their slots, and others in only one slot and no duplicates.
+// 	if (constituents != NULL)
+// 	{
+// 		std::map<ui16, const CArtifact*> tempArtifWorn = artifWorn;
+// 		const ui16 ringSlots[] = {6, 7};
+// 		const ui16 miscSlots[] = {9, 10, 11, 12, 18};
+// 		int rings = 0;
+// 		int misc = 0;
+// 
+// 		VLC->arth->unequipArtifact(tempArtifWorn, slotID);
+// 
+// 		BOOST_FOREACH(ui32 constituentID, *constituents) 
+// 		{
+// 			const CArtifact& constituent = *VLC->arth->artifacts[constituentID];
+// 			const int slot = constituent.possibleSlots[0];
+// 
+// 			if (slot == 6 || slot == 7)
+// 				rings++;
+// 			else if ((slot >= 9 && slot <= 12) || slot == 18)
+// 				misc++;
+// 			else if (tempArtifWorn.find(slot) != tempArtifWorn.end())
+// 				return false;
+// 		}
+// 
+// 		// Ensure enough ring slots are free
+// 		for (int i = 0; i < sizeof(ringSlots)/sizeof(*ringSlots); i++) 
+// 		{
+// 			if (tempArtifWorn.find(ringSlots[i]) == tempArtifWorn.end() || ringSlots[i] == slotID)
+// 				rings--;
+// 		}
+// 		if (rings > 0)
+// 			return false;
+// 
+// 		// Ensure enough misc slots are free.
+// 		for (int i = 0; i < sizeof(miscSlots)/sizeof(*miscSlots); i++) 
+// 		{
+// 			if (tempArtifWorn.find(miscSlots[i]) == tempArtifWorn.end() || miscSlots[i] == slotID)
+// 				misc--;
+// 		}
+// 		if (misc > 0)
+// 			return false;
+// 	}
+// 
+// 	return true;
+// }
+
+// bool CArtifact::canBeAssembledTo (const std::map<ui16, const CArtifact*> &artifWorn, ui32 artifactID) const
+// {
+// 	if (constituentOf == NULL || !vstd::contains(*constituentOf, artifactID))
+// 		return false;
+// 
+// 	const CArtifact &artifact = *VLC->arth->artifacts[artifactID];
+// 	assert(artifact.constituents);
+// 
+// 	BOOST_FOREACH(ui32 constituentID, *artifact.constituents) 
+// 	{
+// 		bool found = false;
+// 		for (std::map<ui16, const CArtifact*>::const_iterator it = artifWorn.begin(); it != artifWorn.end(); ++it) 
+// 		{
+// 			if (it->second->id == constituentID) 
+// 			{
+// 				found = true;
+// 				break;
+// 			}
+// 		}
+// 		if (!found)
+// 			return false;
+// 	}
+// 
+// 	return true;
+// }
 
 CArtifact::CArtifact()
 {
@@ -171,21 +175,25 @@ int CArtifact::getArtClassSerial() const
 	return -1;
 }
 
-void CArtifact::getParents(TCNodes &out, const CBonusSystemNode *root /*= NULL*/) const
+std::string CArtifact::nodeName() const
 {
-	//combined artifact carries bonuses from its parts
-	if(constituents)
-	{
-		BOOST_FOREACH(ui32 id, *constituents)
-			out.insert(VLC->arth->artifacts[id]);
-	}
+	return "Artifact: " + Name();
 }
+// void CArtifact::getParents(TCNodes &out, const CBonusSystemNode *root /*= NULL*/) const
+// {
+// 	//combined artifact carries bonuses from its parts
+// 	if(constituents)
+// 	{
+// 		BOOST_FOREACH(ui32 id, *constituents)
+// 			out.insert(VLC->arth->artifacts[id]);
+// 	}
+// }
 
-void CScroll::Init()
-{
-	bonuses.push_back (Bonus (Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT, 1, id, spellid, Bonus::INDEPENDENT_MAX));
-	//boost::algorithm::replace_first(description, "[spell name]", VLC->spellh->spells[spellid].name);
-}
+// void CScroll::Init()
+// {
+// // 	addNewBonus (Bonus (Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT, 1, id, spellid, Bonus::INDEPENDENT_MAX));
+// // 	//boost::algorithm::replace_first(description, "[spell name]", VLC->spellh->spells[spellid].name);
+// }
 
 CArtHandler::CArtHandler()
 {
@@ -194,12 +202,12 @@ CArtHandler::CArtHandler()
 	// War machines are the default big artifacts.
 	for (ui32 i = 3; i <= 6; i++)
 		bigArtifacts.insert(i);
-	modableArtifacts = boost::assign::map_list_of(1, 1)(146,3)(147,3)(148,3)(150,3)(151,3)(152,3)(154,3)(156,2);
+	//modableArtifacts = boost::assign::map_list_of(1, 1)(146,3)(147,3)(148,3)(150,3)(151,3)(152,3)(154,3)(156,2);
 }
 
 CArtHandler::~CArtHandler()
 {
-	for (std::vector<CArtifact*>::iterator it = artifacts.begin(); it != artifacts.end(); ++it)
+	for (std::vector< ConstTransitivePtr<CArtifact> >::iterator it = artifacts.begin(); it != artifacts.end(); ++it)
 	{
 		delete (*it)->constituents;
 		delete (*it)->constituentOf;
@@ -223,24 +231,24 @@ void CArtHandler::loadArtifacts(bool onlyTxt)
 	std::map<ui32,ui8>::iterator itr;
 	for (int i=0; i<ARTIFACTS_QUANTITY; i++)
 	{
-		CArtifact *art;
-		if ((itr = modableArtifacts.find(i)) != modableArtifacts.end())
-		{
-			switch (itr->second)
-			{
-				case 1:
-					art = new CScroll;
-					break;
-				case 2:
-					art = new CCustomizableArt;
-					break;
-				case 3:
-					art = new CCommanderArt;
-					break;
-			};
-		}
-		else
-			art = new CArtifact;
+		CArtifact *art = new CArtifact();
+// 		if ((itr = modableArtifacts.find(i)) != modableArtifacts.end())
+// 		{
+// 			switch (itr->second)
+// 			{
+// 			case 1:
+// 				art = new CScroll;
+// 				break;
+// 			case 2:
+// 				art = new CCustomizableArt;
+// 				break;
+// 			case 3:
+// 				art = new CCommanderArt;
+// 				break;
+// 			};
+// 		}
+// 		else
+//			art = new CArtifact;
 
 		CArtifact &nart = *art;
 		nart.id=i;
@@ -436,7 +444,7 @@ void CArtHandler::erasePickedArt (si32 id)
 }
 ui16 CArtHandler::getRandomArt(int flags)
 {
-	std::vector<CArtifact*> out;
+	std::vector<ConstTransitivePtr<CArtifact> > out;
 	getAllowed(out, flags);
 	ui16 id = out[ran() % out.size()]->id;
 	erasePickedArt (id);
@@ -444,12 +452,12 @@ ui16 CArtHandler::getRandomArt(int flags)
 }
 ui16 CArtHandler::getArtSync (ui32 rand, int flags)
 {
-	std::vector<CArtifact*> out;
+	std::vector<ConstTransitivePtr<CArtifact> > out;
 	getAllowed(out, flags);
 	CArtifact *art = out[rand % out.size()];
 	return art->id;	
 }
-void CArtHandler::getAllowed(std::vector<CArtifact*> &out, int flags)
+void CArtHandler::getAllowed(std::vector<ConstTransitivePtr<CArtifact> > &out, int flags)
 {
 	if (flags & CArtifact::ART_TREASURE)
 		getAllowedArts (out, &treasures, CArtifact::ART_TREASURE);
@@ -465,7 +473,7 @@ void CArtHandler::getAllowed(std::vector<CArtifact*> &out, int flags)
 		std::fill_n (out.begin(), 64, artifacts[2]); //magic
 	}
 }
-void CArtHandler::getAllowedArts(std::vector<CArtifact*> &out, std::vector<CArtifact*> *arts, int flag)
+void CArtHandler::getAllowedArts(std::vector<ConstTransitivePtr<CArtifact> > &out, std::vector<CArtifact*> *arts, int flag)
 {
 	if (arts->empty()) //restock avaliable arts
 	{
@@ -484,12 +492,12 @@ void CArtHandler::getAllowedArts(std::vector<CArtifact*> &out, std::vector<CArti
 }
 void CArtHandler::giveArtBonus( int aid, Bonus::BonusType type, int val, int subtype, int valType, ILimiter * limiter )
 {
-	Bonus added(Bonus::PERMANENT,type,Bonus::ARTIFACT,val,aid,subtype);
-	added.valType = valType;
-	added.limiter = limiter;
+	Bonus *added = new Bonus(Bonus::PERMANENT,type,Bonus::ARTIFACT,val,aid,subtype);
+	added->valType = valType;
+	added->limiter.reset(limiter);
 	if(type == Bonus::MORALE || Bonus::LUCK)
-		added.description = "\n" + artifacts[aid]->Name()  + (val > 0 ? " +" : " ") + boost::lexical_cast<std::string>(val);
-	artifacts[aid]->bonuses.push_back(added);
+		added->description = "\n" + artifacts[aid]->Name()  + (val > 0 ? " +" : " ") + boost::lexical_cast<std::string>(val);
+	artifacts[aid]->addNewBonus(added);
 }
 
 void CArtHandler::addBonuses()
@@ -691,7 +699,7 @@ void CArtHandler::addBonuses()
 
 	//Angelic Alliance
 	giveArtBonus(129, Bonus::NONEVIL_ALIGNMENT_MIX, 0);
-	giveArtBonus(129, Bonus::OPENING_BATTLE_SPELL, 10, 29); // Prayer
+	giveArtBonus(129, Bonus::OPENING_BATTLE_SPELL, 10, 48); // Prayer
 
 	//Cloak of the Undead King
 	giveArtBonus(130, Bonus::IMPROVED_NECROMANCY, 0);
@@ -746,111 +754,427 @@ void CArtHandler::clear()
 
 }
 
-/**
- * Locally equips an artifact to a hero's worn slots. Unequips an already present artifact.
- * Does not test if the operation is legal.
- * @param artifWorn A hero's set of worn artifacts.
- * @param bonuses Optional list of bonuses to update.
- */
-void CArtHandler::equipArtifact( std::map<ui16, const CArtifact*> &artifWorn, ui16 slotID, const CArtifact* art )
+// /**
+//  * Locally equips an artifact to a hero's worn slots. Unequips an already present artifact.
+//  * Does not test if the operation is legal.
+//  * @param artifWorn A hero's set of worn artifacts.
+//  * @param bonuses Optional list of bonuses to update.
+//  */
+// void CArtHandler::equipArtifact( std::map<ui16, const CArtifact*> &artifWorn, ui16 slotID, const CArtifact* art ) const
+// {
+// 	unequipArtifact(artifWorn, slotID);
+// 
+// 	if (art) //false when artifact is NULL -> slot set to empty
+// 	{
+// 		const CArtifact &artifact = *art;
+// 
+// 		// Add artifact.
+// 		artifWorn[slotID] = art;
+// 
+// 		// Add locks, in reverse order of being removed.
+// 		if (artifact.constituents != NULL) 
+// 		{
+// 			bool destConsumed = false; // Determines which constituent that will be counted for together with the artifact.
+// 
+// 			BOOST_FOREACH(ui32 constituentID, *artifact.constituents) 
+// 			{
+// 				const CArtifact &constituent = *artifacts[constituentID];
+// 
+// 				if (!destConsumed && vstd::contains(constituent.possibleSlots, slotID)) 
+// 				{
+// 					destConsumed = true;
+// 				} 
+// 				else 
+// 				{
+// 					BOOST_FOREACH(ui16 slot, constituent.possibleSlots) 
+// 					{
+// 						if (!vstd::contains(artifWorn, slot)) 
+// 						{
+// 							artifWorn[slot] = VLC->arth->artifacts[145]; //lock
+// 							break;
+// 						}
+// 					}
+// 				}
+// 			}
+// 		}
+// 	}
+// }
+// 
+// /**
+//  * Locally unequips an artifact from a hero's worn slots.
+//  * Does not test if the operation is legal.
+//  * @param artifWorn A hero's set of worn artifacts.
+//  * @param bonuses Optional list of bonuses to update.
+//  */
+// void CArtHandler::unequipArtifact(std::map<ui16, const CArtifact*> &artifWorn, ui16 slotID) const
+// {
+// 	if (!vstd::contains(artifWorn, slotID))
+// 		return;
+// 
+// 	const CArtifact &artifact = *artifWorn[slotID];
+// 
+// 	// Remove artifact, if it's not already removed.
+// 	artifWorn.erase(slotID);
+// 
+// 	// Remove locks, in reverse order of being added.
+// 	if (artifact.constituents != NULL) 
+// 	{
+// 		bool destConsumed = false;
+// 
+// 		BOOST_FOREACH(ui32 constituentID, *artifact.constituents) 
+// 		{
+// 			const CArtifact &constituent = *artifacts[constituentID];
+// 
+// 			if (!destConsumed && vstd::contains(constituent.possibleSlots, slotID)) 
+// 			{
+// 				destConsumed = true;
+// 			} 
+// 			else 
+// 			{
+// 				BOOST_REVERSE_FOREACH(ui16 slot, constituent.possibleSlots) 
+// 				{
+// 					if (vstd::contains(artifWorn, slot) && artifWorn[slot]->id == 145) 
+// 					{
+// 						artifWorn.erase(slot);
+// 						break;
+// 					}
+// 				}
+// 			}
+// 		}
+// 	}
+// }
+
+void CArtHandler::clearHlpLists()
 {
-	unequipArtifact(artifWorn, slotID);
+	treasures.clear();
+	minors.clear();
+	majors.clear();
+	relics.clear();
+}
 
-	if (art) //false when artifact is NULL -> slot set to empty
+void CArtHandler::initAllowedArtifactsList(const std::vector<ui8> &allowed)
+{
+	allowedArtifacts.clear();
+	clearHlpLists();
+	for (int i=0; i<144; ++i) //yes, 144
 	{
-		const CArtifact &artifact = *art;
+		if (allowed[i])
+			allowedArtifacts.push_back(artifacts[i]);
+	}
+}
 
-		// Add artifact.
-		artifWorn[slotID] = art;
+CArtifactInstance::CArtifactInstance()
+{
+	init();
+}
+
+CArtifactInstance::CArtifactInstance( CArtifact *Art)
+{
+	init();
+	setType(Art);
 
-		// Add locks, in reverse order of being removed.
-		if (artifact.constituents != NULL) 
+}
+
+// CArtifactInstance::CArtifactInstance(int aid)
+// {
+// 	init();
+// 	setType(VLC->arth->artifacts[aid]);
+// }
+
+void CArtifactInstance::setType( CArtifact *Art )
+{
+	artType = Art;
+	attachTo(Art);
+}
+
+std::string CArtifactInstance::nodeName() const
+{
+	return "Artifact instance of " + (artType ? artType->Name() : std::string("uninitialized")) + " type";
+}
+
+CArtifactInstance * CArtifactInstance::createScroll( const CSpell *s)
+{
+	CArtifactInstance *ret = new CArtifactInstance(VLC->arth->artifacts[1]);
+	Bonus *b = new Bonus(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, 1, s->id);
+	ret->addNewBonus(b);
+	return ret;
+}
+
+void CArtifactInstance::init()
+{
+	id = -1;
+}
+
+int CArtifactInstance::firstAvailableSlot(const CGHeroInstance *h) const
+{
+	BOOST_FOREACH(ui16 slot, artType->possibleSlots)
+	{
+		if(canBePutAt(ArtifactLocation(h, slot))) //if(artType->fitsAt(h->artifWorn, slot))
 		{
-			bool destConsumed = false; // Determines which constituent that will be counted for together with the artifact.
+			//we've found a free suitable slot.
+			return slot;
+		}
+	}
+
+	//if haven't find proper slot, use backpack
+	return firstBackpackSlot(h);
+}
 
-			BOOST_FOREACH(ui32 constituentID, *artifact.constituents) 
+int CArtifactInstance::firstBackpackSlot(const CGHeroInstance *h) const
+{
+	if(!artType->isBig()) //discard big artifact
+		return Arts::BACKPACK_START + h->artifactsInBackpack.size();
+
+	return -1;
+}
+
+bool CArtifactInstance::canBePutAt(const ArtifactLocation &al, bool assumeDestRemoved /*= false*/) const
+{
+	if(al.slot >= Arts::BACKPACK_START)
+	{
+		if(artType->isBig())
+			return false;
+		
+		//TODO backpack limit
+		return true;
+	}
+
+	if(!vstd::contains(artType->possibleSlots, al.slot))
+		return false;
+
+	return al.hero->isPositionFree(al.slot, assumeDestRemoved);
+}
+
+void CArtifactInstance::putAt(CGHeroInstance *h, ui16 slot)
+{
+	assert(canBePutAt(ArtifactLocation(h, slot)));
+
+	h->setNewArtSlot(slot, this, false);
+	if(slot < Arts::BACKPACK_START)
+		h->attachTo(this);
+}
+
+void CArtifactInstance::removeFrom(CGHeroInstance *h, ui16 slot)
+{
+	assert(h->CArtifactSet::getArt(slot) == this);
+	h->eraseArtSlot(slot);
+	if(slot < Arts::BACKPACK_START)
+		h->detachFrom(this);
+
+	//TODO delete me?
+}
+
+bool CArtifactInstance::canBeDisassembled() const
+{
+	return artType->constituents && artType->constituentOf->size();
+}
+
+std::vector<const CArtifact *> CArtifactInstance::assemblyPossibilities(const CGHeroInstance *h) const
+{
+	std::vector<const CArtifact *> ret;
+	if(!artType->constituentOf //not a part of combined artifact
+		|| artType->constituents) //combined artifact already: no combining of combined artifacts... for now.
+		return ret;
+
+	BOOST_FOREACH(ui32 possibleCombinedArt, *artType->constituentOf) 
+	{
+		const CArtifact * const artifact = VLC->arth->artifacts[possibleCombinedArt];
+		assert(artifact->constituents);
+		bool possible = true;
+
+		BOOST_FOREACH(ui32 constituentID, *artifact->constituents) //check if all constituents are available
+		{
+			if(!h->hasArt(constituentID, true)) //constituent must be equipped
 			{
-				const CArtifact &constituent = *artifacts[constituentID];
-
-				if (!destConsumed && vstd::contains(constituent.possibleSlots, slotID)) 
-				{
-					destConsumed = true;
-				} 
-				else 
-				{
-					BOOST_FOREACH(ui16 slot, constituent.possibleSlots) 
-					{
-						if (!vstd::contains(artifWorn, slot)) 
-						{
-							artifWorn[slot] = VLC->arth->artifacts[145]; //lock
-							break;
-						}
-					}
-				}
+				possible = false;
+				break;
 			}
 		}
+
+		if(possible)
+			ret.push_back(artifact);
 	}
+
+	return ret;
 }
 
-/**
- * Locally unequips an artifact from a hero's worn slots.
- * Does not test if the operation is legal.
- * @param artifWorn A hero's set of worn artifacts.
- * @param bonuses Optional list of bonuses to update.
- */
-void CArtHandler::unequipArtifact(std::map<ui16, const CArtifact*> &artifWorn, ui16 slotID)
+void CArtifactInstance::move(ArtifactLocation &src, ArtifactLocation &dst)
 {
-	if (!vstd::contains(artifWorn, slotID))
-		return;
+	removeFrom(src.hero, src.slot);
+	putAt(dst.hero, dst.slot);
+}
 
-	const CArtifact &artifact = *artifWorn[slotID];
+CArtifactInstance * CArtifactInstance::createNewArtifactInstance(CArtifact *Art)
+{
+	if(!Art->constituents)
+		return new CArtifactInstance(Art);
+	else
+	{
+		CCombinedArtifactInstance * ret = new CCombinedArtifactInstance(Art);
+		ret->createConstituents();
+		return ret;
+	}
+}
 
-	// Remove artifact, if it's not already removed.
-	artifWorn.erase(slotID);
+CArtifactInstance * CArtifactInstance::createNewArtifactInstance(int aid)
+{
+	return createNewArtifactInstance(VLC->arth->artifacts[aid]);
+}
 
-	// Remove locks, in reverse order of being added.
-	if (artifact.constituents != NULL) 
-	{
-		bool destConsumed = false;
+void CArtifactInstance::deserializationFix()
+{
+	setType(artType);
+}
 
-		BOOST_FOREACH(ui32 constituentID, *artifact.constituents) 
+bool CCombinedArtifactInstance::canBePutAt(const ArtifactLocation &al, bool assumeDestRemoved /*= false*/) const
+{
+	bool canMainArtifactBePlaced = CArtifactInstance::canBePutAt(al, assumeDestRemoved);
+	if(!canMainArtifactBePlaced)
+		return false; //no is no...
+	if(al.slot >= Arts::BACKPACK_START)
+		return true; //we can always remove combined art to the backapck
+
+
+	assert(artType->constituents);
+	std::vector<ConstituentInfo> constituentsToBePlaced = constituentsInfo; //we'll remove constituents from that list, as we find a suitable slot for them
+	
+	//we iterate over all active slots and check if constituents fits them
+	for (int i = 0; i < Arts::BACKPACK_START; i++)
+	{
+		for(std::vector<ConstituentInfo>::iterator art = constituentsToBePlaced.begin(); art != constituentsToBePlaced.end(); art++)
 		{
-			const CArtifact &constituent = *artifacts[constituentID];
+			if(art->art->canBePutAt(ArtifactLocation(al.hero, i), i == al.slot)) // i == al.slot because we can remove already worn artifact only from that slot  that is our main destination
+			{
+				constituentsToBePlaced.erase(art);
+				break;
+			}
+		}
+	}
+
+	return constituentsToBePlaced.empty();
+}
+
+bool CCombinedArtifactInstance::canBeDisassembled() const
+{
+	return true;
+}
+
+CCombinedArtifactInstance::CCombinedArtifactInstance(CArtifact *Art)
+	: CArtifactInstance(Art)
+{
+}
+
+CCombinedArtifactInstance::CCombinedArtifactInstance()
+{
+
+}
 
-			if (!destConsumed && vstd::contains(constituent.possibleSlots, slotID)) 
+void CCombinedArtifactInstance::createConstituents()
+{
+	assert(artType);
+	assert(artType->constituents);
+
+	BOOST_FOREACH(ui32 a, *artType->constituents)
+	{
+		addAsConstituent(CArtifactInstance::createNewArtifactInstance(a), -1);
+	}
+}
+
+void CCombinedArtifactInstance::addAsConstituent(CArtifactInstance *art, int slot)
+{
+	assert(vstd::contains(*artType->constituents, art->artType->id));
+	assert(art->parents.size() == 1  &&  art->parents.front() == art->artType);
+	constituentsInfo.push_back(ConstituentInfo(art, slot));
+	attachTo(art);
+}
+
+void CCombinedArtifactInstance::putAt(CGHeroInstance *h, ui16 slot)
+{
+	if(slot >= Arts::BACKPACK_START)
+	{
+		CArtifactInstance::putAt(h, slot);
+		BOOST_FOREACH(ConstituentInfo &ci, constituentsInfo)
+			ci.slot = -1;
+	}
+	else
+	{
+		CArtifactInstance *mainConstituent = figureMainConstituent(slot); //it'll be replaced with combined artifact, not a lock
+		CArtifactInstance::putAt(h, slot); //puts combined art (this)
+
+		BOOST_FOREACH(ConstituentInfo &ci, constituentsInfo)
+		{
+			if(ci.art != mainConstituent)
 			{
-				destConsumed = true;
-			} 
-			else 
+				int pos = -1;
+				if(isbetw(ci.slot, 0, Arts::BACKPACK_START)  &&  ci.art->canBePutAt(ArtifactLocation(h, ci.slot))) //there is a valid suggestion where to place lock 
+					pos = ci.slot;
+				else
+					ci.slot = pos = ci.art->firstAvailableSlot(h);
+
+				assert(pos < Arts::BACKPACK_START);
+				h->setNewArtSlot(pos, ci.art, true); //sets as lock
+			}
+			else
 			{
-				BOOST_REVERSE_FOREACH(ui16 slot, constituent.possibleSlots) 
-				{
-					if (vstd::contains(artifWorn, slot) && artifWorn[slot]->id == 145) 
-					{
-						artifWorn.erase(slot);
-						break;
-					}
-				}
+				ci.slot = -1;
 			}
 		}
 	}
 }
 
-void CArtHandler::clearHlpLists()
+void CCombinedArtifactInstance::removeFrom(CGHeroInstance *h, ui16 slot)
 {
-	treasures.clear();
-	minors.clear();
-	majors.clear();
-	relics.clear();
+	if(slot >= Arts::BACKPACK_START)
+	{
+		CArtifactInstance::removeFrom(h, slot);
+	}
+	else
+	{
+		BOOST_FOREACH(ConstituentInfo &ci, constituentsInfo)
+		{
+			if(ci.slot >= 0)
+			{
+				h->eraseArtSlot(ci.slot);
+				ci.slot = -1;
+			}
+			else
+			{
+				//main constituent
+				CArtifactInstance::removeFrom(h, slot);
+			}
+		}
+	}
 }
 
-void CArtHandler::initAllowedArtifactsList(const std::vector<ui8> &allowed)
+CArtifactInstance * CCombinedArtifactInstance::figureMainConstituent(ui16 slot)
 {
-	allowedArtifacts.clear();
-	clearHlpLists();
-	for (int i=0; i<144; ++i) //yes, 144
+	CArtifactInstance *mainConstituent = NULL; //it'll be replaced with combined artifact, not a lock
+	BOOST_FOREACH(ConstituentInfo &ci, constituentsInfo)
+		if(ci.slot == slot)
+			mainConstituent = ci.art;
+
+	if(!mainConstituent)
 	{
-		if (allowed[i])
-			allowedArtifacts.push_back(artifacts[i]);
+		BOOST_FOREACH(ConstituentInfo &ci, constituentsInfo)
+		{
+			if(vstd::contains(ci.art->artType->possibleSlots, slot))
+			{
+				mainConstituent = ci.art;
+			}
+		}
 	}
+
+	return mainConstituent;
 }
+
+void CCombinedArtifactInstance::deserializationFix()
+{
+	BOOST_FOREACH(ConstituentInfo &ci, constituentsInfo)
+		attachTo(ci.art);
+}
+
+CCombinedArtifactInstance::ConstituentInfo::ConstituentInfo(CArtifactInstance *Art /*= NULL*/, ui16 Slot /*= -1*/)
+{
+	art = Art;
+	slot = Slot;
+}

+ 234 - 0
lib/CArtHandler.h

@@ -0,0 +1,234 @@
+#ifndef __CARTHANDLER_H__
+#define __CARTHANDLER_H__
+#include "../global.h"
+#include "../lib/HeroBonus.h"
+#include <set>
+#include <list>
+
+#include "../lib/ConstTransitivePtr.h"
+
+/*
+ * CArtHandler.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 CDefHandler;
+class CArtifact;
+class CGHeroInstance;
+struct ArtifactLocation;
+
+class DLL_EXPORT CArtifact : public CBonusSystemNode //container for artifacts
+{
+protected:
+	std::string name, description; //set if custom
+public:
+	enum EartClass {ART_SPECIAL=1, ART_TREASURE=2, ART_MINOR=4, ART_MAJOR=8, ART_RELIC=16}; //artifact classes
+	const std::string &Name() const; //getter
+	const std::string &Description() const; //getter
+	bool isBig () const;
+	//bool isModable () const;
+	//bool fitsAt (const std::map<ui16, const CArtifact*> &artifWorn, ui16 slot) const;
+	//bool canBeAssembledTo (const std::map<ui16, const CArtifact*> &artifWorn, ui32 artifactID) const;
+// 	void addBonusesTo (BonusList *otherBonuses) const;
+// 	void removeBonusesFrom (BonusList *otherBonuses) const;
+	virtual void SetProperty (int mod){};
+	virtual void Init(){};
+	int getArtClassSerial() const; //0 - treasure, 1 - minor, 2 - major, 3 - relic, 4 - spell scroll, 5 - other
+	std::string nodeName() const OVERRIDE;
+
+	ui32 price;
+	std::vector<ui16> possibleSlots; //ids of slots where artifact can be placed
+	std::vector<ui32> * constituents; // Artifacts IDs a combined artifact consists of, or NULL.
+	std::vector<ui32> * constituentOf; // Reverse map of constituents.
+	EartClass aClass;
+	si32 id;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CBonusSystemNode&>(*this);
+		h & name & description & price & possibleSlots & constituents & constituentOf & aClass & id;
+	}
+
+	CArtifact();
+	~CArtifact();
+
+	//override
+	//void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const;
+};
+
+class DLL_EXPORT CArtifactInstance : public CBonusSystemNode
+{
+protected:
+	void init();
+	CArtifactInstance(CArtifact *Art);
+public:
+	CArtifactInstance();
+
+	ConstTransitivePtr<CArtifact> artType; 
+	si32 id; //id of the instance
+
+	//CArtifactInstance(int aid);
+
+	std::string nodeName() const OVERRIDE;
+	void deserializationFix();
+	void setType(CArtifact *Art);
+
+	int firstAvailableSlot(const CGHeroInstance *h) const;
+	int firstBackpackSlot(const CGHeroInstance *h) const;
+
+	virtual bool canBePutAt(const ArtifactLocation &al, bool assumeDestRemoved = false) const;
+	virtual bool canBeDisassembled() const;
+	virtual void putAt(CGHeroInstance *h, ui16 slot);
+	virtual void removeFrom(CGHeroInstance *h, ui16 slot);
+
+	std::vector<const CArtifact *> assemblyPossibilities(const CGHeroInstance *h) const;
+	void move(ArtifactLocation &src, ArtifactLocation &dst);
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CBonusSystemNode&>(*this);
+		h & artType & id;
+	}
+
+	static CArtifactInstance *createScroll(const CSpell *s);
+	static CArtifactInstance *createNewArtifactInstance(CArtifact *Art);
+	static CArtifactInstance *createNewArtifactInstance(int aid);
+};
+
+class DLL_EXPORT CCombinedArtifactInstance : public CArtifactInstance
+{
+	CCombinedArtifactInstance(CArtifact *Art);
+public:
+	struct ConstituentInfo
+	{
+		ConstTransitivePtr<CArtifactInstance> art;
+		si16 slot;
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & art & slot;
+		}
+
+		ConstituentInfo(CArtifactInstance *art = NULL, ui16 slot = -1);
+	};
+
+	std::vector<ConstituentInfo> constituentsInfo;
+
+	bool canBePutAt(const ArtifactLocation &al, bool assumeDestRemoved = false) const OVERRIDE;
+	bool canBeDisassembled() const OVERRIDE;
+	void putAt(CGHeroInstance *h, ui16 slot) OVERRIDE;
+	void removeFrom(CGHeroInstance *h, ui16 slot) OVERRIDE;
+
+	void createConstituents();
+	void addAsConstituent(CArtifactInstance *art, int slot);
+	CArtifactInstance *figureMainConstituent(ui16 slot); //main constituent is replcaed with us (combined art), not lock
+
+	CCombinedArtifactInstance();
+
+	void deserializationFix();
+
+	friend class CArtifactInstance;
+	friend class AssembledArtifact;
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CArtifactInstance&>(*this);
+		h & constituentsInfo;
+	}
+};
+
+// class DLL_EXPORT IModableArt : public CArtifact //artifact which can have different properties, such as scroll or banner
+// { //used only for dynamic cast :P
+// public:
+// 	si32 ID; //used for smart serialization
+// 
+// 	template <typename Handler> void serialize(Handler &h, const int version)
+// 	{
+// 		h & static_cast<CArtifact&>(*this);
+// 		h & ID;
+// 	}
+// };
+// 
+// class DLL_EXPORT CScroll : public IModableArt // Spell Scroll
+// {
+// public:
+// 	CScroll(){spellid=0;};
+// 	CScroll(spelltype sid){spellid = sid;};
+// 	spelltype spellid;
+// 	void Init();
+// 	void SetProperty (int mod){spellid = mod;};
+// 	template <typename Handler> void serialize(Handler &h, const int version)
+// 	{
+// 		h & static_cast<IModableArt&>(*this);
+// 		h & spellid;
+// 	}
+// };
+// 
+// class DLL_EXPORT CCustomizableArt : public IModableArt // Warlord's Banner with multiple options
+// {
+// public:
+// 	ui8 mode;
+// 	CCustomizableArt(){mode=0;};
+// 	void Init(){};
+// 	void SetProperty (int mod){};
+// 	template <typename Handler> void serialize(Handler &h, const int version)
+// 	{
+// 		h & static_cast<IModableArt&>(*this);
+// 		h & mode;
+// 	}
+// };
+// 
+// class DLL_EXPORT CCommanderArt : public IModableArt // Growing with time
+// {
+// public:
+// 	ui32 level;
+// 	CCommanderArt(){level = 0;};
+// 	void Init(){};
+// 	void SetProperty (int mod){level = mod;};
+// 	void Upgrade(){level++;};
+// 	template <typename Handler> void serialize(Handler &h, const int version)
+// 	{
+// 		h & static_cast<IModableArt&>(*this);
+// 		h & level;
+// 	}
+// };
+
+class DLL_EXPORT CArtHandler //handles artifacts
+{
+	void giveArtBonus(int aid, Bonus::BonusType type, int val, int subtype = -1, int valType = Bonus::BASE_NUMBER, ILimiter * limiter = NULL);
+public:
+	std::vector<CArtifact*> treasures, minors, majors, relics;
+	std::vector< ConstTransitivePtr<CArtifact> > artifacts;
+	std::vector<CArtifact *> allowedArtifacts;
+	std::set<ui32> bigArtifacts; // Artifacts that cannot be moved to backpack, e.g. war machines.
+	//std::map<ui32, ui8> modableArtifacts; //1-scroll, 2-banner, 3-commander art with progressive bonus
+
+	void loadArtifacts(bool onlyTxt);
+	void sortArts();
+	void addBonuses();
+	void clear();
+	void clearHlpLists();
+	ui16 getRandomArt (int flags);
+	ui16 getArtSync (ui32 rand, int flags);
+	void getAllowedArts(std::vector<ConstTransitivePtr<CArtifact> > &out, std::vector<CArtifact*> *arts, int flag);
+	void getAllowed(std::vector<ConstTransitivePtr<CArtifact> > &out, int flags);
+	void erasePickedArt (si32 id);
+	bool isBigArtifact (ui32 artID) const {return bigArtifacts.find(artID) != bigArtifacts.end();}
+// 	void equipArtifact (std::map<ui16, const CArtifact*> &artifWorn, ui16 slotID, const CArtifact* art) const;
+// 	void unequipArtifact (std::map<ui16, const CArtifact*> &artifWorn, ui16 slotID) const;
+	void initAllowedArtifactsList(const std::vector<ui8> &allowed); //allowed[art_id] -> 0 if not allowed, 1 if allowed
+	static int convertMachineID(int id, bool creToArt);
+	CArtHandler();
+	~CArtHandler();
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & artifacts & allowedArtifacts & treasures & minors & majors & relics;
+		//if(!h.saving) sortArts();
+	}
+};
+
+
+#endif // __CARTHANDLER_H__

+ 2 - 20
hch/CBuildingHandler.cpp → lib/CBuildingHandler.cpp

@@ -144,30 +144,12 @@ void CBuildingHandler::loadBuildings()
 		}
 	}
 
-	//loading ERMU to picture
-	std::ifstream etp(DATA_DIR "/config/ERMU_to_picture.txt");
-
-	assert(etp.is_open());
-
-	for(int g=0; g<44; ++g)
-	{
-		for (int b=0; b<ARRAY_COUNT(ERMUtoPicture); ++b)
-		{
-			std::string buf;
-			etp >> buf;
-			ERMUtoPicture[b][g] = buf;
-		}
-	}
-
-	etp.close();
-
-
 }
 
 CBuildingHandler::~CBuildingHandler()
 {
-	for(std::vector< std::map<int, CBuilding*> >::iterator i=buildings.begin(); i!=buildings.end(); i++)
-		for(std::map<int, CBuilding*>::iterator j=i->begin(); j!=i->end(); j++)
+	for(std::vector< bmap<int, ConstTransitivePtr<CBuilding> > >::iterator i=buildings.begin(); i!=buildings.end(); i++)
+		for(std::map<int, ConstTransitivePtr<CBuilding> >::iterator j=i->begin(); j!=i->end(); j++)
 			delete j->second;
 }
 

+ 5 - 4
hch/CBuildingHandler.h → lib/CBuildingHandler.h

@@ -5,6 +5,8 @@
 #include <vector>
 #include <set>
 
+#include "../lib/ConstTransitivePtr.h"
+
 /*
  * CBuildingHandler.h, part of VCMI engine
  *
@@ -37,9 +39,8 @@ public:
 class DLL_EXPORT CBuildingHandler
 {
 public:
-	std::vector< std::map<int, CBuilding*> > buildings; ///< vector by castle ID, second the building ID (in ERM-U format)
-	std::map<int, std::pair<std::string,std::vector< std::vector< std::vector<int> > > > > hall; //map<castle ID, pair<hall bg name, std::vector< std::vector<building id> >[5]> - external vector is the vector of buildings in the row, internal is the list of buildings for the specific slot
-	std::map<int, std::string> ERMUtoPicture[F_NUMBER]; //maps building ID to it's picture's name for each town type
+	std::vector< bmap<int, ConstTransitivePtr<CBuilding> > > buildings; ///< vector by castle ID, second the building ID (in ERM-U format)
+	bmap<int, std::pair<std::string,std::vector< std::vector< std::vector<int> > > > > hall; //map<castle ID, pair<hall bg name, std::vector< std::vector<building id> >[5]> - external vector is the vector of buildings in the row, internal is the list of buildings for the specific slot
 
 	void loadBuildings(); //main loader
 	~CBuildingHandler(); //d-tor
@@ -47,7 +48,7 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & buildings & hall & ERMUtoPicture;
+		h & buildings & hall;
 	}
 };
 

+ 7 - 6
hch/CCampaignHandler.cpp → lib/CCampaignHandler.cpp

@@ -522,7 +522,9 @@ void CCampaignScenario::prepareCrossoverHeroes( std::vector<CGHeroInstance *> he
 			{
 				BOOST_FOREACH(CGHeroInstance * cgh, crossoverHeroes)
 				{
-					cgh->artifacts -= VLC->arth->artifacts[g];
+					tlog1 << "TODO TODO TODO - take artifacts from hero\n";
+					//TODO how was that supposed to work with worn artifacts?
+					//cgh->artifactsInBackpack -= VLC->arth->artifacts[g];
 				}
 			}
 		}
@@ -531,13 +533,12 @@ void CCampaignScenario::prepareCrossoverHeroes( std::vector<CGHeroInstance *> he
 	//trimming creatures
 	BOOST_FOREACH(CGHeroInstance * cgh, crossoverHeroes)
 	{
-		CCreatureSet army = cgh->getArmy();
-		for (TSlots::iterator j = army.slots.begin(); j != army.slots.end(); j++)
+		for (TSlots::const_iterator j = cgh->Slots().begin(); j != cgh->Slots().end(); j++)
 		{
-			if (! (travelOptions.monstersKeptByHero[j->first / 8] & (1 << (j->first%8)) ))
+			if (!(travelOptions.monstersKeptByHero[j->first / 8] & (1 << (j->first%8)) ))
 			{
-				army.slots.erase(j);
-				j = army.slots.begin();
+				cgh->eraseStack(j->first);
+				j = cgh->Slots().begin();
 			}
 		}
 	}

+ 0 - 0
hch/CCampaignHandler.h → lib/CCampaignHandler.h


+ 78 - 65
hch/CCreatureHandler.cpp → lib/CCreatureHandler.cpp

@@ -12,6 +12,7 @@
 #include <boost/algorithm/string/replace.hpp>
 #include "../lib/VCMI_Lib.h"
 #include "../lib/CGameState.h"
+#include <boost/foreach.hpp>
 
 using namespace boost::assign;
 extern CLodHandler * bitmaph;
@@ -78,17 +79,17 @@ bool CCreature::isDoubleWide() const
 
 bool CCreature::isFlying() const
 {
-	return vstd::contains(bonuses, Bonus::FLYING);
+	return hasBonusOfType(Bonus::FLYING);
 }
 
 bool CCreature::isShooting() const
 {
-	return vstd::contains(bonuses, Bonus::SHOOTER);
+	return hasBonusOfType(Bonus::SHOOTER);
 }
 
 bool CCreature::isUndead() const
 {
-	return vstd::contains(bonuses, Bonus::UNDEAD);
+	return hasBonusOfType(Bonus::UNDEAD);
 }
 
 /**
@@ -126,19 +127,29 @@ CCreature::CCreature()
 }
 void CCreature::addBonus(int val, int type, int subtype /*= -1*/)
 {
-	Bonus added(Bonus::PERMANENT, type, Bonus::CREATURE_ABILITY, val, idNumber, subtype, Bonus::BASE_NUMBER);
-	bonuses.push_back(added);
-}
-void CCreature::getParents(TCNodes &out, const CBonusSystemNode *root /*= NULL*/) const
-{
-	out.insert (VLC->creh->globalEffects);
+	Bonus *added = new Bonus(Bonus::PERMANENT, type, Bonus::CREATURE_ABILITY, val, idNumber, subtype, Bonus::BASE_NUMBER);
+	addNewBonus(added);
 }
+// void CCreature::getParents(TCNodes &out, const CBonusSystemNode *root /*= NULL*/) const
+// {
+// 	out.insert (VLC->creh->globalEffects);
+// }
 bool CCreature::isMyUpgrade(const CCreature *anotherCre) const
 {
 	//TODO upgrade of upgrade?
 	return vstd::contains(upgrades, anotherCre->idNumber);
 }
 
+bool CCreature::valid() const
+{
+	return this == VLC->creh->creatures[idNumber];
+}
+
+std::string CCreature::nodeName() const
+{
+	return "Type of creature " + namePl;
+}
+
 int readNumber(int & befi, int & i, int andame, std::string & buf) //helper function for void CCreatureHandler::loadCreatures() and loadUnitAnimInfo()
 {
 	befi=i;
@@ -312,12 +323,12 @@ void CCreatureHandler::loadCreatures()
 			if(boost::algorithm::find_first(ncre.abilityRefs, "const_raises_morale"))
 			{
 				ncre.addBonus(+1, Bonus::MORALE);;
-				ncre.bonuses.back().effectRange = Bonus::ONLY_ALLIED_ARMY;
+				ncre.bonuses.back()->effectRange = Bonus::ONLY_ALLIED_ARMY;
 			}
 			if(boost::algorithm::find_first(ncre.abilityRefs, "const_lowers_morale"))
 			{
 				ncre.addBonus(-1, Bonus::MORALE);;
-				ncre.bonuses.back().effectRange = Bonus::ONLY_ENEMY_ARMY;
+				ncre.bonuses.back()->effectRange = Bonus::ONLY_ENEMY_ARMY;
 			}
 			if(boost::algorithm::find_first(ncre.abilityRefs, "KING_1"))
 				ncre.addBonus(0, Bonus::KING1);
@@ -364,7 +375,7 @@ void CCreatureHandler::loadCreatures()
 		case '+': //add new ability
 			{
 				int creatureID;
-				Bonus nsf;
+				Bonus *nsf = new Bonus();
 				si32 buf;
 				std::string type;
 
@@ -380,29 +391,29 @@ void CCreatureHandler::loadCreatures()
 						cre->doubleWide = true;
 					else if(type == "ENEMY_MORALE_DECREASING")
 					{
-						cre->addBonus(-1, Bonus::MORALE);;
-						cre->bonuses.back().effectRange = Bonus::ONLY_ENEMY_ARMY;
+						cre->addBonus(-1, Bonus::MORALE);
+						cre->bonuses.back()->effectRange = Bonus::ONLY_ENEMY_ARMY;
 					}
 					else if(type == "ENEMY_LUCK_DECREASING")
 					{
-						cre->addBonus(-1, Bonus::LUCK);;
-						cre->bonuses.back().effectRange = Bonus::ONLY_ENEMY_ARMY;
+						cre->addBonus(-1, Bonus::LUCK);
+						cre->bonuses.back()->effectRange = Bonus::ONLY_ENEMY_ARMY;
 					}
 					else
 						tlog1 << "Error: invalid type " << type << " in cr_abils.txt" << std::endl;
 					break;
 				}
-				nsf.type = it->second;
+				nsf->type = it->second;
 
-				reader >> buf; nsf.val = buf;
-				reader >> buf; nsf.subtype = buf;
-				reader >> buf; nsf.additionalInfo = buf;
-				nsf.source = Bonus::CREATURE_ABILITY;
-				nsf.id = cre->idNumber;
-				nsf.duration = Bonus::ONE_BATTLE;
-				nsf.turnsRemain = 0;
+				reader >> buf; nsf->val = buf;
+				reader >> buf; nsf->subtype = buf;
+				reader >> buf; nsf->additionalInfo = buf;
+				nsf->source = Bonus::CREATURE_ABILITY;
+				nsf->id = cre->idNumber;
+				nsf->duration = Bonus::ONE_BATTLE;
+				nsf->turnsRemain = 0;
 
-				cre->bonuses += nsf;
+				cre->addNewBonus(nsf);
 				break;
 			}
 		case '-': //remove ability
@@ -424,7 +435,8 @@ void CCreatureHandler::loadCreatures()
 
 				Bonus::BonusType ecf = static_cast<Bonus::BonusType>(typeNo);
 
-				creatures[creatureID]->bonuses -= ecf;
+				Bonus *b = creatures[creatureID]->getBonus(Selector::type(ecf));
+				creatures[creatureID]->removeBonus(b);
 				break;
 			}
 		case '0': //end reading
@@ -457,8 +469,8 @@ void CCreatureHandler::loadCreatures()
 	}
 	ifs.close();
 	ifs.clear();
-	for(i=1;i<=10;i++)
-		levelCreatures.insert(std::pair<int,std::vector<CCreature*> >(i,std::vector<CCreature*>()));
+	for(i = 1; i <= CRE_LEVELS; i++)
+		levelCreatures[i];
 
 	tlog5 << "\t\tReading config/monsters.txt" << std::endl;
 	ifs.open(DATA_DIR "/config/monsters.txt");
@@ -467,11 +479,11 @@ void CCreatureHandler::loadCreatures()
 		{
 			int id, lvl;
 			ifs >> id >> lvl;
-			if(lvl>0) 
- 	        { 
-	 	        creatures[id]->level = lvl; 
-	            levelCreatures[lvl].push_back(creatures[id]); 
- 	        } 
+			if(lvl>0)
+			{
+				creatures[id]->level = lvl;
+				levelCreatures[lvl].push_back(creatures[id]);
+			}
 		}
 	}
 	ifs.close();
@@ -604,7 +616,8 @@ void CCreatureHandler::loadCreatures()
 	if (STACK_EXP) 	//reading default stack experience bonuses
 	{
 		buf = bitmaph->getTextFile("CREXPBON.TXT");
-		int it = 0, creid;
+		int it = 0;
+		si32 creid = -1;
 		commonBonuses.resize(8); //8 tiers
 		stackExperience b;
 		b.expBonuses.resize(10);
@@ -616,12 +629,12 @@ void CCreatureHandler::loadCreatures()
 		loadToIt (dump2, buf, it, 4); //crop comment
 		for (i = 0; i < 8; ++i)
 		{
-			commonBonuses[i].push_back(b);//health bonus common for all
+			commonBonuses[i].push_back(new stackExperience(b));//health bonus common for all
 			for (int j = 0; j < 4; ++j) //four modifiers common for tiers
 			{
 				loadToIt (dump2, buf, it, 4); //ignore index
 				loadStackExp(b, buf, it);
-				commonBonuses[i].push_back(b);
+				commonBonuses[i].push_back(new stackExperience(b));
 				loadToIt (dump2, buf, it, 3); //crop comment
 			}
 		}
@@ -629,17 +642,17 @@ void CCreatureHandler::loadCreatures()
 		{
 			loadToIt(creid, buf, it, 4); //get index
 			loadStackExp(b, buf, it);
-			creatures[creid]->bonuses.push_back(b); //experience list is common for creatures of that type
+			creatures[creid]->bonuses.push_back(new stackExperience(b)); //experience list is common for creatures of that type
 			loadToIt (dump2, buf, it, 3); //crop comment
-		}
-		while (it < buf.size());
-		for (std::vector<CCreature*>::iterator i = creatures.begin(); i != creatures.end(); i++)
-		{
-			if (it = (*i)->level < 7)
-				std::copy(commonBonuses[it-1].begin(), commonBonuses[it-1].end(), (*i)->bonuses.begin());
-			else
-				std::copy(commonBonuses[7].begin(), commonBonuses[7].end(), (*i)->bonuses.begin()); //common for tiers 8+
-		}
+		} while (it < buf.size());
+
+ 		BOOST_FOREACH(CCreature *c, creatures)
+ 		{
+ 			if (it = c->level < 7)
+ 				std::copy(commonBonuses[it-1].begin(), commonBonuses[it-1].end(), c->bonuses.begin());
+ 			else
+ 				std::copy(commonBonuses[7].begin(), commonBonuses[7].end(), c->bonuses.begin()); //common for tiers 8+
+ 		}
 	} //end of stack experience
 }
 
@@ -720,25 +733,25 @@ void CCreatureHandler::loadStackExp(stackExperience & b, std::string & src, int
 	loadToIt(mod, src, it, 4);
 	switch (buf[0])
 	{
-		case 'H':
-			b.type = Bonus::STACK_HEALTH;
-			break;
-		case 'A':
-			b.type = Bonus::PRIMARY_SKILL;
-			b.subtype = PrimarySkill::ATTACK;
-			break;
-		case 'D':
-			b.type = Bonus::PRIMARY_SKILL;
-			b.subtype = PrimarySkill::DEFENSE;
-			break;
-		case 'M': //Max damage
-			b.type = Bonus::CREATURE_DAMAGE;
-			b.subtype = 2;
-			break;
-		case 'm': //Min damage
-			b.type = Bonus::CREATURE_DAMAGE;
-			b.subtype = 1;
-			break;
+	case 'H':
+		b.type = Bonus::STACK_HEALTH;
+		break;
+	case 'A':
+		b.type = Bonus::PRIMARY_SKILL;
+		b.subtype = PrimarySkill::ATTACK;
+		break;
+	case 'D':
+		b.type = Bonus::PRIMARY_SKILL;
+		b.subtype = PrimarySkill::DEFENSE;
+		break;
+	case 'M': //Max damage
+		b.type = Bonus::CREATURE_DAMAGE;
+		b.subtype = 2;
+		break;
+	case 'm': //Min damage
+		b.type = Bonus::CREATURE_DAMAGE;
+		b.subtype = 1;
+		break;
 	}
 	loadToIt (b.val, src, it, 4); //basic value, not particularly useful but existent
 	for (int i = 0; i < 10; ++i)

+ 11 - 8
hch/CCreatureHandler.h → lib/CCreatureHandler.h

@@ -6,10 +6,10 @@
 #include <map>
 #include <set>
 
-#include "CSoundBase.h"
 #include "../lib/HeroBonus.h"
 #include "../lib/CGameState.h"
 #include "../lib/CCreatureSet.h"
+#include "../lib/ConstTransitivePtr.h"
 
 /*
  * CCreatureHandler.h, part of VCMI engine
@@ -61,8 +61,11 @@ public:
 	static int getQuantityID(const int & quantity); //0 - a few, 1 - several, 2 - pack, 3 - lots, 4 - horde, 5 - throng, 6 - swarm, 7 - zounds, 8 - legion
 	bool isMyUpgrade(const CCreature *anotherCre) const;
 
+	bool valid() const;
+
 	void addBonus(int val, int type, int subtype = -1);
-	void getParents(TCNodes &out, const CBonusSystemNode *root /*= NULL*/) const;
+	std::string nodeName() const OVERRIDE;
+	//void getParents(TCNodes &out, const CBonusSystemNode *root /*= NULL*/) const;
 
 	template<typename RanGen>
 	int getRandomAmount(RanGen &ranGen)
@@ -102,15 +105,15 @@ public:
 	CBonusSystemNode *globalEffects;
 	std::set<int> notUsedMonsters;
 	std::set<TCreature> doubledCreatures; //they get double week
-	std::vector<CCreature*> creatures; //creature ID -> creature info
-	std::map<int,std::vector<CCreature*> > levelCreatures; //level -> list of creatures
-	std::map<std::string,int> nameToID;
-	std::map<int,std::string> idToProjectile;
-	std::map<int,bool> idToProjectileSpin; //if true, appropriate projectile is spinning during flight
+	std::vector<ConstTransitivePtr<CCreature> > creatures; //creature ID -> creature info
+	bmap<int,std::vector<ConstTransitivePtr< CCreature> > > levelCreatures; //level -> list of creatures
+	bmap<std::string,int> nameToID;
+	bmap<int,std::string> idToProjectile;
+	bmap<int,bool> idToProjectileSpin; //if true, appropriate projectile is spinning during flight
 	std::vector<si8> factionAlignments; //1 for good, 0 for neutral and -1 for evil with faction ID as index
 	int factionToTurretCreature[F_NUMBER]; //which creature's animation should be used to dispaly creature in turret while siege
 
-	std::map<TBonusType, std::pair<std::string, std::string>> stackBonuses; // bonus => name, description
+	std::map<TBonusType, std::pair<std::string, std::string> > stackBonuses; // bonus => name, description
 	std::vector<BonusList> commonBonuses; // levels 1-8 from CREXPBON.txt
 
 	void loadCreatures();

+ 362 - 91
lib/CCreatureSet.cpp

@@ -1,58 +1,75 @@
 #define VCMI_DLL
 #include "CCreatureSet.h"
-#include "../hch/CCreatureHandler.h"
+#include "CCreatureHandler.h"
 #include "VCMI_Lib.h"
 #include <assert.h>
-#include "../hch/CObjectHandler.h"
+#include "CObjectHandler.h"
 #include "IGameCallback.h"
 #include "CGameState.h"
-#include "../hch/CGeneralTextHandler.h"
-#include "../hch/CSpellHandler.h"
+#include "CGeneralTextHandler.h"
+#include <sstream>
+#include "CSpellHandler.h"
 #include <boost/lexical_cast.hpp>
 #include <boost/algorithm/string/replace.hpp>
 
-const CStackInstance CCreatureSet::operator[](TSlot slot) const
+const CStackInstance &CCreatureSet::operator[](TSlot slot) const
 {
-	TSlots::const_iterator i = slots.find(slot);
-	if (i != slots.end())
-		return i->second;
+	TSlots::const_iterator i = stacks.find(slot);
+	if (i != stacks.end())
+		return *i->second;
 	else
 		throw std::string("That slot is empty!");
 }
 
-const CCreature* CCreatureSet::getCreature(TSlot slot) const /*workaround of map issue */
+const CCreature* CCreatureSet::getCreature(TSlot slot) const
 {
-	TSlots::const_iterator i = slots.find(slot);
-	if (i != slots.end())
-		return i->second.type;
+	TSlots::const_iterator i = stacks.find(slot);
+	if (i != stacks.end())
+		return i->second->type;
 	else
 		return NULL;
 }
 
 bool CCreatureSet::setCreature(TSlot slot, TCreature type, TQuantity quantity) /*slots 0 to 6 */
 {
-	slots[slot] = CStackInstance(type, quantity);  //brutal force
-	if (quantity == 0)
-		slots.erase(slot);
-
-	if (slots.size() > ARMY_SIZE) 
+	if(slot > 6 || slot < 0)
+	{
+		tlog1 << "Cannot set slot " << slot << std::endl;
 		return false;
-	else 
+	}
+	if(!quantity)
+	{
+		tlog2 << "Using set creature to delete stack?\n";
+		eraseStack(slot);
 		return true;
+	}
+
+	if(hasStackAtSlot(slot)) //remove old creature
+		eraseStack(slot);
+
+	putStack(slot, new CStackInstance(type, quantity));
+	return true;
 }
 
 TSlot CCreatureSet::getSlotFor(TCreature creature, ui32 slotsAmount/*=7*/) const /*returns -1 if no slot available */
 {
-	for(TSlots::const_iterator i=slots.begin(); i!=slots.end(); ++i)
+	return getSlotFor(VLC->creh->creatures[creature], slotsAmount);
+}
+
+TSlot CCreatureSet::getSlotFor(const CCreature *c, ui32 slotsAmount/*=ARMY_SIZE*/) const
+{
+	assert(c->valid());
+	for(TSlots::const_iterator i=stacks.begin(); i!=stacks.end(); ++i)
 	{
-		if(i->second.type->idNumber == creature)
+		assert(i->second->type->valid());
+		if(i->second->type == c)
 		{
 			return i->first; //if there is already such creature we return its slot id
 		}
 	}
 	for(ui32 i=0; i<slotsAmount; i++)
 	{
-		if(slots.find(i) == slots.end())
+		if(stacks.find(i) == stacks.end())
 		{
 			return i; //return first free slot
 		}
@@ -60,11 +77,11 @@ TSlot CCreatureSet::getSlotFor(TCreature creature, ui32 slotsAmount/*=7*/) const
 	return -1; //no slot available
 }
 
-int CCreatureSet::getAmount(TSlot slot) const
+int CCreatureSet::getStackCount(TSlot slot) const
 {
-	TSlots::const_iterator i = slots.find(slot);
-	if (i != slots.end())
-		return i->second.count;
+	TSlots::const_iterator i = stacks.find(slot);
+	if (i != stacks.end())
+		return i->second->count;
 	else
 		return 0; //TODO? consider issuing a warning
 }
@@ -72,12 +89,12 @@ int CCreatureSet::getAmount(TSlot slot) const
 bool CCreatureSet::mergableStacks(std::pair<TSlot, TSlot> &out, TSlot preferable /*= -1*/) const /*looks for two same stacks, returns slot positions */
 {
 	//try to match creature to our preferred stack
-	if(preferable >= 0  &&  vstd::contains(slots, preferable))
+	if(preferable >= 0  &&  vstd::contains(stacks, preferable))
 	{
-		const CCreature *cr = slots.find(preferable)->second.type;
-		for(TSlots::const_iterator j=slots.begin(); j!=slots.end(); ++j)
+		const CCreature *cr = stacks.find(preferable)->second->type;
+		for(TSlots::const_iterator j=stacks.begin(); j!=stacks.end(); ++j)
 		{
-			if(cr == j->second.type && j->first != preferable)
+			if(cr == j->second->type && j->first != preferable)
 			{
 				out.first = preferable;
 				out.second = j->first;
@@ -86,11 +103,11 @@ bool CCreatureSet::mergableStacks(std::pair<TSlot, TSlot> &out, TSlot preferable
 		}
 	}
 
-	for(TSlots::const_iterator i=slots.begin(); i!=slots.end(); ++i)
+	for(TSlots::const_iterator i=stacks.begin(); i!=stacks.end(); ++i)
 	{
-		for(TSlots::const_iterator j=slots.begin(); j!=slots.end(); ++j)
+		for(TSlots::const_iterator j=stacks.begin(); j!=stacks.end(); ++j)
 		{
-			if(i->second.type == j->second.type  &&  i->first != j->first)
+			if(i->second->type == j->second->type  &&  i->first != j->first)
 			{
 				out.first = i->first;
 				out.second = j->first;
@@ -103,11 +120,11 @@ bool CCreatureSet::mergableStacks(std::pair<TSlot, TSlot> &out, TSlot preferable
 
 void CCreatureSet::sweep()
 {
-	for(TSlots::iterator i=slots.begin(); i!=slots.end(); ++i)
+	for(TSlots::iterator i=stacks.begin(); i!=stacks.end(); ++i)
 	{
-		if(!i->second.count)
+		if(!i->second->count)
 		{
-			slots.erase(i);
+			stacks.erase(i);
 			sweep();
 			break;
 		}
@@ -116,43 +133,53 @@ void CCreatureSet::sweep()
 
 void CCreatureSet::addToSlot(TSlot slot, TCreature cre, TQuantity count, bool allowMerging/* = true*/)
 {
-	assert(slot >= 0);
 	const CCreature *c = VLC->creh->creatures[cre];
-	assert(!vstd::contains(slots, slot) || (slots[slot].type == c && allowMerging)); //that slot was empty or contained same type creature
-	slots[slot].type = c;
-	slots[slot].count += count;
 
-	//TODO
-	const CArmedInstance *armedObj = dynamic_cast<const CArmedInstance *>(this);
-	if(armedObj && !slots[slot].armyObj)
-		slots[slot].armyObj = armedObj;
+	if(!hasStackAtSlot(slot))
+	{
+		setCreature(slot, cre, count);
+	}
+	else if(getCreature(slot) == c && allowMerging) //that slot was empty or contained same type creature
+	{
+		setStackCount(slot, getStackCount(slot) + count);
+	}
+	else
+	{
+		tlog1 << "Failed adding to slot!\n";
+	}
 }
 
-void CCreatureSet::addToSlot(TSlot slot, const CStackInstance &stack, bool allowMerging/* = true*/)
+void CCreatureSet::addToSlot(TSlot slot, CStackInstance *stack, bool allowMerging/* = true*/)
 {
-	assert(stack.type == VLC->creh->creatures[stack.type->idNumber]);
-	addToSlot(slot, stack.type->idNumber, stack.count, allowMerging	);
+	assert(stack->valid(true));
+
+	if(!hasStackAtSlot(slot))
+	{
+		putStack(slot, stack);
+	}
+	else if(allowMerging && stack->type == getCreature(slot))
+	{
+		joinStack(slot, stack);
+	}
+	else
+	{
+		tlog1 << "Cannot add to slot " << slot << " stack " << *stack << std::endl;
+	}
 }
 
 bool CCreatureSet::validTypes(bool allowUnrandomized /*= false*/) const
 {
-	for(TSlots::const_iterator i=slots.begin(); i!=slots.end(); ++i)
+	for(TSlots::const_iterator i=stacks.begin(); i!=stacks.end(); ++i)
 	{
-		bool isRand = (i->second.idRand != -1);
-		if(!isRand)
-		{
-			assert(i->second.type);
-			assert(i->second.type == VLC->creh->creatures[i->second.type->idNumber]);
-		}
-		else
-			assert(allowUnrandomized);
+		if(!i->second->valid(allowUnrandomized))
+			return false;
 	}
 	return true;
 }
 
 bool CCreatureSet::slotEmpty(TSlot slot) const
 {
-	return !vstd::contains(slots, slot);
+	return !hasStackAtSlot(slot);
 }
 
 bool CCreatureSet::needsLastStack() const
@@ -163,28 +190,23 @@ bool CCreatureSet::needsLastStack() const
 int CCreatureSet::getArmyStrength() const
 {
 	int ret = 0;
-	for(TSlots::const_iterator i = slots.begin(); i != slots.end(); i++)
-		ret += i->second.type->AIValue * i->second.count;
+	for(TSlots::const_iterator i = stacks.begin(); i != stacks.end(); i++)
+		ret += i->second->type->AIValue * i->second->count;
 	return ret;
 }
 
 ui64 CCreatureSet::getPower (TSlot slot) const
 {
-	return getCreature(slot)->AIValue * getAmount(slot);
+	return getCreature(slot)->AIValue * getStackCount(slot);
 }
 std::string CCreatureSet::getRoughAmount (TSlot slot) const
 {
-	return VLC->generaltexth->arraytxt[174 + 3*CCreature::getQuantityID(getAmount(slot))];
+	return VLC->generaltexth->arraytxt[174 + 3*CCreature::getQuantityID(getStackCount(slot))];
 }
 
 int CCreatureSet::stacksCount() const
 {
-	return slots.size();
-}
-
-void CCreatureSet::addStack(TSlot slot, const CStackInstance &stack)
-{
-	addToSlot(slot, stack, false);
+	return stacks.size();
 }
 
 void CCreatureSet::setFormation(bool tight)
@@ -194,26 +216,31 @@ void CCreatureSet::setFormation(bool tight)
 
 void CCreatureSet::setStackCount(TSlot slot, TQuantity count)
 {
-	assert(vstd::contains(slots, slot));
+	assert(hasStackAtSlot(slot));
 	assert(count > 0);
-	slots[slot].count = count;
+	stacks[slot]->count = count;
+	armyChanged();
 }
 
 void CCreatureSet::clear()
 {
-	slots.clear();
+	while(!stacks.empty())
+	{
+		eraseStack(stacks.begin()->first);
+	}
 }
 
 const CStackInstance& CCreatureSet::getStack(TSlot slot) const
 {
-	assert(vstd::contains(slots, slot));
-	return slots.find(slot)->second;
+	assert(hasStackAtSlot(slot));
+	return *stacks.find(slot)->second;
 }
 
 void CCreatureSet::eraseStack(TSlot slot)
 {
-	assert(vstd::contains(slots, slot));
-	slots.erase(slot);
+	assert(hasStackAtSlot(slot));
+	CStackInstance *toErase = detachStack(slot);
+	delNull(toErase);
 }
 
 bool CCreatureSet::contains(const CStackInstance *stack) const
@@ -221,30 +248,170 @@ bool CCreatureSet::contains(const CStackInstance *stack) const
 	if(!stack) 
 		return false;
 
-	for(TSlots::const_iterator i = slots.begin(); i != slots.end(); ++i)
-		if(&i->second == stack)
+	for(TSlots::const_iterator i = stacks.begin(); i != stacks.end(); ++i)
+		if(i->second == stack)
 			return true;
 
 	return false;
 }
 
+TSlot CCreatureSet::findStack(const CStackInstance *stack) const
+{
+	if(!stack) 
+		return -1;
+
+	for(TSlots::const_iterator i = stacks.begin(); i != stacks.end(); ++i)
+		if(i->second == stack)
+			return i->first;
+
+	return -1;
+}
+
+CArmedInstance * CCreatureSet::castToArmyObj()
+{
+	return dynamic_cast<CArmedInstance *>(this);
+}
+
+void CCreatureSet::putStack(TSlot slot, CStackInstance *stack)
+{
+	assert(!hasStackAtSlot(slot));
+	stacks[slot] = stack;
+	stack->setArmyObj(castToArmyObj());
+	armyChanged();
+}
+
+void CCreatureSet::joinStack(TSlot slot, CStackInstance * stack)
+{
+	const CCreature *c = getCreature(slot);
+	assert(c == stack->type);
+	assert(c);
+
+	//TODO move stuff 
+	changeStackCount(slot, stack->count);
+	delNull(stack);
+}
+
+void CCreatureSet::changeStackCount(TSlot slot, TQuantity toAdd)
+{
+	setStackCount(slot, getStackCount(slot) + toAdd);
+}
+
+CCreatureSet::CCreatureSet()
+{
+	formation = false;
+}
+
+CCreatureSet::CCreatureSet(const CCreatureSet&)
+{
+	assert(0);
+}
+
+CCreatureSet::~CCreatureSet()
+{
+	clear();
+}
+
+void CCreatureSet::setToArmy(CSimpleArmy &src)
+{
+	clear();
+	while(src)
+	{
+		TSimpleSlots::iterator i = src.army.begin();
+
+		assert(i->second.type);
+		assert(i->second.count);
+
+		putStack(i->first, new CStackInstance(i->second.type, i->second.count));
+		src.army.erase(i);
+	}
+}
+
+CStackInstance * CCreatureSet::detachStack(TSlot slot)
+{
+	assert(hasStackAtSlot(slot));
+	CStackInstance *ret = stacks[slot];
+
+	//if(CArmedInstance *armedObj = castToArmyObj())
+	{
+		ret->setArmyObj(NULL); //detaches from current armyobj
+	}
+
+	assert(!ret->armyObj); //we failed detaching?
+	stacks.erase(slot);
+	armyChanged();
+	return ret;
+}
+
+void CCreatureSet::setStackType(TSlot slot, const CCreature *type)
+{
+	assert(hasStackAtSlot(slot));
+	CStackInstance *s = stacks[slot];
+	s->setType(type->idNumber);
+	armyChanged();
+}
+
+bool CCreatureSet::canBeMergedWith(const CCreatureSet &cs, bool allowMergingStacks) const
+{
+	if(!allowMergingStacks)
+	{
+		int freeSlots = stacksCount() - ARMY_SIZE;
+		std::set<const CCreature*> cresToAdd;
+		for(TSlots::const_iterator i = cs.stacks.begin(); i != cs.stacks.end(); i++)
+		{
+			TSlot dest = getSlotFor(i->second->type);
+			if(dest < 0 || hasStackAtSlot(dest))
+				cresToAdd.insert(i->second->type);
+		}
+		return cresToAdd.size() <= freeSlots;
+	}
+	else
+	{
+		std::set<const CCreature*> cres;
+
+		//get types of creatures that need their own slot
+		for(TSlots::const_iterator i = cs.stacks.begin(); i != cs.stacks.end(); i++)
+			cres.insert(i->second->type);
+		for(TSlots::const_iterator i = stacks.begin(); i != stacks.end(); i++)
+			cres.insert(i->second->type);
+
+		return cres.size() <= ARMY_SIZE;
+	}
+}
+
+bool CCreatureSet::hasStackAtSlot(TSlot slot) const
+{
+	return vstd::contains(stacks, slot);
+}
+
+CCreatureSet & CCreatureSet::operator=(const CCreatureSet&cs)
+{
+	assert(0);
+	return *this;
+}
+
+void CCreatureSet::armyChanged()
+{
+}
+
 CStackInstance::CStackInstance()
+	: armyObj(_armyObj)
 {
 	init();
 }
 
-CStackInstance::CStackInstance(TCreature id, TQuantity Count, const CArmedInstance *ArmyObj)
+CStackInstance::CStackInstance(TCreature id, TQuantity Count)
+	: armyObj(_armyObj)
 {
 	init();
 	setType(id);
 	count = Count;
-	armyObj = ArmyObj;
 }
 
 CStackInstance::CStackInstance(const CCreature *cre, TQuantity Count)
+	: armyObj(_armyObj)
 {
 	init();
-	type = cre;
+	setType(cre);
 	count = Count;
 }
 
@@ -254,7 +421,7 @@ void CStackInstance::init()
 	count = 0;
 	type = NULL;
 	idRand = -1;
-	armyObj = NULL;
+	_armyObj = NULL;
 	nodeType = STACK;
 }
 
@@ -265,20 +432,17 @@ int CStackInstance::getQuantityID() const
 
 void CStackInstance::setType(int creID)
 {
-	type = VLC->creh->creatures[creID];
+	setType(VLC->creh->creatures[creID]);
 }
 
-void CStackInstance::getParents(TCNodes &out, const CBonusSystemNode *source /*= NULL*/) const
+void CStackInstance::setType(const CCreature *c)
 {
-	out.insert(type);
+	if(type)
+		detachFrom(const_cast<CCreature*>(type));
 
-	if(source && source != this) //we should be root, if not - do not inherit anything
-		return;
+	type = c;
 
-	if(armyObj)
-		out.insert(armyObj);
-	else
-		out.insert(&IObjectInterface::cb->gameState()->globalEffects);
+	attachTo(const_cast<CCreature*>(type));
 }
 std::string CStackInstance::bonusToString(Bonus *bonus, bool description) const
 {
@@ -342,11 +506,11 @@ std::string CStackInstance::bonusToString(Bonus *bonus, bool description) const
 					boost::algorithm::replace_first(text, "%s", VLC->creh->creatures[bonus->subtype]->namePl);
 					break;
 				case Bonus::SPELL_IMMUNITY:
-					boost::algorithm::replace_first(text, "%s", VLC->spellh->spells[bonus->subtype].name);
+					boost::algorithm::replace_first(text, "%s", VLC->spellh->spells[bonus->subtype]->name);
 					break;
 				case Bonus::SPELL_AFTER_ATTACK:
 					boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(bonus->additionalInfo % 100));
-					boost::algorithm::replace_first(text, "%s", VLC->spellh->spells[bonus->subtype].name);
+					boost::algorithm::replace_first(text, "%s", VLC->spellh->spells[bonus->subtype]->name);
 					break;
 				default:
 					{}//TODO: allow custom bonus types... someday, somehow
@@ -365,7 +529,7 @@ std::string CStackInstance::bonusToString(Bonus *bonus, bool description) const
 					break;
 				case Bonus::SPELL_IMMUNITY:
 				case Bonus::SPELL_AFTER_ATTACK:
-					boost::algorithm::replace_first(text, "%s", VLC->spellh->spells[bonus->subtype].name);
+					boost::algorithm::replace_first(text, "%s", VLC->spellh->spells[bonus->subtype]->name);
 					break;
 			}
 		}
@@ -375,7 +539,114 @@ std::string CStackInstance::bonusToString(Bonus *bonus, bool description) const
 		return "";
 }
 
+void CStackInstance::setArmyObj(const CArmedInstance *ArmyObj)
+{
+	if(_armyObj)
+		detachFrom(const_cast<CArmedInstance*>(_armyObj));
+
+	_armyObj = ArmyObj;
+	if(ArmyObj)
+	{
+		attachTo(const_cast<CArmedInstance*>(_armyObj));
+	}
+}
+// void CStackInstance::getParents(TCNodes &out, const CBonusSystemNode *source /*= NULL*/) const
+// {
+// 	out.insert(type);
+// 
+// 	if(source && source != this) //we should be root, if not - do not inherit anything
+// 		return;
+// 
+// 	if(armyObj)
+// 		out.insert(armyObj);
+// 	else
+// 		out.insert(&IObjectInterface::cb->gameState()->globalEffects);
+// }
+
 std::string CStackInstance::getQuantityTXT(bool capitalized /*= true*/) const
 {
 	return VLC->generaltexth->arraytxt[174 + getQuantityID()*3 + 2 - capitalized];
+}
+
+bool CStackInstance::valid(bool allowUnrandomized) const
+{
+	bool isRand = (idRand != -1);
+	if(!isRand)
+	{
+		return (type  &&  type == VLC->creh->creatures[type->idNumber]);
+	}
+	else
+		return allowUnrandomized;
+}
+
+CStackInstance::~CStackInstance()
+{
+
+}
+
+std::string CStackInstance::nodeName() const
+{
+	std::ostringstream oss;
+	oss << "Stack of " << count << " creatures of ";
+	if(type)
+		oss << type->namePl;
+	else if(idRand)
+		oss << "[no type, idRand=" << idRand << "]";
+	else
+		oss << "[UNDEFINED TYPE]";
+
+	return oss.str();
+}
+
+void CStackInstance::deserializationFix()
+{
+	setType(type);
+	setArmyObj(armyObj);
+}
+
+CStackBasicDescriptor::CStackBasicDescriptor()
+{
+	type = NULL;
+	count = -1;
+}
+
+CStackBasicDescriptor::CStackBasicDescriptor(TCreature id, TQuantity Count)
+	: type (VLC->creh->creatures[id]), count(Count)
+{
+}
+
+CStackBasicDescriptor::CStackBasicDescriptor(const CCreature *c, TQuantity Count)
+	: type(c), count(Count)
+{
+}
+
+DLL_EXPORT std::ostream & operator<<(std::ostream & str, const CStackInstance & sth)
+{
+	if(!sth.valid(true))
+		str << "an invalid stack!";
+
+	str << "stack with " << sth.count << " of ";
+	if(sth.type)
+		str << sth.type->namePl;
+	else
+		str << sth.idRand;
+
+	return str;
+}
+
+void CSimpleArmy::clear()
+{
+	army.clear();
+}
+
+CSimpleArmy::operator bool() const
+{
+	return army.size();
+}
+
+bool CSimpleArmy::setCreature(TSlot slot, TCreature cre, TQuantity count)
+{
+	assert(!vstd::contains(army, slot));
+	army[slot] = CStackBasicDescriptor(cre, count);
+	return true;
 }

+ 83 - 25
lib/CCreatureSet.h

@@ -9,72 +9,128 @@ class CCreature;
 class CGHeroInstance;
 class CArmedInstance;
 
-//a few typedefs for CCreatureSet
-typedef si32 TSlot;
-typedef si32 TQuantity; 
-typedef ui32 TCreature; //creature id
 
-const int ARMY_SIZE = 7;
+class DLL_EXPORT CStackBasicDescriptor
+{
+public:
+	const CCreature *type;
+	TQuantity count;
+
+	CStackBasicDescriptor();
+	CStackBasicDescriptor(TCreature id, TQuantity Count);
+	CStackBasicDescriptor(const CCreature *c, TQuantity Count);
 
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & type & count;
+	}
+};
 
-class DLL_EXPORT CStackInstance : public CBonusSystemNode
+class DLL_EXPORT CStackInstance : public CBonusSystemNode, public CStackBasicDescriptor
 {
+	const CArmedInstance *_armyObj; //stack must be part of some army, army must be part of some object
 public:
 	int idRand; //hlp variable used during loading game -> "id" placeholder for randomization
 
-	const CArmedInstance *armyObj; //stack must be part of some army, army must be part of some object
-	const CCreature *type;
-	TQuantity count;
+	const CArmedInstance * const & armyObj; //stack must be part of some army, army must be part of some object
 	ui32 experience; //TODO: handle
 	//TODO: stack artifacts
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CBonusSystemNode&>(*this);
-		h & armyObj & type & count & experience;
+		h & static_cast<CStackBasicDescriptor&>(*this);
+		h & _armyObj & experience;
 	}
 
 	//overrides CBonusSystemNode
-	void getParents(TCNodes &out, const CBonusSystemNode *source = NULL) const;  //retrieves list of parent nodes (nodes to inherit bonuses from), source is the prinary asker
+	//void getParents(TCNodes &out, const CBonusSystemNode *source = NULL) const;  //retrieves list of parent nodes (nodes to inherit bonuses from), source is the prinary asker
 	std::string bonusToString(Bonus *bonus, bool description) const; // how would bonus description look for this particular type of node
 
 	int getQuantityID() const;
 	std::string getQuantityTXT(bool capitalized = true) const;
 	void init();
 	CStackInstance();
-	CStackInstance(TCreature id, TQuantity count, const CArmedInstance *ArmyObj = NULL);
+	CStackInstance(TCreature id, TQuantity count);
 	CStackInstance(const CCreature *cre, TQuantity count);
+	~CStackInstance();
+
 	void setType(int creID);
+	void setType(const CCreature *c);
+	void setArmyObj(const CArmedInstance *ArmyObj);
+	bool valid(bool allowUnrandomized) const;
+	virtual std::string nodeName() const OVERRIDE;
+	void deserializationFix();
 };
 
+DLL_EXPORT std::ostream & operator<<(std::ostream & str, const CStackInstance & sth);
 
-typedef std::map<TSlot, CStackInstance> TSlots;
+typedef std::map<TSlot, CStackInstance*> TSlots;
+typedef std::map<TSlot, CStackBasicDescriptor> TSimpleSlots;
 
+class IArmyDescriptor
+{
+public:
+	virtual void clear() = 0;
+	virtual bool setCreature(TSlot slot, TCreature cre, TQuantity count) = 0;
+};
+
+//simplified version of CCreatureSet
+class DLL_EXPORT CSimpleArmy : public IArmyDescriptor
+{
+public:
+	TSimpleSlots army;
+	void clear() OVERRIDE;
+	bool setCreature(TSlot slot, TCreature cre, TQuantity count) OVERRIDE;
+	operator bool() const;
 
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & army;
+	}
+};
 
-class DLL_EXPORT CCreatureSet //seven combined creatures
+class DLL_EXPORT CCreatureSet : public IArmyDescriptor //seven combined creatures
 {
+	CCreatureSet(const CCreatureSet&);;
+	CCreatureSet &operator=(const CCreatureSet&);
 public:
-	TSlots slots; //slots[slot_id]=> pair(creature_id,creature_quantity)
+	TSlots stacks; //slots[slot_id]->> pair(creature_id,creature_quantity)
 	ui8 formation; //false - wide, true - tight
 
-	const CStackInstance operator[](TSlot slot) const; 
+	CCreatureSet();
+	virtual ~CCreatureSet();
+	virtual void armyChanged();
+
+	const CStackInstance &operator[](TSlot slot) const; 
 
-	const TSlots &Slots() const {return slots;}
+	const TSlots &Slots() const {return stacks;}
 
 	void addToSlot(TSlot slot, TCreature cre, TQuantity count, bool allowMerging = true); //Adds stack to slot. Slot must be empty or with same type creature
-	void addToSlot(TSlot slot, const CStackInstance &stack, bool allowMerging = true); //Adds stack to slot. Slot must be empty or with same type creature
-	void addStack(TSlot slot, const CStackInstance &stack); //adds new stack to the army, slot must be empty
-	bool setCreature (TSlot slot, TCreature type, TQuantity quantity); //slots 0 to 6, if quantity=0, erases stack
-	void clear();
+	void addToSlot(TSlot slot, CStackInstance *stack, bool allowMerging = true); //Adds stack to slot. Slot must be empty or with same type creature
+	void clear() OVERRIDE;
 	void setFormation(bool tight);
+	CArmedInstance *castToArmyObj();
+
+	//basic operations
+	void putStack(TSlot slot, CStackInstance *stack); //adds new stack to the army, slot must be empty
 	void setStackCount(TSlot slot, TQuantity count); //stack must exist!
-	void eraseStack(TSlot slot);
+	CStackInstance *detachStack(TSlot slot); //removes stack from army but doesn't destroy it (so it can be moved somewhere else or safely deleted)
+	void setStackType(TSlot slot, const CCreature *type);
+
+	//derivative 
+	void eraseStack(TSlot slot); //slot must be occupied
+	void joinStack(TSlot slot, CStackInstance * stack); //adds new stack to the existing stack of the same type
+	void changeStackCount(TSlot slot, TQuantity toAdd); //stack must exist!
+	bool setCreature (TSlot slot, TCreature type, TQuantity quantity) OVERRIDE; //replaces creature in stack; slots 0 to 6, if quantity=0 erases stack
+	void setToArmy(CSimpleArmy &src); //erases all our army and moves stacks from src to us; src MUST NOT be an armed object! WARNING: use it wisely. Or better do not use at all.
 	
 	const CStackInstance& getStack(TSlot slot) const; 
 	const CCreature* getCreature(TSlot slot) const; //workaround of map issue;
-	int getAmount (TSlot slot) const;
+	int getStackCount (TSlot slot) const;
+	TSlot findStack(const CStackInstance *stack) const; //-1 if none
 	TSlot getSlotFor(TCreature creature, ui32 slotsAmount=ARMY_SIZE) const; //returns -1 if no slot available
+	TSlot getSlotFor(const CCreature *c, ui32 slotsAmount=ARMY_SIZE) const; //returns -1 if no slot available
 	bool mergableStacks(std::pair<TSlot, TSlot> &out, TSlot preferable = -1) const; //looks for two same stacks, returns slot positions;
 	bool validTypes(bool allowUnrandomized = false) const; //checks if all types of creatures are set properly
 	bool slotEmpty(TSlot slot) const;
@@ -83,16 +139,18 @@ public:
 	int getArmyStrength() const; //sum of AI values of creatures
 	ui64 getPower (TSlot slot) const; //value of specific stack
 	std::string getRoughAmount (TSlot slot) const; //rough size of specific stack
+	bool hasStackAtSlot(TSlot slot) const;
 	
 	bool contains(const CStackInstance *stack) const;
+	bool canBeMergedWith(const CCreatureSet &cs, bool allowMergingStacks = true) const;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & slots & formation;
+		h & stacks & formation;
 	}
 	operator bool() const
 	{
-		return slots.size() > 0;
+		return stacks.size() > 0;
 	}
 	void sweep();
 };

+ 4 - 6
hch/CDefObjInfoHandler.cpp → lib/CDefObjInfoHandler.cpp

@@ -132,19 +132,17 @@ void CDefObjInfoHandler::load()
 		static const char *holeDefs[] = {"AVLHOLD0.DEF", "AVLHLDS0.DEF", "AVLHOLG0.DEF", "AVLHLSN0.DEF",
 			"AVLHOLS0.DEF", "AVLHOLR0.DEF", "AVLHOLX0.DEF", "AVLHOLL0.DEF"};
 
-		CGDefInfo *& tmp = gobjs[124][i];
 		if(i)
 		{
-			tmp = new CGDefInfo;
-			*tmp = *gobjs[124][0];
+			gobjs[124][i] = new CGDefInfo(*gobjs[124][0]);
+			gobjs[124][i]->name = holeDefs[i];
 		}
-		tmp->name = holeDefs[i];
 	}
 }
  
 CDefObjInfoHandler::~CDefObjInfoHandler()
 {
-	for(std::map<int,std::map<int,CGDefInfo*> >::iterator i=gobjs.begin(); i!=gobjs.end(); i++)
-		for(std::map<int,CGDefInfo*>::iterator j=i->second.begin(); j!=i->second.end(); j++)
+	for(bmap<int,bmap<int, ConstTransitivePtr<CGDefInfo> > >::iterator i=gobjs.begin(); i!=gobjs.end(); i++)
+		for(bmap<int, ConstTransitivePtr<CGDefInfo> >::iterator j=i->second.begin(); j!=i->second.end(); j++)
 			delete j->second;
 }

+ 4 - 3
hch/CDefObjInfoHandler.h → lib/CDefObjInfoHandler.h

@@ -3,6 +3,7 @@
 #include <vector>
 #include <map>
 #include "../global.h"
+#include "../lib/ConstTransitivePtr.h"
 
 /*
  * CDefObjInfoHandler.h, part of VCMI engine
@@ -28,9 +29,9 @@ public:
 	si32 id, subid; //of object described by this defInfo
 	si32 terrainAllowed, //on which terrain it is possible to place object
 		 terrainMenu; //in which menus in map editor object will be showed
+	CDefEssential * handler; 
 	si32 width, height; //tiles
 	si32 type; //(0- ground, 1- towns, 2-creatures, 3- heroes, 4-artifacts, 5- resources)   
-	CDefEssential * handler;
 	si32 printPriority;
 	bool isVisitable() const;
 	bool operator<(const CGDefInfo& por) const
@@ -51,8 +52,8 @@ void fetchInfoFromMSK();
 class DLL_EXPORT CDefObjInfoHandler
 {
 public:
-	std::map<int,std::map<int,CGDefInfo*> > gobjs;
-	std::map<int,CGDefInfo*> castles;
+	bmap<int, bmap<int, ConstTransitivePtr<CGDefInfo> > > gobjs;
+	bmap<int, ConstTransitivePtr<CGDefInfo> > castles;
 
 	void load();
 	~CDefObjInfoHandler();

Plik diff jest za duży
+ 147 - 745
lib/CGameState.cpp


+ 72 - 176
lib/CGameState.h

@@ -4,7 +4,7 @@
 #include <cassert>
 
 #ifndef _MSC_VER
-#include "../hch/CCreatureHandler.h"
+#include "CCreatureHandler.h"
 #include "VCMI_Lib.h"
 #include "map.h"
 #endif
@@ -21,6 +21,8 @@
 #include "../tchar_amigaos4.h"
 #endif
 
+#include "ConstTransitivePtr.h"
+
 
 /*
  * CGameState.h, part of VCMI engine
@@ -68,6 +70,13 @@ namespace boost
 	class shared_mutex;
 }
 
+//numbers of creatures are exact numbers if detailed else they are quantity ids (0 - a few, 1 - several and so on; additionaly -1 - unknown)
+struct DLL_EXPORT ArmyDescriptor : public std::map<TSlot, CStackBasicDescriptor>
+{
+	ArmyDescriptor(const CArmedInstance *army, bool detailed); //not detailed -> quantity ids as count
+	ArmyDescriptor();
+};
+
 struct DLL_EXPORT InfoAboutHero
 {
 private:
@@ -83,7 +92,8 @@ public:
 	const CHeroClass *hclass;
 	std::string name;
 	int portrait;
-	CCreatureSet army; //numbers of creatures are exact numbers if detailed else they are quantity ids (0 - a few, 1 - several and so on)
+
+	ArmyDescriptor army; 
 
 	InfoAboutHero();
 	InfoAboutHero(const InfoAboutHero & iah);
@@ -123,22 +133,22 @@ public:
 	ui8 human; //true if human controlled player, false for AI
 	ui32 currentSelection; //id of hero/town, 0xffffffff if none
 	ui8 team;
-	//std::vector<std::vector<std::vector<ui8> > > * fogOfWarMap; //pointer to team's fog of war
 	std::vector<si32> resources;
-	std::vector<CGHeroInstance *> heroes;
-	std::vector<CGTownInstance *> towns;
-	std::vector<CGHeroInstance *> availableHeroes; //heroes available in taverns
-	std::vector<CGDwelling *> dwellings; //used for town growth
+	std::vector<ConstTransitivePtr<CGHeroInstance> > heroes;
+	std::vector<ConstTransitivePtr<CGTownInstance> > towns;
+	std::vector<ConstTransitivePtr<CGHeroInstance> > availableHeroes; //heroes available in taverns
+	std::vector<ConstTransitivePtr<CGDwelling> > dwellings; //used for town growth
 
 	ui8 enteredWinningCheatCode, enteredLosingCheatCode; //if true, this player has entered cheat codes for loss / victory
 	ui8 status; //0 - in game, 1 - loser, 2 - winner <- uses EStatus enum
 	ui8 daysWithoutCastle;
 
 	PlayerState();
+	std::string nodeName() const OVERRIDE;
 
 	//override
-	void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const; 
-	void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;
+	//void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const; 
+	//void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -161,163 +171,13 @@ public:
 	
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & players & fogOfWarMap;
-		h & static_cast<CBonusSystemNode&>(*this);
-	}
-
-};
-
-struct DLL_EXPORT CObstacleInstance
-{
-	int uniqueID;
-	int ID; //ID of obstacle (defines type of it)
-	int pos; //position on battlefield
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & ID & pos & uniqueID;
-	}
-};
-
-//only for use in BattleInfo
-struct DLL_EXPORT SiegeInfo
-{
-	ui8 wallState[8]; //[0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; 1 - intact, 2 - damaged, 3 - destroyed
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & wallState;
-	}
-};
-
-struct DLL_EXPORT BattleInfo : public CBonusSystemNode
-{
-	ui8 side1, side2; //side1 - attacker, side2 - defender
-	si32 round, activeStack;
-	ui8 siege; //    = 0 ordinary battle    = 1 a siege with a Fort    = 2 a siege with a Citadel    = 3 a siege with a Castle
-	si32 tid; //used during town siege - id of attacked town; -1 if not town defence
-	int3 tile; //for background and bonuses
-	CGHeroInstance *heroes[2];
-	CArmedInstance *belligerents[2]; //may be same as heroes
-	std::vector<CStack*> stacks;
-	std::vector<CObstacleInstance> obstacles;
-	ui8 castSpells[2]; //[0] - attacker, [1] - defender
-	SiegeInfo si;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & side1 & side2 & round & activeStack & siege & tid & tile & stacks & belligerents & obstacles
-			& castSpells & si;
-		h & heroes;
+		h & id & players & fogOfWarMap;
 		h & static_cast<CBonusSystemNode&>(*this);
 	}
 
-	//////////////////////////////////////////////////////////////////////////
-	void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;
-	//////////////////////////////////////////////////////////////////////////
-
-	const CStack * getNextStack() const; //which stack will have turn after current one
-	void getStackQueue(std::vector<const CStack *> &out, int howMany, int turn = 0, int lastMoved = -1) const; //returns stack in order of their movement action
-	CStack * getStack(int stackID, bool onlyAlive = true);
-	const CStack * getStack(int stackID, bool onlyAlive = true) const;
-	CStack * getStackT(int tileID, bool onlyAlive = true);
-	const CStack * getStackT(int tileID, bool onlyAlive = true) const;
-	void getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<int> & occupyable, bool flying, int stackToOmmit=-1) const; //send pointer to at least 187 allocated bytes
-	static bool isAccessible(int hex, bool * accessibility, bool twoHex, bool attackerOwned, bool flying, bool lastPos); //helper for makeBFS
-	void makeBFS(int start, bool*accessibility, int *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying, bool fillPredecessors) const; //*accessibility must be prepared bool[187] array; last two pointers must point to the at least 187-elements int arrays - there is written result
-	std::pair< std::vector<int>, int > getPath(int start, int dest, bool*accessibility, bool flyingCreature, bool twoHex, bool attackerOwned); //returned value: pair<path, length>; length may be different than number of elements in path since flying vreatures jump between distant hexes
-	std::vector<int> getAccessibility(int stackID, bool addOccupiable) const; //returns vector of accessible tiles (taking into account the creature range)
-
-	bool isStackBlocked(int ID); //returns true if there is neighboring enemy stack
-	static signed char mutualPosition(int hex1, int hex2); //returns info about mutual position of given hexes (-1 - they're distant, 0 - left top, 1 - right top, 2 - right, 3 - right bottom, 4 - left bottom, 5 - left)
-	static std::vector<int> neighbouringTiles(int hex);
-	static si8 getDistance(int hex1, int hex2); //returns distance between given hexes
-	ui32 calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky); //charge - number of hexes travelled before attack (for champion's jousting)
-	std::pair<ui32, ui32> calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky); //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
-	void calculateCasualties(std::map<ui32,si32> *casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount)
-	std::set<CStack*> getAttackedCreatures(const CSpell * s, int skillLevel, ui8 attackerOwner, int destinationTile); //calculates stack affected by given spell
-	static int calculateSpellDuration(const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower);
-	CStack * generateNewStack(const CStackInstance &base, int stackID, bool attackerOwned, int slot, int /*TerrainTile::EterrainType*/ terrain, int position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
-	ui32 getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //returns cost of given spell
-	int hexToWallPart(int hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found
-	int lineToWallHex(int line) const; //returns hex with wall in given line
-	std::pair<const CStack *, int> getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const; //if attackerOwned is indetermnate, returened stack is of any owner; hex is the number of hex we should be looking from; returns (nerarest creature, predecessorHex)
-	ui32 calculateSpellBonus(ui32 baseDamage, const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature) const;
-	ui32 calculateSpellDmg(const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const; //calculates damage inflicted by spell
-	ui32 calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack) const;
-	si8 hasDistancePenalty(int stackID, int destHex); //determines if given stack has distance penalty shooting given pos
-	si8 sameSideOfWall(int pos1, int pos2); //determines if given positions are on the same side of wall
-	si8 hasWallPenalty(int stackID, int destHex); //determines if given stack has wall penalty shooting given pos
-	si8 canTeleportTo(int stackID, int destHex, int telportLevel); //determines if given stack can teleport to given place
 };
 
-class DLL_EXPORT CStack : public CStackInstance
-{ 
-public:
-	ui32 ID; //unique ID of stack
-	ui32 baseAmount;
-	ui32 firstHPleft; //HP of first creature in stack
-	ui8 owner, slot;  //owner - player colour (255 for neutrals), slot - position in garrison (may be 255 for neutrals/called creatures)
-	ui8 attackerOwned; //if true, this stack is owned by attakcer (this one from left hand side of battle)
-	si16 position; //position on battlefield; -2 - keep, -3 - lower tower, -4 - upper tower
-	ui8 counterAttacks; //how many counter attacks can be performed more in this turn (by default set at the beginning of the round to 1)
-	si16 shots; //how many shots left
-
-	std::set<ECombatInfo> state;
-	//overrides
-	const CCreature* getCreature() const {return type;}
-
-	CStack(const CStackInstance *base, int O, int I, bool AO, int S); //c-tor
-	CStack() : ID(-1), baseAmount(-1), firstHPleft(-1), owner(255), slot(255), attackerOwned(true), position(-1), counterAttacks(1) {} //c-tor
-	const Bonus * getEffect(ui16 id, int turn = 0) const; //effect id (SP)
-	ui8 howManyEffectsSet(ui16 id) const; //returns amount of effects with given id set for this stack
-	bool willMove(int turn = 0) const; //if stack has remaining move this turn
-	bool moved(int turn = 0) const; //if stack was already moved this turn
-	bool canMove(int turn = 0) const; //if stack can move
-	ui32 Speed(int turn = 0) const; //get speed of creature with all modificators
-	BonusList getSpellBonuses() const;
-	void stackEffectToFeature(BonusList & sf, const Bonus & sse);
-	std::vector<si32> activeSpells() const; //returns vector of active spell IDs sorted by time of cast
-
-	static inline Bonus featureGenerator(Bonus::BonusType type, si16 subtype, si32 value, ui16 turnsRemain, si32 additionalInfo = 0, si32 limit = Bonus::NO_LIMIT)
-	{
-		Bonus hb(makeFeature(type, Bonus::N_TURNS, subtype, value, Bonus::SPELL_EFFECT, turnsRemain, additionalInfo));
-		hb.effectRange = limit;
-		hb.source = Bonus::CASTED_SPELL; //right?
-		return hb;
-	}
 
-	static inline Bonus featureGeneratorVT(Bonus::BonusType type, si16 subtype, si32 value, ui16 turnsRemain, ui8 valType)
-	{
-		Bonus ret(makeFeature(type, Bonus::N_TURNS, subtype, value, Bonus::SPELL_EFFECT, turnsRemain));
-		ret.valType = valType;
-		ret.source = Bonus::CASTED_SPELL; //right?
-		return ret;
-	}
-
-	bool doubleWide() const;
-	int occupiedHex() const; //returns number of occupied hex (not the position) if stack is double wide; otherwise -1
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CStackInstance&>(*this);
-		h & ID & baseAmount & firstHPleft & owner & slot & attackerOwned & position & state & counterAttacks
-			& shots;
-	}
-	bool alive() const //determines if stack is alive
-	{
-		return vstd::contains(state,ALIVE);
-	}
-};
-
-class DLL_EXPORT CMP_stack
-{
-	int phase; //rules of which phase will be used
-	int turn;
-public:
-
-	bool operator ()(const CStack* a, const CStack* b);
-	CMP_stack(int Phase = 1, int Turn = 0);
-};
 
 struct UpgradeInfo
 {
@@ -387,27 +247,65 @@ struct DLL_EXPORT CPathsInfo
 	~CPathsInfo();
 };
 
+struct DLL_EXPORT DuelParameters
+{
+	si32 terType, bfieldType;
+	struct SideSettings
+	{
+		struct StackSettings
+		{
+			si32 type;
+			si32 count;
+			template <typename Handler> void serialize(Handler &h, const int version)
+			{
+				h & type & count;
+			}
+
+			StackSettings();
+			StackSettings(si32 Type, si32 Count);
+		} stacks[ARMY_SIZE];
+
+		si32 heroId; //-1 if none
+		std::set<si32> spells;
+
+		SideSettings();
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & stacks & heroId & spells;
+		}
+	} sides[2];
+
+	DuelParameters();
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & terType & bfieldType & sides;
+	}
+};
+
+
+class BattleInfo;
+
 class DLL_EXPORT CGameState
 {
 public:
-	StartInfo* scenarioOps, *initialOpts; //second one is a copy of settings received from pregame (not randomized)
-	CCampaignState *campaign;
+	ConstTransitivePtr<StartInfo> scenarioOps, initialOpts; //second one is a copy of settings received from pregame (not randomized)
+	ConstTransitivePtr<CCampaignState> campaign;
 	ui32 seed;
 	ui8 currentPlayer; //ID of player currently having turn
-	BattleInfo *curB; //current battle
+	ConstTransitivePtr<BattleInfo> curB; //current battle
 	ui32 day; //total number of days in game
-	Mapa * map;
-	std::map<ui8, PlayerState> players; //ID <-> player state
-	std::map<ui8, TeamState> teams; //ID <-> team state
-	std::map<int, CGDefInfo*> villages, forts, capitols; //def-info for town graphics
+	ConstTransitivePtr<Mapa> map;
+	bmap<ui8, PlayerState> players; //ID <-> player state
+	bmap<ui8, TeamState> teams; //ID <-> team state
+	bmap<int, ConstTransitivePtr<CGDefInfo> > villages, forts, capitols; //def-info for town graphics
 	CBonusSystemNode globalEffects;
 
 	struct DLL_EXPORT HeroesPool
 	{
-		std::map<ui32,CGHeroInstance *> heroesPool; //[subID] - heroes available to buy; NULL if not available
-		std::map<ui32,ui8> pavailable; // [subid] -> which players can recruit hero (binary flags)
+		bmap<ui32, ConstTransitivePtr<CGHeroInstance> > heroesPool; //[subID] - heroes available to buy; NULL if not available
+		bmap<ui32,ui8> pavailable; // [subid] -> which players can recruit hero (binary flags)
 
-		CGHeroInstance * pickHeroFor(bool native, int player, const CTown *town, std::map<ui32,CGHeroInstance *> &available, const CHeroClass *bannedClass = NULL) const;
+		CGHeroInstance * pickHeroFor(bool native, int player, const CTown *town, bmap<ui32, ConstTransitivePtr<CGHeroInstance> > &available, const CHeroClass *bannedClass = NULL) const;
 
 		template <typename Handler> void serialize(Handler &h, const int version)
 		{
@@ -432,12 +330,7 @@ public:
 	CGTownInstance *getTown(int objid);
 	const CGHeroInstance *getHero(int objid) const;
 	const CGTownInstance *getTown(int objid) const;
-	bool battleCanFlee(int player); //returns true if player can flee from the battle
-	int battleGetStack(int pos, bool onlyAlive); //returns ID of stack at given tile
 	int battleGetBattlefieldType(int3 tile = int3());//   1. sand/shore   2. sand/mesas   3. dirt/birches   4. dirt/hills   5. dirt/pines   6. grass/hills   7. grass/pines   8. lava   9. magic plains   10. snow/mountains   11. snow/trees   12. subterranean   13. swamp/trees   14. fiery fields   15. rock lands   16. magic clouds   17. lucid pools   18. holy ground   19. clover field   20. evil fog   21. "favourable winds" text on magic plains background   22. cursed ground   23. rough   24. ship to ship   25. ship
-	const CGHeroInstance * battleGetOwner(int stackID); //returns hero that owns given stack; NULL if none
-	si8 battleMaxSpellLevel(); //calculates maximum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, SPELL_LEVELS is returned
-	bool battleCanShoot(int ID, int dest); //determines if stack with given ID shoot at the selected destination
 	UpgradeInfo getUpgradeInfo(const CStackInstance &stack);
 	int getPlayerRelations(ui8 color1, ui8 color2);// 0 = enemy, 1 = ally, 2 = same player
 	//float getMarketEfficiency(int player, int mode=0);
@@ -453,7 +346,10 @@ public:
 	ui8 checkForStandardWin() const; //returns color of player that accomplished standard victory conditions or 255 if no winner
 	bool checkForStandardLoss(ui8 player) const; //checks if given player lost the game
 	void obtainPlayersStats(SThievesGuildInfo & tgi, int level); //fills tgi with info about other players that is available at given level of thieves' guild
-	std::map<ui32,CGHeroInstance *> unusedHeroesFromPool(); //heroes pool without heroes that are available in taverns
+	bmap<ui32, ConstTransitivePtr<CGHeroInstance> > unusedHeroesFromPool(); //heroes pool without heroes that are available in taverns
+	BattleInfo * setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town);
+
+	void buildBonusSystemTree();
 
 	bool isVisible(int3 pos, int player);
 	bool isVisible(const CGObjectInstance *obj, int player);

+ 2 - 2
hch/CGeneralTextHandler.cpp → lib/CGeneralTextHandler.cpp

@@ -393,7 +393,7 @@ void CGeneralTextHandler::load()
 	for(int hh=0; hh<45; ++hh)
 	{
 		loadToIt(tmp, strin, itr, 3);
-		trimQuotation(tmp);
+		trimQuotation(tmp); 
 		jktexts.push_back(tmp);
 	}
 
@@ -404,7 +404,7 @@ void CGeneralTextHandler::load()
 		loadToIt(tmp, strin, itr, 3);
 		tavernInfo.push_back(tmp);
 	}
-	
+
 	itr = 0;
 	strin = bitmaph->getTextFile("TURNDUR.TXT");
 	for(int hh=0; hh<11; ++hh)

+ 1 - 1
hch/CGeneralTextHandler.h → lib/CGeneralTextHandler.h

@@ -36,7 +36,7 @@ public:
 	std::vector<std::string> overview;//text for Kingdom Overview window
 	std::vector<std::string> colors; //names of player colors ("red",...)
 	std::vector<std::string> capColors; //names of player colors with first letter capitalized ("Red",...)
-	std::vector<std::string> turnDurations; //turn durations for pregame (1 Minute ... Unlimited)
+	std::vector<std::string> turnDurations; //turn durations for pregame (1 Minute ... Unlimited) 
 
 	//artifacts
 	std::vector<std::string> artifEvents;

+ 0 - 0
hch/CHeroHandler.cpp → lib/CHeroHandler.cpp


+ 8 - 2
hch/CHeroHandler.h → lib/CHeroHandler.h

@@ -5,6 +5,8 @@
 #include <vector>
 #include <set>
 
+#include "../lib/ConstTransitivePtr.h"
+
 /*
  * CHeroHandler.h, part of VCMI engine
  *
@@ -24,6 +26,10 @@ struct SSpecialtyInfo
 	si32 val;
 	si32 subtype;
 	si32 additionalinfo;
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & type & val & subtype & additionalinfo;
+	}
 };
 
 class DLL_EXPORT CHero
@@ -50,7 +56,7 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & name & ID & lowStack & highStack & refTypeStack	& heroType & startingSpell & heroClass;
+		h & name & ID & lowStack & highStack & refTypeStack	& heroClass & heroType & secSkillsInit & spec & startingSpell & sex;
 	}
 };
 
@@ -118,7 +124,7 @@ const int PUZZLES_PER_FACTION = 48;
 class DLL_EXPORT CHeroHandler
 {
 public:
-	std::vector<CHero*> heroes; //changed from nodrze
+	std::vector< ConstTransitivePtr<CHero> > heroes; //changed from nodrze
 	std::vector<CHeroClass *> heroClasses;
 	std::vector<ui64> expPerLevel; //expPerLEvel[i] is amount of exp needed to reach level i; if it is not in this vector, multiplicate last value by 1,2 to get next value
 	

+ 8 - 13
hch/CLodHandler.cpp → lib/CLodHandler.cpp

@@ -60,16 +60,6 @@ std::string readString(const unsigned char * bufor, int &i)
 	return ret;
 }
 
-Entry CLodHandler::getEntry(const std::string name, LodFileType type)
-{
-	Entry ret;
-	std::set<Entry>::iterator it = entries.find(Entry(name, type));
-	
-	if (it!=entries.end())
-		ret = *it;
-	return ret;
-}
-
 unsigned char * CLodHandler::giveFile(const std::string defName, LodFileType type, int * length)
 {
 	std::string fname = defName;
@@ -77,13 +67,18 @@ unsigned char * CLodHandler::giveFile(const std::string defName, LodFileType typ
 	int dotPos = fname.find_last_of('.');
 	if ( dotPos != -1 )
 		fname.erase(dotPos);
-		
-	Entry ourEntry = getEntry(fname, type);
-	if(!vstd::contains(entries, ourEntry)) //nothing's been found
+
+	int count = entries.size();
+
+	boost::unordered_set<Entry>::const_iterator en_it = entries.find(Entry(fname, type));
+	
+	if(en_it == entries.end()) //nothing's been found
 	{
 		tlog1 << "Cannot find file: " << fname << std::endl;
 		return NULL;
 	}
+	Entry ourEntry = *en_it;
+
 	if(length) *length = ourEntry.realSize;
 	mutex->lock();
 

+ 15 - 8
hch/CLodHandler.h → lib/CLodHandler.h

@@ -6,6 +6,7 @@
 #include <fstream>
 #include <set>
 #include <map>
+#include <boost/unordered_set.hpp>
 
 /*
  * CLodhandler.h, part of VCMI engine
@@ -52,6 +53,7 @@ enum LodFileType{
 	FILE_OTHER
 };
 
+
 struct Entry
 {
 	// Info extracted from LOD file
@@ -62,12 +64,6 @@ struct Entry
 		size;	//and with
 	LodFileType type;// file type determined by extension
 
-	bool operator<(const Entry & comp) const
-	{
-		return type==comp.type ? name<comp.name
-		                       : type<comp.type;
-	}
-	
 	bool operator == (const Entry & comp) const
 	{
 		return (type==comp.type || comp.type== FILE_ANY) && name==comp.name;
@@ -78,6 +74,18 @@ struct Entry
 	Entry(){};
 };
 
+template<>
+struct boost::hash<Entry> : public std::unary_function<Entry, std::size_t> {
+private:
+	boost::hash<std::string> stringHasher;
+public:
+	std::size_t operator()(Entry const& en) const
+	{
+		//do NOT improve this hash function as we need same-name hash collisions for find to work properly
+		return stringHasher(en.name);
+	}
+};
+
 class DLL_EXPORT CLodHandler
 {
 	std::map<std::string, LodFileType> extMap;// to convert extensions to file type
@@ -88,12 +96,11 @@ class DLL_EXPORT CLodHandler
 	std::string myDir; //load files from this dir instead of .lod file
 
 	void initEntry(Entry &e, const std::string name);
-	Entry getEntry(const std::string name, LodFileType);
 	int infs2(unsigned char * in, int size, int realSize, unsigned char*& out, int wBits=15); //zlib fast handler
 
 public:
 
-	std::set<Entry> entries;
+	boost::unordered_set<Entry> entries;
 
 	CLodHandler();
 	~CLodHandler();

+ 1 - 1
lib/CMapInfo.cpp

@@ -3,7 +3,7 @@
 #include "CMapInfo.h"
 #include "../StartInfo.h"
 #include "map.h"
-#include "../hch/CCampaignHandler.h"
+#include "CCampaignHandler.h"
 
 void CMapInfo::countPlayers()
 {

Plik diff jest za duży
+ 258 - 327
lib/CObjectHandler.cpp


+ 107 - 30
hch/CObjectHandler.h → lib/CObjectHandler.h

@@ -12,6 +12,8 @@
 #include "../lib/VCMI_Lib.h"
 #endif
 #include "../lib/CCreatureSet.h"
+#include "../lib/ConstTransitivePtr.h"
+#include <boost/unordered_set.hpp>
 
 /*
  * CObjectHandler.h, part of VCMI engine
@@ -23,6 +25,7 @@
  *
  */
 
+class CArtifactInstance;
 struct MetaString;
 struct BattleInfo;
 class IGameCallback;
@@ -62,7 +65,7 @@ public:
 	ui32 m13489val;
 	std::vector<ui32> m2stats;
 	std::vector<ui16> m5arts; //artifacts id
-	TSlots m6creatures; //pair[cre id, cre count], CreatureSet info irrelevant
+	std::vector<CStackBasicDescriptor> m6creatures; //pair[cre id, cre count], CreatureSet info irrelevant
 	std::vector<ui32> m7resources;
 
 	std::string firstVisitText, nextVisitText, completedText;
@@ -155,7 +158,7 @@ public:
 	virtual ui8 getPassableness() const; //bitmap - if the bit is set the corresponding player can pass through the visitable tiles of object, even if it's blockvis; if not set - default properties from definfo are used
 	virtual int3 getSightCenter() const; //"center" tile from which the sight distance is calculated
 	virtual int getSightRadious() const; //sight distance (should be used if player-owned structure)
-	void getSightTiles(std::set<int3> &tiles) const; //returns reference to the set
+	void getSightTiles(boost::unordered_set<int3, ShashInt3> &tiles) const; //returns reference to the set
 	int getOwner() const;
 	void setOwner(int ow);
 	int getWidth() const; //returns width of object graphic in tiles
@@ -222,14 +225,16 @@ class DLL_EXPORT CArmedInstance: public CGObjectInstance, public CBonusSystemNod
 public:
 	BattleInfo *battle; //set to the current battle, if engaged
 
-	void setArmy(const CCreatureSet &src);
 	CCreatureSet& getArmy() const;
 	void randomizeArmy(int type);
+	void updateMoraleBonusFromArmy();
+
+	void armyChanged() OVERRIDE;
 
 	//////////////////////////////////////////////////////////////////////////
-	void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const;
-	void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;
-	int valOfGlobalBonuses(CSelector selector) const; //used only for castle interface
+	//void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const;
+	//void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;
+	int valOfGlobalBonuses(CSelector selector) const; //used only for castle interface								???
 	//////////////////////////////////////////////////////////////////////////
 
 	CArmedInstance();
@@ -242,9 +247,59 @@ public:
 	}
 };
 
-class DLL_EXPORT CGHeroInstance : public CArmedInstance, public IBoatGenerator
+struct DLL_EXPORT ArtSlotInfo
+{
+	ConstTransitivePtr<CArtifactInstance> artifact;
+	ui8 locked; //if locked, then artifact points to the combined artifact
+
+	ArtSlotInfo()
+	{
+		locked = false;
+	}
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & artifact & locked;
+	}
+};
+
+class DLL_EXPORT CArtifactSet
+{
+public:
+	std::vector<ArtSlotInfo> artifactsInBackpack; //hero's artifacts from bag
+	bmap<ui16, ArtSlotInfo> artifactsWorn; //map<position,artifact_id>; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5
+
+	ArtSlotInfo &retreiveNewArtSlot(ui16 slot);
+	void setNewArtSlot(ui16 slot, CArtifactInstance *art, bool locked);
+	void eraseArtSlot(ui16 slot);
+
+	const ArtSlotInfo *getSlot(ui16 pos) const;
+	const CArtifactInstance* getArt(ui16 pos) const; //NULL - no artifact
+	CArtifactInstance* getArt(ui16 pos); //NULL - no artifact
+	si32 getArtPos(int aid, bool onlyWorn = true) const; //looks for equipped artifact with given ID and returns its slot ID or -1 if none(if more than one such artifact lower ID is returned)
+	si32 getArtPos(const CArtifactInstance *art) const;
+	bool hasArt(ui32 aid, bool onlyWorn = false) const; //checks if hero possess artifact of given id (either in backack or worn)
+	bool isPositionFree(ui16 pos, bool onlyLockCheck = false) const;
+	si32 getArtTypeId(ui16 pos) const;
+
+
+	virtual ~CArtifactSet();
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & artifactsInBackpack & artifactsWorn;
+	}
+};
+
+class DLL_EXPORT CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet
 {
 public:
+	enum SecondarySkill
+	{
+		PATHFINDING = 0, ARCHERY, LOGISTICS, SCOUTING, DIPLOMACY, NAVIGATION, LEADERSHIP, WISDOM, MYSTICISM,
+		LUCK, BALLISTICS, EAGLE_EYE, NECROMANCY, ESTATES, FIRE_MAGIC, AIR_MAGIC, WATER_MAGIC, EARTH_MAGIC,
+		SCHOLAR, TACTICS, ARTILLERY, LEARNING, OFFENCE, ARMORER, INTELLIGENCE, SORCERY, RESISTANCE,
+		FIRST_AID
+	};
 	//////////////////////////////////////////////////////////////////////////
 
 	ui8 moveDir; //format:	123
@@ -267,10 +322,13 @@ public:
 	ui8 inTownGarrison; // if hero is in town garrison 
 	const CGTownInstance * visitedTown; //set if hero is visiting town or in the town garrison
 	const CGBoat *boat; //set to CGBoat when sailing
-	std::vector<const CArtifact*> artifacts; //hero's artifacts from bag
-	std::map<ui16, const CArtifact*> artifWorn; //map<position,artifact_id>; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5
+	
+
+	//std::vector<const CArtifact*> artifacts; //hero's artifacts from bag
+	//std::map<ui16, const CArtifact*> artifWorn; //map<position,artifact_id>; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5
 	std::set<ui32> spells; //known spells (spell IDs)
 
+
 	struct DLL_EXPORT Patrol
 	{
 		Patrol(){patrolling=false;patrolRadious=-1;};
@@ -281,6 +339,7 @@ public:
 			h & patrolling & patrolRadious;
 		}
 	} patrol;
+
 	struct DLL_EXPORT HeroSpecial : CBonusSystemNode
 	{
 		bool growthsWithLevel;
@@ -298,15 +357,16 @@ 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 & artifacts & artifWorn & spells & patrol & moveDir;
+			& sex & inTownGarrison & /*artifacts & artifWorn & */spells & patrol & moveDir;
 
 		h & type & speciality;
 		//visitied town pointer will be restored by map serialization method
 	}
 	//////////////////////////////////////////////////////////////////////////
-	void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const;
-	void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;
+// 	void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const;
+// 	void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;
 	//////////////////////////////////////////////////////////////////////////
 	int3 getSightCenter() const; //"center" tile from which the sight distance is calculated
 	int getSightRadious() const; //sight distance (should be used if player-owned structure)
@@ -317,6 +377,7 @@ public:
 
 	//////////////////////////////////////////////////////////////////////////
 	
+	bool hasSpellbook() const;
 	EAlignment getAlignment() const;
 	const std::string &getBiography() const;
 	bool needsLastStack()const;
@@ -333,15 +394,15 @@ public:
 	int getCurrentMorale(int stack=-1, bool town=false) const; //if stack - position of creature, if -1 then morale for hero is calculated; town - if bonuses from town (tavern) should be considered
 	TModDescr getCurrentMoraleModifiers(int stack=-1, bool town=false) const; //args as above
 	int getPrimSkillLevel(int id) const; //0-attack, 1-defence, 2-spell power, 3-knowledge
-	ui8 getSecSkillLevel(const int & ID) const; //0 - no skill
-	void setSecSkillLevel(int which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value
+	ui8 getSecSkillLevel(SecondarySkill skill) const; //0 - no skill
+	void setSecSkillLevel(SecondarySkill which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value
 
 	int maxMovePoints(bool onLand) const;
 
-	const CArtifact* getArtAtPos(ui16 pos) const; //NULL - no artifact
-	const CArtifact * getArt(int pos) const;
-	si32 getArtPos(int aid) const; //looks for equipped artifact with given ID and returns its slot ID or -1 if none(if more than one such artifact lower ID is returned)
-	bool hasArt(ui32 aid) const; //checks if hero possess artifact of given id (either in backack or worn)
+// 	const CArtifact* getArtAtPos(ui16 pos) const; //NULL - no artifact
+// 	const CArtifact * getArt(int pos) const;
+// 	si32 getArtPos(int aid) const; //looks for equipped artifact with given ID and returns its slot ID or -1 if none(if more than one such artifact lower ID is returned)
+// 	bool hasArt(ui32 aid) const; //checks if hero possess artifact of given id (either in backack or worn)
 
 	//int getSpellSecLevel(int spell) const; //returns level of secondary ability (fire, water, earth, air magic) known to this hero and applicable to given spell; -1 if error
 	static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
@@ -349,16 +410,18 @@ public:
 	int getTotalStrength() const;
 	ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = NULL) const; //returns level on which given spell would be cast by this hero (0 - none, 1 - basic etc); optionally returns number of selected school by arg - 0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic,
 	bool canCastThisSpell(const CSpell * spell) const; //determines if this hero can cast given spell; takes into account existing spell in spellbook, existing spellbook and artifact bonuses
-	CStackInstance calculateNecromancy (const BattleResult &battleResult) const;
-	void showNecromancyDialog(const CStackInstance &raisedStack) const;
+	CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const;
+	void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const;
 
 	//////////////////////////////////////////////////////////////////////////
 
 	void initHero(); 
 	void initHero(int SUBID); 
 
+	void putArtifact(ui16 pos, CArtifactInstance *art);
+	void putInBackpack(CArtifactInstance *art);
 	void initExp();
-	void initArmy(CCreatureSet *dst = NULL);
+	void initArmy(IArmyDescriptor *dst = NULL);
 	void giveArtifact (ui32 aid);
 	void initHeroDefInfo();
 	void pushPrimSkill(int which, int val);
@@ -370,6 +433,9 @@ public:
 
 	//////////////////////////////////////////////////////////////////////////
 
+
+	virtual std::string nodeName() const OVERRIDE;
+	void deserializationFix();
 	void setPropertyDer(ui8 what, ui32 val);//synchr
 	void initObj();
 	void onHeroVisit(const CGHeroInstance * h) const;
@@ -494,14 +560,19 @@ public:
 	}
 };
 
+class DLL_EXPORT CTownAndVisitingHero : public CBonusSystemNode
+{
+};
+
 class DLL_EXPORT CGTownInstance : public CGDwelling, public IShipyard, public IMarket
 {
 public:
+	CTownAndVisitingHero townAndVis;
 	CTown * town;
 	std::string name; // name of town
 	si32 builded; //how many buildings has been built this turn
 	si32 destroyed; //how many buildings has been destroyed this turn
-	const CGHeroInstance * garrisonHero, *visitingHero;
+	ConstTransitivePtr<CGHeroInstance> garrisonHero, visitingHero;
 	ui32 identifier; //special identifier from h3m (only > RoE maps)
 	si32 alignment;
 	std::set<si32> forbiddenBuildings, builtBuildings;
@@ -524,12 +595,18 @@ public:
 		for (std::vector<CGTownBuilding*>::iterator i = bonusingBuildings.begin(); i!=bonusingBuildings.end(); i++)
 			(*i)->town = this;
 
-		h & town;
+		h & town & townAndVis;
 		//garrison/visiting hero pointers will be restored in the map serialization
 	}
 	//////////////////////////////////////////////////////////////////////////
-	void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const;
-	void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;
+
+	std::string nodeName() const OVERRIDE;
+	void deserializationFix();
+	void recreateBuildingsBonuses();
+	void setVisitingHero(CGHeroInstance *h);
+	void setGarrisonedHero(CGHeroInstance *h);
+// 	void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const;
+// 	void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;
 	//////////////////////////////////////////////////////////////////////////
 
 	ui8 getPassableness() const; //bitmap - if the bit is set the corresponding player can pass through the visitable tiles of object, even if it's blockvis; if not set - default properties from definfo are used
@@ -674,7 +751,7 @@ public:
 	}
 };
 
-class DLL_EXPORT CGSeerHut : public CGObjectInstance, public CQuest
+class DLL_EXPORT CGSeerHut : public CArmedInstance, public CQuest //army is used when giving reward
 {
 public:
 	ui8 rewardType; //type of reward: 0 - no reward; 1 - experience; 2 - mana points; 3 - morale bonus; 4 - luck bonus; 5 - resources; 6 - main ability bonus (attak, defence etd.); 7 - secondary ability gain; 8 - artifact; 9 - spell; 10 - creature
@@ -763,8 +840,9 @@ public:
 class DLL_EXPORT CGArtifact : public CArmedInstance
 {
 public:
+	CArtifactInstance *storedArtifact;
 	std::string message;
-	ui32 spell; //if it's spell scroll
+
 	void onHeroVisit(const CGHeroInstance * h) const;
 	void fightForArt(ui32 agreed, const CGHeroInstance *h) const;
 	void endBattle(BattleResult *result, const CGHeroInstance *h) const;
@@ -774,7 +852,7 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CArmedInstance&>(*this);
-		h & message & spell;
+		h & message & storedArtifact;
 	}
 };
 
@@ -1109,7 +1187,6 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CPlayersVisited&>(*this);
-		h & players;
 	}
 };
 
@@ -1222,7 +1299,7 @@ class DLL_EXPORT CObjectHandler
 {
 public:
 	std::vector<si32> cregens; //type 17. dwelling subid -> creature ID
-	std::map <ui32, std::vector <BankConfig*> > banksInfo; //[index][preset]
+	std::map <ui32, std::vector < ConstTransitivePtr<BankConfig> > > banksInfo; //[index][preset]
 	std::map <ui32, std::string> creBanksNames; //[crebank index] -> name of this creature bank
 	std::vector<ui32> resVals; //default values of resources in gold
 

+ 31 - 33
hch/CSpellHandler.cpp → lib/CSpellHandler.cpp

@@ -2,7 +2,6 @@
 #include "../stdafx.h"
 #include "CSpellHandler.h"
 #include "CLodHandler.h"
-#include "CSoundBase.h"
 #include "../lib/VCMI_Lib.h"
 #include <boost/algorithm/string/replace.hpp>
 #include <cctype>
@@ -217,10 +216,10 @@ void CSpellHandler::loadSpells()
 	{
 		if(spells.size()==81)
 			break;
-		CSpell nsp; //new currently being read spell
+		CSpell * nsp = new CSpell; //new currently being read spell
 
-		loadToIt(nsp.name,buf,i,4);
-		if(nsp.name == std::string(""))
+		loadToIt(nsp->name,buf,i,4);
+		if(nsp->name == std::string(""))
 		{
 			if(ifHit == 0)
 			{
@@ -232,50 +231,49 @@ void CSpellHandler::loadSpells()
 			}
 			for(int z=0; z<3; ++z)
 				loadToIt(pom,buf,i,3);
-			loadToIt(nsp.name,buf,i,4);
+			loadToIt(nsp->name,buf,i,4);
 			++ifHit;
 		}
 
-		loadToIt(nsp.abbName,buf,i,4);
-		loadToIt(nsp.level,buf,i,4);
+		loadToIt(nsp->abbName,buf,i,4);
+		loadToIt(nsp->level,buf,i,4);
 		loadToIt(pom,buf,i,4);
-		nsp.earth = startsWithX(pom);
+		nsp->earth = startsWithX(pom);
 		loadToIt(pom,buf,i,4);
-		nsp.water = startsWithX(pom);
+		nsp->water = startsWithX(pom);
 		loadToIt(pom,buf,i,4);
-		nsp.fire = startsWithX(pom);
+		nsp->fire = startsWithX(pom);
 		loadToIt(pom,buf,i,4);
-		nsp.air = startsWithX(pom);
+		nsp->air = startsWithX(pom);
 
-		nsp.costs.resize(4);
+		nsp->costs.resize(4);
 		for (int z = 0; z < 4 ; z++)
-			loadToIt(nsp.costs[z],buf,i,4);
-		loadToIt(nsp.power,buf,i,4);
-		nsp.powers.resize(4);
+			loadToIt(nsp->costs[z],buf,i,4);
+		loadToIt(nsp->power,buf,i,4);
+		nsp->powers.resize(4);
 		for (int z = 0; z < 4 ; z++)
-			loadToIt(nsp.powers[z],buf,i,4);
+			loadToIt(nsp->powers[z],buf,i,4);
 
-		nsp.probabilities.resize(9);
+		nsp->probabilities.resize(9);
 		for (int z = 0; z < 9 ; z++)
-			loadToIt(nsp.probabilities[z],buf,i,4);
+			loadToIt(nsp->probabilities[z],buf,i,4);
 
-		nsp.AIVals.resize(4);
+		nsp->AIVals.resize(4);
 		for (int z = 0; z < 4 ; z++)
-			loadToIt(nsp.AIVals[z],buf,i,4);
+			loadToIt(nsp->AIVals[z],buf,i,4);
 
-		nsp.descriptions.resize(4);
+		nsp->descriptions.resize(4);
 		for (int z = 0; z < 4 ; z++)
 		{
-			loadToIt(nsp.descriptions[z],buf,i,4);
-			boost::algorithm::replace_all(nsp.descriptions[z],"\"","");
+			loadToIt(nsp->descriptions[z],buf,i,4);
+			boost::algorithm::replace_all(nsp->descriptions[z],"\"","");
 		}
 
-		loadToIt(nsp.attributes,buf,i,3);
-		nsp.id = spells.size();
-		nsp.combatSpell = combSpells;
-		nsp.creatureAbility = creatureAbility;
-		nsp.mainEffectAnim = -1;
-		nsp.soundID = soundBase::invalid;
+		loadToIt(nsp->attributes,buf,i,3);
+		nsp->id = spells.size();
+		nsp->combatSpell = combSpells;
+		nsp->creatureAbility = creatureAbility;
+		nsp->mainEffectAnim = -1;
 		spells.push_back(nsp);
 	}
 	//loading of additional spell traits
@@ -297,13 +295,13 @@ void CSpellHandler::loadSpells()
 		{
 			int buf;
 			ast >> buf;
-			spells[spellID].positiveness = buf;
+			spells[spellID]->positiveness = buf;
 			ast >> buf;
-			spells[spellID].mainEffectAnim = buf;
+			spells[spellID]->mainEffectAnim = buf;
 
-			spells[spellID].range.resize(4);
+			spells[spellID]->range.resize(4);
 			for(int g=0; g<4; ++g)
-				ast>>spells[spellID].range[g];
+				ast>>spells[spellID]->range[g];
 			ast>>spellID;
 		}
 	}

+ 2 - 3
hch/CSpellHandler.h → lib/CSpellHandler.h

@@ -5,7 +5,7 @@
 #include <vector>
 #include <set>
 
-#include "CSoundBase.h"
+#include "../lib/ConstTransitivePtr.h"
 
 /*
  * CSpellHandler.h, part of VCMI engine
@@ -41,7 +41,6 @@ public:
 	std::vector<std::string> range; //description of spell's range in SRSL by magic school level
 	std::set<ui16> rangeInHexes(unsigned int centralHex, ui8 schoolLvl ) const; //convert range to specific hexes
 	si16 mainEffectAnim; //main spell effect animation, in AC format (or -1 when none)
-	soundBase::soundID soundID;	// spell sound id
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -61,7 +60,7 @@ class DLL_EXPORT CSpellHandler
 {
 public:
 	CSpellHandler();
-	std::vector<CSpell> spells;
+	std::vector< ConstTransitivePtr<CSpell> > spells;
 	void loadSpells();
 
 	template <typename Handler> void serialize(Handler &h, const int version)

+ 0 - 0
hch/CTownHandler.cpp → lib/CTownHandler.cpp


+ 0 - 0
hch/CTownHandler.h → lib/CTownHandler.h


+ 8 - 8
lib/Connection.cpp

@@ -7,7 +7,7 @@
 
 #ifndef _MSC_VER
 #include "../lib/RegisterTypes.cpp"
-#include "../hch/CObjectHandler.h"
+#include "CObjectHandler.h"
 #endif
 
 //for smart objs serialization over net
@@ -15,13 +15,13 @@
 #include "../StartInfo.h"
 #include "CGameState.h"
 #include "map.h"
-#include "../hch/CObjectHandler.h"
-#include "../hch/CCreatureHandler.h"
+#include "CObjectHandler.h"
+#include "CCreatureHandler.h"
 #include "VCMI_Lib.h"
-#include "../hch/CArtHandler.h"
-#include "../hch/CHeroHandler.h"
-#include "../hch/CTownHandler.h"
-#include "../hch/CCampaignHandler.h"
+#include "CArtHandler.h"
+#include "CHeroHandler.h"
+#include "CTownHandler.h"
+#include "CCampaignHandler.h"
 #include "NetPacks.h"
 
 
@@ -405,6 +405,6 @@ void CSerializer::addStdVecItems(CGameState *gs, LibClasses *lib)
 	registerVectoredType(&lib->heroh->heroes, &CHero::ID);
 	registerVectoredType(&lib->creh->creatures, &CCreature::idNumber);
 	registerVectoredType(&lib->arth->artifacts, &CArtifact::id);
-	registerVectoredType(&gs->map->artInstances, &IModableArt::ID);
+	registerVectoredType(&gs->map->artInstances, &CArtifactInstance::id);
 	smartVectorMembersSerialization = true;
 }

+ 57 - 8
lib/Connection.h

@@ -16,6 +16,7 @@
 #include <boost/type_traits/is_array.hpp>
 #include <boost/type_traits/remove_pointer.hpp>
 #include <boost/type_traits/remove_const.hpp>
+#include <boost/unordered_set.hpp>
 
 #include <boost/mpl/eval_if.hpp>
 #include <boost/mpl/equal_to.hpp>
@@ -23,6 +24,8 @@
 #include <boost/mpl/identity.hpp>
 #include <boost/any.hpp>
 
+#include "ConstTransitivePtr.h"
+
 const ui32 version = 727;
 class CConnection;
 class CGObjectInstance;
@@ -232,10 +235,10 @@ struct SerializationLevel
 template <typename T>
 struct VectorisedObjectInfo
 {
-	const std::vector<T*> *vector;	//pointer to the appropriate vector
+	const std::vector<ConstTransitivePtr<T> > *vector;	//pointer to the appropriate vector
 	const si32 T::*idPtr;			//pointer to the field representing the position in the vector
 
-	VectorisedObjectInfo(const std::vector<T*> *Vector, const si32 T::*IdPtr)
+	VectorisedObjectInfo(const std::vector< ConstTransitivePtr<T> > *Vector, const si32 T::*IdPtr)
 		:vector(Vector), idPtr(IdPtr)
 	{
 	}
@@ -259,6 +262,11 @@ public:
 	{
 		vectors[&typeid(T)] = VectorisedObjectInfo<T>(Vector, IdPtr);
 	}
+	template <typename T>
+	void registerVectoredType(const std::vector<ConstTransitivePtr<T> > *Vector, const si32 T::*IdPtr)
+	{
+		vectors[&typeid(T)] = VectorisedObjectInfo<T>(Vector, IdPtr);
+	}
 
 	template <typename T>
 	const VectorisedObjectInfo<T> *getVectorisedTypeInfo()
@@ -290,7 +298,7 @@ public:
 
 		assert(oInfo.vector);
 		assert(oInfo.vector->size() > id);
-		return (*oInfo.vector)[id];
+		return const_cast<T*>((*oInfo.vector)[id].get());
 	}
 
 	template <typename T>
@@ -413,11 +421,14 @@ public:
 			typedef typename VectorisedTypeFor<TObjectType>::type VType;
  			if(const VectorisedObjectInfo<VType> *info = getVectorisedTypeInfo<VType>())
  			{
- 				*this << getIdFromVectorItem<VType>(*info, data);
- 				return;
+				si32 id = getIdFromVectorItem<VType>(*info, data);
+				*this << id;
+				if(id != -1) //vector id is enough
+ 					return;
  			}
  		}
 
+
 		if(smartPointerSerialization)
 		{
 			std::map<const void*,ui32>::iterator i = savedPointers.find(data);
@@ -488,6 +499,12 @@ public:
 		const_cast<T&>(data).serialize(*this,version);
 	}
 	template <typename T>
+	void saveSerializable(const boost::shared_ptr<T> &data)
+	{
+		T *internalPtr = data.get();
+		*this << internalPtr;
+	}
+	template <typename T>
 	void saveSerializable(const std::vector<T> &data)
 	{
 		boost::uint32_t length = data.size();
@@ -504,6 +521,15 @@ public:
 		for(typename std::set<T>::iterator i=d.begin();i!=d.end();i++)
 			*this << *i;
 	}
+	template <typename T, typename U>
+	void saveSerializable(const boost::unordered_set<T, U> &data)
+	{
+		boost::unordered_set<T, U> &d = const_cast<boost::unordered_set<T, U> &>(data);
+		boost::uint32_t length = d.size();
+		*this << length;
+		for(typename boost::unordered_set<T, U>::iterator i=d.begin();i!=d.end();i++)
+			*this << *i;
+	}
 	template <typename T>
 	void saveSerializable(const std::list<T> &data)
 	{
@@ -673,10 +699,13 @@ public:
 			typedef typename VectorisedTypeFor<TObjectType>::type VType;									 //eg: CGHeroInstance -> CGobjectInstance
 			if(const VectorisedObjectInfo<VType> *info = getVectorisedTypeInfo<VType>())
 			{
-				ui32 id;
+				si32 id;
 				*this >> id;
-				data = static_cast<T>(getVectorItemFromId(*info, id));
-				return;
+				if(id != -1)
+				{
+					data = static_cast<T>(getVectorItemFromId(*info, id));
+					return;
+				}
 			}
 		}
 
@@ -735,6 +764,13 @@ public:
 	};
 
 
+	template <typename T>
+	void loadSerializable(boost::shared_ptr<T> &data)
+	{
+		T *internalPtr;
+		*this >> internalPtr;
+		data.reset(internalPtr);
+	}
 	template <typename T>
 	void loadSerializable(std::vector<T> &data)
 	{
@@ -754,6 +790,17 @@ public:
 			data.insert(ins);
 		}
 	}
+	template <typename T, typename U>
+	void loadSerializable(boost::unordered_set<T, U> &data)
+	{
+		READ_CHECK_U32(length);
+		T ins;
+		for(ui32 i=0;i<length;i++)
+		{
+			*this >> ins;
+			data.insert(ins);
+		}
+	}
 	template <typename T>
 	void loadSerializable(std::list<T> &data)
 	{
@@ -897,4 +944,6 @@ public:
 
 };
 
+#define BONUS_TREE_DESERIALIZATION_FIX if(!h.saving) deserializationFix();
+
 #endif // __CONNECTION_H__

+ 66 - 0
lib/ConstTransitivePtr.h

@@ -0,0 +1,66 @@
+#pragma once
+
+class CGameHandler;
+
+template <typename T>
+class ConstTransitivePtr
+{
+	T *ptr;
+	ConstTransitivePtr(const T *Ptr)
+		: ptr(const_cast<T*>(Ptr)) 
+	{}
+public:
+	ConstTransitivePtr(T *Ptr = NULL)
+		: ptr(Ptr) 
+	{}
+
+	const T& operator*() const
+	{
+		return *ptr;
+	}
+	T& operator*()
+	{
+		return *ptr;
+	}
+	operator const T*() const
+	{
+		return ptr;
+	}
+	T* get()
+	{
+		return ptr;
+	}
+	const T* get() const
+	{
+		return ptr;
+	}
+	operator T*() 
+	{
+		return ptr;
+	}
+	T *operator->() 
+	{
+		return ptr;
+	}
+	const T *operator->() const 
+	{
+		return ptr;
+	}
+	const T*operator=(T *t)
+	{
+		return ptr = t;
+	}
+
+	void dellNull()
+	{
+		delete ptr;
+		ptr = NULL;
+	}
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & ptr;
+	}
+
+	friend CGameHandler;
+};

+ 289 - 101
lib/HeroBonus.cpp

@@ -2,20 +2,25 @@
 #include "HeroBonus.h"
 #include <boost/foreach.hpp>
 #include "VCMI_Lib.h"
-#include "../hch/CSpellHandler.h"
+#include "CSpellHandler.h"
 #include <sstream>
-#include "../hch/CCreatureHandler.h"
+#include "CCreatureHandler.h"
 #include <boost/assign/list_of.hpp>
 #include "CCreatureSet.h"
 #include <boost/algorithm/string/trim.hpp>
+#include <boost/bind.hpp>
+#include "CHeroHandler.h"
+#include "CGeneralTextHandler.h"
 
-#define FOREACH_CONST_PARENT(pname, source) 	TCNodes parents; getParents(parents, source); BOOST_FOREACH(const CBonusSystemNode *pname, parents)
-#define FOREACH_PARENT(pname, source) 	TNodes parents; getParents(parents, source); BOOST_FOREACH(CBonusSystemNode *pname, parents)
+#define FOREACH_CONST_PARENT(pname) 	TCNodes parents; getParents(parents); BOOST_FOREACH(const CBonusSystemNode *pname, parents)
+#define FOREACH_PARENT(pname) 	TNodes parents; getParents(parents); BOOST_FOREACH(CBonusSystemNode *pname, parents)
 
 #define BONUS_NAME(x) ( #x, Bonus::x )
 	DLL_EXPORT const std::map<std::string, int> bonusNameMap = boost::assign::map_list_of BONUS_LIST;
 #undef BONUS_NAME
 
+#define BONUS_LOG_LINE(x) tlog0 << x << std::endl
+
 int DLL_EXPORT BonusList::totalValue() const
 {
 	int base = 0;
@@ -25,7 +30,7 @@ int DLL_EXPORT BonusList::totalValue() const
 	int indepMax = 0;
 	bool hasIndepMax = false;
 
-	for(const_iterator i = begin(); i != end(); i++)
+	BOOST_FOREACH(Bonus *i, *this)
 	{
 		switch(i->valType)
 		{
@@ -65,88 +70,56 @@ int DLL_EXPORT BonusList::totalValue() const
 }
 const DLL_EXPORT Bonus * BonusList::getFirst(const CSelector &selector) const
 {
-	for (const_iterator i = begin(); i != end(); i++)
-		if(selector(*i))
+	BOOST_FOREACH(Bonus *i, *this)
+		if(selector(i))
 			return &*i;
 	return NULL;
 }
 
 DLL_EXPORT Bonus * BonusList::getFirst(const CSelector &select)
 {
-	for (iterator i = begin(); i != end(); i++)
-		if(select(*i))
+	BOOST_FOREACH(Bonus *i, *this)
+		if(select(i))
 			return &*i;
 	return NULL;
 }
 
 void DLL_EXPORT BonusList::getModifiersWDescr(TModDescr &out) const
 {
-	for(const_iterator i = begin(); i != end(); i++)
+	BOOST_FOREACH(Bonus *i, *this)
 		out.push_back(std::make_pair(i->val, i->Description()));
 }
 
 void DLL_EXPORT BonusList::getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *source /*= NULL*/) const
 {
-	for(const_iterator i = begin(); i != end(); i++)
-		if(selector(*i) && i->effectRange == Bonus::NO_LIMIT)
-			out.push_back(*i);
+	BOOST_FOREACH(Bonus *i, *this)
+		if(selector(i) && i->effectRange == Bonus::NO_LIMIT)
+			out.push_back(i);
 }
 
 void DLL_EXPORT BonusList::getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *source /*= NULL*/) const
 {
-	for(const_iterator i = begin(); i != end(); i++)
-		if(selector(*i) && (!limit || limit(*i)))
-			out.push_back(*i);
-}
-
-namespace HHLP
-{
-	class SourceComp
-	{
-	public:
-		Bonus::BonusSource src;
-		SourceComp(Bonus::BonusSource _src) : src(_src)
-		{
-		}
-		bool operator()(const Bonus & bon)
-		{
-			return bon.source == src;
-		}
-	};
+	BOOST_FOREACH(Bonus *i, *this)
+		if(selector(i) && (!limit || limit(i)))
+			out.push_back(i);
 }
 
 
 void DLL_EXPORT BonusList::removeSpells(Bonus::BonusSource sourceType)
 {
-	std::remove_if(begin(), end(), HHLP::SourceComp(sourceType));
+	remove_if(Selector::sourceType(sourceType));
 }
 
 void BonusList::limit(const CBonusSystemNode &node)
 {
-limit_start:
-	for(iterator i = begin(); i != end(); i++)
-	{
-		if(i->limiter && i->limiter->limit(*i, node))
-		{
-			iterator toErase = i;
-			if(i != begin())
-			{
-				i--;
-				erase(toErase);
-			}
-			else
-			{
-				erase(toErase);
-				goto limit_start;
-			}
-		}
-	}
+	remove_if(boost::bind(&CBonusSystemNode::isLimitedOnUs, boost::ref(node), _1));
 }
 
 int CBonusSystemNode::valOfBonuses(Bonus::BonusType type, const CSelector &selector) const
 {
 	return valOfBonuses(Selector::type(type) && selector);
 }
+
 int CBonusSystemNode::valOfBonuses(Bonus::BonusType type, int subtype /*= -1*/) const
 {
 	CSelector s = Selector::type(type);
@@ -155,13 +128,14 @@ int CBonusSystemNode::valOfBonuses(Bonus::BonusType type, int subtype /*= -1*/)
 
 	return valOfBonuses(s);
 }
-int CBonusSystemNode::valOfBonuses(const CSelector &selector, const CBonusSystemNode *root/* = NULL*/) const
+
+int CBonusSystemNode::valOfBonuses(const CSelector &selector) const
 {
 	BonusList hlp;
-	getBonuses(hlp, selector, root);
+	getBonuses(hlp, selector);
 	return hlp.totalValue();
 }
-bool CBonusSystemNode::hasBonus(const CSelector &selector, const CBonusSystemNode *root/* = NULL*/) const
+bool CBonusSystemNode::hasBonus(const CSelector &selector) const
 {
 	return getBonuses(selector).size() > 0;
 }
@@ -181,10 +155,7 @@ Bonus * CBonusSystemNode::getBonus(const CSelector &selector)
 	if(ret)
 		return ret;
 
-	//FOREACH_PARENT(p, this)
-	TNodes parents;
-	getParents (parents, this);
-	BOOST_FOREACH (CBonusSystemNode *pname, parents)
+	FOREACH_PARENT(pname)
 	{
 		ret = pname->getBonus(selector);
 		if (ret)
@@ -194,12 +165,17 @@ Bonus * CBonusSystemNode::getBonus(const CSelector &selector)
 	return NULL;
 }
 
+const Bonus * CBonusSystemNode::getBonus( const CSelector &selector ) const
+{
+	return (const_cast<CBonusSystemNode*>(this))->getBonus(selector);
+}
+
 void CBonusSystemNode::getModifiersWDescr(TModDescr &out, Bonus::BonusType type, int subtype /*= -1 */) const
 {
-	getModifiersWDescr(out, Selector::typeSybtype(type, subtype));
+	getModifiersWDescr(out, subtype != -1 ? Selector::typeSybtype(type, subtype) : Selector::type(type));
 }
 
-void CBonusSystemNode::getModifiersWDescr(TModDescr &out, const CSelector &selector, const CBonusSystemNode *root /*= NULL*/) const
+void CBonusSystemNode::getModifiersWDescr(TModDescr &out, const CSelector &selector) const
 {
 	getBonuses(selector).getModifiersWDescr(out);
 }
@@ -208,31 +184,26 @@ int CBonusSystemNode::getBonusesCount(int from, int id) const
 	return getBonusesCount(Selector::source(from, id));
 }
 
-int CBonusSystemNode::getBonusesCount(const CSelector &selector, const CBonusSystemNode *root/* = NULL*/) const
+int CBonusSystemNode::getBonusesCount(const CSelector &selector) const
 {
-	return getBonuses(selector, root).size();
+	return getBonuses(selector).size();
 }
 
-void CBonusSystemNode::getParents(TCNodes &out, const CBonusSystemNode *root) const /*retreives list of parent nodes (nodes to inherit bonuses from) */
+void CBonusSystemNode::getParents(TCNodes &out) const /*retreives list of parent nodes (nodes to inherit bonuses from) */
 {
-	return;
+	BOOST_FOREACH(const CBonusSystemNode *parent, parents)
+		out.insert(parent);
 }
 
-void CBonusSystemNode::getParents(TNodes &out, const CBonusSystemNode *root /*= NULL*/)
+void CBonusSystemNode::getParents(TNodes &out)
 {
-	//de-constify above
-	TCNodes hlp;
-	getParents(hlp, root);
-	BOOST_FOREACH(const CBonusSystemNode *pname, hlp)
-		out.insert(const_cast<CBonusSystemNode*>(pname));
+	BOOST_FOREACH(const CBonusSystemNode *parent, parents)
+		out.insert(const_cast<CBonusSystemNode*>(parent));
 }
 
 void CBonusSystemNode::getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root /*= NULL*/) const
 {
-	//FOREACH_CONST_PARENT(p, root ? root : this) //unwinded macro
-	TCNodes parents;
-	getParents(parents, root ? root : this);
-	BOOST_FOREACH(const CBonusSystemNode *p, parents)
+	FOREACH_CONST_PARENT(p) //unwinded macro
 		p->getBonuses(out, selector, root ? root : this);
 
 	bonuses.getBonuses(out, selector);
@@ -241,31 +212,24 @@ void CBonusSystemNode::getBonuses(BonusList &out, const CSelector &selector, con
 		out.limit(*this);
 }
 
-BonusList CBonusSystemNode::getBonuses(const CSelector &selector, const CBonusSystemNode *root /*= NULL*/) const
+BonusList CBonusSystemNode::getBonuses(const CSelector &selector) const
 {
 	BonusList ret;
-	getBonuses(ret, selector, root);
+	getBonuses(ret, selector);
 	return ret;
 }
 
-void CBonusSystemNode::getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= NULL*/) const
+void CBonusSystemNode::getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit) const
 {
-	//FOREACH_CONST_PARENT(p, root ? root : this) //unwinded macro
-	TCNodes parents;
-	getParents(parents, root ? root : this);
-	BOOST_FOREACH(const CBonusSystemNode *p, parents)
-		p->getBonuses(out, selector, limit, root ? root : this);
-
-	bonuses.getBonuses(out, selector, limit);
-
-	if(!root)
-		out.limit(*this);
+	getBonuses(out, selector); //first get all the bonuses
+	out.remove_if(std::not1(limit)); //now remove the ones we don't like
+	out.limit(*this); //apply bonuses' limiters
 }
 
-BonusList CBonusSystemNode::getBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= NULL*/) const
+BonusList CBonusSystemNode::getBonuses(const CSelector &selector, const CSelector &limit) const
 {
 	BonusList ret;
-	getBonuses(ret, selector, limit, root);
+	getBonuses(ret, selector, limit);
 	return ret;
 }
 
@@ -346,7 +310,141 @@ CBonusSystemNode::CBonusSystemNode()
 
 CBonusSystemNode::~CBonusSystemNode()
 {
+	detachFromAll();
+
+	if(children.size())
+	{
+		tlog2 << "Warning: an orphaned child!\n";
+		while(children.size())
+			children.front()->detachFrom(this);
+	}
+}
+
+void CBonusSystemNode::attachTo(CBonusSystemNode *parent)
+{
+	assert(!vstd::contains(parents, parent));
+	parents.push_back(parent);
+// 	BOOST_FOREACH(Bonus *b, exportedBonuses)
+// 		propagateBonus(b);
+// 
+// 	if(parent->weActAsBonusSourceOnly())
+// 	{
+// 
+// 	}
+
+	parent->newChildAttached(this);
+}
+
+void CBonusSystemNode::detachFrom(CBonusSystemNode *parent)
+{
+	assert(vstd::contains(parents, parent));
+	parents -= parent;
+	 //unpropagate bonus
+
+	parent->childDetached(this);
+}
+
+void CBonusSystemNode::popBonuses(const CSelector &s)
+{
+	//TODO
+	//prop
+	BonusList bl;
+	exportedBonuses.getBonuses(bl, s);
+	BOOST_FOREACH(Bonus *b, bl)
+		removeBonus(b);
+
+	BOOST_FOREACH(CBonusSystemNode *child, children)
+		child->popBonuses(s);
+}
+
+// void CBonusSystemNode::addNewBonus(const Bonus &b)
+// {
+// 	addNewBonus(new Bonus(b));
+// }
+
+void CBonusSystemNode::addNewBonus(Bonus *b)
+{
+	exportedBonuses.push_back(b);
+	propagateBonus(b);
+}
+
+void CBonusSystemNode::removeBonus(Bonus *b)
+{
+	exportedBonuses -= b;
+	CBonusSystemNode *whereIsOurBonus = whereToPropagate(b);
+	whereIsOurBonus->bonuses -= b;
+	delNull(b);
+}
+
+CBonusSystemNode * CBonusSystemNode::whereToPropagate(Bonus *b)
+{
+	if(b->propagator)
+		return b->propagator->getDestNode(this);
+	else
+		return this;
+}
+
+bool CBonusSystemNode::isLimitedOnUs(Bonus *b) const
+{
+	return b->limiter && b->limiter->limit(b, *this);
+}
+
+bool CBonusSystemNode::weActAsBonusSourceOnly() const
+{
+	switch(nodeType)
+	{
+	case CREATURE:
+	case ARTIFACT:
+	case ARTIFACT_INSTANCE:
+		return true;
+	default:
+		return false;
+	}
+}
+
+TNodesVector & CBonusSystemNode::nodesOnWhichWePropagate()
+{
+	return weActAsBonusSourceOnly() ? children : parents;
+}
+
+void CBonusSystemNode::propagateBonus(Bonus * b)
+{
+	whereToPropagate(b)->bonuses.push_back(b);
+}
+
+void CBonusSystemNode::newChildAttached(CBonusSystemNode *child)
+{
+	assert(!vstd::contains(children, child));
+	children.push_back(child);
+	BONUS_LOG_LINE(child->nodeName() << " #attached to# " << nodeName());
+}
 
+void CBonusSystemNode::childDetached(CBonusSystemNode *child)
+{
+	assert(vstd::contains(children, child));
+	children -= child;
+	BONUS_LOG_LINE(child->nodeName() << " #detached from# " << nodeName());
+}
+
+void CBonusSystemNode::detachFromAll()
+{
+	while(parents.size())
+		detachFrom(parents.front());
+}
+
+bool CBonusSystemNode::isIndependentNode() const
+{
+	return parents.empty() && children.empty();
+}
+
+std::string CBonusSystemNode::nodeName() const
+{
+	return std::string("Bonus system node of type ") + typeid(*this).name();
+}
+
+void CBonusSystemNode::deserializationFix()
+{
+	tlog2 << "Deserialization fix called on bare CBSN? Shouldn't be...\n";
 }
 
 int NBonus::valOf(const CBonusSystemNode *obj, Bonus::BonusType type, int subtype /*= -1*/)
@@ -379,7 +477,7 @@ int NBonus::getCount(const CBonusSystemNode *obj, int from, int id)
 const CSpell * Bonus::sourceSpell() const
 {
 	if(source == SPELL_EFFECT)
-		return &VLC->spellh->spells[id];
+		return VLC->spellh->spells[id];
 	return NULL;
 }
 
@@ -401,8 +499,11 @@ std::string Bonus::Description() const
 	case CREATURE_ABILITY:
 		str << VLC->creh->creatures[id]->namePl;
 		break;
+	case SECONDARY_SKILL:
+		str << VLC->generaltexth->skillName[id] << " secondary skill";
+		break;
 	}
-	
+
 	return str.str();
 }
 
@@ -413,7 +514,6 @@ Bonus::Bonus(ui8 Dur, ui8 Type, ui8 Src, si32 Val, ui32 ID, std::string Desc, si
 	turnsRemain = 0;
 	valType = ADDITIVE_VALUE;
 	effectRange = NO_LIMIT;
-	limiter = NULL;
 	boost::algorithm::trim(description);
 }
 
@@ -423,7 +523,6 @@ Bonus::Bonus(ui8 Dur, ui8 Type, ui8 Src, si32 Val, ui32 ID, si32 Subtype/*=-1*/,
 	additionalInfo = -1;
 	turnsRemain = 0;
 	effectRange = NO_LIMIT;
-	limiter = NULL;
 }
 
 Bonus::Bonus()
@@ -433,7 +532,16 @@ Bonus::Bonus()
 	turnsRemain = 0;
 	valType = ADDITIVE_VALUE;
 	effectRange = NO_LIMIT;
-	limiter = NULL;
+}
+
+Bonus::~Bonus()
+{
+}
+
+Bonus * Bonus::addLimiter(ILimiter *Limiter)
+{
+	limiter.reset(Limiter);
+	return this;
 }
 
 CSelector DLL_EXPORT operator&&(const CSelector &first, const CSelector &second)
@@ -478,7 +586,7 @@ namespace Selector
 	{
 		Bonus dummy;
 		dummy.type = type;
-		return sel(dummy);
+		return sel(&dummy);
 	}
 
 	bool DLL_EXPORT matchesTypeSubtype(const CSelector &sel, TBonusType type, TBonusSubtype subtype)
@@ -486,16 +594,29 @@ namespace Selector
 		Bonus dummy;
 		dummy.type = type;
 		dummy.subtype = subtype;
-		return sel(dummy);
+		return sel(&dummy);
+	}
+}
+
+const CCreature * retrieveCreature(const CBonusSystemNode *node)
+{
+	switch(node->nodeType)
+	{
+	case CBonusSystemNode::CREATURE:
+		return (static_cast<const CCreature *>(node));
+	case CBonusSystemNode::STACK:
+		return (static_cast<const CStackInstance *>(node))->type;
+	default:
+		return NULL;
 	}
 }
 
 DLL_EXPORT std::ostream & operator<<(std::ostream &out, const BonusList &bonusList)
 {
 	int i = 0;
-	BOOST_FOREACH(const Bonus &b, bonusList)
+	BOOST_FOREACH(const Bonus *b, bonusList)
 	{
-		out << "Bonus " << i++ << "\n" << b << std::endl;
+		out << "Bonus " << i++ << "\n" << *b << std::endl;
 	}
 	return out;
 }
@@ -525,12 +646,12 @@ ILimiter::~ILimiter()
 {
 }
 
-bool ILimiter::limit(const Bonus &b, const CBonusSystemNode &node) const /*return true to drop the bonus */
+bool ILimiter::limit(const Bonus *b, const CBonusSystemNode &node) const /*return true to drop the bonus */
 {
 	return false;
 }
 
-bool CCreatureTypeLimiter::limit(const Bonus &b, const CBonusSystemNode &node) const
+bool CCreatureTypeLimiter::limit(const Bonus *b, const CBonusSystemNode &node) const
 {
 	switch (node.nodeType)
 	{	
@@ -571,7 +692,7 @@ HasAnotherBonusLimiter::HasAnotherBonusLimiter( TBonusType bonus, TBonusSubtype
 {
 }
 
-bool HasAnotherBonusLimiter::limit( const Bonus &b, const CBonusSystemNode &node ) const
+bool HasAnotherBonusLimiter::limit( const Bonus *b, const CBonusSystemNode &node ) const
 {
 	if(isSubtypeRelevant)
 	{
@@ -582,3 +703,70 @@ bool HasAnotherBonusLimiter::limit( const Bonus &b, const CBonusSystemNode &node
 		return !node.hasBonusOfType(static_cast<Bonus::BonusType>(type));
 	}
 }
+
+IPropagator::~IPropagator()
+{
+
+}
+
+CBonusSystemNode * IPropagator::getDestNode(CBonusSystemNode *source)
+{
+	return source;
+}
+
+CreatureNativeTerrainLimiter::CreatureNativeTerrainLimiter(int TerrainType) 
+	: terrainType(TerrainType)
+{
+}
+
+CreatureNativeTerrainLimiter::CreatureNativeTerrainLimiter()
+{
+
+}
+bool CreatureNativeTerrainLimiter::limit(const Bonus *b, const CBonusSystemNode &node) const
+{
+	const CCreature *c = retrieveCreature(&node);
+	return !c || VLC->heroh->nativeTerrains[c->faction] != terrainType; //drop bonus for non-creatures or non-native residents
+}
+
+CreatureFactionLimiter::CreatureFactionLimiter(int Faction)
+	: faction(Faction)
+{
+}
+
+CreatureFactionLimiter::CreatureFactionLimiter()
+{
+}
+bool CreatureFactionLimiter::limit(const Bonus *b, const CBonusSystemNode &node) const
+{
+	const CCreature *c = retrieveCreature(&node);
+	return !c || c->faction != faction; //drop bonus for non-creatures or non-native residents
+}
+
+CreatureAlignmentLimiter::CreatureAlignmentLimiter()
+{
+}
+
+CreatureAlignmentLimiter::CreatureAlignmentLimiter(si8 Alignment)
+	: alignment(Alignment)
+{
+}
+
+bool CreatureAlignmentLimiter::limit(const Bonus *b, const CBonusSystemNode &node) const
+{
+	const CCreature *c = retrieveCreature(&node);
+	if(!c) 
+		return true;
+	switch(alignment)
+	{
+	case GOOD:
+		return !c->isGood(); //if not good -> return true (drop bonus)
+	case NEUTRAL:
+		return c->isEvil() || c->isGood();
+	case EVIL:
+		return !c->isEvil();
+	default:
+		tlog1 << "Warning: illegal alignment in limiter!\n";
+		return true;
+	}
+}

+ 155 - 50
lib/HeroBonus.h

@@ -4,6 +4,7 @@
 #include <list>
 #include <set>
 #include <boost/function.hpp>
+#include <boost/smart_ptr/shared_ptr.hpp>
 
 /*
  * HeroBonus.h, part of VCMI engine
@@ -20,11 +21,13 @@ class CSpell;
 struct Bonus;
 class CBonusSystemNode;
 class ILimiter;
+class IPropagator;
 
 typedef std::vector<std::pair<int,std::string> > TModDescr; //modifiers values and their descriptions
 typedef std::set<CBonusSystemNode*> TNodes;
 typedef std::set<const CBonusSystemNode*> TCNodes;
-typedef boost::function<bool(const Bonus&)> CSelector;
+typedef std::vector<CBonusSystemNode *> TNodesVector;
+typedef boost::function<bool(const Bonus*)> CSelector;
 
 namespace PrimarySkill
 {
@@ -172,13 +175,14 @@ struct DLL_EXPORT Bonus
 		N_TURNS = 16, //used during battles, after battle bonus is always removed
 		N_DAYS = 32,
 		UNITL_BEING_ATTACKED = 64,/*removed after attack and counterattacks are performed*/
-		UNTIL_ATTACK = 128 /*removed after attack and counterattacks are performed*/
+		UNTIL_ATTACK = 128, /*removed after attack and counterattacks are performed*/
+		STACK_GETS_TURN = 256 /*removed when stack gets its turn - used for defensive stance*/
 	};
 	enum BonusSource
 	{
-		ARTIFACT, 
-		OBJECT, 
-		CASTED_SPELL,
+		ARTIFACT,
+		ARTIFACT_INSTANCE,
+		OBJECT,
 		CREATURE_ABILITY,
 		TERRAIN_NATIVE,
 		TERRAIN_OVERLAY,
@@ -190,7 +194,8 @@ struct DLL_EXPORT Bonus
 		ARMY,
 		CAMPAIGN_BONUS,
 		SPECIAL_WEEK,
-		STACK_EXPERIENCE
+		STACK_EXPERIENCE,
+		OTHER /*used for defensive stance*/
 	};
 
 	enum LimitEffect
@@ -211,7 +216,7 @@ struct DLL_EXPORT Bonus
 		INDEPENDENT_MAX //used for SPELL bonus
 	};
 
-	ui8 duration; //uses BonusDuration values
+	ui16 duration; //uses BonusDuration values
 	si16 turnsRemain; //used if duration is N_TURNS or N_DAYS
 
 	TBonusType type; //uses BonusType values - says to what is this bonus - 1 byte
@@ -225,13 +230,15 @@ struct DLL_EXPORT Bonus
 	si32 additionalInfo;
 	ui8 effectRange; //if not NO_LIMIT, bonus will be ommitted by default
 
-	ILimiter *limiter;
+	boost::shared_ptr<ILimiter> limiter;
+	boost::shared_ptr<IPropagator> propagator;
 
 	std::string description; 
 
 	Bonus(ui8 Dur, ui8 Type, ui8 Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype=-1);
 	Bonus(ui8 Dur, ui8 Type, ui8 Src, si32 Val, ui32 ID, si32 Subtype=-1, ui8 ValType = ADDITIVE_VALUE);
 	Bonus();
+	~Bonus();
 
 // 	//comparison
 // 	bool operator==(const HeroBonus &other)
@@ -250,25 +257,29 @@ struct DLL_EXPORT Bonus
 		h & duration & type & subtype & source & val & id & description & additionalInfo & turnsRemain & valType & effectRange & limiter;
 	}
 
-	static bool OneDay(const Bonus &hb)
+	static bool OneDay(const Bonus *hb)
 	{
-		return hb.duration & Bonus::ONE_DAY;
+		return hb->duration & Bonus::ONE_DAY;
 	}
-	static bool OneWeek(const Bonus &hb)
+	static bool OneWeek(const Bonus *hb)
 	{
-		return hb.duration & Bonus::ONE_WEEK;
+		return hb->duration & Bonus::ONE_WEEK;
 	}
-	static bool OneBattle(const Bonus &hb)
+	static bool OneBattle(const Bonus *hb)
 	{
-		return hb.duration & Bonus::ONE_BATTLE;
+		return hb->duration & Bonus::ONE_BATTLE;
 	}
-	static bool UntilAttack(const Bonus &hb)
+	static bool UntilGetsTurn(const Bonus *hb)
 	{
-		return hb.duration & Bonus::UNTIL_ATTACK;
+		return hb->duration & Bonus::STACK_GETS_TURN;
 	}
-	static bool UntilBeingAttacked(const Bonus &hb)
+	static bool UntilAttack(const Bonus *hb)
 	{
-		return hb.duration & Bonus::UNITL_BEING_ATTACKED;
+		return hb->duration & Bonus::UNTIL_ATTACK;
+	}
+	static bool UntilBeingAttacked(const Bonus *hb)
+	{
+		return hb->duration & Bonus::UNITL_BEING_ATTACKED;
 	}
 	static bool IsFrom(const Bonus &hb, ui8 source, ui32 id) //if id==0xffffff then id doesn't matter
 	{
@@ -289,6 +300,8 @@ struct DLL_EXPORT Bonus
 	const CSpell * sourceSpell() const;
 
 	std::string Description() const;
+
+	Bonus *addLimiter(ILimiter *Limiter); //returns this for convenient chain-calls
 };
 
 struct DLL_EXPORT stackExperience : public Bonus
@@ -305,7 +318,7 @@ struct DLL_EXPORT stackExperience : public Bonus
 
 DLL_EXPORT std::ostream & operator<<(std::ostream &out, const Bonus &bonus);
 
-class BonusList : public std::list<Bonus>
+class BonusList : public std::list<Bonus*>
 {
 public:
 	int DLL_EXPORT totalValue() const; //subtype -> subtype of bonus, if -1 then any
@@ -322,18 +335,25 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & static_cast<std::list<Bonus>&>(*this);
+		h & static_cast<std::list<Bonus*>&>(*this);
 	}
 };
 
 DLL_EXPORT std::ostream & operator<<(std::ostream &out, const BonusList &bonusList);
 
+class DLL_EXPORT IPropagator
+{
+public:
+	virtual ~IPropagator();
+	virtual CBonusSystemNode *getDestNode(CBonusSystemNode *source);
+};
+	
 class DLL_EXPORT ILimiter
 {
 public:
 	virtual ~ILimiter();
 
-	virtual bool limit(const Bonus &b, const CBonusSystemNode &node) const; //return true to drop the bonus
+	virtual bool limit(const Bonus *b, const CBonusSystemNode &node) const; //return true to drop the bonus
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{}
@@ -342,7 +362,12 @@ public:
 class DLL_EXPORT CBonusSystemNode
 {
 public:
-	BonusList bonuses;
+	BonusList bonuses; //wielded bonuses (local and up-propagated here)
+	BonusList exportedBonuses;
+
+	TNodesVector parents, //parents -> we inherit bonuses from them, we may attach our bonuses to them
+									children;
+
 	ui8 nodeType;
 
 	CBonusSystemNode();
@@ -351,19 +376,21 @@ public:
 	//new bonusing node interface
 	// * selector is predicate that tests if HeroBonus matches our criteria
 	// * root is node on which call was made (NULL will be replaced with this)
-	virtual void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const;  //retrieves list of parent nodes (nodes to inherit bonuses from), source is the prinary asker
-	virtual void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;
-
-	void getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = NULL) const;
-	BonusList getBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = NULL) const;
-	BonusList getBonuses(const CSelector &selector, const CBonusSystemNode *root = NULL) const;
-	int getBonusesCount(const CSelector &selector, const CBonusSystemNode *root = NULL) const;
-	int valOfBonuses(const CSelector &selector, const CBonusSystemNode *root = NULL) const;
-	bool hasBonus(const CSelector &selector, const CBonusSystemNode *root = NULL) const;
-	void getModifiersWDescr(TModDescr &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;  //out: pairs<modifier value, modifier description>* 
+	void getParents(TCNodes &out) const;  //retrieves list of parent nodes (nodes to inherit bonuses from), source is the prinary asker
+	void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;
+
 	virtual std::string bonusToString(Bonus *bonus, bool description) const {return "";}; //description or bonus name
 
 	//////////////////////////////////////////////////////////////////////////
+	//interface
+	void getModifiersWDescr(TModDescr &out, const CSelector &selector) const;  //out: pairs<modifier value, modifier description>
+	int getBonusesCount(const CSelector &selector) const;
+	int valOfBonuses(const CSelector &selector) const;
+	bool hasBonus(const CSelector &selector) const;
+	BonusList getBonuses(const CSelector &selector, const CSelector &limit) const;
+	void getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit) const;
+	BonusList getBonuses(const CSelector &selector) const;
+
 	//legacy interface 
 	int valOfBonuses(Bonus::BonusType type, const CSelector &selector) const;
 	int valOfBonuses(Bonus::BonusType type, int subtype = -1) const; //subtype -> subtype of bonus, if -1 then anyt;
@@ -371,8 +398,8 @@ public:
 	bool hasBonusFrom(ui8 source, ui32 sourceID) const;
 	void getModifiersWDescr( TModDescr &out, Bonus::BonusType type, int subtype = -1 ) const;  //out: pairs<modifier value, modifier description>
 	int getBonusesCount(int from, int id) const;
-	virtual ui32 getMinDamage() const; //used for stacks and creatures only
-	virtual ui32 getMaxDamage() const;
+	ui32 getMinDamage() const; //used for stacks and creatures only
+	ui32 getMaxDamage() const;
 
 	int MoraleVal() const; //range [-3, +3]
 	int LuckVal() const; //range [-3, +3]
@@ -381,18 +408,42 @@ public:
 	ui16 MaxHealth() const; //get max HP of stack with all modifiers
 
 
+	const Bonus *getBonus(const CSelector &selector) const;
 	//non-const interface
-	void getParents(TNodes &out, const CBonusSystemNode *root = NULL);  //retrieves list of parent nodes (nodes to inherit bonuses from), source is the prinary asker
+	void getParents(TNodes &out);  //retrieves list of parent nodes (nodes to inherit bonuses from), source is the prinary asker
 	Bonus *getBonus(const CSelector &selector);
 
+	void attachTo(CBonusSystemNode *parent);
+	void detachFrom(CBonusSystemNode *parent);
+	void detachFromAll();
+	void addNewBonus(Bonus *b); //b will be deleted with destruction of node
+
+	void newChildAttached(CBonusSystemNode *child);
+	void childDetached(CBonusSystemNode *child);
+	void propagateBonus(Bonus * b);
+	//void addNewBonus(const Bonus &b); //b will copied
+	void removeBonus(Bonus *b);
+
+	TNodesVector &nodesOnWhichWePropagate();
+	bool isIndependentNode() const; //node is independent when it has no parents nor children
+	bool weActAsBonusSourceOnly() const;
+	bool isLimitedOnUs(Bonus *b) const; //if bonus should be removed from list acquired from this node
+	CBonusSystemNode *whereToPropagate(Bonus *b);
+
+	void popBonuses(const CSelector &s);
+	virtual std::string nodeName() const;
+	void deserializationFix();
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & bonuses & nodeType;
+		h & exportedBonuses;
+		//h & parents & children;
 	}
 
 	enum ENodeTypes
 	{
-		UNKNOWN, STACK, SPECIALITY, ARTIFACT, CREATURE
+		UNKNOWN, STACK, SPECIALITY, ARTIFACT, CREATURE, ARTIFACT_INSTANCE, HERO
 	};
 };
 
@@ -407,7 +458,7 @@ namespace NBonus
 };
 
 //generates HeroBonus from given data
-inline Bonus makeFeature(Bonus::BonusType type, ui8 duration, si16 subtype, si32 value, Bonus::BonusSource source, ui16 turnsRemain = 0, si32 additionalInfo = 0)
+inline Bonus makeFeatureVal(Bonus::BonusType type, ui8 duration, si16 subtype, si32 value, Bonus::BonusSource source, ui16 turnsRemain = 0, si32 additionalInfo = 0)
 {
 	Bonus sf;
 	sf.type = type;
@@ -421,6 +472,13 @@ inline Bonus makeFeature(Bonus::BonusType type, ui8 duration, si16 subtype, si32
 	return sf;
 }
 
+//generates HeroBonus from given data
+inline Bonus * makeFeature(Bonus::BonusType type, ui8 duration, si16 subtype, si32 value, Bonus::BonusSource source, ui16 turnsRemain = 0, si32 additionalInfo = 0)
+{
+	return new Bonus(makeFeatureVal(type, duration, subtype, value, source, turnsRemain, additionalInfo));
+}
+
+
 class DLL_EXPORT CSelectorsConjunction
 {
 	const CSelector first, second;
@@ -429,7 +487,7 @@ public:
 		:first(First), second(Second)
 	{
 	}
-	bool operator()(const Bonus &bonus) const
+	bool operator()(const Bonus *bonus) const
 	{
 		return first(bonus) && second(bonus);
 	}
@@ -444,7 +502,7 @@ public:
 		:first(First), second(Second)
 	{
 	}
-	bool operator()(const Bonus &bonus) const
+	bool operator()(const Bonus *bonus) const
 	{
 		return first(bonus) || second(bonus);
 	}
@@ -461,9 +519,9 @@ public:
 		: ptr(Ptr), val(Val)
 	{
 	}
-	bool operator()(const Bonus &bonus) const
+	bool operator()(const Bonus *bonus) const
 	{
-		return bonus.*ptr == val;
+		return bonus->*ptr == val;
 	}
 	CSelectFieldEqual& operator()(const T &setVal)
 	{
@@ -472,16 +530,16 @@ public:
 	}
 };
 
-class CWillLastTurns
+class DLL_EXPORT CWillLastTurns
 {
 public:
 	int turnsRequested;
 
-	bool operator()(const Bonus &bonus) const
+	bool operator()(const Bonus *bonus) const
 	{
 		return turnsRequested <= 0					//every present effect will last zero (or "less") turns
-			|| !(bonus.duration & Bonus::N_TURNS)	//so do every not expriing after N-turns effect
-			|| bonus.turnsRemain > turnsRequested;	
+			|| !(bonus->duration & Bonus::N_TURNS)	//so do every not expriing after N-turns effect
+			|| bonus->turnsRemain > turnsRequested;	
 	}
 	CWillLastTurns& operator()(const int &setVal)
 	{
@@ -490,7 +548,7 @@ public:
 	}
 };
 
-class CCreatureTypeLimiter : public ILimiter //affect only stacks of given creature (and optionally it's upgrades)
+class DLL_EXPORT CCreatureTypeLimiter : public ILimiter //affect only stacks of given creature (and optionally it's upgrades)
 {
 public:
 	const CCreature *creature;
@@ -499,7 +557,7 @@ public:
 	CCreatureTypeLimiter();
 	CCreatureTypeLimiter(const CCreature &Creature, ui8 IncludeUpgrades = true);
 
-	bool limit(const Bonus &b, const CBonusSystemNode &node) const;
+	bool limit(const Bonus *b, const CBonusSystemNode &node) const OVERRIDE;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -507,17 +565,17 @@ public:
 	}
 };
 
-class HasAnotherBonusLimiter : public ILimiter //applies only to nodes that have another bonus working
+class DLL_EXPORT HasAnotherBonusLimiter : public ILimiter //applies only to nodes that have another bonus working
 {
 public:
 	TBonusType type;
 	TBonusSubtype subtype;
 	ui8 isSubtypeRelevant; //check for subtype only if this is true
 
-	HasAnotherBonusLimiter(TBonusType bonus);
+	HasAnotherBonusLimiter(TBonusType bonus = Bonus::NONE);
 	HasAnotherBonusLimiter(TBonusType bonus, TBonusSubtype _subtype);
 
-	bool limit(const Bonus &b, const CBonusSystemNode &node) const;
+	bool limit(const Bonus *b, const CBonusSystemNode &node) const OVERRIDE;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -525,6 +583,53 @@ public:
 	}
 };
 
+class DLL_EXPORT CreatureNativeTerrainLimiter : public ILimiter //applies only to creatures that are on their native terrain 
+{
+public:
+	si8 terrainType;
+	CreatureNativeTerrainLimiter();
+	CreatureNativeTerrainLimiter(int TerrainType);
+
+	bool limit(const Bonus *b, const CBonusSystemNode &node) const OVERRIDE;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & terrainType;
+	}
+};
+
+class DLL_EXPORT CreatureFactionLimiter : public ILimiter //applies only to creatures of given faction
+{
+public:
+	si8 faction;
+	CreatureFactionLimiter();
+	CreatureFactionLimiter(int TerrainType);
+
+	bool limit(const Bonus *b, const CBonusSystemNode &node) const OVERRIDE;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & faction;
+	}
+};
+
+class DLL_EXPORT CreatureAlignmentLimiter : public ILimiter //applies only to creatures of given alignment
+{
+public:
+	si8 alignment;
+	CreatureAlignmentLimiter();
+	CreatureAlignmentLimiter(si8 Alignment);
+
+	bool limit(const Bonus *b, const CBonusSystemNode &node) const OVERRIDE;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & alignment;
+	}
+};
+
+const CCreature *retrieveCreature(const CBonusSystemNode *node);
+
 namespace Selector
 {
 	extern DLL_EXPORT CSelectFieldEqual<TBonusType> type;

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików