瀏覽代碼

New non battle stack class. (Base for future improved bonus system, stack exp / arts)
Fixed #443

Michał W. Urbańczyk 15 年之前
父節點
當前提交
993036e4a1
共有 14 個文件被更改,包括 265 次插入403 次删除
  1. 16 16
      AI/GeniusAI/CGeniusAI.cpp
  2. 8 8
      client/CBattleInterface.cpp
  3. 2 0
      client/CMT.cpp
  4. 6 13
      client/GUIClasses.cpp
  5. 7 7
      client/Graphics.cpp
  6. 3 0
      global.h
  7. 107 136
      hch/CObjectHandler.cpp
  8. 2 2
      hch/CObjectHandler.h
  9. 1 119
      int3.h
  10. 26 13
      lib/CGameState.cpp
  11. 11 2
      lib/NetPacks.h
  12. 8 2
      lib/NetPacksLib.cpp
  13. 25 34
      lib/map.cpp
  14. 43 51
      server/CGameHandler.cpp

+ 16 - 16
AI/GeniusAI/CGeniusAI.cpp

@@ -316,8 +316,8 @@ float CGeniusAI::TownObjective::getValue() const
 
 	  case upgradeCreatures:
 		  UpgradeInfo ui = AI->m_cb->getUpgradeInfo(whichTown->t,which);
-		  ID = whichTown->creaturesInGarrison.slots[which].first;
-		  howMany = whichTown->creaturesInGarrison.slots[which].second;
+		  ID = whichTown->creaturesInGarrison.slots[which].type->idNumber;
+		  howMany = whichTown->creaturesInGarrison.slots[which].count;
 
 		  newID = ui.newID.back();
 		  int upgrade_serial = ui.newID.size() - 1;
@@ -424,7 +424,7 @@ void CGeniusAI::TownObjective::print() const
 
 		  case upgradeCreatures:
 			  UpgradeInfo ui = AI->m_cb->getUpgradeInfo(whichTown->t,which);
-			  ID = whichTown->creaturesInGarrison.slots[which].first;
+			  ID = whichTown->creaturesInGarrison.slots[which].type->idNumber;
 			  cout << "upgrade " << VLC->creh->creatures[ID].namePl;
 			  //ui.cost	
 		  break;
@@ -735,7 +735,7 @@ void CGeniusAI::HeroObjective::fulfill(CGeniusAI& cg,
 	  //upgrade hero's units
 	  cout << "visiting town" << endl;
 	  CCreatureSet hcreatures = h->h->army;
-	  for (std::map< si32, std::pair<ui32,si32> >::const_iterator
+	  for (TSlots::const_iterator
          i = hcreatures.slots.begin();
          i != hcreatures.slots.end();
          i++) { // For each hero slot.
@@ -749,7 +749,7 @@ void CGeniusAI::HeroObjective::fulfill(CGeniusAI& cg,
                j = ui.cost[ii].begin();
                j != ui.cost[ii].end();
                j++)
-					  if (hgs.resourceAmounts[j->first] < j->second * i->second.second)
+					  if (hgs.resourceAmounts[j->first] < j->second * i->second.count)
 						  canUpgrade = false;
 		  }
 			
@@ -757,7 +757,7 @@ void CGeniusAI::HeroObjective::fulfill(CGeniusAI& cg,
 		  {
 			  cg.m_cb->upgradeCreature(h->h, i->first, ui.newID.back());
 			  cout << "upgrading hero's "
-             << VLC->creh->creatures[i->second.first].namePl
+             << i->second.type->namePl
              << endl;
 		  }
 	  }
@@ -767,33 +767,33 @@ void CGeniusAI::HeroObjective::fulfill(CGeniusAI& cg,
 	  int weakestCreatureStack;
 	  int weakestCreatureAIValue = 99999; // TODO: Wtf??
 
-	  for (std::map< si32, std::pair<ui32,si32> >::const_iterator
+	  for (TSlots::const_iterator
          i = tcreatures.slots.begin();
          i != tcreatures.slots.end();
          i++) {
-		  if (VLC->creh->creatures[i->second.first].AIValue < 
+		  if (i->second.type->AIValue < 
           weakestCreatureAIValue) {
-			  weakestCreatureAIValue  = VLC->creh->creatures[i->second.first].AIValue;
+			  weakestCreatureAIValue  = i->second.type->AIValue;
 			  weakestCreatureStack    = i->first;
 		  }
     }
-	  for (std::map< si32, std::pair<ui32, si32> >::const_iterator
+	  for (TSlots::const_iterator
         i = tcreatures.slots.begin();
         i != tcreatures.slots.end();
         i++) { // For each town slot.
 		  hcreatures = h->h->army;
-		  int hSlot = hcreatures.getSlotFor(i->second.first);
+		  int hSlot = hcreatures.getSlotFor(i->second.type->idNumber);
 
 		  if (hSlot == -1)
         continue;
 		  cout << "giving hero "
-           << VLC->creh->creatures[i->second.first].namePl
+           << i->second.type->namePl
            << endl;
 		  if (hcreatures.slots.find(hSlot) != hcreatures.slots.end()) {
         // 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.second - 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);
@@ -868,7 +868,7 @@ void CGeniusAI::addTownObjectives(HypotheticalGameState::TownModel& t,
 	}
 
   // Upgrade creatures.
-	for (std::map< si32, std::pair<ui32, si32> >::iterator
+	for (TSlots::iterator
       i = t.creaturesInGarrison.slots.begin();
       i != t.creaturesInGarrison.slots.end();
       i++) {
@@ -881,7 +881,7 @@ void CGeniusAI::addTownObjectives(HypotheticalGameState::TownModel& t,
           j = ui.cost[upgrade_serial].begin();
           j != ui.cost[upgrade_serial].end();
           j++)
-				if (hgs.resourceAmounts[j->first] < j->second * i->second.second)
+				if (hgs.resourceAmounts[j->first] < j->second * i->second.count)
 					canAfford = false;
 			if (canAfford) {
 				TownObjective to(hgs,AIObjective::upgradeCreatures,&t,i->first,this);
@@ -942,7 +942,7 @@ void CGeniusAI::TownObjective::fulfill(CGeniusAI& cg,
 
 	case upgradeCreatures:
 		UpgradeInfo ui = cg.m_cb->getUpgradeInfo(whichTown->t, which);
-		ID = whichTown->creaturesInGarrison.slots[which].first;
+		ID = whichTown->creaturesInGarrison.slots[which].type->idNumber;
 		newID = ui.newID.back();
     // TODO: reduce resources in hgs
 		cg.m_cb->upgradeCreature(whichTown->t, which, newID);

+ 8 - 8
client/CBattleInterface.cpp

@@ -3652,12 +3652,12 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect
 	{
 		int bestMonsterID = -1;
 		int bestPower = 0;
-		for(std::map<si32,std::pair<ui32,si32> >::const_iterator it = owner->army1->slots.begin(); it!=owner->army1->slots.end(); ++it)
+		for(TSlots::const_iterator it = owner->army1->slots.begin(); it!=owner->army1->slots.end(); ++it)
 		{
-			if( CGI->creh->creatures[it->first].AIValue > bestPower)
+			if( it->second.type->AIValue > bestPower)
 			{
-				bestPower = CGI->creh->creatures[it->first].AIValue;
-				bestMonsterID = it->first;
+				bestPower = it->second.type->AIValue;
+				bestMonsterID = it->second.type->idNumber;
 			}
 		}
 		SDL_BlitSurface(graphics->bigImgs[bestMonsterID], NULL, background, &genRect(64, 58, 21, 38));
@@ -3674,12 +3674,12 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect
 	{
 		int bestMonsterID = -1;
 		int bestPower = 0;
-		for(std::map<si32,std::pair<ui32,si32> >::const_iterator it = owner->army2->slots.begin(); it!=owner->army2->slots.end(); ++it)
+		for(TSlots::const_iterator it = owner->army2->slots.begin(); it!=owner->army2->slots.end(); ++it)
 		{
-			if( CGI->creh->creatures[it->second.first].AIValue > bestPower)
+			if( it->second.type->AIValue > bestPower)
 			{
-				bestPower = CGI->creh->creatures[it->second.first].AIValue;
-				bestMonsterID = it->second.first;
+				bestPower = it->second.type->AIValue;
+				bestMonsterID = it->second.type->idNumber;
 			}
 		}
 		SDL_BlitSurface(graphics->bigImgs[bestMonsterID], NULL, background, &genRect(64, 58, 391, 38));

+ 2 - 0
client/CMT.cpp

@@ -528,6 +528,8 @@ void startGame(StartInfo * options)
 		ev.user.code = 1;
 		SDL_PushEvent(&ev);
 	}
+	else
+		setResolution = true;
 
 	client = new CClient;
 	if(options->mode == 0) //new game

+ 6 - 13
client/GUIClasses.cpp

@@ -487,17 +487,13 @@ void CGarrisonInt::createSlots()
 	if(set1)
 	{
 		sup = new std::vector<CGarrisonSlot*>(7,(CGarrisonSlot *)(NULL));
-		for
-			(std::map<si32,std::pair<ui32,si32> >::const_iterator i=set1->slots.begin();
-			i!=set1->slots.end(); i++)
-		{
-			(*sup)[i->first] =
-				new CGarrisonSlot(this, pos.x + (i->first*(w+interx)), pos.y,i->first, 0, 
-									&CGI->creh->creatures[i->second.first],i->second.second);
-		}
+		for(TSlots::const_iterator i=set1->slots.begin(); i!=set1->slots.end(); i++)
+			(*sup)[i->first] =	new CGarrisonSlot(this, pos.x + (i->first*(w+interx)), pos.y, i->first, 0, i->second.type,i->second.count);
+
 		for(int i=0; i<sup->size(); i++)
 			if((*sup)[i] == NULL)
 				(*sup)[i] = new CGarrisonSlot(this, pos.x + (i*(w+interx)), pos.y,i,0,NULL, 0);
+
 		if (shiftPos)
 			for (int i=shiftPos; i<sup->size(); i++)
 			{
@@ -508,13 +504,10 @@ void CGarrisonInt::createSlots()
 	if(set2)
 	{
 		sdown = new std::vector<CGarrisonSlot*>(7,(CGarrisonSlot *)(NULL));
-		for
-			(std::map<si32,std::pair<ui32,si32> >::const_iterator i=set2->slots.begin();
-			i!=set2->slots.end(); i++)
+		for(TSlots::const_iterator i=set2->slots.begin(); i!=set2->slots.end(); i++)
 		{
 			(*sdown)[i->first] =
-				new CGarrisonSlot(this, pos.x + (i->first*(w+interx)) + garOffset.x, pos.y + garOffset.y,i->first,1, 
-									&CGI->creh->creatures[i->second.first],i->second.second);
+				new CGarrisonSlot(this, pos.x + (i->first*(w+interx)) + garOffset.x, pos.y + garOffset.y,i->first,1, i->second.type,i->second.count);
 		}
 		for(int i=0; i<sdown->size(); i++)
 			if((*sdown)[i] == NULL)

+ 7 - 7
client/Graphics.cpp

@@ -51,17 +51,17 @@ SDL_Surface * Graphics::drawHeroInfoWin(const InfoAboutHero &curh)
 	blitAt(graphics->portraitLarge[curh.portrait],11,12,ret); //portrait
 
 	//army
-	for (std::map<si32,std::pair<ui32,si32> >::const_iterator i=curh.army.slots.begin(); i!=curh.army.slots.end();i++)
+	for (TSlots::const_iterator i = curh.army.slots.begin(); i!=curh.army.slots.end();i++)
 	{
-		blitAt(graphics->smallImgs[(*i).second.first],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.second,buf,10);
+			SDL_itoa((*i).second.count,buf,10);
 			printAtMiddle(buf,slotsPos[(*i).first].first+17,slotsPos[(*i).first].second+41,FONT_TINY,zwykly,ret);
 		}
 		else
 		{
-			printAtMiddle(VLC->generaltexth->arraytxt[174 + 3*i->second.second],slotsPos[(*i).first].first+17,slotsPos[(*i).first].second+41,FONT_TINY,zwykly,ret);
+			printAtMiddle(VLC->generaltexth->arraytxt[174 + 3*i->second.count],slotsPos[(*i).first].first+17,slotsPos[(*i).first].second+41,FONT_TINY,zwykly,ret);
 		}
 	}
 
@@ -108,15 +108,15 @@ 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 (std::map<si32,std::pair<ui32,si32> >::const_iterator i=curh.army.slots.begin(); i!=curh.army.slots.end();i++)
+	for (TSlots::const_iterator i=curh.army.slots.begin(); i!=curh.army.slots.end();i++)
 	{
 		//if(!i->second.second)
 		//	continue;
-		blitAt(graphics->smallImgs[(*i).second.first],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)
 		{
 			// Show exact creature amount.
-			SDL_itoa((*i).second.second,buf,10);
+			SDL_itoa((*i).second.count,buf,10);
 			printAtMiddle(buf,slotsPos[(*i).first].first+17,slotsPos[(*i).first].second+41,FONT_TINY,zwykly,ret);
 		}
 		else

+ 3 - 0
global.h

@@ -15,6 +15,8 @@ typedef boost::int32_t si32; //signed int 32 bits (4 bytes)
 typedef boost::int16_t si16; //signed int 16 bits (2 bytes)
 typedef boost::int8_t si8; //signed int 8 bits (1 byte)
 #include "int3.h"
+#include <map>
+#include <vector>
 #define CHECKTIME 1
 #if CHECKTIME
 #include "timeHandler.h"
@@ -295,6 +297,7 @@ void delNull(T* &ptr) //deleted pointer and sets it to NULL
 	ptr = NULL;
 }
 
+#include "lib/CCreatureSet.h"
 #include "CConsoleHandler.h"
 extern DLL_EXPORT std::ostream *logfile;
 extern DLL_EXPORT CConsoleHandler *console;

+ 107 - 136
hch/CObjectHandler.cpp

@@ -461,11 +461,11 @@ static int lowestSpeed(const CGHeroInstance * chi)
 		tlog1 << "Error! Hero " << chi->id << " ("<<chi->name<<") has no army!\n";
 		return 20;
 	}
-	std::map<si32,std::pair<ui32,si32> >::const_iterator i = chi->army.slots.begin();
-	ui32 ret = VLC->creh->creatures[(*i++).second.first].speed;
+	TSlots::const_iterator i = chi->army.slots.begin();
+	ui32 ret = (i++)->second.type->speed;
 	for (;i!=chi->army.slots.end();i++)
 	{
-		ret = std::min(ret,VLC->creh->creatures[(*i).second.first].speed);
+		ret = std::min(ret, i->second.type->speed);
 	}
 	return ret;
 }
@@ -709,6 +709,7 @@ void CGHeroInstance::initHero(int SUBID)
 
 void CGHeroInstance::initHero()
 {
+	assert(army.validTypes(true));
 	if(ID == HEROI_TYPE)
 		initHeroDefInfo();
 	if(!type)
@@ -748,42 +749,45 @@ void CGHeroInstance::initHero()
 		level = VLC->heroh->level(exp);
 	}
 
+	army.formation = false;
 	if (!army.slots.size()) //standard army//initial army
 	{
-		int pom, pom2=0;
+		int howManyStacks = 0; //how many stacks will hero receives <1 - 3>
+		int pom = ran()%100;
+		int warMachinesGiven = 0;
 
-		int x = 0; //how many stacks will hero receives <1 - 3>
-		pom = ran()%100;
 		if(pom < 9)
-			x = 1;
+			howManyStacks = 1;
 		else if(pom < 79)
-			x = 2;
+			howManyStacks = 2;
 		else
-			x = 3;
+			howManyStacks = 3;
 
-		for(int x=0;x<3;x++)
+		for(int stackNo=0; stackNo<3; stackNo++)
 		{
-			pom = (VLC->creh->nameToID[type->refTypeStack[x]]);
-			if(pom>=145 && pom<=149) //war machine
+			int creID = (VLC->creh->nameToID[type->refTypeStack[stackNo]]);
+			int range = type->highStack[stackNo] - type->lowStack[stackNo];
+			int count = ran()%(range+1) + type->lowStack[stackNo];
+
+			if(creID>=145 && creID<=149) //war machine
 			{
-				pom2++;
-				switch (pom)
+				warMachinesGiven++;
+				switch (creID)
 				{
 				case 145: //catapult
 					VLC->arth->equipArtifact(artifWorn, 16, 3);
 					break;
 				default:
-					VLC->arth->equipArtifact(artifWorn, 9+CArtHandler::convertMachineID(pom,true), CArtHandler::convertMachineID(pom,true));
+					VLC->arth->equipArtifact(artifWorn, 9+CArtHandler::convertMachineID(creID,true), CArtHandler::convertMachineID(creID,true));
 					break;
 				}
-				continue;
 			}
-			army.slots[x-pom2].first = pom;
-			pom = type->highStack[x] - type->lowStack[x];
-			army.slots[x-pom2].second = ran()%(pom+1) + type->lowStack[x];
-			army.formation = false;
+			else
+				army.slots[stackNo-warMachinesGiven] = CStackInstance(creID, count);
 		}
 	}
+
+	assert(army.validTypes());
 	hoverName = VLC->generaltexth->allTexts[15];
 	boost::algorithm::replace_first(hoverName,"%s",name);
 	boost::algorithm::replace_first(hoverName,"%s", type->heroClass->name);
@@ -915,10 +919,10 @@ std::vector<std::pair<int,std::string> > CGHeroInstance::getCurrentMoraleModifie
 	{
 		bool archangelInArmy = false;
 		std::set<si8> factions;
-		for(std::map<si32,std::pair<ui32,si32> >::const_iterator i=army.slots.begin(); i!=army.slots.end(); i++)
+		for(TSlots::const_iterator i=army.slots.begin(); i!=army.slots.end(); i++)
 		{
 			// Take Angelic Alliance troop-mixing freedom of non-evil, non-Conflux units into account.
-			const si8 faction = VLC->creh->creatures[i->second.first].faction;
+			const si8 faction = i->second.type->faction;
 			if (hasBonusOfType(HeroBonus::NONEVIL_ALIGNMENT_MIX)
 				&& ((faction >= 0 && faction <= 2) || faction == 6 || faction == 7))
 			{
@@ -929,7 +933,7 @@ std::vector<std::pair<int,std::string> > CGHeroInstance::getCurrentMoraleModifie
 				factions.insert(faction);
 			}
 
-			if(i->second.first == 13)
+			if(i->second.type->idNumber == 13)
 				archangelInArmy = true;
 		}
 
@@ -997,7 +1001,7 @@ const HeroBonus * CGHeroInstance::getBonus( int from, int id ) const
 void CGHeroInstance::setPropertyDer( ui8 what, ui32 val )
 {
 	if(what == 3)
-		army.slots[0].second = val;
+		army.slots[0].count = val;
 }
 
 double CGHeroInstance::getHeroStrength() const
@@ -1063,7 +1067,7 @@ bool CGHeroInstance::canCastThisSpell(const CSpell * spell) const
  * type and second value the amount. Both values are returned as -1 if necromancy
  * could not be applied.
  */
-std::pair<ui32, si32> CGHeroInstance::calculateNecromancy (const BattleResult &battleResult) const
+CStackInstance CGHeroInstance::calculateNecromancy (const BattleResult &battleResult) const
 {
 	const ui8 necromancyLevel = getSecSkillLevel(12);
 
@@ -1099,10 +1103,10 @@ std::pair<ui32, si32> CGHeroInstance::calculateNecromancy (const BattleResult &b
 		if (raisedUnits <= 0)
 			raisedUnits = 1;
 
-		return std::pair<ui32, si32>(raisedUnitType->idNumber, raisedUnits);
+		return CStackInstance(raisedUnitType->idNumber, raisedUnits);
 	}
 
-	return std::pair<ui32, si32>(-1, -1);
+	return CStackInstance();
 }
 
 /**
@@ -1110,21 +1114,23 @@ std::pair<ui32, si32> CGHeroInstance::calculateNecromancy (const BattleResult &b
  * @param raisedStack Pair where the first element represents ID of the raised creature
  * and the second element the amount.
  */
-void CGHeroInstance::showNecromancyDialog (std::pair<ui32, si32> raisedStack) const
+void CGHeroInstance::showNecromancyDialog(const CStackInstance &raisedStack) const
 {
-	const CCreature &unitType = VLC->creh->creatures[raisedStack.first];
 	InfoWindow iw;
 	iw.soundID = soundBase::GENIE;
 	iw.player = tempOwner;
-	iw.components.push_back(Component(3, unitType.idNumber, raisedStack.second, 0));
+	iw.components.push_back(Component(raisedStack));
 
-	if (raisedStack.second > 1) { // Practicing the dark arts of necromancy, ... (plural)
+	if (raisedStack.count > 1) // Practicing the dark arts of necromancy, ... (plural)
+	{ 
 		iw.text.addTxt(MetaString::GENERAL_TXT, 145);
-		iw.text.addReplacement(raisedStack.second);
-		iw.text.addReplacement(MetaString::CRE_PL_NAMES, unitType.idNumber);
-	} else { // Practicing the dark arts of necromancy, ... (singular)
+		iw.text.addReplacement(raisedStack.count);
+		iw.text.addReplacement(MetaString::CRE_PL_NAMES, raisedStack.type->idNumber);
+	} 
+	else // Practicing the dark arts of necromancy, ... (singular)
+	{ 
 		iw.text.addTxt(MetaString::GENERAL_TXT, 146);
-		iw.text.addReplacement(MetaString::CRE_SING_NAMES, unitType.idNumber);
+		iw.text.addReplacement(MetaString::CRE_SING_NAMES, raisedStack.type->idNumber);
 	}
 
 	cb->showInfoDialog(&iw);
@@ -1273,16 +1279,13 @@ void CGDwelling::initObj()
 	case 17:
 		{
 			int crid = VLC->objh->cregens[subID];
-			CCreature *crs = &VLC->creh->creatures[crid];
+			const CCreature *crs = &VLC->creh->creatures[crid];
 
 			creatures.resize(1);
 			creatures[0].second.push_back(crid);
 			hoverName = VLC->generaltexth->creGens[subID];
 			if(crs->level > 4)
-			{
-				army.slots[0].first = crs->idNumber;
-				army.slots[0].second = (8 - crs->level) * 3;
-			}
+				army.slots[0] = CStackInstance(crs, (8 - crs->level) * 3);
 			if (getOwner() != 255)
 				cb->gameState()->players[getOwner()].dwellings.push_back (this);
 		}
@@ -1297,10 +1300,8 @@ void CGDwelling::initObj()
 			creatures[2].second.push_back(116); //Gold Golem
 			creatures[3].second.push_back(117); //Diamond Golem
 			//guards
-			army.slots[0].first = 116;
-			army.slots[0].second = 9;
-			army.slots[1].first = 117;
-			army.slots[1].second = 6;
+			army.slots[0] = CStackInstance(116, 9);
+			army.slots[1] = CStackInstance(117, 6);
 		}
 		else if(subID == 0) // Elemental Conflux 
 		{
@@ -1309,8 +1310,7 @@ void CGDwelling::initObj()
 			creatures[2].second.push_back(113); //Earth Elemental
 			creatures[3].second.push_back(115); //Water Elemental
 			//guards
-			army.slots[0].first = 113;
-			army.slots[0].second = 12;
+			army.slots[0] = CStackInstance(113, 12);
 		}
 		else
 		{
@@ -1353,8 +1353,8 @@ void CGDwelling::onHeroVisit( const CGHeroInstance * h ) const
 		bd.flags = BlockingDialog::ALLOW_CANCEL;
 		bd.text.addTxt(MetaString::GENERAL_TXT, 421); //Much to your dismay, the %s is guarded by %s %s. Do you wish to fight the guards?
 		bd.text.addReplacement(ID == 17 ? MetaString::CREGENS : MetaString::CREGENS4, subID);
-		bd.text.addReplacement(MetaString::ARRAY_TXT, 176 + CCreature::getQuantityID(army.slots.begin()->second.second)*3);
-		bd.text.addReplacement(MetaString::CRE_PL_NAMES, army.slots.begin()->second.first);
+		bd.text.addReplacement(MetaString::ARRAY_TXT, 176 + army.slots.begin()->second.getQuantityID()*3);
+		bd.text.addReplacement(army.slots.begin()->second);
 		cb->showBlockingDialog(&bd, boost::bind(&CGDwelling::wantsFight, this, h, _1));
 		return;
 	}
@@ -1435,8 +1435,8 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h, ui32 answer ) co
 
 				SetGarrisons sg;
 				sg.garrs[h->id] = h->army;
-				sg.garrs[h->id].slots[slot].first = crid;
-				sg.garrs[h->id].slots[slot].second += creatures[0].first;
+				sg.garrs[h->id].slots[slot].setType(crid);
+				sg.garrs[h->id].slots[slot].count += creatures[0].first;
 
 				InfoWindow iw;
 				iw.player = h->tempOwner;
@@ -2282,13 +2282,14 @@ bool CArmedInstance::needsLastStack() const
 int CArmedInstance::getArmyStrength() const
 {
 	int ret = 0;
-	for(std::map<si32,std::pair<ui32,si32> >::const_iterator i=army.slots.begin(); i!=army.slots.end(); i++)
-		ret += VLC->creh->creatures[i->second.first].AIValue * i->second.second;
+	for(TSlots::const_iterator i = army.slots.begin(); i != army.slots.end(); i++)
+		ret += i->second.type->AIValue * i->second.count;
 	return ret;
 }
+
 ui64 CArmedInstance::getPower (TSlot slot) const
 {
-	return VLC->creh->creatures[army.getCreature(slot)].AIValue * army.getAmount(slot);
+	return army.getCreature(slot)->AIValue * army.getAmount(slot);
 }
 std::string CArmedInstance::getRoughAmount (TSlot slot) const
 {
@@ -2344,7 +2345,7 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
 			BlockingDialog ynd(true,false);
 			ynd.player = h->tempOwner;
 			std::string tmp = VLC->generaltexth->advobtxt[90];
-			boost::algorithm::replace_first(tmp,"%d",boost::lexical_cast<std::string>(army.slots.find(0)->second.second));
+			boost::algorithm::replace_first(tmp,"%d",boost::lexical_cast<std::string>(army.slots.find(0)->second.count));
 			boost::algorithm::replace_first(tmp,"%d",boost::lexical_cast<std::string>(action));
 			boost::algorithm::replace_first(tmp,"%s",VLC->creh->creatures[subID].namePl);
 			ynd.text << tmp;
@@ -2370,7 +2371,7 @@ void CGCreature::endBattle( BattleResult *result ) const
 		//cb->setAmount(id, army.slots.find(0)->second.second - killedAmount);	
 
 		MetaString ms;
-		int pom = CCreature::getQuantityID(army.slots.find(0)->second.second);
+		int pom = army.slots.find(0)->second.getQuantityID();
 		pom = 174 + 3*pom + 1;
 		ms << std::pair<ui8,ui32>(6,pom) << " " << std::pair<ui8,ui32>(7,subID);
 		cb->setHoverName(id,&ms);
@@ -2399,8 +2400,8 @@ void CGCreature::initObj()
 		break;
 	}
 
-	army.slots[0].first = subID;
-	si32 &amount = army.slots[0].second;
+	army.slots[0].setType(subID);
+	si32 &amount = army.slots[0].count;
 	CCreature &c = VLC->creh->creatures[subID];
 	if(!amount)
 		if(c.ammMax == c.ammMin)
@@ -2409,7 +2410,7 @@ void CGCreature::initObj()
 			amount = c.ammMin + (ran() % (c.ammMax - c.ammMin));
 
 	MetaString ms;
-	int pom = CCreature::getQuantityID(army.slots.find(0)->second.second);
+	int pom = army.slots.find(0)->second.getQuantityID();
 	pom = 174 + 3*pom + 1;
 	ms << std::pair<ui8,ui32>(6,pom) << " " << std::pair<ui8,ui32>(7,subID);
 	ms.toString(hoverName);
@@ -2446,11 +2447,12 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
 
 		int count = 0, //how many creatures of our kind has hero
 			totalCount = 0;
-		for (std::map<si32,std::pair<ui32,si32> >::const_iterator i = h->army.slots.begin(); i != h->army.slots.end(); i++)
+
+		for (TSlots::const_iterator i = h->army.slots.begin(); i != h->army.slots.end(); i++)
 		{
-			if(vstd::contains(myKindCres,i->second.first))
-				count += i->second.second;
-			totalCount += i->second.second;
+			if(vstd::contains(myKindCres,i->second.type->idNumber))
+				count += i->second.count;
+			totalCount += i->second.count;
 		}
 
 		if(count*2 > totalCount)
@@ -2465,7 +2467,7 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
 			if(h->getSecSkillLevel(4) + sympathy + 1 >= character)
 				return 0; //join for free
 			else if(h->getSecSkillLevel(4) * 2  +  sympathy  +  1 >= character)
-				return VLC->creh->creatures[subID].cost[6] * army.slots.find(0)->second.second; //join for gold
+				return VLC->creh->creatures[subID].cost[6] * army.slots.find(0)->second.count; //join for gold
 		}
 	}
 
@@ -2537,14 +2539,7 @@ void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) co
 			//add creatures
 			SetGarrisons sg;
 			sg.garrs[h->id] = h->army;
-			if(vstd::contains(h->army.slots,slot)) //add to already present stack
-			{
-				sg.garrs[h->id].slots[slot].second += army.slots.find(0)->second.second;
-			}
-			else //add as a new stack
-			{
-				sg.garrs[h->id].slots[slot] = army.slots.find(0)->second;
-			}
+			sg.garrs[h->id].addToSlot(slot, subID, army[0].count);
 			cb->sendAndApply(&sg);
 			cb->removeObject(id);
 		}
@@ -3231,10 +3226,10 @@ bool CQuest::checkQuest (const CGHeroInstance * h) const
 				{
 					for (count = 0, it = h->army.slots.begin(); it !=  h->army.slots.end(); ++it)
 					{
-						if (it->second.first == cre->second.first)
-							count += it->second.second;
+						if (it->second.type == cre->second.type)
+							count += it->second.count;
 					}
-					if (count < cre->second.second) //not enough creatures of this kind
+					if (count < cre->second.count) //not enough creatures of this kind
 						return false;
 				}
 			}
@@ -3323,24 +3318,20 @@ const std::string & CGSeerHut::getHoverText() const
 				ms.addReplacement(VLC->heroh->heroes[m13489val]->name);
 				break;
 			case MISSION_KILL_CREATURE:
-			{
-				const TStack* stack = cb->gameState()->map->monsters[m13489val]->army.getStack(0);
-				if (stack->first == 1)
-					ms.addReplacement (MetaString::CRE_SING_NAMES, stack->first);
-				else
-					ms.addReplacement (MetaString::CRE_PL_NAMES, stack->first);
-			}
+				{
+					ms.addReplacement(cb->gameState()->map->monsters[m13489val]->army[0]);
+				}
 				break;
 			case MISSION_ART:
-			{
-				MetaString loot;
-				for (std::vector<ui16>::const_iterator it = m5arts.begin(); it != m5arts.end(); ++it)
 				{
-					loot << "%s";
-					loot.addReplacement (MetaString::ART_NAMES, *it);
+					MetaString loot;
+					for (std::vector<ui16>::const_iterator it = m5arts.begin(); it != m5arts.end(); ++it)
+					{
+						loot << "%s";
+						loot.addReplacement (MetaString::ART_NAMES, *it);
+					}
+					ms.addReplacement(loot.buildList());
 				}
-				ms.addReplacement(loot.buildList());
-			}
 				break;
 			case MISSION_ARMY:
 			case MISSION_RESOURCES:
@@ -3418,18 +3409,15 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
 					iw.text.addReplacement(VLC->heroh->heroes[m13489val]->name);
 					break;
 				case MISSION_KILL_CREATURE:
-				{
-					const TStack* stack = cb->gameState()->map->monsters[m13489val]->army.getStack(0);
-					iw.components.push_back (Component (Component::CREATURE, stack->first, stack->second, 0));
-					if (stack->first == 1)
-						iw.text.addReplacement (MetaString::CRE_SING_NAMES, stack->first);
-					else
-						iw.text.addReplacement (MetaString::CRE_PL_NAMES, stack->first);
-					if (std::count(firstVisitText.begin(), firstVisitText.end(), '%') == 2) //say where is placed monster
 					{
-						iw.text.addReplacement (VLC->generaltexth->arraytxt[147+checkDirection()]);
+						CStackInstance stack = cb->gameState()->map->monsters[m13489val]->army[0];
+						iw.components.push_back (Component(stack));
+						iw.text.addReplacement(stack);
+						if (std::count(firstVisitText.begin(), firstVisitText.end(), '%') == 2) //say where is placed monster
+						{
+							iw.text.addReplacement (VLC->generaltexth->arraytxt[147+checkDirection()]);
+						}
 					}
-				}
 					break;
 				case MISSION_ART:
 				{
@@ -3448,12 +3436,9 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
 					MetaString loot;
 					for (TSlots::const_iterator it = m6creatures.begin(); it != m6creatures.end(); ++it)
 					{
-						iw.components.push_back (Component (Component::CREATURE, it->second.first, it->second.second, 0));
+						iw.components.push_back(Component(it->second));
 						loot << "%s";
-						if (it->second.second == 1)
-							loot.addReplacement (MetaString::CRE_SING_NAMES, it->second.first);
-						else
-							loot.addReplacement (MetaString::CRE_PL_NAMES, it->second.first);
+						loot.addReplacement(it->second);
 					}
 					iw.text.addReplacement	(loot.buildList());
 				}
@@ -3531,12 +3516,9 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
 					MetaString loot;
 					for (TSlots::const_iterator it = m6creatures.begin(); it != m6creatures.end(); ++it)
 					{
-						bd.components.push_back (Component (Component::CREATURE, it->second.first, it->second.second, 0));
+						bd.components.push_back(Component(it->second));
 						loot << "%s";
-						if (it->second.second == 1)
-							loot.addReplacement (MetaString::CRE_SING_NAMES, it->second.first);
-						else
-							loot.addReplacement (MetaString::CRE_PL_NAMES, it->second.first);
+						loot.addReplacement(it->second);
 					}
 					bd.text.addReplacement	(loot.buildList());
 				}
@@ -3565,11 +3547,7 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
 					break;
 				case MISSION_KILL_CREATURE:
 				{
-					const TStack* stack = cb->gameState()->map->monsters[m13489val]->army.getStack(0);
-					if (stack->first == 1)
-						bd.text.addReplacement (MetaString::CRE_SING_NAMES, stack->first);
-					else
-						bd.text.addReplacement (MetaString::CRE_PL_NAMES, stack->first);
+					bd.text.addReplacement(cb->gameState()->map->monsters[m13489val]->army[0]);
 					if (std::count(firstVisitText.begin(), firstVisitText.end(), '%') == 2) //say where is placed monster
 					{
 						bd.text.addReplacement (VLC->generaltexth->arraytxt[147+checkDirection()]);
@@ -3893,9 +3871,9 @@ void CGBonusingObject::onHeroVisit( const CGHeroInstance * h ) const
 	case 94: //Stables TODO: upgrade Cavaliers
 		sound = soundBase::horse20;
 		std::set<ui32> slots;
-		for (std::map<si32,std::pair<ui32,si32> >::const_iterator i = h->army.slots.begin(); i != h->army.slots.end(); ++i)
+		for (TSlots::const_iterator i = h->army.slots.begin(); i != h->army.slots.end(); ++i)
 		{
-			if(i->second.first == 10)
+			if(i->second.type->idNumber == 10)
 				slots.insert(i->first);
 		}
 		if (!slots.empty())
@@ -4232,17 +4210,14 @@ void CGPandoraBox::giveContents( const CGHeroInstance *h, bool afterBattle ) con
 	{ //this part is taken straight from creature bank
 		MetaString loot; 
 		CCreatureSet ourArmy = creatures;
-		for (std::map<si32,std::pair<ui32,si32> >::const_iterator i = ourArmy.slots.begin(); i != ourArmy.slots.end(); i++)
+		for(TSlots::const_iterator i = ourArmy.slots.begin(); i != ourArmy.slots.end(); i++)
 		{ //build list of joined creatures
-			iw.components.push_back (Component(Component::CREATURE, i->second.first, i->second.second, 0));
+			iw.components.push_back(Component(i->second));
 			loot << "%s";
-			if (i->second.second == 1)
-				loot.addReplacement (MetaString::CRE_SING_NAMES, i->second.first);
-			else
-				loot.addReplacement (MetaString::CRE_PL_NAMES, i->second.first);
+			loot.addReplacement(i->second);
 		}
 
-		if (ourArmy.slots.size() == 1 && ourArmy.slots.begin()->second.second == 1)
+		if (ourArmy.slots.size() == 1 && ourArmy.slots.begin()->second.count == 1)
 			iw.text.addTxt (MetaString::ADVOB_TXT, 185);
 		else
 			iw.text.addTxt (MetaString::ADVOB_TXT, 186);
@@ -5063,23 +5038,19 @@ void CBank::endBattle (const CGHeroInstance *h, const BattleResult *result) cons
 		CCreatureSet ourArmy;
 		for (std::vector< std::pair <ui16, ui32> >::const_iterator it = bc->creatures.begin(); it != bc->creatures.end(); it++)
 		{
-			int slot = ourArmy.getSlotFor (it->first);
-			ourArmy.slots[slot].first = it->first;
-			ourArmy.slots[slot].second += it->second;
+			int slot = ourArmy.getSlotFor(it->first);
+			ourArmy.addToSlot(slot, it->first, it->second);
 		}
-		for (std::map<si32,std::pair<ui32,si32> >::const_iterator i = ourArmy.slots.begin(); i != ourArmy.slots.end(); i++)
+		for (TSlots::const_iterator i = ourArmy.slots.begin(); i != ourArmy.slots.end(); i++)
 		{
-			iw.components.push_back (Component(Component::CREATURE, i->second.first, i->second.second, 0));
+			iw.components.push_back(Component(i->second));
 			loot << "%s";
-			if (i->second.second == 1)
-				loot.addReplacement (MetaString::CRE_SING_NAMES, i->second.first);
-			else
-				loot.addReplacement (MetaString::CRE_PL_NAMES, i->second.first);
+			loot.addReplacement (i->second);
 		}
 
 		if (ourArmy.slots.size())
 		{
-			if (ourArmy.slots.size() == 1 && ourArmy.slots.begin()->second.second == 1)
+			if (ourArmy.slots.size() == 1 && ourArmy.slots.begin()->second.count == 1)
 				iw.text.addTxt (MetaString::ADVOB_TXT, 185);
 			else
 				iw.text.addTxt (MetaString::ADVOB_TXT, 186);
@@ -5351,13 +5322,13 @@ void CGSirens::onHeroVisit( const CGHeroInstance * h ) const
 		int xp = 0;
 		SetGarrisons sg;
 		sg.garrs[h->id] = h->army;
-		for (std::map<si32,std::pair<ui32,si32> >::const_iterator i = h->army.slots.begin(); i != h->army.slots.end(); i++)
+		for (TSlots::const_iterator i = h->army.slots.begin(); i != h->army.slots.end(); i++)
 		{
-			int drown = (int)(i->second.second * 0.3);
+			int drown = (int)(i->second.count * 0.3);
 			if(drown)
 			{
-				sg.garrs[h->id].slots[i->first].second -= drown;
-				xp += drown * VLC->creh->creatures[i->second.first].hitPoints;
+				sg.garrs[h->id].slots[i->first].count -= drown;
+				xp += drown * i->second.type->hitPoints;
 			}
 		}
 

+ 2 - 2
hch/CObjectHandler.h

@@ -341,8 +341,8 @@ public:
 	int getTotalStrength() const;
 	ui8 getSpellSchoolLevel(const CSpell * spell) const; //returns level on which given spell would be cast by this hero (0 - none, 1 - basic etc)
 	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
-	std::pair<ui32, si32> calculateNecromancy (const BattleResult &battleResult) const;
-	void showNecromancyDialog (std::pair<ui32, si32> raisedStack) const;
+	CStackInstance calculateNecromancy (const BattleResult &battleResult) const;
+	void showNecromancyDialog(const CStackInstance &raisedStack) const;
 
 	//////////////////////////////////////////////////////////////////////////
 

+ 1 - 119
int3.h

@@ -1,8 +1,6 @@
 #ifndef __INT3_H__
 #define __INT3_H__
-#include <map>
-#include <vector>
-#include <cmath>
+
 
 /*
  * int3.h, part of VCMI engine
@@ -14,122 +12,6 @@
  *
  */
 
-class CCreature;
-
-//a few typedefs for CCreatureSet
-typedef si32 TSlot, TQuantity;
-typedef ui32 TCreature;
-typedef std::pair<TCreature, TQuantity> TStack;
-typedef std::map<TSlot, TStack> TSlots;
-
-class CCreatureSet //seven combined creatures
-{
-public:
-	TSlots slots; //slots[slot_id]=> pair(creature_id,creature_quantity)
-	ui8 formation; //false - wide, true - tight
-
-	int getCreature (TSlot slot) const //workaround of map issue
-	{
-		std::map<TSlot, TStack>::const_iterator i = slots.find(slot);
-		if (i != slots.end())
-			return i->second.first;
-		else
-			return -1;
-	}
-	int getAmount (TSlot slot) const
-	{
-		std::map<TSlot, TStack>::const_iterator i = slots.find(slot);
-		if (i != slots.end())
-			return i->second.second;
-		else
-			return -1;
-	}
-	const TStack* getStack (TSlot slot) const
-	{
-		std::map<TSlot, TStack>::const_iterator i = slots.find(slot);
-		if (i != slots.end())
-			return &(i->second);
-		else
-			return NULL;
-	}
-	bool setCreature (TSlot slot, TCreature type, TQuantity quantity) //slots 0 to 6
-	{
-		slots[slot] = TStack(type, quantity);  //brutal force
-		if (quantity == 0)
-			slots.erase(slot);
-		if (slots.size() > 7) return false;
-		else return true;
-	}
-	TSlot getSlotFor(TCreature creature, ui32 slotsAmount=7) const //returns -1 if no slot available
-	{	
-		for(TSlots::const_iterator i=slots.begin(); i!=slots.end(); ++i)
-		{
-			if(i->second.first == creature)
-			{
-				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())
-			{
-				return i; //return first free slot
-			}
-		}
-		return -1; //no slot available
-	}
-	bool mergableStacks(std::pair<TSlot, TSlot> &out, TSlot preferable = -1) //looks for two same stacks, returns slot positions
-	{
-		//try to match creature to our preferred stack
-		if(preferable >= 0  &&  slots.find(preferable) != slots.end())
-		{
-			TCreature id = slots[preferable].first;
-			for(TSlots::const_iterator j=slots.begin(); j!=slots.end(); ++j)
-			{
-				if(id == j->second.first && j->first != preferable)
-				{
-					out.first = preferable;
-					out.second = j->first;
-					return true;
-				}
-			}
-		}
-
-		for(TSlots::const_iterator i=slots.begin(); i!=slots.end(); ++i)
-		{
-			for(TSlots::const_iterator j=slots.begin(); j!=slots.end(); ++j)
-			{
-				if(i->second.first == j->second.first  &&  i->first != j->first)
-				{
-					out.first = i->first;
-					out.second = j->first;
-					return true;
-				}
-			}
-		}
-		return false;
-	}
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & slots & formation;
-	}
-	operator bool() const
-	{
-		return slots.size() > 0;
-	}
-	void sweep()
-	{
-		for(TSlots::iterator i=slots.begin(); i!=slots.end(); ++i)
-		{
-			if(!i->second.second)
-			{
-				slots.erase(i);
-				sweep();
-				break;
-			}
-		}
-	}
-};
 
 class int3
 {

+ 26 - 13
lib/CGameState.cpp

@@ -322,6 +322,17 @@ DLL_EXPORT std::string MetaString::buildList () const
 	return lista;
 }
 
+void MetaString::addReplacement(const CStackInstance &stack)
+{
+	assert(stack.count); //valid count
+	assert(stack.type); //valid type
+
+	if (stack.count == 1)
+		addReplacement (CRE_SING_NAMES, stack.type->idNumber);
+	else
+		addReplacement (CRE_PL_NAMES, stack.type->idNumber);
+}
+
 static CGObjectInstance * createObject(int id, int subid, int3 pos, int owner)
 {
 	CGObjectInstance * nobj;
@@ -1125,14 +1136,16 @@ std::pair<int,int> CGameState::pickObject (CGObjectInstance *obj)
 void randomizeArmy(CArmedInstance * army, int type)
 {
 	int max = VLC->creh->creatures.size();
-	for (std::map<si32,std::pair<ui32,si32> >::iterator j=army->army.slots.begin(); j!=army->army.slots.end();j++)
+	for (TSlots::iterator j=army->army.slots.begin(); j!=army->army.slots.end();j++)
 	{
-		if(j->second.first > max)
+		if(j->second.idRand > max)
 		{
-			if(j->second.first % 2)
-				j->second.first = VLC->townh->towns[type].basicCreatures[ (j->second.first-197) / 2 -1];
+			if(j->second.idRand % 2)
+				j->second.setType(VLC->townh->towns[type].basicCreatures[(j->second.idRand-197) / 2 -1]);
 			else
-				j->second.first = VLC->townh->towns[type].upgradedCreatures[ (j->second.first-197) / 2 -1];
+				j->second.setType(VLC->townh->towns[type].upgradedCreatures[(j->second.idRand-197) / 2 -1]);
+
+			j->second.idRand = -1;
 		}
 	}
 	return;
@@ -1743,7 +1756,7 @@ const CGHeroInstance * CGameState::battleGetOwner(int stackID)
 UpgradeInfo CGameState::getUpgradeInfo(const CArmedInstance *obj, int stackPos)
 {
 	UpgradeInfo ret;
-	const CCreature *base = &VLC->creh->creatures[obj->army.slots.find(stackPos)->second.first];
+	const CCreature *base = obj->army.slots.find(stackPos)->second.type;
 	if((obj->ID == TOWNI_TYPE)  ||  ((obj->ID == HEROI_TYPE) && static_cast<const CGHeroInstance*>(obj)->visitedTown))
 	{
 		const CGTownInstance * t;
@@ -2574,8 +2587,8 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange(const CStack* attacker, cons
 	}
 
 	//damage cannot be less than 1
-	amin(returnedVal.first, 1);
-	amin(returnedVal.second, 1);
+	amax(returnedVal.first, 1);
+	amax(returnedVal.second, 1);
 
 	return returnedVal;
 }
@@ -2999,8 +3012,8 @@ int CGameState::victoryCheck( ui8 player ) const
 						&&  (ai = dynamic_cast<const CArmedInstance*>(map->objects[i]))) //contains army
 					{
 						for(TSlots::const_iterator i=ai->army.slots.begin(); i!=ai->army.slots.end(); ++i) //iterate through army
-							if(i->second.first == map->victoryCondition.ID) //it's searched creature
-								total += i->second.second;
+							if(i->second.type->idNumber == map->victoryCondition.ID) //it's searched creature
+								total += i->second.count;
 					}
 				}
 
@@ -3292,7 +3305,7 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
 			{
 				for(TSlots::const_iterator it = g->second.heroes[b]->army.slots.begin(); it != g->second.heroes[b]->army.slots.end(); ++it)
 				{
-					int toCmp = it->second.first; //ID of creature we should compare with the best one
+					int toCmp = it->second.type->idNumber; //ID of creature we should compare with the best one
 					if(bestCre == -1 || VLC->creh->creatures[bestCre].AIValue < VLC->creh->creatures[toCmp].AIValue)
 					{
 						bestCre = toCmp;
@@ -3755,9 +3768,9 @@ void InfoAboutHero::initFromHero( const CGHeroInstance *h, bool detailed )
 	else
 	{
 		//hide info about hero stacks counts using descriptives names ids
-		for(std::map<si32,std::pair<ui32,si32> >::iterator i = army.slots.begin(); i != army.slots.end(); ++i)
+		for(TSlots::iterator i = army.slots.begin(); i != army.slots.end(); ++i)
 		{
-			i->second.second = CCreature::getQuantityID(i->second.second);
+			i->second.count = i->second.getQuantityID();
 		}
 	}
 }

+ 11 - 2
lib/NetPacks.h

@@ -123,6 +123,7 @@ public:
 		message.push_back(TREPLACE_NUMBER);
 		numbers.push_back(txt);
 	}
+	DLL_EXPORT void addReplacement(const CStackInstance &stack); //adds sing or plural name;
 	DLL_EXPORT std::string buildList () const;
 	void clear()
 	{
@@ -679,8 +680,16 @@ struct Component : public CPack //2002 helper for object scrips informations
 	{
 		h & id & subtype & val & when;
 	}
-	Component(){type = 2002;};
-	Component(ui16 Type, ui16 Subtype, si32 Val, si16 When):id(Type),subtype(Subtype),val(Val),when(When){type = 2002;};
+	Component()
+	{
+		type = 2002;
+	}
+	DLL_EXPORT explicit Component(const CStackInstance &stack);
+	Component(ui16 Type, ui16 Subtype, si32 Val, si16 When)
+		:id(Type),subtype(Subtype),val(Val),when(When)
+	{
+		type = 2002;
+	}
 };
 
 struct InfoWindow : public CPackForClient //103  - displays simple info window

+ 8 - 2
lib/NetPacksLib.cpp

@@ -172,7 +172,7 @@ DLL_EXPORT void SetAvailableHeroes::applyGs( CGameState *gs )
 	if(h  &&  flags & 1)
 	{
 		h->army.slots.clear();
-		h->army.slots[0] = std::pair<ui32,si32>(VLC->creh->nameToID[h->type->refTypeStack[0]],1);
+		h->army.slots[0] = CStackInstance(VLC->creh->nameToID[h->type->refTypeStack[0]],1);
 	}
 
 	h = (hid2>=0 ?  gs->hpool.heroesPool[hid2] : NULL);
@@ -180,7 +180,7 @@ DLL_EXPORT void SetAvailableHeroes::applyGs( CGameState *gs )
 	if(flags & 2)
 	{
 		h->army.slots.clear();
-		h->army.slots[0] = std::pair<ui32,si32>(VLC->creh->nameToID[h->type->refTypeStack[0]],1);
+		h->army.slots[0] = CStackInstance(VLC->creh->nameToID[h->type->refTypeStack[0]],1);
 	}
 }
 
@@ -1221,4 +1221,10 @@ DLL_EXPORT void YourTurn::applyGs( CGameState *gs )
 DLL_EXPORT void SetSelection::applyGs( CGameState *gs )
 {
 	gs->getPlayer(player)->currentSelection = id;
+}
+
+DLL_EXPORT Component::Component(const CStackInstance &stack)
+	:id(CREATURE), subtype(stack.type->idNumber), val(stack.count), when(0)
+{
+
 }

+ 25 - 34
lib/map.cpp

@@ -143,42 +143,32 @@ static EDefType getDefType(CGDefInfo * a)
 
 static CCreatureSet readCreatureSet(const unsigned char * bufor, int &i, int number, bool version) //version==true for >RoE maps
 {
-	if(version)
-	{
-		CCreatureSet ret;
-		std::pair<ui32,si32> ins;
-		for(int ir=0;ir<number;ir++)
-		{
-			int rettt = readNormalNr(bufor,i+ir*4, 2);
-			if(rettt==0xffff) continue;
-			if(rettt>65520)
-				rettt = 65536-rettt+VLC->creh->creatures.size();//this will happens when random object has random army
-			ins.first = rettt;
-			ins.second = readNormalNr(bufor,i+ir*4+2, 2);
-			std::pair<si32,std::pair<ui32,si32> > tt(ir,ins);
-			ret.slots.insert(tt);
-		}
-		i+=number*4;
-		return ret;
-	}
-	else
+	const int bytesPerCre = version ? 4 : 3,
+		idBytes = version ? 2 : 1,
+		maxID = version ? 0xffff : 0xff;
+
+	CCreatureSet ret;
+	for(int ir=0;ir < number; ir++)
 	{
-		CCreatureSet ret;
-		std::pair<ui32,si32> ins;
-		for(int ir=0;ir<number;ir++)
+		int creID = readNormalNr(bufor,i+ir*bytesPerCre, idBytes);
+		int count = readNormalNr(bufor,i+ir*bytesPerCre+idBytes, 2);
+
+		if(creID == maxID) //empty slot
+			continue;
+		if(creID > maxID - 0xf)
 		{
-			int rettt = readNormalNr(bufor,i+ir*3, 1);
-			if(rettt==0xff) continue;
-			if(rettt>240)
-				rettt = 256-rettt+VLC->creh->creatures.size();
-			ins.first = rettt;
-			ins.second = readNormalNr(bufor,i+ir*3+1, 2);
-			std::pair<si32,std::pair<ui32,si32> > tt(ir,ins);
-			ret.slots.insert(tt);
+			creID = maxID + 1 - creID + VLC->creh->creatures.size();//this will happen when random object has random army
+			ret.slots[ir].idRand = creID;
 		}
-		i+=number*3;
-		return ret;
+		else
+			ret.slots[ir].setType(creID);
+
+		ret.slots[ir].count = count;
 	}
+	i+=number*bytesPerCre;
+	
+	ret.validTypes(true);
+	return ret;
 }
 CMapHeader::CMapHeader(const unsigned char *map)
 {
@@ -900,6 +890,7 @@ void Mapa::loadHero( CGObjectInstance * &nobj, const unsigned char * bufor, int
 	}
 	if(readChar(bufor,i))//true if hero has nonstandard garrison
 		nhi->army = readCreatureSet(bufor,i,7,(version>RoE));
+
 	nhi->army.formation =bufor[i]; ++i; //formation
 	bool artSet = bufor[i]; ++i; //true if artifact set is not default (hero has some artifacts)
 	int artmask = version == RoE ? 0xff : 0xffff;
@@ -1494,7 +1485,7 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
 					cre->identifier = readNormalNr(bufor,i); i+=4;
 					monsters[cre->identifier] = cre;
 				}
-				cre->army.slots[0].second = readNormalNr(bufor,i, 2); i+=2;
+				cre->army.slots[0].count = readNormalNr(bufor,i, 2); i+=2;
 				cre->character = bufor[i]; ++i;
 				bool isMesTre = bufor[i]; ++i; //true if there is message or treasury
 				if(isMesTre)
@@ -2046,7 +2037,7 @@ void Mapa::loadQuest(CQuest * guard, const unsigned char * bufor, int & i)
 			{
 				ui32 creType = readNormalNr(bufor,i, 2); i+=2;
 				ui32 creNumb = readNormalNr(bufor,i, 2); i+=2;
-				guard->m6creatures[hh] = std::make_pair(creType,creNumb);
+				guard->m6creatures[hh] = CStackInstance(creType,creNumb);
 			}
 			break;
 		}

+ 43 - 51
server/CGameHandler.cpp

@@ -309,10 +309,10 @@ static CCreatureSet takeCasualties(int color, const CCreatureSet &set, BattleInf
 			continue;
 
 		CStack *st = bat->stacks[i];
-		if(st->owner==color && vstd::contains(set.slots,st->slot) && st->amount < set.slots.find(st->slot)->second.second)
+		if(st->owner==color && vstd::contains(set.slots,st->slot) && st->amount < set.slots.find(st->slot)->second.count)
 		{
 			if(st->alive())
-				ret.slots[st->slot].second = st->amount;
+				ret.slots[st->slot].count = st->amount;
 			else
 				ret.slots.erase(st->slot);
 		}
@@ -577,12 +577,12 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
 	const CGHeroInstance *winnerHero = battleResult.data->winner != 0 ? hero2 : hero1;
 	if (winnerHero) 
 	{
-		std::pair<ui32, si32> raisedStack = winnerHero->calculateNecromancy(*battleResult.data);
+		CStackInstance raisedStack = winnerHero->calculateNecromancy(*battleResult.data);
 
 		// Give raised units to winner and show dialog, if any were raised.
-		if (raisedStack.first != -1) 
+		if (raisedStack.type) 
 		{
-			int slot = winnerHero->army.getSlotFor(raisedStack.first);
+			int slot = winnerHero->army.getSlotFor(raisedStack.type->idNumber);
 
 			if (slot != -1) 
 			{
@@ -590,7 +590,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
 
 				sg.garrs[winnerHero->id] = winnerHero->army;
 				if (vstd::contains(winnerHero->army.slots, slot)) // Add to existing stack.
-					sg.garrs[winnerHero->id].slots[slot].second += raisedStack.second;
+					sg.garrs[winnerHero->id].slots[slot].count += raisedStack.count;
 				else // Create a new stack.
 					sg.garrs[winnerHero->id].slots[slot] = raisedStack;
 				winnerHero->showNecromancyDialog(raisedStack);
@@ -1039,8 +1039,6 @@ void CGameHandler::run(bool resume)
 	{
 		if(!resume)
 			newTurn();
-		else
-			resume = false;
 
 		std::map<ui8,PlayerState>::iterator i;
 		if(!resume)
@@ -1048,6 +1046,7 @@ void CGameHandler::run(bool resume)
 		else
 			i = gs->players.find(gs->currentPlayer);
 
+		resume = false;
 		for(; i != gs->players.end(); i++)
 		{
 
@@ -1059,12 +1058,11 @@ void CGameHandler::run(bool resume)
 				continue; 
 			}
 			states.setFlag(i->first,&PlayerStatus::makingTurn,true);
-			gs->currentPlayer = i->first;
+
 			{
 				YourTurn yt;
 				yt.player = i->first;
-				boost::unique_lock<boost::mutex> lock(*connections[i->first]->wmx);
-				*connections[i->first] << &yt;    
+				sendAndApply(&yt);
 			}
 
 			//wait till turn is done
@@ -1151,7 +1149,7 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 	//battleStartpos read
 
 	int k = 0; //stack serial 
-	for(std::map<si32,std::pair<ui32,si32> >::const_iterator i = army1.slots.begin(); i!=army1.slots.end(); i++, k++)
+	for(TSlots::const_iterator i = army1.slots.begin(); i!=army1.slots.end(); i++, k++)
 	{
 		int pos;
 		if(creatureBank)
@@ -1161,12 +1159,12 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 		else
 			pos = attackerLoose[army1.slots.size()-1][k];
 
-		CStack * stack = curB->generateNewStack(hero1, i->second.first, i->second.second, stacks.size(), true, i->first, gs->map->terrain[tile.x][tile.y][tile.z].tertype, pos);
+		CStack * stack = curB->generateNewStack(hero1, i->second.type->idNumber, i->second.count, stacks.size(), true, i->first, gs->map->terrain[tile.x][tile.y][tile.z].tertype, pos);
 		stacks.push_back(stack);
 	}
 	
 	k = 0;
-	for(std::map<si32,std::pair<ui32,si32> >::const_iterator i = army2.slots.begin(); i!=army2.slots.end(); i++, k++)
+	for(TSlots::const_iterator i = army2.slots.begin(); i!=army2.slots.end(); i++, k++)
 	{
 		int pos;
 		if(creatureBank)
@@ -1176,7 +1174,7 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 		else
 			pos = defenderLoose[army2.slots.size()-1][k];
 
-		CStack * stack = curB->generateNewStack(hero2, i->second.first, i->second.second, stacks.size(), false, i->first, gs->map->terrain[tile.x][tile.y][tile.z].tertype, pos);
+		CStack * stack = curB->generateNewStack(hero2, i->second.type->idNumber, i->second.count, stacks.size(), false, i->first, gs->map->terrain[tile.x][tile.y][tile.z].tertype, pos);
 		stacks.push_back(stack);
 	}
 
@@ -1778,11 +1776,10 @@ void CGameHandler::giveCreatures (int objid, const CGHeroInstance * h, CCreature
 	CCreatureSet heroArmy = h->army;
 	while (creatures.slots.size() > 0)
 	{
-		int slot = heroArmy.getSlotFor (creatures.slots.begin()->second.first);
+		int slot = heroArmy.getSlotFor(creatures.slots.begin()->second.type->idNumber);
 		if (slot < 0)
 			break;
-		heroArmy.slots[slot].first = creatures.slots.begin()->second.first;
-		heroArmy.slots[slot].second += creatures.slots.begin()->second.second;
+		heroArmy.addToSlot(slot, creatures.slots.begin()->second);
 		creatures.slots.erase (creatures.slots.begin());
 	}
 
@@ -1809,11 +1806,12 @@ void CGameHandler::takeCreatures (int objid, TSlots creatures) //probably we cou
 	CCreatureSet newArmy = obj->army;
 	while (creatures.size() > 0)
 	{
-		int slot = newArmy.getSlotFor (creatures.begin()->second.first);
+		int slot = newArmy.getSlotFor(creatures.begin()->second.type->idNumber);
 		if (slot < 0)
 			break;
-		newArmy.slots[slot].first = creatures.begin()->second.first;
-		newArmy.slots[slot].second -= creatures.begin()->second.second;
+
+		newArmy.slots[slot].type = creatures.begin()->second.type;
+		newArmy.slots[slot].count -= creatures.begin()->second.count;
 		creatures.erase (creatures.begin());
 	}
 	SetGarrisons sg;
@@ -2242,20 +2240,20 @@ bool CGameHandler::arrangeStacks( si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2,
 		std::swap(S1.slots[p1],S2.slots[p2]); //swap slots
 
 		//if one of them is empty, remove entry
-		if(!S1.slots[p1].second)
+		if(!S1.slots[p1].count)
 			S1.slots.erase(p1);
-		if(!S2.slots[p2].second)
+		if(!S2.slots[p2].count)
 			S2.slots.erase(p2);
 	}
 	else if(what==2)//merge
 	{
-		if(S1.slots[p1].first != S2.slots[p2].first) //not same creature
+		if(S1.slots[p1].type != S2.slots[p2].type) //not same creature
 		{
 			complain("Cannot merge different creatures stacks!");
 			return false; 
 		}
 
-		S2.slots[p2].second += S1.slots[p1].second;
+		S2.slots[p2].count += S1.slots[p1].count;
 		S1.slots.erase(p1);
 	}
 	else if(what==3) //split
@@ -2270,30 +2268,30 @@ bool CGameHandler::arrangeStacks( si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2,
 
 		if(vstd::contains(S2.slots,p2))	 //dest. slot not free - it must be "rebalancing"...
 		{
-			int total = S1.slots[p1].second + S2.slots[p2].second;
+			int total = S1.slots[p1].count + S2.slots[p2].count;
 			if( (total < val   &&   complain("Cannot split that stack, not enough creatures!"))
-				|| (S2.slots[p2].first != S1.slots[p1].first && complain("Cannot rebalance different creatures stacks!"))
+				|| (S2.slots[p2].type != S1.slots[p1].type && complain("Cannot rebalance different creatures stacks!"))
 			)
 			{
 				return false; 
 			}
 			
-			S2.slots[p2].second = val;
-			S1.slots[p1].second = total - val;
+			S2.slots[p2].count = val;
+			S1.slots[p1].count = total - val;
 		}
 		else //split one stack to the two
 		{
-			if(S1.slots[p1].second < val)//not enough creatures
+			if(S1.slots[p1].count < val)//not enough creatures
 			{
 				complain("Cannot split that stack, not enough creatures!");
 				return false; 
 			}
-			S2.slots[p2].first = S1.slots[p1].first;
-			S2.slots[p2].second = val;
-			S1.slots[p1].second -= val;
+			S2.slots[p2].type = S1.slots[p1].type;
+			S2.slots[p2].count = val;
+			S1.slots[p1].count -= val;
 		}
 
-		if(!S1.slots[p1].second) //if we've moved all creatures
+		if(!S1.slots[p1].count) //if we've moved all creatures
 			S1.slots.erase(p1);
 	}
 	if((s1->needsLastStack() && !S1.slots.size()) //it's not allowed to take last stack from hero army!
@@ -2519,14 +2517,8 @@ bool CGameHandler::recruitCreatures( si32 objid, ui32 crid, ui32 cram )
 
 	SetGarrisons sg;
 	sg.garrs[dst->id] = dst->army;
-	if(sg.garrs[dst->id].slots.find(slot) == sg.garrs[dst->id].slots.end()) //take a free slot
-	{
-		sg.garrs[dst->id].slots[slot] = std::make_pair(crid,cram);
-	}
-	else //add creatures to a already existing stack
-	{
-		sg.garrs[dst->id].slots[slot].second += cram;
-	}
+	sg.garrs[dst->id] .addToSlot(slot, crid, cram);
+
 	sendAndApply(&sr); 
 	sendAndApply(&sac);
 	sendAndApply(&sg);
@@ -2538,7 +2530,7 @@ bool CGameHandler::upgradeCreature( ui32 objid, ui8 pos, ui32 upgID )
 	CArmedInstance *obj = static_cast<CArmedInstance*>(gs->map->objects[objid]);
 	UpgradeInfo ui = gs->getUpgradeInfo(obj,pos);
 	int player = obj->tempOwner;
-	int crQuantity = obj->army.slots[pos].second;
+	int crQuantity = obj->army.slots[pos].count;
 
 	//check if upgrade is possible
 	if((ui.oldID<0 || !vstd::contains(ui.newID,upgID)) && complain("That upgrade is not possible!")) 
@@ -2575,7 +2567,7 @@ bool CGameHandler::upgradeCreature( ui32 objid, ui8 pos, ui32 upgID )
 	//upgrade creature
 	SetGarrisons sg;
 	sg.garrs[objid] = obj->army;
-	sg.garrs[objid].slots[pos].first = upgID;
+	sg.garrs[objid].slots[pos].setType(upgID);
 	sendAndApply(&sg);	
 	return true;
 }
@@ -2588,7 +2580,7 @@ bool CGameHandler::garrisonSwap( si32 tid )
 		CCreatureSet csn = town->visitingHero->army, cso = town->army;
 		while(!cso.slots.empty())//while there are unmoved creatures
 		{
-			int pos = csn.getSlotFor(cso.slots.begin()->second.first);
+			int pos = csn.getSlotFor(cso.slots.begin()->second.type->idNumber);
 			if(pos<0)
 			{
 				//try to merge two other stacks to make place
@@ -2596,7 +2588,7 @@ bool CGameHandler::garrisonSwap( si32 tid )
 				if(csn.mergableStacks(toMerge, cso.slots.begin()->first))
 				{
 					//merge
-					csn.slots[toMerge.second].second += csn.slots[toMerge.first].second;
+					csn.slots[toMerge.second].count += csn.slots[toMerge.first].count;
 					csn.slots[toMerge.first] = cso.slots.begin()->second;
 				}
 				else
@@ -2607,12 +2599,12 @@ bool CGameHandler::garrisonSwap( si32 tid )
 			}
 			else if(csn.slots.find(pos) != csn.slots.end()) //add creatures to the existing stack
 			{
-				csn.slots[pos].second += cso.slots.begin()->second.second;
+				csn.slots[pos].count += cso.slots.begin()->second.count;
 			}
 			else //move stack on the free pos
 			{
-				csn.slots[pos].first = cso.slots.begin()->second.first;
-				csn.slots[pos].second = cso.slots.begin()->second.second;
+				csn.slots[pos].type = cso.slots.begin()->second.type;
+				csn.slots[pos].count = cso.slots.begin()->second.count;
 			}
 			cso.slots.erase(cso.slots.begin());
 		}
@@ -3350,7 +3342,7 @@ void CGameHandler::playerMessage( ui8 player, const std::string &message )
 		sg.garrs[hero->id] = hero->army;
 		for(int i=0;i<7;i++)
 			if(!vstd::contains(sg.garrs[hero->id].slots,i))
-				sg.garrs[hero->id].slots[i] = std::pair<ui32,si32>(13,5);
+				sg.garrs[hero->id].slots[i] = CStackInstance(13,5);
 		sendAndApply(&sg);
 	}
 	else if(message == "vcmiangband") //gives 10 black knight into each slot
@@ -3361,7 +3353,7 @@ void CGameHandler::playerMessage( ui8 player, const std::string &message )
 		sg.garrs[hero->id] = hero->army;
 		for(int i=0;i<7;i++)
 			if(!vstd::contains(sg.garrs[hero->id].slots,i))
-				sg.garrs[hero->id].slots[i] = std::pair<ui32,si32>(66,10);
+				sg.garrs[hero->id].slots[i] = CStackInstance(66,10);
 		sendAndApply(&sg);
 	}
 	else if(message == "vcminoldor") //all war machines