Browse Source

New bonus system. Unified HeroBonus and StackFeature. Still early version, will need improvements and development.
If you encounter any new crashes / bugs / unacceptable slowdowns, please PM me.

Michał W. Urbańczyk 15 years ago
parent
commit
a14606f32d

+ 28 - 27
AI/GeniusAI/BattleLogic.cpp

@@ -41,7 +41,7 @@ ui8 side; //who made this action: false - left, true - right player
 /**
  *	Implementation of CBattleLogic class.
  */
-CBattleLogic::CBattleLogic(ICallback *cb,  CCreatureSet *army1, CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side) :
+CBattleLogic::CBattleLogic(ICallback *cb,  const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side) :
 	m_iCurrentTurn(-2),
 	m_bIsAttacker(!side),
 	m_cb(cb),
@@ -121,23 +121,24 @@ void CBattleLogic::MakeStatistics(int currentCreatureId)
 	for (map_stacks::const_iterator it = allStacks.begin(); it != allStacks.end(); ++it)
 	{
 		const CStack *st = &it->second;
+		const int stackHP = st->valOfBonuses(Bonus::STACK_HEALTH);
 
 		if ((it->second.attackerOwned != 0) != m_bIsAttacker)
 		{
 			int id = it->first;
-			if (st->amount < 1)
+			if (st->count < 1)
 			{
 				continue;
 			}
 			// make stats
-			int hitPoints = st->amount * st->creature->hitPoints - (st->creature->hitPoints - st->firstHPleft);
+			int hitPoints = st->count * stackHP - (stackHP - st->firstHPleft);
 
-			m_statMaxDamage.push_back(std::pair<int, int>(id, st->creature->damageMax * st->amount));
-			m_statMinDamage.push_back(std::pair<int, int>(id, st->creature->damageMin * st->amount));
+			m_statMaxDamage.push_back(std::pair<int, int>(id, st->type->damageMax * st->count));
+			m_statMinDamage.push_back(std::pair<int, int>(id, st->type->damageMin * st->count));
 			m_statHitPoints.push_back(std::pair<int, int>(id, hitPoints));
-			m_statMaxSpeed.push_back(std::pair<int, int>(id, st->creature->speed));
+			m_statMaxSpeed.push_back(std::pair<int, int>(id, stackHP));
 
-			totalEnemyDamage += (st->creature->damageMax + st->creature->damageMin) * st->amount / 2;
+			totalEnemyDamage += (st->type->damageMax + st->type->damageMin) * st->count / 2;
 			totalEnemyHitPoints += hitPoints;
 
 			// calculate casualties
@@ -174,32 +175,32 @@ void CBattleLogic::MakeStatistics(int currentCreatureId)
 				}
 			}
 
-			cs.damage_max = (int)(currentStack->creature->damageMax * currentStack->amount * damageFactor);
+			cs.damage_max = (int)(currentStack->type->damageMax * currentStack->count * damageFactor);
 			if (cs.damage_max > hitPoints)
 			{
 				cs.damage_max = hitPoints;
 			}
 
-			cs.damage_min = (int)(currentStack->creature->damageMin * currentStack->amount * damageFactor);
+			cs.damage_min = (int)(currentStack->type->damageMin * currentStack->count * damageFactor);
 			if (cs.damage_min > hitPoints)
 			{
 				cs.damage_min = hitPoints;
 			}
 
-			cs.amount_max = cs.damage_max / st->creature->hitPoints;
-			cs.amount_min = cs.damage_min / st->creature->hitPoints;
+			cs.amount_max = cs.damage_max / stackHP;
+			cs.amount_min = cs.damage_min / stackHP;
 
-			cs.leftHitPoints_for_max = (hitPoints - cs.damage_max) % st->creature->hitPoints;
-			cs.leftHitPoint_for_min = (hitPoints - cs.damage_min) % st->creature->hitPoints;
+			cs.leftHitPoints_for_max = (hitPoints - cs.damage_max) % stackHP;
+			cs.leftHitPoint_for_min = (hitPoints - cs.damage_min) % stackHP;
 
 			m_statCasualties.push_back(std::pair<int, SCreatureCasualties>(id, cs));
 
-			if (st->creature->isShooting() && st->shots > 0)
+			if (st->type->isShooting() && st->shots > 0)
 			{
 				m_statDistanceFromShooters.push_back(std::pair<int, int>(id, m_battleHelper.GetShortestDistance(currentStack->position, st->position)));
 			}
 
-			if (currentStack->hasFeatureOfType(StackFeature::FLYING) || (currentStack->creature->isShooting() && currentStack->shots > 0))
+			if (currentStack->hasBonusOfType(Bonus::FLYING) || (currentStack->type->isShooting() && currentStack->shots > 0))
 			{
 				m_statDistance.push_back(std::pair<int, int>(id, m_battleHelper.GetShortestDistance(currentStack->position, st->position)));
 			}
@@ -210,13 +211,13 @@ void CBattleLogic::MakeStatistics(int currentCreatureId)
 		}
 		else
 		{
-			if (st->amount < 1)
+			if (st->count < 1)
 			{
 				continue;
 			}
-			int hitPoints = st->amount * st->creature->hitPoints - (st->creature->hitPoints - st->firstHPleft);
+			int hitPoints = st->count * stackHP - (stackHP - st->firstHPleft);
 
-			totalDamage += (st->creature->damageMax + st->creature->damageMin) * st->amount / 2;
+			totalDamage += (st->type->damageMax + st->type->damageMin) * st->count / 2;
 			totalHitPoints += hitPoints;
 		}
 	}
@@ -258,7 +259,7 @@ void CBattleLogic::MakeStatistics(int currentCreatureId)
 BattleAction CBattleLogic::MakeDecision(int stackID)
 {
 	const CStack *currentStack = m_cb->battleGetStackByID(stackID);
-	if(currentStack->position < 0 || currentStack->creature->idNumber == 147) //turret or first aid kit
+	if(currentStack->position < 0 || currentStack->type->idNumber == 147) //turret or first aid kit
 	{
 		return MakeDefend(stackID);
 	}
@@ -318,8 +319,8 @@ std::vector<int> CBattleLogic::GetAvailableHexesForAttacker(const CStack *defend
 {
 	int x = m_battleHelper.DecodeXPosition(defender->position);
 	int y = m_battleHelper.DecodeYPosition(defender->position);
-	bool defenderIsDW = defender->hasFeatureOfType(StackFeature::DOUBLE_WIDE);
-	bool attackerIsDW = attacker->hasFeatureOfType(StackFeature::DOUBLE_WIDE);
+	bool defenderIsDW = defender->doubleWide();
+	bool attackerIsDW = attacker->doubleWide();
 	// TOTO: should be std::vector<int> but for debug purpose std::pair is used
 	typedef std::pair<int, int> hexPoint;
 	std::list<hexPoint> candidates;
@@ -424,7 +425,7 @@ std::vector<int> CBattleLogic::GetAvailableHexesForAttacker(const CStack *defend
 		int new_pos = m_battleHelper.GetBattleFieldPosition(it->first, it->second);
 		const CStack *st = m_cb->battleGetStackByPos(new_pos);
 
-		if (st == NULL || st->amount < 1)
+		if (st == NULL || st->count < 1)
 		{
 			if (attackerIsDW)
 			{
@@ -449,7 +450,7 @@ std::vector<int> CBattleLogic::GetAvailableHexesForAttacker(const CStack *defend
 				}
 				assert(tail_pos >= 0 && "Error during calculation position of double wide creature");
 				//CStack *tailStack = m_cb->battleGetStackByPos(tail_pos);
-				if (st != NULL && st->amount >= 1)
+				if (st != NULL && st->count >= 1)
 				{
 					continue;
 				}
@@ -572,7 +573,7 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
 
 		// if double wide calculate tail
 		int tail_pos = -1;
-		if (attackerStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
+		if (attackerStack->doubleWide())
 		{
 			int x_pos = m_battleHelper.DecodeXPosition(attackerStack->position);
 			int y_pos = m_battleHelper.DecodeYPosition(attackerStack->position);
@@ -657,7 +658,7 @@ list<int> CBattleLogic::PerformBerserkAttack(int stackID, int &additionalInfo)
 			}
 			for (creature_stat::const_iterator it2 = m_statDistance.begin(); it2 != m_statDistance.end(); ++it2)
 			{
-				if (it2->first == it->first && it2->second - 1 <= c.speed)
+				if (it2->first == it->first && it2->second - 1 <= c.valOfBonuses(Bonus::STACKS_SPEED))
 				{
 					creatures.push_front(it->first);
 				}
@@ -761,9 +762,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->creature)
+		if (c && c->type)
 		{
-			message += c->creature->nameRef;
+			message += c->type->nameRef;
 		}
 		else
 		{

+ 3 - 3
AI/GeniusAI/BattleLogic.h

@@ -63,7 +63,7 @@ private:
 		int leftHitPoint_for_min; // scenario
 	};
 public:
-	CBattleLogic(ICallback *cb, CCreatureSet *army1, CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side);
+	CBattleLogic(ICallback *cb, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side);
 	~CBattleLogic();
 
 	void SetCurrentTurn(int turn);
@@ -77,8 +77,8 @@ private:
 	int m_iCurrentTurn;
 	bool m_bIsAttacker;
 	ICallback *m_cb;
-	CCreatureSet *m_army1;
-	CCreatureSet *m_army2;
+	const CCreatureSet *m_army1;
+	const CCreatureSet *m_army2;
 	int3 m_tile;
 	CGHeroInstance *m_hero1;
 	CGHeroInstance *m_hero2;

+ 28 - 28
AI/GeniusAI/CGeniusAI.cpp

@@ -61,7 +61,7 @@ CGeniusAI::HypotheticalGameState::TownModel::TownModel(
 {
 	hasBuilt = static_cast<bool>(t->builded);
 	creaturesToRecruit = t->creatures;
-	creaturesInGarrison = t->army;
+	creaturesInGarrison = t->getArmy();
 }
 
 CGeniusAI::HypotheticalGameState::HypotheticalGameState(CGeniusAI& ai)
@@ -295,7 +295,7 @@ float CGeniusAI::TownObjective::getValue() const
 	  case recruitCreatures:
       // Buy upgraded if possible.
 		  ID = whichTown->creaturesToRecruit[which].second.back();
-		  creature = &VLC->creh->creatures[ID];
+		  creature = VLC->creh->creatures[ID];
 		  howMany = whichTown->creaturesToRecruit[which].first;
       creatures_max = 0; // Max creatures you can recruit of this type.
       
@@ -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].type->idNumber;
-		  howMany = whichTown->creaturesInGarrison.slots[which].count;
+		  ID = whichTown->creaturesInGarrison.getCreature(which)->idNumber;
+		  howMany = whichTown->creaturesInGarrison.getAmount(which);
 
 		  newID = ui.newID.back();
 		  int upgrade_serial = ui.newID.size() - 1;
@@ -388,7 +388,7 @@ void CGeniusAI::TownObjective::print() const
 	  case recruitCreatures:
       // Buy upgraded if possible.
 		  ID = whichTown->creaturesToRecruit[which].second.back();
-		  creature = &VLC->creh->creatures[ID];
+		  creature = VLC->creh->creatures[ID];
 		  howMany = whichTown->creaturesToRecruit[which].first;
       creatures_max = 0;
 
@@ -424,8 +424,8 @@ void CGeniusAI::TownObjective::print() const
 
 		  case upgradeCreatures:
 			  UpgradeInfo ui = AI->m_cb->getUpgradeInfo(whichTown->t,which);
-			  ID = whichTown->creaturesInGarrison.slots[which].type->idNumber;
-			  cout << "upgrade " << VLC->creh->creatures[ID].namePl;
+			  ID = whichTown->creaturesInGarrison.getCreature(which)->idNumber;
+			  cout << "upgrade " << VLC->creh->creatures[ID]->namePl;
 			  //ui.cost	
 		  break;
 	} // switch(type)
@@ -734,10 +734,10 @@ void CGeniusAI::HeroObjective::fulfill(CGeniusAI& cg,
   if (town && object->getOwner() == cg.m_cb->getMyColor()) {
 	  //upgrade hero's units
 	  cout << "visiting town" << endl;
-	  CCreatureSet hcreatures = h->h->army;
+	  CCreatureSet hcreatures = h->h->getArmy();
 	  for (TSlots::const_iterator
-         i = hcreatures.slots.begin();
-         i != hcreatures.slots.end();
+         i = hcreatures.Slots().begin();
+         i != hcreatures.Slots().end();
          i++) { // For each hero slot.
 		  UpgradeInfo ui = cg.m_cb->getUpgradeInfo(h->h,i->first);
 
@@ -763,13 +763,13 @@ void CGeniusAI::HeroObjective::fulfill(CGeniusAI& cg,
 	  }
 
 	  // Give town's units to hero.
-	  CCreatureSet tcreatures = town->army;
+	  CCreatureSet tcreatures = town->getArmy();
 	  int weakestCreatureStack;
 	  int weakestCreatureAIValue = 99999; // TODO: Wtf??
 
 	  for (TSlots::const_iterator
-         i = tcreatures.slots.begin();
-         i != tcreatures.slots.end();
+         i = tcreatures.Slots().begin();
+         i != tcreatures.Slots().end();
          i++) {
 		  if (i->second.type->AIValue < 
           weakestCreatureAIValue) {
@@ -778,10 +778,10 @@ void CGeniusAI::HeroObjective::fulfill(CGeniusAI& cg,
 		  }
     }
 	  for (TSlots::const_iterator
-        i = tcreatures.slots.begin();
-        i != tcreatures.slots.end();
+        i = tcreatures.Slots().begin();
+        i != tcreatures.Slots().end();
         i++) { // For each town slot.
-		  hcreatures = h->h->army;
+		  hcreatures = h->h->getArmy();
 		  int hSlot = hcreatures.getSlotFor(i->second.type->idNumber);
 
 		  if (hSlot == -1)
@@ -789,7 +789,7 @@ void CGeniusAI::HeroObjective::fulfill(CGeniusAI& cg,
 		  cout << "giving hero "
            << i->second.type->namePl
            << endl;
-		  if (hcreatures.slots.find(hSlot) != hcreatures.slots.end()) {
+		  if (!hcreatures.slotEmpty(hSlot)) {
         // Can't take garrisonHero's last unit.
 			  if ( (i->first == weakestCreatureStack)
             && (town->garrisonHero != NULL) )
@@ -855,22 +855,22 @@ void CGeniusAI::addTownObjectives(HypotheticalGameState::TownModel& t,
 		
     int ID = t.creaturesToRecruit[i].second.back();
     // m_cb->getCCreatureByID(ID);
-		const CCreature *creature = &VLC->creh->creatures[ID];
+		const CCreature *creature = VLC->creh->creatures[ID];
 		bool canAfford = true;
 		for (int ii = 0; ii < creature->cost.size(); ii++)
 			if (creature->cost[ii] > hgs.resourceAmounts[ii])
 				canAfford = false; // Can we afford at least one creature?
 		if (!canAfford) continue;
 				
-		//cout << "town has " << t.t->creatures[i].first  << " "<< creature->namePl << " (AI Strength " << creature->AIValue << ")." << endl;
+		//cout << "town has " << t.t->creatures[i]->first  << " "<< creature->namePl << " (AI Strength " << creature->AIValue << ")." << endl;
 		TownObjective to(hgs, AIObjective::recruitCreatures, &t, i, this);
 		currentTownObjectives.insert(to);
 	}
 
   // Upgrade creatures.
-	for (TSlots::iterator
-      i = t.creaturesInGarrison.slots.begin();
-      i != t.creaturesInGarrison.slots.end();
+	for (TSlots::const_iterator
+      i = t.creaturesInGarrison.Slots().begin();
+      i != t.creaturesInGarrison.Slots().end();
       i++) {
 		UpgradeInfo ui = m_cb->getUpgradeInfo(t.t, i->first);
 		if (ui.newID.size() != 0) {
@@ -927,7 +927,7 @@ void CGeniusAI::TownObjective::fulfill(CGeniusAI& cg,
 	case recruitCreatures:
     // Buy upgraded if possible.
 		ID = whichTown->creaturesToRecruit[which].second.back();
-		creature = &VLC->creh->creatures[ID];
+		creature = VLC->creh->creatures[ID];
 		howMany = whichTown->creaturesToRecruit[which].first;
 		for (int i = 0; i < creature->cost.size(); i++)
       // TODO: rewrite.
@@ -942,11 +942,11 @@ void CGeniusAI::TownObjective::fulfill(CGeniusAI& cg,
 
 	case upgradeCreatures:
 		UpgradeInfo ui = cg.m_cb->getUpgradeInfo(whichTown->t, which);
-		ID = whichTown->creaturesInGarrison.slots[which].type->idNumber;
+		ID = whichTown->creaturesInGarrison.getCreature(which)->idNumber;
 		newID = ui.newID.back();
     // TODO: reduce resources in hgs
 		cg.m_cb->upgradeCreature(whichTown->t, which, newID);
-		cout << "upgrading " << VLC->creh->creatures[ID].namePl << endl;
+		cout << "upgrading " << VLC->creh->creatures[ID]->namePl << endl;
 		break;
 	}
 }
@@ -1077,7 +1077,7 @@ void CGeniusAI::startFirstTurn()
 		if (hgs.townModels.front().creaturesToRecruit[i].first == 0)
       continue;
 		int ID = hgs.townModels.front().creaturesToRecruit[i].second.back();
-		const CCreature *creature = &VLC->creh->creatures[ID];
+		const CCreature *creature = VLC->creh->creatures[ID];
 		bool canAfford = true;
     for (int ii = 0; ii < creature->cost.size(); ii++) {
 			if (creature->cost[ii] > hgs.resourceAmounts[ii])
@@ -1246,7 +1246,7 @@ void CGeniusAI::battleStacksAttacked(std::set<BattleStackAttacked>& bsa)
 /**
  * called by engine when battle starts; side=0 - left, side=1 - right
  */
-void CGeniusAI::battleStart(CCreatureSet *army1, CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side)
+void CGeniusAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side)
 {
   // TODO: Battle logic what...
 	assert(!m_battleLogic);
@@ -1275,7 +1275,7 @@ void CGeniusAI::battleEnd(BattleResult* br)
 	for (std::map<ui32,si32>::iterator i = br->casualties[0].begin();\
        i != br->casualties[0].end();
        i++)
-		cout << i->second << " " << VLC->creh->creatures[i->first].namePl << endl;
+		cout << i->second << " " << VLC->creh->creatures[i->first]->namePl << endl;
 				
 	delete m_battleLogic;
 	m_battleLogic = NULL;

+ 1 - 1
AI/GeniusAI/CGeniusAI.h

@@ -208,7 +208,7 @@ public:
 	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(SpellCast *sc);
-	virtual void battleStart(CCreatureSet *army1, CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side); //called by engine when battle starts; side=0 - left, side=1 - right
+	virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, 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);

+ 9 - 9
CCallback.cpp

@@ -68,7 +68,7 @@ void CCallback::recruitCreatures(const CGObjectInstance *obj, ui32 ID, ui32 amou
 
 bool CCallback::dismissCreature(const CArmedInstance *obj, int stackPos)
 {
-	if(((player>=0)  &&  obj->tempOwner != player) || (obj->army.slots.size()<2  && obj->needsLastStack()))
+	if(((player>=0)  &&  obj->tempOwner != player) || (obj->stacksCount()<2  && obj->needsLastStack()))
 		return false;
 
 	DisbandCreature pack(stackPos,obj->id);
@@ -379,7 +379,7 @@ const CCreatureSet* CCallback::getGarrison(const CGObjectInstance *obj) const
 	if(!armi)
 		return NULL;
 	else 
-		return &armi->army;
+		return armi;
 }
 
 int CCallback::swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2)
@@ -560,7 +560,7 @@ CCreature CCallback::battleGetCreature(int number)
 	for(size_t h=0; h<gs->curB->stacks.size(); ++h)
 	{
 		if(gs->curB->stacks[h]->ID == number) //creature found
-			return *(gs->curB->stacks[h]->creature);
+			return *(gs->curB->stacks[h]->type);
 	}
 #ifndef __GNUC__
 	throw new std::exception("Cannot find the creature");
@@ -776,7 +776,7 @@ void CCallback::trade( int mode, int id1, int id2, int val1 )
 
 void CCallback::setFormation(const CGHeroInstance * hero, bool tight)
 {
-	const_cast<CGHeroInstance*>(hero)->army.formation = tight;
+	const_cast<CGHeroInstance*>(hero)-> formation = tight;
 	SetFormation pack(hero->id,tight);
 	sendRequest(&pack);
 }
@@ -939,12 +939,12 @@ void CCallback::dig( const CGObjectInstance *hero )
 
 si8 CCallback::battleGetStackMorale( int stackID )
 {
-	return gs->curB->Morale( gs->curB->getStack(stackID) );
+	return gs->curB->getStack(stackID)->MoraleVal();
 }
 
 si8 CCallback::battleGetStackLuck( int stackID )
 {
-	return gs->curB->Luck( gs->curB->getStack(stackID) );
+	return gs->curB->getStack(stackID)->LuckVal();
 }
 
 void CCallback::castSpell(const CGHeroInstance *hero, int spellID, const int3 &pos)
@@ -987,7 +987,7 @@ InfoAboutTown::~InfoAboutTown()
 void InfoAboutTown::initFromTown( const CGTownInstance *t, bool detailed )
 {
 	obj = t;
-	army = t->army;
+	army = t->getArmy();
 	built = t->builded;
 	fortLevel = t->fortLevel();
 	name = t->name;
@@ -1006,7 +1006,7 @@ void InfoAboutTown::initFromTown( const CGTownInstance *t, bool detailed )
 	/*else
 	{
 		//hide info about hero stacks counts
-		for(std::map<si32,std::pair<ui32,si32> >::iterator i = army.slots.begin(); i != army.slots.end(); ++i)
+		for(std::map<si32,std::pair<ui32,si32> >::iterator i = slots.begin(); i != slots.end(); ++i)
 		{
 			i->second.second = 0;
 		}
@@ -1017,7 +1017,7 @@ void InfoAboutTown::initFromGarrison(const CGGarrison *garr, bool detailed)
 {
 	obj = garr;
 	fortLevel = 0;
-	army = garr->army;
+	army = garr->getArmy();
 	name = CGI->generaltexth->names[33]; // "Garrison"
 	owner = garr->tempOwner;
 	built = false;

+ 4 - 15
CGameInterface.h

@@ -36,7 +36,7 @@ struct BattleAttack;
 struct BattleStackAttacked;
 struct SpellCast;
 struct SetStackEffect;
-struct HeroBonus;
+struct Bonus;
 struct PackageApplied;
 struct SetObjectProperty;
 struct CatapultAttack;
@@ -53,17 +53,6 @@ class CObstacle
 	//TODO: add some kind of the blockmap
 };
 
-struct StackState
-{
-	StackState(){attackBonus=defenseBonus=healthBonus=speedBonus=morale=luck=shotsLeft=currentHealth=0;};
-	int attackBonus, defenseBonus, healthBonus, speedBonus;
-	int currentHealth;
-	int shotsLeft;
-	std::set<int> effects; //IDs of spells affecting stack
-	int morale, luck;
-	int dmgMultiplier; //for ballista dmg bonus handling
-};
-
 class CGameInterface
 {
 public:
@@ -98,8 +87,8 @@ public:
 	virtual void yourTurn(){};
 	virtual void centerView (int3 pos, int focusTime){};
 	virtual void availableCreaturesChanged(const CGDwelling *town){};
-	virtual void heroBonusChanged(const CGHeroInstance *hero, const HeroBonus &bonus, bool gain){};//if gain hero received bonus, else he lost it
-	virtual void playerBonusChanged(const HeroBonus &bonus, bool gain){};//if gain hero received bonus, else he lost it
+	virtual void heroBonusChanged(const CGHeroInstance *hero, const Bonus &bonus, bool gain){};//if gain hero received bonus, else he lost it
+	virtual void playerBonusChanged(const Bonus &bonus, bool gain){};//if gain hero received bonus, else he lost it
 	virtual void requestRealized(PackageApplied *pa){};
 	virtual void heroExchangeStarted(si32 hero1, si32 hero2){};
 	virtual void objectPropertyChanged(const SetObjectProperty * sop){}; //eg. mine has been flagged
@@ -121,7 +110,7 @@ public:
 	virtual void battleStackMoved(int ID, int dest, int distance, bool end){};
 	virtual void battleSpellCast(SpellCast *sc){};
 	virtual void battleStacksEffectsSet(SetStackEffect & sse){};//called when a specific effect is set to stacks
-	virtual void battleStart(CCreatureSet *army1, CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side){}; //called by engine when battle starts; side=0 - left, side=1 - right
+	virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, 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){}; //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

+ 2 - 2
client/CAdvmapInterface.cpp

@@ -1898,7 +1898,7 @@ void CAdvMapInt::tileHovered(const int3 &tile)
 						const CGTownInstance* townObj = dynamic_cast<const CGTownInstance*>(objAtTile);
 
 						// Show movement cursor for unguarded enemy towns, otherwise attack cursor.
-						if (townObj && townObj->army.slots.empty())
+						if (townObj && !townObj->stacksCount())
 							CGI->curh->changeGraphic(0, 9 + turns*6);
 						else
 							CGI->curh->changeGraphic(0, 5 + turns*6);
@@ -1938,7 +1938,7 @@ void CAdvMapInt::tileHovered(const int3 &tile)
 					const CGGarrison* garrObj = dynamic_cast<const CGGarrison*>(objAtTile); //TODO evil evil cast!
 
 					// Show battle cursor for guarded enemy garrisons, otherwise movement cursor.
-					if (garrObj  &&  garrObj->tempOwner != LOCPLINT->playerID  &&  !garrObj->army.slots.empty())
+					if (garrObj  &&  garrObj->tempOwner != LOCPLINT->playerID  &&  garrObj->stacksCount())
 						CGI->curh->changeGraphic(0, 5 + turns*6);
 					else
 						CGI->curh->changeGraphic(0, 9 + turns*6);

+ 84 - 106
client/CBattleInterface.cpp

@@ -216,7 +216,7 @@ bool CSpellEffectAnim::init()
 			}
 
 			// Correction for 2-hex creatures.
-			if (destStack != NULL && destStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
+			if (destStack != NULL && destStack->doubleWide())
 				be.x += (destStack->attackerOwned ? -1 : 1)*tilePos.w/2;
 
 			owner->battleEffects.push_back(be);
@@ -376,7 +376,7 @@ void CReverseAnim::nextFrame()
 			owner->creAnims[stackID]->pos.x = coords.x;
 			//creAnims[stackID]->pos.y = coords.second;
 
-			if(curs->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
+			if(curs->doubleWide())
 			{
 				if(curs->attackerOwned)
 				{
@@ -445,7 +445,7 @@ bool CDefenceAnim::init()
 		if(IDby != -1)
 		{
 			int attackerAnimType = owner->creAnims[IDby]->getType();
-			if( attackerAnimType == 11 && attackerAnimType == 12 && attackerAnimType == 13 && owner->creAnims[IDby]->getFrame() < attacker->creature->attackClimaxFrame )
+			if( attackerAnimType == 11 && attackerAnimType == 12 && attackerAnimType == 13 && owner->creAnims[IDby]->getFrame() < attacker->type->attackClimaxFrame )
 				return false;
 		}
 
@@ -465,7 +465,7 @@ bool CDefenceAnim::init()
 	const CStack * attacked = owner->curInt->cb->battleGetStackByID(stackID, false);
 
 	//reverse unit if necessary
-	if(attacker && isToReverse(attacked->position, attacker->position, owner->creDir[stackID], attacker->hasFeatureOfType(StackFeature::DOUBLE_WIDE), owner->creDir[IDby]))
+	if(attacker && isToReverse(attacked->position, attacker->position, owner->creDir[stackID], attacker->doubleWide(), owner->creDir[IDby]))
 	{
 		owner->addNewAnim(new CReverseAnim(owner, stackID, attacked->position, true));
 		return false;
@@ -476,7 +476,7 @@ bool CDefenceAnim::init()
 	{		
 		for(std::list<SProjectileInfo>::const_iterator it = owner->projectiles.begin(); it != owner->projectiles.end(); ++it)
 		{
-			if(it->creID == attacker->creature->idNumber)
+			if(it->creID == attacker->type->idNumber)
 			{
 				return false;
 			}
@@ -488,13 +488,13 @@ bool CDefenceAnim::init()
 		
 	if(killed)
 	{
-		CGI->soundh->playSound(battle_sound(attacked->creature, killed));
+		CGI->soundh->playSound(battle_sound(attacked->type, killed));
 		owner->creAnims[stackID]->setType(5); //death
 	}
 	else
 	{
 		// TODO: this block doesn't seems correct if the unit is defending.
-		CGI->soundh->playSound(battle_sound(attacked->creature, wince));
+		CGI->soundh->playSound(battle_sound(attacked->type, wince));
 		owner->creAnims[stackID]->setType(3); //getting hit
 	}
 
@@ -572,7 +572,7 @@ bool CBattleStackMoved::init()
 		endAnim();
 		return false;
 	}
-	bool twoTiles = movedStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE);
+	bool twoTiles = movedStack->doubleWide();
 	
 	Point begPosition = CBattleHex::getXYUnitAnim(curStackPos, movedStack->attackerOwned, movedStack, owner);
 	Point endPosition = CBattleHex::getXYUnitAnim(destHex, movedStack->attackerOwned, movedStack, owner);
@@ -598,11 +598,11 @@ bool CBattleStackMoved::init()
 	//unit reversed
 
 	if(owner->moveSh <= 0)
-		owner->moveSh = CGI->soundh->playSound(battle_sound(movedStack->creature, move), -1);
+		owner->moveSh = CGI->soundh->playSound(battle_sound(movedStack->type, move), -1);
 
 	//step shift calculation
 	posX = owner->creAnims[stackID]->pos.x, posY = owner->creAnims[stackID]->pos.y; // for precise calculations ;]
-	if(mutPos == -1 && movedStack->hasFeatureOfType(StackFeature::FLYING)) 
+	if(mutPos == -1 && movedStack->hasBonusOfType(Bonus::FLYING)) 
 	{
 		steps *= distance;
 		steps /= 2; //to make animation faster
@@ -668,7 +668,7 @@ void CBattleStackMoved::endAnim()
 
 	if(movedStack)
 	{
-		bool twoTiles = movedStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE);
+		bool twoTiles = movedStack->doubleWide();
 
 		if(endMoving)
 		{
@@ -714,7 +714,7 @@ bool CBattleMoveStart::init()
 		return false;
 	}
 
-	CGI->soundh->playSound(battle_sound(movedStack->creature, startMoving));
+	CGI->soundh->playSound(battle_sound(movedStack->type, startMoving));
 
 	return true;
 }
@@ -759,7 +759,7 @@ bool CBattleMoveEnd::init()
 		return false;
 	}
 
-	CGI->soundh->playSound(battle_sound(movedStack->creature, endMoving));
+	CGI->soundh->playSound(battle_sound(movedStack->type, endMoving));
 
 	owner->creAnims[stackID]->setType(21);
 
@@ -800,9 +800,9 @@ void CBattleAttack::nextFrame()
 	if(owner->creAnims[stackID]->onFirstFrameInGroup())
 	{
 		if(shooting)
-			CGI->soundh->playSound(battle_sound(attackingStack->creature, shoot));
+			CGI->soundh->playSound(battle_sound(attackingStack->type, shoot));
 		else
-			CGI->soundh->playSound(battle_sound(attackingStack->creature, attack));
+			CGI->soundh->playSound(battle_sound(attackingStack->type, attack));
 	}
 	else if(owner->creAnims[stackID]->onLastFrameInGroup())
 	{
@@ -824,7 +824,7 @@ CBattleAttack::CBattleAttack(CBattleInterface * _owner, int _stackID, int _dest,
 	attackingStack = owner->curInt->cb->battleGetStackByID(_stackID, false);
 
 	assert(attackingStack && "attackingStack is NULL in CBattleAttack::CBattleAttack !\n");
-	if(attackingStack->creature->idNumber != 145) //catapult is allowed to attack not-creature
+	if(attackingStack->type->idNumber != 145) //catapult is allowed to attack not-creature
 	{
 		assert(attackedStack && "attackedStack is NULL in CBattleAttack::CBattleAttack !\n");
 	}
@@ -857,7 +857,7 @@ bool CMeleeAttack::init()
 	int reversedShift = 0; //shift of attacking stack's position due to reversing
 	if(attackingStack->attackerOwned)
 	{
-		if(attackingStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && BattleInfo::mutualPosition(attackingStackPosBeforeReturn, dest) == -1)
+		if(attackingStack->doubleWide() && BattleInfo::mutualPosition(attackingStackPosBeforeReturn, dest) == -1)
 		{
 			if(BattleInfo::mutualPosition(attackingStackPosBeforeReturn + (attackingStack->attackerOwned ? -1 : 1), dest) >= 0) //if reversing stack will make its position adjacent to dest
 			{
@@ -867,7 +867,7 @@ bool CMeleeAttack::init()
 	}
 	else //if(astack->attackerOwned)
 	{
-		if(attackingStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && BattleInfo::mutualPosition(attackingStackPosBeforeReturn, dest) == -1)
+		if(attackingStack->doubleWide() && BattleInfo::mutualPosition(attackingStackPosBeforeReturn, dest) == -1)
 		{
 			if(BattleInfo::mutualPosition(attackingStackPosBeforeReturn + (attackingStack->attackerOwned ? -1 : 1), dest) >= 0) //if reversing stack will make its position adjacent to dest
 			{
@@ -878,7 +878,7 @@ bool CMeleeAttack::init()
 	}
 
 	//reversing stack if necessary
-	if(isToReverse(attackingStackPosBeforeReturn, dest, owner->creDir[stackID], attackedStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE), owner->creDir[attackedStack->ID]))
+	if(isToReverse(attackingStackPosBeforeReturn, dest, owner->creDir[stackID], attackedStack->doubleWide(), owner->creDir[attackedStack->ID]))
 	{
 		owner->addNewAnim(new CReverseAnim(owner, stackID, attackingStackPosBeforeReturn, true));
 		return false;
@@ -953,7 +953,7 @@ bool CShootingAnim::init()
 		projectileAngle = -projectileAngle;
 
 	SProjectileInfo spi;
-	spi.creID = shooter->creature->idNumber;
+	spi.creID = shooter->type->idNumber;
 	spi.reverse = !shooter->attackerOwned;
 
 	spi.step = 0;
@@ -975,18 +975,18 @@ bool CShootingAnim::init()
 
 	if(projectileAngle > straightAngle) //upper shot
 	{
-		spi.x = xycoord.x + 200 + shooter->creature->upperRightMissleOffsetX;
-		spi.y = xycoord.y + 100 - shooter->creature->upperRightMissleOffsetY;
+		spi.x = xycoord.x + 200 + shooter->type->upperRightMissleOffsetX;
+		spi.y = xycoord.y + 100 - shooter->type->upperRightMissleOffsetY;
 	}
 	else if(projectileAngle < -straightAngle) //lower shot
 	{
-		spi.x = xycoord.x + 200 + shooter->creature->lowerRightMissleOffsetX;
-		spi.y = xycoord.y + 150 - shooter->creature->lowerRightMissleOffsetY;
+		spi.x = xycoord.x + 200 + shooter->type->lowerRightMissleOffsetX;
+		spi.y = xycoord.y + 150 - shooter->type->lowerRightMissleOffsetY;
 	}
 	else //straight shot
 	{
-		spi.x = xycoord.x + 200 + shooter->creature->rightMissleOffsetX;
-		spi.y = xycoord.y + 125 - shooter->creature->rightMissleOffsetY;
+		spi.x = xycoord.x + 200 + shooter->type->rightMissleOffsetX;
+		spi.y = xycoord.y + 125 - shooter->type->rightMissleOffsetY;
 	}
 	spi.lastStep = sqrt((float)((destcoord.x - spi.x)*(destcoord.x - spi.x) + (destcoord.y - spi.y) * (destcoord.y - spi.y))) / 40;
 	if(spi.lastStep == 0)
@@ -1003,7 +1003,7 @@ bool CShootingAnim::init()
 		spi.frameNum = ((M_PI/2.0f - projectileAngle) / (2.0f *M_PI) + 1/((float)(2*(owner->idToProjectile[spi.creID]->ourImages.size()-1)))) * (owner->idToProjectile[spi.creID]->ourImages.size()-1);
 	}
 	//set delay
-	spi.animStartDelay = CGI->creh->creatures[spi.creID].attackClimaxFrame;
+	spi.animStartDelay = CGI->creh->creatures[spi.creID]->attackClimaxFrame;
 	owner->projectiles.push_back(spi);
 
 	//attack aniamtion
@@ -1064,7 +1064,7 @@ void CBattleInterface::addNewAnim(CBattleAnimation * anim)
 	animsAreDisplayed.setn(true);
 }
 
-CBattleInterface::CBattleInterface(CCreatureSet * army1, CCreatureSet * army2, CGHeroInstance *hero1, CGHeroInstance *hero2, const SDL_Rect & myRect, CPlayerInterface * att, CPlayerInterface * defen)
+CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSet * army2, CGHeroInstance *hero1, CGHeroInstance *hero2, const SDL_Rect & myRect, CPlayerInterface * att, CPlayerInterface * defen)
 	: attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0), activeStack(-1), stackToActivate(-1),
 	  mouseHoveredStack(-1), previouslyHoveredHex(-1), currentlyHoveredHex(-1), spellDestSelectMode(false),
 	  spellToCast(NULL), givenCommand(NULL), myTurn(false), resWindow(NULL), animIDhelper(0),
@@ -1228,25 +1228,25 @@ CBattleInterface::CBattleInterface(CCreatureSet * army1, CCreatureSet * army2, C
 	//loading projectiles for units
 	for(std::map<int, CStack>::iterator g = stacks.begin(); g != stacks.end(); ++g)
 	{
-		int creID = (g->second.creature->idNumber == 149) ? CGI->creh->factionToTurretCreature[siegeH->town->town->typeID] : g->second.creature->idNumber; //id of creature whose shots should be loaded
-		if(g->second.creature->isShooting() && CGI->creh->idToProjectile[creID] != std::string())
+		int creID = (g->second.type->idNumber == 149) ? CGI->creh->factionToTurretCreature[siegeH->town->town->typeID] : g->second.type->idNumber; //id of creature whose shots should be loaded
+		if(g->second.type->isShooting() && CGI->creh->idToProjectile[creID] != std::string())
 		{	
-			idToProjectile[g->second.creature->idNumber] = CDefHandler::giveDef(CGI->creh->idToProjectile[creID]);
+			idToProjectile[g->second.type->idNumber] = CDefHandler::giveDef(CGI->creh->idToProjectile[creID]);
 
-			if(idToProjectile[g->second.creature->idNumber]->ourImages.size() > 2) //add symmetric images
+			if(idToProjectile[g->second.type->idNumber]->ourImages.size() > 2) //add symmetric images
 			{
-				for(int k = idToProjectile[g->second.creature->idNumber]->ourImages.size()-2; k > 1; --k)
+				for(int k = idToProjectile[g->second.type->idNumber]->ourImages.size()-2; k > 1; --k)
 				{
 					Cimage ci;
-					ci.bitmap = CSDL_Ext::rotate01(idToProjectile[g->second.creature->idNumber]->ourImages[k].bitmap);
+					ci.bitmap = CSDL_Ext::rotate01(idToProjectile[g->second.type->idNumber]->ourImages[k].bitmap);
 					ci.groupNumber = 0;
 					ci.imName = std::string();
-					idToProjectile[g->second.creature->idNumber]->ourImages.push_back(ci);
+					idToProjectile[g->second.type->idNumber]->ourImages.push_back(ci);
 				}
 			}
-			for(int s=0; s<idToProjectile[g->second.creature->idNumber]->ourImages.size(); ++s) //alpha transforming
+			for(int s=0; s<idToProjectile[g->second.type->idNumber]->ourImages.size(); ++s) //alpha transforming
 			{
-				CSDL_Ext::alphaTransform(idToProjectile[g->second.creature->idNumber]->ourImages[s].bitmap);
+				CSDL_Ext::alphaTransform(idToProjectile[g->second.type->idNumber]->ourImages[s].bitmap);
 			}
 		}
 	}
@@ -1589,7 +1589,7 @@ void CBattleInterface::show(SDL_Surface * to)
 		{
 			int curStackID = stackAliveByHex[b][v];
 			
-			if(!stacks[curStackID].hasFeatureOfType(StackFeature::FLYING) || creAnims[curStackID]->getType() != 0)
+			if(!stacks[curStackID].hasBonusOfType(Bonus::FLYING) || creAnims[curStackID]->getType() != 0)
 				showAliveStack(curStackID, stacks, to);
 			else
 				flyingStacks.push_back(curStackID);
@@ -1733,7 +1733,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 				{
 					if(shere->owner == curInt->playerID) //our stack
 					{
-						if(sactive->hasFeatureOfType(StackFeature::HEALER))
+						if(sactive->hasBonusOfType(Bonus::HEALER))
 						{
 							//display the possibility to heal this creature
 							CGI->curh->changeGraphic(1,17);
@@ -1745,7 +1745,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 						}
 						//setting console text
 						char buf[500];
-						sprintf(buf, CGI->generaltexth->allTexts[297].c_str(), shere->amount == 1 ? shere->creature->nameSing.c_str() : shere->creature->namePl.c_str());
+						sprintf(buf, CGI->generaltexth->allTexts[297].c_str(), shere->count == 1 ? shere->type->nameSing.c_str() : shere->type->namePl.c_str());
 						console->alterTxt = buf;
 						console->whoSetAlter = 0;
 						mouseHoveredStack = shere->ID;
@@ -1773,7 +1773,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 						std::ostringstream estDmg;
 						estDmg << estimatedDmg.first << " - " << estimatedDmg.second;
 						//printing
-						sprintf(buf, CGI->generaltexth->allTexts[296].c_str(), shere->amount == 1 ? shere->creature->nameSing.c_str() : shere->creature->namePl.c_str(), sactive->shots, estDmg.str().c_str());
+						sprintf(buf, CGI->generaltexth->allTexts[296].c_str(), shere->count == 1 ? shere->type->nameSing.c_str() : shere->type->namePl.c_str(), sactive->shots, estDmg.str().c_str());
 						console->alterTxt = buf;
 						console->whoSetAlter = 0;
 					}
@@ -1797,7 +1797,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 						sectorCursor.push_back(12);
 						sectorCursor.push_back(7);
 
-						const bool doubleWide = curInt->cb->battleGetStackByID(activeStack)->hasFeatureOfType(StackFeature::DOUBLE_WIDE);
+						const bool doubleWide = curInt->cb->battleGetStackByID(activeStack)->doubleWide();
 						bool aboveAttackable = true, belowAttackable = true;
 
 						// Exclude directions which cannot be attacked from.
@@ -1923,7 +1923,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 						std::ostringstream estDmg;
 						estDmg << estimatedDmg.first << " - " << estimatedDmg.second;
 						//printing
-						sprintf(buf, CGI->generaltexth->allTexts[36].c_str(), shere->amount == 1 ? shere->creature->nameSing.c_str() : shere->creature->namePl.c_str(), estDmg.str().c_str());
+						sprintf(buf, CGI->generaltexth->allTexts[36].c_str(), shere->count == 1 ? shere->type->nameSing.c_str() : shere->type->namePl.c_str(), estDmg.str().c_str());
 						console->alterTxt = buf;
 						console->whoSetAlter = 0;
 					}
@@ -1934,7 +1934,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 						console->whoSetAlter = 0;
 					}
 				}
-				else if( sactive && sactive->hasFeatureOfType(StackFeature::CATAPULT) && isCatapultAttackable(myNumber) ) //catapulting
+				else if( sactive && sactive->hasBonusOfType(Bonus::CATAPULT) && isCatapultAttackable(myNumber) ) //catapulting
 				{
 					CGI->curh->changeGraphic(1,16);
 					console->alterTxt = "";
@@ -1954,15 +1954,15 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 				if(sactive) //there can be a moment when stack is dead ut next is not yet activated
 				{
 					char buf[500];
-					if(sactive->hasFeatureOfType(StackFeature::FLYING))
+					if(sactive->hasBonusOfType(Bonus::FLYING))
 					{
 						CGI->curh->changeGraphic(1,2);
-						sprintf(buf, CGI->generaltexth->allTexts[295].c_str(), sactive->amount == 1 ? sactive->creature->nameSing.c_str() : sactive->creature->namePl.c_str());
+						sprintf(buf, CGI->generaltexth->allTexts[295].c_str(), sactive->count == 1 ? sactive->type->nameSing.c_str() : sactive->type->namePl.c_str());
 					}
 					else
 					{
 						CGI->curh->changeGraphic(1,1);
-						sprintf(buf, CGI->generaltexth->allTexts[294].c_str(), sactive->amount == 1 ? sactive->creature->nameSing.c_str() : sactive->creature->namePl.c_str());
+						sprintf(buf, CGI->generaltexth->allTexts[294].c_str(), sactive->count == 1 ? sactive->type->nameSing.c_str() : sactive->type->namePl.c_str());
 					}
 
 					console->alterTxt = buf;
@@ -1994,7 +1994,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 			//get dead stack if we cast resurrection or animate dead
 			const CStack * stackUnder = curInt->cb->battleGetStackByPos(myNumber, spellToCast->additionalInfo != 38 && spellToCast->additionalInfo != 39);
 
-			if(stackUnder && spellToCast->additionalInfo == 39 && !stackUnder->hasFeatureOfType(StackFeature::UNDEAD)) //animate dead can be cast only on undead creatures
+			if(stackUnder && spellToCast->additionalInfo == 39 && !stackUnder->hasBonusOfType(Bonus::UNDEAD)) //animate dead can be cast only on undead creatures
 				stackUnder = NULL;
 
 			bool whichCase; //for cases 1, 2 and 3
@@ -2027,7 +2027,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 					CGI->curh->changeGraphic(3, 0);
 					//setting console text
 					char buf[500];
-					std::string creName = stackUnder->amount > 1 ? stackUnder->creature->namePl : stackUnder->creature->nameSing;
+					std::string creName = stackUnder->count > 1 ? stackUnder->type->namePl : stackUnder->type->nameSing;
 						sprintf(buf, CGI->generaltexth->allTexts[27].c_str(), CGI->spellh->spells[spellToCast->additionalInfo].name.c_str(), creName.c_str());
 					console->alterTxt = buf;
 					console->whoSetAlter = 0;
@@ -2182,12 +2182,12 @@ void CBattleInterface::newStack(int stackID)
 
 	if(newStack->position < 0) //turret
 	{
-		const CCreature & turretCreature = CGI->creh->creatures[ CGI->creh->factionToTurretCreature[siegeH->town->town->typeID] ];
+		const CCreature & turretCreature = *CGI->creh->creatures[ CGI->creh->factionToTurretCreature[siegeH->town->town->typeID] ];
 		creAnims[stackID] = new CCreatureAnimation(turretCreature.animDefName);	
 	}
 	else
 	{
-		creAnims[stackID] = new CCreatureAnimation(newStack->creature->animDefName);	
+		creAnims[stackID] = new CCreatureAnimation(newStack->type->animDefName);	
 	}
 	creAnims[stackID]->setType(2);
 	creAnims[stackID]->pos = Rect(coords.x, coords.y, creAnims[newStack->ID]->fullWidth, creAnims[newStack->ID]->fullHeight);
@@ -2239,13 +2239,13 @@ void CBattleInterface::newRound(int number)
 	std::map<int, CStack> stacks = curInt->cb->battleGetStacks();
 	for(std::map<int, CStack>::const_iterator it = stacks.begin(); it != stacks.end(); ++it)
 	{
-		if( it->second.hasFeatureOfType(StackFeature::HP_REGENERATION) && it->second.alive() )
+		if( it->second.hasBonusOfType(Bonus::HP_REGENERATION) && it->second.alive() )
 			displayEffect(74, it->second.position);
 
-		if( it->second.hasFeatureOfType(StackFeature::FULL_HP_REGENERATION, 0) && it->second.alive() )
+		if( it->second.hasBonusOfType(Bonus::FULL_HP_REGENERATION, 0) && it->second.alive() )
 			displayEffect(4, it->second.position);
 
-		if( it->second.hasFeatureOfType(StackFeature::FULL_HP_REGENERATION, 1) && it->second.alive() )
+		if( it->second.hasBonusOfType(Bonus::FULL_HP_REGENERATION, 1) && it->second.alive() )
 			displayEffect(74, it->second.position);
 	}
 }
@@ -2317,7 +2317,7 @@ void CBattleInterface::hexLclicked(int whichOne)
 {
 	const CStack * actSt = curInt->cb->battleGetStackByID(activeStack);
 	if( ((whichOne%BFIELD_WIDTH)!=0 && (whichOne%BFIELD_WIDTH)!=(BFIELD_WIDTH-1)) //if player is trying to attack enemey unit or move creature stack
-		|| (actSt->hasFeatureOfType(StackFeature::CATAPULT) && !spellDestSelectMode )
+		|| (actSt->hasBonusOfType(Bonus::CATAPULT) && !spellDestSelectMode )
 		)
 	{
 		if(!myTurn)
@@ -2362,7 +2362,7 @@ void CBattleInterface::hexLclicked(int whichOne)
 				if(std::find(shadedHexes.begin(),shadedHexes.end(),whichOne)!=shadedHexes.end())// and it's in our range
 				{
 					CGI->curh->changeGraphic(1, 6); //cursor should be changed
-					if(curInt->cb->battleGetStackByID(activeStack)->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
+					if(curInt->cb->battleGetStackByID(activeStack)->doubleWide())
 					{
 						std::vector<int> acc = curInt->cb->battleGetAvailableHexes(activeStack, false);
 						int shiftedDest = whichOne + (curInt->cb->battleGetStackByID(activeStack)->attackerOwned ? 1 : -1);
@@ -2376,7 +2376,7 @@ void CBattleInterface::hexLclicked(int whichOne)
 						giveCommand(2,whichOne,activeStack);
 					}
 				}
-				else if(actSt->hasFeatureOfType(StackFeature::CATAPULT) && isCatapultAttackable(whichOne)) //attacking (catapult)
+				else if(actSt->hasBonusOfType(Bonus::CATAPULT) && isCatapultAttackable(whichOne)) //attacking (catapult)
 				{
 					giveCommand(9,whichOne,activeStack);
 				}
@@ -2395,7 +2395,7 @@ void CBattleInterface::hexLclicked(int whichOne)
 				{
 				case 12: //from bottom right
 					{
-						bool doubleWide = actStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE);
+						bool doubleWide = actStack->doubleWide();
 						int destHex = whichOne + ( (whichOne/BFIELD_WIDTH)%2 ? BFIELD_WIDTH : BFIELD_WIDTH+1 ) +
 							(actStack->attackerOwned && doubleWide ? 1 : 0);
 						if(vstd::contains(shadedHexes, destHex))
@@ -2431,7 +2431,7 @@ void CBattleInterface::hexLclicked(int whichOne)
 					}
 				case 8: //from left
 					{
-						if(actStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && !actStack->attackerOwned)
+						if(actStack->doubleWide() && !actStack->attackerOwned)
 						{
 							std::vector<int> acc = curInt->cb->battleGetAvailableHexes(activeStack, false);
 							if(vstd::contains(acc, whichOne))
@@ -2464,7 +2464,7 @@ void CBattleInterface::hexLclicked(int whichOne)
 					}
 				case 10: //from top right
 					{
-						bool doubleWide = actStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE);
+						bool doubleWide = actStack->doubleWide();
 						int destHex = whichOne - ( (whichOne/BFIELD_WIDTH)%2 ? BFIELD_WIDTH : BFIELD_WIDTH-1 ) +
 							(actStack->attackerOwned && doubleWide ? 1 : 0);
 						if(vstd::contains(shadedHexes, destHex))
@@ -2483,7 +2483,7 @@ void CBattleInterface::hexLclicked(int whichOne)
 					}
 				case 11: //from right
 					{
-						if(actStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && actStack->attackerOwned)
+						if(actStack->doubleWide() && actStack->attackerOwned)
 						{
 							std::vector<int> acc = curInt->cb->battleGetAvailableHexes(activeStack, false);
 							if(vstd::contains(acc, whichOne))
@@ -2541,7 +2541,7 @@ void CBattleInterface::hexLclicked(int whichOne)
 				}
 
 			}
-			else if (actSt->hasFeatureOfType(StackFeature::HEALER) && actSt->owner == dest->owner) //friendly creature we can heal
+			else if (actSt->hasBonusOfType(Bonus::HEALER) && actSt->owner == dest->owner) //friendly creature we can heal
 			{
 				giveCommand(12, whichOne, activeStack); //command healing
 
@@ -2689,7 +2689,7 @@ void CBattleInterface::spellCast(SpellCast * sc)
 			boost::algorithm::replace_first(text, "%s", "Creature"); //TODO: better fix
 		}
 		boost::algorithm::replace_first(text, "%s", CGI->spellh->spells[sc->id].name);
-		boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetStackByID(*sc->affectedCres.begin(), false)->creature->namePl );
+		boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetStackByID(*sc->affectedCres.begin(), false)->type->namePl );
 		console->addText(text);
 	}
 	else
@@ -2900,14 +2900,14 @@ void CBattleInterface::showAliveStack(int ID, const std::map<int, CStack> & stac
 	creAnims[ID]->nextFrame(to, creAnims[ID]->pos.x, creAnims[ID]->pos.y, creDir[ID], animCount, incrementFrame, ID==activeStack, ID==mouseHoveredStack); //increment always when moving, never if stack died
 
 	//printing amount
-	if(curStack.amount > 0 //don't print if stack is not alive
+	if(curStack.count > 0 //don't print if stack is not alive
 		&& (!curInt->curAction
 			|| (curInt->curAction->stackNumber != ID //don't print if stack is currently taking an action
 				&& (curInt->curAction->actionType != 6  ||  curStack.position != curInt->curAction->additionalInfo) //nor if it's an object of attack
 				&& (curInt->curAction->destinationTile != curStack.position) //nor if it's on destination tile for current action
 				)
 			)
-			&& !curStack.hasFeatureOfType(StackFeature::SIEGE_WEAPON) //and not a war machine...
+			&& !curStack.hasBonusOfType(Bonus::SIEGE_WEAPON) //and not a war machine...
 	)
 	{
 		int xAdd = curStack.attackerOwned ? 220 : 202;
@@ -2941,7 +2941,7 @@ void CBattleInterface::showAliveStack(int ID, const std::map<int, CStack> & stac
 		SDL_BlitSurface(amountBG, NULL, to, &genRect(amountNormal->h, amountNormal->w, creAnims[ID]->pos.x + xAdd, creAnims[ID]->pos.y + 260));
 		//blitting amount
 		CSDL_Ext::printAtMiddle(
-			makeNumberShort(curStack.amount),
+			makeNumberShort(curStack.count),
 			creAnims[ID]->pos.x + xAdd + 15,
 			creAnims[ID]->pos.y + 260 + 5,
 			FONT_TINY,
@@ -3045,18 +3045,18 @@ void CBattleInterface::printConsoleAttacked(int ID, int dmg, int killed, int IDb
 	char tabh[200];
 	const CStack * attacker = curInt->cb->battleGetStackByID(IDby, false);
 	const CStack * defender = curInt->cb->battleGetStackByID(ID, false);
-	int end = sprintf(tabh, CGI->generaltexth->allTexts[attacker->amount > 1 ? 377 : 376].c_str(),
-		(attacker->amount > 1 ? attacker->creature->namePl.c_str() : attacker->creature->nameSing.c_str()),
+	int end = sprintf(tabh, CGI->generaltexth->allTexts[attacker->count > 1 ? 377 : 376].c_str(),
+		(attacker->count > 1 ? attacker->type->namePl.c_str() : attacker->type->nameSing.c_str()),
 		dmg);
 	if(killed > 0)
 	{
 		if(killed > 1)
 		{
-			sprintf(tabh + end, CGI->generaltexth->allTexts[379].c_str(), killed, defender->creature->namePl.c_str());
+			sprintf(tabh + end, CGI->generaltexth->allTexts[379].c_str(), killed, defender->type->namePl.c_str());
 		}
 		else //killed == 1
 		{
-			sprintf(tabh + end, CGI->generaltexth->allTexts[378].c_str(), defender->creature->nameSing.c_str());
+			sprintf(tabh + end, CGI->generaltexth->allTexts[378].c_str(), defender->type->nameSing.c_str());
 		}
 	}
 
@@ -3219,14 +3219,14 @@ void CBattleInterface::startAction(const BattleAction* action)
 		break;
 	}
 
-	if(txtid > 0  &&  stack->amount != 1)
+	if(txtid > 0  &&  stack->count != 1)
 		txtid++; //move to plural text
 	else if(txtid < 0)
 		txtid = -txtid;
 
 	if(txtid)
 	{
-		sprintf(txt, CGI->generaltexth->allTexts[txtid].c_str(),  (stack->amount != 1) ? stack->creature->namePl.c_str() : stack->creature->nameSing.c_str(), 0);
+		sprintf(txt, CGI->generaltexth->allTexts[txtid].c_str(),  (stack->count != 1) ? stack->type->namePl.c_str() : stack->type->nameSing.c_str(), 0);
 		console->addText(txt);
 	}
 
@@ -3386,7 +3386,7 @@ Point CBattleHex::getXYUnitAnim(const int & hexNum, const bool & attacker, const
 	Point ret(-500, -500); //returned value
 	if(stack->position < 0) //creatures in turrets
 	{
-		const CCreature & turretCreature = CGI->creh->creatures[ CGI->creh->factionToTurretCreature[cbi->siegeH->town->town->typeID] ];
+		const CCreature & turretCreature = *CGI->creh->creatures[ CGI->creh->factionToTurretCreature[cbi->siegeH->town->town->typeID] ];
 		int xShift = turretCreature.isDoubleWide() ? 44 : 0;
 
 		switch(stack->position)
@@ -3415,7 +3415,7 @@ Point CBattleHex::getXYUnitAnim(const int & hexNum, const bool & attacker, const
 			ret.x = -219 + 22 * ( ((hexNum/BFIELD_WIDTH) + 1)%2 ) + 44 * (hexNum % BFIELD_WIDTH);
 		}
 		//shifting position for double - hex creatures
-		if(stack && stack->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
+		if(stack && stack->doubleWide())
 		{
 			if(attacker)
 			{
@@ -3483,7 +3483,7 @@ void CBattleHex::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 		{
 			char tabh[160];
 			const CStack * attackedStack = myInterface->curInt->cb->battleGetStackByPos(myNumber);
-			const std::string & attackedName = attackedStack->amount == 1 ? attackedStack->creature->nameSing : attackedStack->creature->namePl;
+			const std::string & attackedName = attackedStack->count == 1 ? attackedStack->type->nameSing : attackedStack->type->namePl;
 			sprintf(tabh, CGI->generaltexth->allTexts[220].c_str(), attackedName.c_str());
 			myInterface->console->alterTxt = std::string(tabh);
 			setAlterText = true;
@@ -3511,32 +3511,10 @@ void CBattleHex::clickRight(tribool down, bool previousState)
 	{
 		const CStack & myst = *myInterface->curInt->cb->battleGetStackByID(stID); //stack info
 		if(!myst.alive()) return;
-		StackState *pom = NULL;
 		if(down)
 		{
-			pom = new StackState();
-			const CGHeroInstance *h = myst.owner == myInterface->attackingHeroInstance->tempOwner ? myInterface->attackingHeroInstance : myInterface->defendingHeroInstance;
-
-			pom->attackBonus = myst.Attack() - myst.creature->attack;
-			pom->defenseBonus = myst.Defense() - myst.creature->defence;
-			pom->luck = myInterface->curInt->cb->battleGetStackLuck(myst.ID);
-			pom->morale = myInterface->curInt->cb->battleGetStackMorale(myst.ID);
-			pom->speedBonus = myst.Speed() - myst.creature->speed;
-			pom->healthBonus = myst.MaxHealth() - myst.creature->hitPoints;
-			if(myst.hasFeatureOfType(StackFeature::SIEGE_WEAPON))
-				pom->dmgMultiplier = h->getPrimSkillLevel(0) + 1;
-			else
-				pom->dmgMultiplier = 1;
-
-			pom->shotsLeft = myst.shots;
-			for(int vb=0; vb<myst.effects.size(); ++vb)
-			{
-				pom->effects.insert(myst.effects[vb].id);
-			}
-			pom->currentHealth = myst.firstHPleft;
-			GH.pushInt(new CCreInfoWindow(myst.creature->idNumber, 0, myst.amount, pom, 0, 0, NULL));
+			GH.pushInt(new CCreInfoWindow(myst));
 		}
-		delete pom;
 	}
 }
 
@@ -3660,7 +3638,7 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect
 	{
 		int bestMonsterID = -1;
 		int bestPower = 0;
-		for(TSlots::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( it->second.type->AIValue > bestPower)
 			{
@@ -3670,7 +3648,7 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect
 		}
 		SDL_BlitSurface(graphics->bigImgs[bestMonsterID], NULL, background, &genRect(64, 58, 21, 38));
 		//setting attackerName
-		attackerName =  CGI->creh->creatures[bestMonsterID].namePl;
+		attackerName =  CGI->creh->creatures[bestMonsterID]->namePl;
 	}
 	if(owner->defendingHeroInstance) //a hero defended
 	{
@@ -3682,7 +3660,7 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect
 	{
 		int bestMonsterID = -1;
 		int bestPower = 0;
-		for(TSlots::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( it->second.type->AIValue > bestPower)
 			{
@@ -3692,7 +3670,7 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect
 		}
 		SDL_BlitSurface(graphics->bigImgs[bestMonsterID], NULL, background, &genRect(64, 58, 391, 38));
 		//setting defenderName
-		defenderName =  CGI->creh->creatures[bestMonsterID].namePl;
+		defenderName =  CGI->creh->creatures[bestMonsterID]->namePl;
 	}
 
 	//printing attacker and defender's names
@@ -4124,16 +4102,16 @@ void CStackQueue::StackBox::showAll( SDL_Surface *to )
 		//SDL_UpdateRect(bg, 0, 0, 0, 0);
 		CSDL_Ext::blit8bppAlphaTo24bpp(bg, NULL, to, &genRect(bg->h, bg->w, pos.x, pos.y));
 		//blitAt(bg, pos, to);
-		blitAt(graphics->bigImgs[my->creature->idNumber], pos.x +9, pos.y + 1, to);
-		printAtMiddleLoc(makeNumberShort(my->amount), pos.w/2, pos.h - 12, FONT_MEDIUM, zwykly, to);
+		blitAt(graphics->bigImgs[my->type->idNumber], pos.x +9, pos.y + 1, to);
+		printAtMiddleLoc(makeNumberShort(my->count), pos.w/2, pos.h - 12, FONT_MEDIUM, zwykly, to);
 	}
 	else
 	{
 		blitAt(graphics->smallImgs[-2], pos, to);
-		blitAt(graphics->smallImgs[my->creature->idNumber], pos, to);
+		blitAt(graphics->smallImgs[my->type->idNumber], pos, to);
 		const SDL_Color &ownerColor = (my->owner == 255 ? *graphics->neutralColor : graphics->playerColors[my->owner]);
 		CSDL_Ext::drawBorder(to, pos, int3(ownerColor.r, ownerColor.g, ownerColor.b));
-		printAtMiddleLoc(makeNumberShort(my->amount), pos.w/2, pos.h - 8, FONT_TINY, zwykly, to);
+		printAtMiddleLoc(makeNumberShort(my->count), pos.w/2, pos.h - 8, FONT_TINY, zwykly, to);
 	}
 }
 

+ 2 - 2
client/CBattleInterface.h

@@ -382,7 +382,7 @@ private:
 	CBattleConsole * console;
 	CBattleHero * attackingHero, * defendingHero; //fighting heroes
 	CStackQueue *queue;
-	CCreatureSet * army1, * army2; //fighting armies
+	const CCreatureSet * army1, * army2; //fighting armies
 	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)
@@ -445,7 +445,7 @@ public:
 	unsigned int animIDhelper; //for giving IDs for animations
 	static CondSh<bool> animsAreDisplayed; //for waiting with the end of battle for end of anims
 
-	CBattleInterface(CCreatureSet * army1, CCreatureSet * army2, CGHeroInstance *hero1, CGHeroInstance *hero2, const SDL_Rect & myRect, CPlayerInterface * att, CPlayerInterface * defen); //c-tor
+	CBattleInterface(const CCreatureSet * army1, const CCreatureSet * army2, CGHeroInstance *hero1, CGHeroInstance *hero2, const SDL_Rect & myRect, CPlayerInterface * att, CPlayerInterface * defen); //c-tor
 	~CBattleInterface(); //d-tor
 
 	//std::vector<TimeInterested*> timeinterested; //animation handling

+ 18 - 18
client/CCastleInterface.cpp

@@ -287,7 +287,7 @@ void CHeroGSlot::clickLeft(tribool down, bool previousState)
 					LOCPLINT->showInfoDialog(tmp,std::vector<SComponent*>(), soundBase::sound_todo);
 					allow = false;
 				}
-				else if(!other->hero->army.slots.size()) //hero has no creatures - strange, but if we have appropriate error message...
+				else if(!other->hero->stacksCount()) //hero has no creatures - strange, but if we have appropriate error message...
 				{
 					LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[19],std::vector<SComponent*>(), soundBase::sound_todo); //This hero has no creatures.  A hero must have creatures before he can brave the dangers of the countryside.
 					allow = false;
@@ -986,7 +986,7 @@ void CCastleInterface::recreateBuildings()
 			Structure * st = CGI->townh->structures[town->subID][20];
 			buildings.push_back(new CBuildingRect(st));
 			s.insert(std::pair<int,int>(st->group,st->ID));
-			isThereShip = true;
+			isThereShip = true; 
 		}
 	}
 
@@ -1110,7 +1110,7 @@ void CCastleInterface::CCreaInfo::hover(bool on)
 	if(on)
 	{
 		std::string descr=CGI->generaltexth->allTexts[588];
-		boost::algorithm::replace_first(descr,"%s",CGI->creh->creatures[crid].namePl);
+		boost::algorithm::replace_first(descr,"%s",CGI->creh->creatures[crid]->namePl);
 		GH.statusbar->print(descr);
 	}
 	else
@@ -1142,12 +1142,12 @@ void CCastleInterface::CCreaInfo::clickRight(tribool down, bool previousState)
 		int summ=0, cnt=0;
 		int level=(bid-30)%CREATURES_PER_TOWN;
 		std::string descr=CGI->generaltexth->allTexts[589];//Growth of creature is number
-		boost::algorithm::replace_first(descr,"%s",CGI->creh->creatures[crid].nameSing);
+		boost::algorithm::replace_first(descr,"%s",CGI->creh->creatures[crid]->nameSing);
 		boost::algorithm::replace_first(descr,"%d", boost::lexical_cast<std::string>(
 			ci->town->creatureGrowth(level)));
 
 		descr +="\n"+CGI->generaltexth->allTexts[590];
-		summ = CGI->creh->creatures[crid].growth;
+		summ = CGI->creh->creatures[crid]->growth;
 		boost::algorithm::replace_first(descr,"%d", boost::lexical_cast<std::string>(summ));
 		
 
@@ -1159,19 +1159,19 @@ void CCastleInterface::CCreaInfo::clickRight(tribool down, bool previousState)
 		if(ci->town->town->hordeLvl[0]==level)//horde, x to summ
 		if((bld.find(18)!=bld.end()) || (bld.find(19)!=bld.end()))
 			summ+=AddToString(CGI->buildh->buildings[ci->town->subID][18]->Name()+" %+d",descr,
-				CGI->creh->creatures[crid].hordeGrowth);
+				CGI->creh->creatures[crid]->hordeGrowth);
 
 		if(ci->town->town->hordeLvl[1]==level)//horde, x to summ
 		if((bld.find(24)!=bld.end()) || (bld.find(25)!=bld.end()))
 			summ+=AddToString(CGI->buildh->buildings[ci->town->subID][24]->Name()+" %+d",descr,
-				CGI->creh->creatures[crid].hordeGrowth);
+				CGI->creh->creatures[crid]->hordeGrowth);
 
 		cnt = 0;
 		int creaLevel = (bid-30)%CREATURES_PER_TOWN;//dwellings have unupgraded units
 
 		for (std::vector<CGDwelling*>::const_iterator it = CGI->state->players[ci->town->tempOwner].dwellings.begin();
 			it !=CGI->state->players[ci->town->tempOwner].dwellings.end(); ++it)
-				if (CGI->creh->creatures[ci->town->town->basicCreatures[level]].idNumber == (*it)->creatures[0].second[0])
+				if (CGI->creh->creatures[ci->town->town->basicCreatures[level]]->idNumber == (*it)->creatures[0].second[0])
 					cnt++;//external dwellings count to summ
 		summ+=AddToString(CGI->generaltexth->allTexts[591],descr,cnt);
 
@@ -1180,9 +1180,9 @@ void CCastleInterface::CCreaInfo::clickRight(tribool down, bool previousState)
 		{
 			if(ch)
 			{
-				for(std::list<HeroBonus>::const_iterator i=ch->bonuses.begin(); i != ch->bonuses.end(); i++)
-					if(i->type == HeroBonus::CREATURE_GROWTH && i->subtype == level)
-						if (i->source == HeroBonus::ARTIFACT)
+				for(std::list<Bonus>::const_iterator i=ch->bonuses.begin(); i != ch->bonuses.end(); i++)
+					if(i->type == Bonus::CREATURE_GROWTH && i->subtype == level)
+						if (i->source == Bonus::ARTIFACT)
 							summ+=AddToString(CGI->arth->artifacts[i->id].Name()+" %+d",descr,i->val);
 			};
 			ch = ci->town->visitingHero;
@@ -1810,7 +1810,7 @@ void CFortScreen::draw( CCastleInterface * owner, bool first)
 	{
 		bool upgraded = owner->town->creatureDwelling(i,true);
 		bool present = owner->town->creatureDwelling(i,false);
-		CCreature *c = &CGI->creh->creatures[upgraded ? owner->town->town->upgradedCreatures[i] : owner->town->town->basicCreatures[i]];
+		CCreature *c = CGI->creh->creatures[upgraded ? owner->town->town->upgradedCreatures[i] : owner->town->town->basicCreatures[i]];
 		printAtMiddle(c->namePl,positions[i].x+79,positions[i].y+10,FONT_SMALL,zwykly,bg); //cr. name
 		blitAt(owner->bicons->ourImages[30+i+upgraded*7].bitmap,positions[i].x+4,positions[i].y+21,bg); //dwelling pic
 		printAtMiddle(CGI->buildh->buildings[owner->town->subID][30+i+upgraded*7]->Name(),positions[i].x+79,positions[i].y+100,FONT_SMALL,zwykly,bg); //dwelling name
@@ -1823,12 +1823,12 @@ void CFortScreen::draw( CCastleInterface * owner, bool first)
 
 		//attack
 		printAt(CGI->generaltexth->allTexts[190],positions[i].x+288,positions[i].y+5,FONT_SMALL,zwykly,bg);
-		SDL_itoa(c->attack,buf,10);
+		SDL_itoa(c->Attack(),buf,10);
 		printTo(buf,positions[i].x+381,positions[i].y+21,FONT_SMALL,zwykly,bg);
 
 		//defense
 		printAt(CGI->generaltexth->allTexts[191],positions[i].x+288,positions[i].y+25,FONT_SMALL,zwykly,bg);
-		SDL_itoa(c->defence,buf,10);
+		SDL_itoa(c->Defense(),buf,10);
 		printTo(buf,positions[i].x+381,positions[i].y+41,FONT_SMALL,zwykly,bg);
 
 		//damage
@@ -1845,12 +1845,12 @@ void CFortScreen::draw( CCastleInterface * owner, bool first)
 
 		//health
 		printAt(CGI->generaltexth->zelp[439].first,positions[i].x+288,positions[i].y+66,FONT_SMALL,zwykly,bg);
-		SDL_itoa(c->hitPoints,buf,10);
+		SDL_itoa(c->MaxHealth(),buf,10);
 		printTo(buf,positions[i].x+381,positions[i].y+82,FONT_SMALL,zwykly,bg);
 
 		//speed
 		printAt(CGI->generaltexth->zelp[441].first,positions[i].x+288,positions[i].y+87,FONT_SMALL,zwykly,bg);
-		SDL_itoa(c->speed,buf,10);
+		SDL_itoa(c->valOfBonuses(Bonus::STACKS_SPEED), buf,10);
 		printTo(buf,positions[i].x+381,positions[i].y+103,FONT_SMALL,zwykly,bg);
 
 		if(present)//growth
@@ -2016,10 +2016,10 @@ CBlacksmithDialog::CBlacksmithDialog(bool possible, int creMachineID, int aid, i
 	blitAt(bg2,64,50,bmp);
 	SDL_FreeSurface(bg2);
 
-	CCreatureAnimation cra(CGI->creh->creatures[creMachineID].animDefName);
+	CCreatureAnimation cra(CGI->creh->creatures[creMachineID]->animDefName);
 	cra.nextFrameMiddle(bmp,170,120,true,0,false);
 	char pom[75];
-	sprintf(pom,CGI->generaltexth->allTexts[274].c_str(),CGI->creh->creatures[creMachineID].nameSing.c_str()); //build a new ...
+	sprintf(pom,CGI->generaltexth->allTexts[274].c_str(),CGI->creh->creatures[creMachineID]->nameSing.c_str()); //build a new ...
 	printAtMiddle(pom,165,28,FONT_MEDIUM,tytulowy,bmp);
 	printAtMiddle(CGI->generaltexth->jktexts[43],165,218,FONT_MEDIUM,zwykly,bmp); //resource cost
 	SDL_itoa(CGI->arth->artifacts[aid].price,pom,10);

+ 3 - 3
client/CHeroWindow.cpp

@@ -286,7 +286,7 @@ void CHeroWindow::setHero(const CGHeroInstance *hero)
 
 	//setting formations
 	formations->onChange = 0;
-	formations->select(hero->army.formation,true);
+	formations->select(hero->formation,true);
 	formations->onChange = boost::bind(&CCallback::setFormation, LOCPLINT->cb, hero, _1);
 
 	morale->set(true, hero);
@@ -451,8 +451,8 @@ void CHeroWindow::redrawCurBack()
 	}
 
 	//morale and luck printing
-	blitAt(graphics->luck42->ourImages[curHero->getCurrentLuck()+3].bitmap, 239, 182, curBack);
-	blitAt(graphics->morale42->ourImages[curHero->getCurrentMorale()+3].bitmap, 181, 182, curBack);
+	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);
 

+ 7 - 7
client/CKingdomInterface.cpp

@@ -142,7 +142,7 @@ CKingdomInterface::CKingdomInterface()
 				if ( obj->ID == 17 )//dwelling, text is a plural name of a creature
 				{
 					objList[obj->subID].first += 1;
-					objList[obj->subID].second = & CGI->creh->creatures[CGI->objh->cregens[obj->subID]].namePl;
+					objList[obj->subID].second = & CGI->creh->creatures[CGI->objh->cregens[obj->subID]]->namePl;
 				}
 				else if (addObjects.find(curElm) != addObjects.end())
 				{//object from addObjects map, text is name of the object
@@ -552,11 +552,11 @@ void CKingdomInterface::CTownItem::setTown(const CGTownInstance * newTown)
 			crid = town->town->basicCreatures[i];
 
 		std::string descr=CGI->generaltexth->allTexts[588];
-		boost::algorithm::replace_first(descr,"%s",CGI->creh->creatures[crid].namePl);
+		boost::algorithm::replace_first(descr,"%s",CGI->creh->creatures[crid]->namePl);
 		creaGrowth[i]->hoverText = descr;
 
 		descr=CGI->generaltexth->heroscrn[1];
-		boost::algorithm::replace_first(descr,"%s",CGI->creh->creatures[crid].namePl);
+		boost::algorithm::replace_first(descr,"%s",CGI->creh->creatures[crid]->namePl);
 		creaCount[i]->hoverText = descr;
 		creaCount[i]->town = town;
 	}
@@ -910,12 +910,12 @@ void CKingdomInterface::CHeroItem::showAll(SDL_Surface * to)
 								(i<4)?(pos.y+26):(pos.y+6),to);
 		if (i>3) continue;//primary skills text
 		std::ostringstream str;
-		str << (hero->primSkills[i]);
+		str << (hero->getPrimSkillLevel(i));
 		CSDL_Ext::printAtMiddle(str.str(),pos.x+94+36*i,pos.y+66,FONT_SMALL,zwykly,to);
 	}
 	{//luck and morale pics, experience and mana text
-		blitAt(graphics->luck30->ourImages[hero->getCurrentLuck()+3].bitmap,pos.x+222,pos.y+30,to);
-		blitAt(graphics->morale30->ourImages[hero->getCurrentMorale()+3].bitmap,pos.x+222,pos.y+54,to);
+		blitAt(graphics->luck30->ourImages[hero->LuckVal()+3].bitmap,pos.x+222,pos.y+30,to);
+		blitAt(graphics->morale30->ourImages[hero->MoraleVal()+3].bitmap,pos.x+222,pos.y+54,to);
 		std::ostringstream str;
 		str << (hero->exp);
 		CSDL_Ext::printAtMiddle(str.str(),(pos.x+348),(pos.y+31),FONT_TINY,zwykly,to);
@@ -1100,7 +1100,7 @@ void CKingdomInterface::CTownItem::CCreaPlace::clickRight(tribool down, bool pre
 			crid = town->town->upgradedCreatures[type];
 		else
 			crid = town->town->basicCreatures[type];
-		GH.pushInt(new CCreInfoWindow(crid, 0, town->creatures[type].first, NULL, 0, 0, NULL));
+		GH.pushInt(new CCreInfoWindow(crid, 0, town->creatures[type].first));
 	}
 }
 

+ 8 - 8
client/CPlayerInterface.cpp

@@ -436,7 +436,7 @@ 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 ? &town->visitingHero->army : NULL;
+		c->garr->set2 = town->visitingHero;
 		c->garr->recreateSlots();
 	}
 	GH.totalRedraw();
@@ -499,7 +499,7 @@ void CPlayerInterface::buildChanged(const CGTownInstance *town, int buildingID,
 	}
 }
 
-void CPlayerInterface::battleStart(CCreatureSet *army1, CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side) //called by engine when battle starts; side=0 - left, side=1 - right
+void CPlayerInterface::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side)
 {
 	if(LOCPLINT != this)
 	{ //another local interface should do this
@@ -639,7 +639,7 @@ BattleAction CPlayerInterface::activeStack(int stackID) //called when it's turn
 		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->amount != 1) ? stack->creature->namePl : stack->creature->nameSing);
+			boost::algorithm::replace_first(hlp,"%s",(stack->count != 1) ? stack->type->namePl : stack->type->nameSing);
 			battleInt->displayEffect(20,stack->position);
 			battleInt->console->addText(hlp);
 		}
@@ -748,7 +748,7 @@ void CPlayerInterface::battleAttack(BattleAttack *ba)
 	{
 		const CStack *stack = cb->battleGetStackByID(ba->stackAttacking);
 		std::string hlp = CGI->generaltexth->allTexts[45];
-		boost::algorithm::replace_first(hlp,"%s",(stack->amount != 1) ? stack->creature->namePl.c_str() : stack->creature->nameSing.c_str());
+		boost::algorithm::replace_first(hlp,"%s",(stack->count != 1) ? stack->type->namePl.c_str() : stack->type->nameSing.c_str());
 		battleInt->console->addText(hlp);
 		battleInt->displayEffect(18,stack->position);
 	}
@@ -926,9 +926,9 @@ void CPlayerInterface::availableCreaturesChanged( const CGDwelling *town )
 	}
 }
 
-void CPlayerInterface::heroBonusChanged( const CGHeroInstance *hero, const HeroBonus &bonus, bool gain )
+void CPlayerInterface::heroBonusChanged( const CGHeroInstance *hero, const Bonus &bonus, bool gain )
 {
-	if(bonus.type == HeroBonus::NONE)	return;
+	if(bonus.type == Bonus::NONE)	return;
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	updateInfo(hero);
 }
@@ -1092,7 +1092,7 @@ void CPlayerInterface::objectPropertyChanged(const SetObjectProperty * sop)
 {
 	//redraw minimap if owner changed
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-	if(sop->what == 1)
+	if(sop->what == ObjProperty::OWNER)
 	{
 		const CGObjectInstance * obj = cb->getObjectInfo(sop->id);
 		std::set<int3> pos = obj->getBlockedPos();
@@ -1725,7 +1725,7 @@ void CPlayerInterface::gameOver(ui8 player, bool victory )
 	}
 }
 
-void CPlayerInterface::playerBonusChanged( const HeroBonus &bonus, bool gain )
+void CPlayerInterface::playerBonusChanged( const Bonus &bonus, bool gain )
 {
 
 }

+ 3 - 3
client/CPlayerInterface.h

@@ -166,8 +166,8 @@ public:
 	void newObject(const CGObjectInstance * obj);
 	void yourTurn();
 	void availableCreaturesChanged(const CGDwelling *town);
-	void heroBonusChanged(const CGHeroInstance *hero, const HeroBonus &bonus, bool gain);//if gain hero received bonus, else he lost it
-	void playerBonusChanged(const HeroBonus &bonus, bool gain);
+	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);
@@ -189,7 +189,7 @@ public:
 	void battleSpellCast(SpellCast *sc);
 	void battleStacksEffectsSet(SetStackEffect & sse); //called when a specific effect is set to stacks
 	void battleStacksAttacked(std::vector<BattleStackAttacked> & bsa);
-	void battleStart(CCreatureSet *army1, CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side); //called by engine when battle starts; side=0 - left, side=1 - right
+	void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side); //called by engine when battle starts; side=0 - left, side=1 - right
 	void battlefieldPrepared(int battlefieldType, std::vector<CObstacle*> obstacles); //called when battlefield is prepared, prior the battle beginning
 	void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks); //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

+ 1 - 1
client/CPreGame.cpp

@@ -1995,7 +1995,7 @@ void OptionsTab::SelectedBox::clickRight( tribool down, bool previousState )
 		{
 			int c = t.basicCreatures[i];
 			blitAt(graphics->smallImgs[c], x, y, bmp);
-			CSDL_Ext::printAtMiddleWB(CGI->creh->creatures[c].nameSing, x + 16, y + 45, FONT_TINY, 10, zwykly, bmp);
+			CSDL_Ext::printAtMiddleWB(CGI->creh->creatures[c]->nameSing, x + 16, y + 45, FONT_TINY, 10, zwykly, bmp);
 
 			if(i == 2)
 			{

+ 1 - 1
client/Client.cpp

@@ -157,7 +157,7 @@ void CClient::run()
 	} 
 	catch (const std::exception& e)
 	{	
-		tlog3 << "Lost connection to server, ending listening thread!\n";					
+		tlog3 << "Lost connection to server, ending listening thread!\n";
 		tlog1 << e.what() << std::endl;
 		if(!terminate) //rethrow (-> boom!) only if closing connection was unexpected
 		{

+ 136 - 213
client/GUIClasses.cpp

@@ -45,6 +45,7 @@
 #include "../hch/CVideoHandler.h"
 #include "../StartInfo.h"
 #include "CPreGame.h"
+#include "../lib/HeroBonus.h"
 
 /*
  * GUIClasses.cpp, part of VCMI engine
@@ -68,25 +69,6 @@ CFocusable * CFocusable::inputWithFocus;
 #undef min
 #undef max
 
-static StackState* getStackState(const CGObjectInstance *obj, int pos, bool town)
-{
-	const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(obj);
-	if(!h) return NULL;
-
-	StackState *pom = new StackState();
-	pom->shotsLeft = -1;
-	pom->healthBonus = h->valOfBonuses(HeroBonus::STACK_HEALTH);
-	pom->currentHealth = 0;
-	pom->attackBonus = h->getPrimSkillLevel(0);
-	pom->defenseBonus = h->getPrimSkillLevel(1);
-	pom->luck = h->getCurrentLuck();
-	pom->morale = h->getCurrentMorale(pos,town);
-	pom->speedBonus = h->valOfBonuses(HeroBonus::STACKS_SPEED);
-	pom->dmgMultiplier = 1;
-	return pom;
-}
-
-
 void CGarrisonSlot::hover (bool on)
 {
 	////Hoverable::hover(on);
@@ -143,7 +125,7 @@ void CGarrisonSlot::hover (bool on)
 			{
 				const CArmedInstance *highl = owner->highlighted->getObj(); 
 				if(  highl->needsLastStack()		//we are moving stack from hero's
-				  && highl->army.slots.size() == 1	//it's only stack
+				  && highl->stacksCount() == 1	//it's only stack
 				  && owner->highlighted->upg != upg	//we're moving it to the other garrison
 				  )
 				{
@@ -175,12 +157,8 @@ const CArmedInstance * CGarrisonSlot::getObj()
 
 void CGarrisonSlot::clickRight(tribool down, bool previousState)
 {
-	StackState *pom = getStackState(getObj(),ID, GH.topInt() == LOCPLINT->castleInt);
 	if(down && creature)
-	{
-		GH.pushInt(new CCreInfoWindow(creature->idNumber, 0, count, pom, 0, 0, NULL));
-	}
-	delete pom;
+		GH.pushInt(new CCreInfoWindow(*myStack));
 }
 void CGarrisonSlot::clickLeft(tribool down, bool previousState)
 {
@@ -196,7 +174,6 @@ void CGarrisonSlot::clickLeft(tribool down, bool previousState)
 		{
 			if(owner->highlighted == this) //view info
 			{
-				StackState *pom2 = getStackState(getObj(), ID, GH.topInt() == LOCPLINT->castleInt);
 				UpgradeInfo pom = LOCPLINT->cb->getUpgradeInfo(getObj(), ID);
 
 				CCreInfoWindow *creWindow = NULL;
@@ -204,14 +181,14 @@ void CGarrisonSlot::clickLeft(tribool down, bool previousState)
 				{
 
 					creWindow = new CCreInfoWindow(
-						creature->idNumber, 1, count, pom2,
+						*myStack, 1, 
 						boost::bind(&CCallback::upgradeCreature, LOCPLINT->cb, getObj(), ID, pom.newID[0]), //bind upgrade function
 						boost::bind(&CCallback::dismissCreature, LOCPLINT->cb, getObj(), ID), &pom);
 				}
 				else
 				{
 					creWindow = new CCreInfoWindow(
-						creature->idNumber, 1, count, pom2, 0, 
+						*myStack, 1, 0, 
 						boost::bind(&CCallback::dismissCreature, LOCPLINT->cb, getObj(), ID), NULL);
 				}
 
@@ -225,7 +202,6 @@ void CGarrisonSlot::clickLeft(tribool down, bool previousState)
 
 				show(screen2);
 				refr = true;
-				delete pom2;
 			}
 			else 
 			{
@@ -250,12 +226,12 @@ void CGarrisonSlot::clickLeft(tribool down, bool previousState)
 						int last = -1;
 						if(upg != owner->highlighted->upg) //not splitting within same army
 						{
-							if(owner->highlighted->getObj()->army.slots.size() == 1 //we're splitting away the last stack
+							if(owner->highlighted->getObj()->stacksCount() == 1 //we're splitting away the last stack
 								&& owner->highlighted->getObj()->needsLastStack() )
 							{
 								last = 0;
 							}
-							if(getObj()->army.slots.size() == 1 //destination army can't be emptied, unless we're rebalancing two stacks of same creature
+							if(getObj()->stacksCount() == 1 //destination army can't be emptied, unless we're rebalancing two stacks of same creature
 								&& owner->highlighted->creature == creature
 								&& getObj()->needsLastStack() )
 							{
@@ -323,13 +299,15 @@ void CGarrisonSlot::deactivate()
 	deactivateRClick();
 	deactivateHover();
 }
-CGarrisonSlot::CGarrisonSlot(CGarrisonInt *Owner, int x, int y, int IID, int Upg, const CCreature * Creature, int Count)
+CGarrisonSlot::CGarrisonSlot(CGarrisonInt *Owner, int x, int y, int IID, int Upg, const CStackInstance * Creature)
 {
+	//assert(Creature == CGI->creh->creatures[Creature->idNumber]);
 	active = false;
 	upg = Upg;
-	count = Count;
 	ID = IID;
-	creature = Creature;
+	myStack = Creature;
+	creature = Creature ? Creature->type : NULL;
+	count = Creature ? Creature->count : 0;
 	pos.x = x;
 	pos.y = y;
 	if(Owner->smallIcons)
@@ -487,12 +465,12 @@ void CGarrisonInt::createSlots()
 	if(set1)
 	{
 		sup = new std::vector<CGarrisonSlot*>(7,(CGarrisonSlot *)(NULL));
-		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(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);
 
 		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);
+				(*sup)[i] = new CGarrisonSlot(this, pos.x + (i*(w+interx)), pos.y,i,0,NULL);
 
 		if (shiftPos)
 			for (int i=shiftPos; i<sup->size(); i++)
@@ -504,14 +482,14 @@ void CGarrisonInt::createSlots()
 	if(set2)
 	{
 		sdown = new std::vector<CGarrisonSlot*>(7,(CGarrisonSlot *)(NULL));
-		for(TSlots::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, i->second.type,i->second.count);
+				new CGarrisonSlot(this, pos.x + (i->first*(w+interx)) + garOffset.x, pos.y + garOffset.y,i->first,1, &i->second);
 		}
 		for(int i=0; i<sdown->size(); i++)
 			if((*sdown)[i] == NULL)
-				(*sdown)[i] = new CGarrisonSlot(this, pos.x + (i*(w+interx)) + garOffset.x,	pos.y + garOffset.y,i,1, NULL, 0);
+				(*sdown)[i] = new CGarrisonSlot(this, pos.x + (i*(w+interx)) + garOffset.x,	pos.y + garOffset.y,i,1, NULL);
 		if (shiftPos)
 			for (int i=shiftPos; i<sup->size(); i++)
 			{
@@ -906,7 +884,7 @@ void SComponent::init(Etype Type, int Subtype, int Val)
 		subtitle = CGI->spellh->spells[Subtype].name;
 		break;
 	case creature:
-		subtitle = boost::lexical_cast<std::string>(Val) + " " + CGI->creh->creatures[Subtype].*(Val != 1 ? &CCreature::namePl : &CCreature::nameSing);
+		subtitle = boost::lexical_cast<std::string>(Val) + " " + CGI->creh->creatures[Subtype]->*(Val != 1 ? &CCreature::namePl : &CCreature::nameSing);
 		break;
 	case experience:
 		description = CGI->generaltexth->allTexts[241];
@@ -1835,7 +1813,7 @@ void CRecruitmentWindow::Max()
 void CRecruitmentWindow::Buy()
 {
 	int crid = creatures[which].ID,
-		dstslot = dst->army.getSlotFor(crid);
+		dstslot = dst-> getSlotFor(crid);
 
 	if(dstslot < 0) //no available slot
 	{
@@ -1843,7 +1821,7 @@ void CRecruitmentWindow::Buy()
 		if(dst->ID == HEROI_TYPE)
 		{
 			txt = CGI->generaltexth->allTexts[425]; //The %s would join your hero, but there aren't enough provisions to support them.
-			boost::algorithm::replace_first(txt, "%s", slider->value > 1 ? CGI->creh->creatures[crid].namePl : CGI->creh->creatures[crid].nameSing);
+			boost::algorithm::replace_first(txt, "%s", slider->value > 1 ? CGI->creh->creatures[crid]->namePl : CGI->creh->creatures[crid]->nameSing);
 		}
 		else
 		{
@@ -1908,7 +1886,7 @@ void CRecruitmentWindow::clickRight(tribool down, bool previousState)
 			const int sCREATURE_WIDTH = CREATURE_WIDTH; // gcc -O0 workaround
 			if(isItIn(&genRect(132,sCREATURE_WIDTH,pos.x+curx,pos.y+64),GH.current->motion.x,GH.current->motion.y))
 			{
-				CCreInfoWindow *popup = new CCreInfoWindow(creatures[i].ID, 0, 0, NULL, NULL, NULL, NULL);
+				CCreInfoWindow *popup = new CCreInfoWindow(creatures[i].ID, 0, 0);
 				GH.pushInt(popup);
 				break;
 			}
@@ -1952,7 +1930,7 @@ void CRecruitmentWindow::show(SDL_Surface * to)
 	printAtMiddle(pom,pos.x+205,pos.y+254,FONT_SMALL,zwykly,to);
 	SDL_itoa(slider->value,pom,10); //recruit
 	printAtMiddle(pom,pos.x+279,pos.y+254,FONT_SMALL,zwykly,to);
-	printAtMiddle(CGI->generaltexth->allTexts[16] + " " + CGI->creh->creatures[creatures[which].ID].namePl,pos.x+243,pos.y+32,FONT_BIG,tytulowy,to); //eg "Recruit Dragon flies"
+	printAtMiddle(CGI->generaltexth->allTexts[16] + " " + CGI->creh->creatures[creatures[which].ID]->namePl,pos.x+243,pos.y+32,FONT_BIG,tytulowy,to); //eg "Recruit Dragon flies"
 
 	int curx = pos.x+115-creatures[which].res.size()*16;
 	for(int i=0;i<creatures[which].res.size();i++)
@@ -2059,7 +2037,7 @@ void CRecruitmentWindow::initCres()
 
 			cur.amount = dwelling->creatures[i].first;
 			cur.ID = dwelling->creatures[i].second[j];
-			const CCreature *cre = &CGI->creh->creatures[cur.ID];
+			const CCreature *cre = CGI->creh->creatures[cur.ID];
 			cur.pic = new CCreaturePic(cre);
 
 			for(int k=0; k<cre->cost.size(); k++)
@@ -2105,11 +2083,11 @@ CSplitWindow::CSplitWindow(int cid, int max, CGarrisonInt *Owner, int Last, int
 	slider = new CSlider(pos.x+21,pos.y+194,257,boost::bind(&CSplitWindow::sliderMoved,this,_1),0,sliderPositions,val,true);
 	a1 = max-val;
 	a2 = val;
-	anim = new CCreaturePic(&CGI->creh->creatures[cid]);
+	anim = new CCreaturePic(CGI->creh->creatures[cid]);
 	anim->anim->setType(1);
 
 	std::string title = CGI->generaltexth->allTexts[256];
-	boost::algorithm::replace_first(title,"%s",CGI->creh->creatures[cid].namePl);
+	boost::algorithm::replace_first(title,"%s",CGI->creh->creatures[cid]->namePl);
 	printAtMiddle(title,150,34,FONT_BIG,tytulowy,bitmap);
 }
 
@@ -2226,7 +2204,7 @@ void CSplitWindow::clickLeft(tribool down, bool previousState)
 void CCreInfoWindow::show(SDL_Surface * to)
 {
 	char pom[15];
-	blitAt(bitmap,pos.x,pos.y,to);
+	blitAt(*bitmap,pos.x,pos.y,to);
 	anim->blitPic(to,pos.x+21,pos.y+48,(type) && !(anf%4));
 	if(++anf==4) 
 		anf=0;
@@ -2240,130 +2218,11 @@ void CCreInfoWindow::show(SDL_Surface * to)
 		ok->show(to);
 }
 
-CCreInfoWindow::CCreInfoWindow(int Cid, int Type, int creatureCount, StackState *State, boost::function<void()> Upg, boost::function<void()> Dsm, UpgradeInfo *ui)
+CCreInfoWindow::CCreInfoWindow(const CStackInstance &st, int Type, boost::function<void()> Upg, boost::function<void()> Dsm, UpgradeInfo *ui)
 	: type(Type), dsm(Dsm), dismiss(0), upgrade(0), ok(0)
 {
-	//active = false;
-	anf = 0;
-	c = &CGI->creh->creatures[Cid];
-	SDL_Surface *hhlp = BitmapHandler::loadBitmap("CRSTKPU.bmp");
-	graphics->blueToPlayersAdv(hhlp,LOCPLINT->playerID);
-	bitmap = SDL_ConvertSurface(hhlp,screen->format,0);
-	SDL_SetColorKey(bitmap,SDL_SRCCOLORKEY,SDL_MapRGB(bitmap->format,0,255,255));
-	SDL_FreeSurface(hhlp);
-	pos.x = screen->w/2 - bitmap->w/2;
-	pos.y = screen->h/2 - bitmap->h/2;
-	pos.w = bitmap->w;
-	pos.h = bitmap->h;
-	anim = new CCreaturePic(c);
-	if(!type) anim->anim->setType(2);
-
-	char pom[75];int hlp=0;
-
-	if(creatureCount)
-	{
-		SDL_itoa(creatureCount,pom,10);
-		count = pom;
-	}
-
-	printAtMiddle(c->namePl,149,30,FONT_SMALL,tytulowy,bitmap); //creature name
-
-	//atttack
-	printAt(CGI->generaltexth->primarySkillNames[0],155,48,FONT_SMALL,zwykly,bitmap);
-	SDL_itoa(c->attack,pom,10);
-	if(State && State->attackBonus)
-	{
-		int hlp;
-		if(c->attack > 0)
-			hlp = log10f(c->attack)+2;
-		else
-			hlp = 2;
-		pom[hlp-1] = ' '; pom[hlp] = '(';
-		SDL_itoa(c->attack+State->attackBonus,pom+hlp+1,10);
-		hlp += 2+(int)log10f(State->attackBonus+c->attack);
-		pom[hlp] = ')'; pom[hlp+1] = '\0';
-	}
-	printTo(pom,276,61,FONT_SMALL,zwykly,bitmap);
-
-	//defense
-	printAt(CGI->generaltexth->primarySkillNames[1],155,67,FONT_SMALL,zwykly,bitmap);
-	SDL_itoa(c->defence,pom,10);
-	if(State && State->defenseBonus)
-	{
-		int hlp;
-		if(c->defence > 0)
-			hlp = log10f(c->defence)+2;
-		else
-			hlp = 2;
-		pom[hlp-1] = ' '; pom[hlp] = '(';
-		SDL_itoa(c->defence+State->defenseBonus,pom+hlp+1,10);
-		hlp += 2+(int)log10f(State->defenseBonus+c->defence);
-		pom[hlp] = ')'; pom[hlp+1] = '\0';
-	}
-	printTo(pom,276,80,FONT_SMALL,zwykly,bitmap);
-
-	//shots
-	if(c->shots)
-	{
-		printAt(CGI->generaltexth->allTexts[198], 155, 86, FONT_SMALL, zwykly, bitmap);
-		if(State  &&  State->shotsLeft >= 0)
-			sprintf(pom,"%d (%d)", c->shots, State->shotsLeft);
-		else
-			SDL_itoa(c->shots, pom, 10);
-		printTo(pom, 276, 99, FONT_SMALL, zwykly, bitmap);
-	}
-
-	//damage
-	int dmgMin = c->damageMin * (State ? State->dmgMultiplier : 1);
-	int dmgMax = c->damageMax * (State ? State->dmgMultiplier : 1);
-
-	printAt(CGI->generaltexth->allTexts[199], 155, 105, FONT_SMALL, zwykly, bitmap);
-	SDL_itoa(dmgMin, pom, 10);
-	if(dmgMin > 0)
-		hlp = log10f(dmgMin) + 2;
-	else
-		hlp = 2;
-	pom[hlp-1]=' '; pom[hlp]='-'; pom[hlp+1]=' ';
-	SDL_itoa(dmgMax, pom+hlp+2, 10);
-	printTo(pom, 276, 118, FONT_SMALL, zwykly, bitmap);
-
-	//health
-	printAt(CGI->generaltexth->allTexts[388],155,124,FONT_SMALL,zwykly,bitmap);
-	if(State  &&  State->healthBonus)
-		sprintf(pom,"%d (%d)",c->hitPoints, c->hitPoints + State->healthBonus);
-	else
-		SDL_itoa(c->hitPoints,pom,10);
-	printTo(pom,276,137,FONT_SMALL,zwykly,bitmap);
-
-	//remaining health
-	if(State && State->currentHealth)
-	{
-		printAt(CGI->generaltexth->allTexts[200],155,143,FONT_SMALL,zwykly,bitmap);
-		SDL_itoa(State->currentHealth,pom,10);
-		printTo(pom,276,156,FONT_SMALL,zwykly,bitmap);
-	}
-
-	//speed
-	printAt(CGI->generaltexth->zelp[441].first,155,162,FONT_SMALL,zwykly,bitmap);
-	SDL_itoa(c->speed,pom,10);
-	if(State && State->speedBonus)
-	{
-		int hlp;
-		if(c->speed > 0)
-			hlp = log10f(c->speed)+2;
-		else
-			hlp = 2;
-		pom[hlp-1] = ' '; pom[hlp] = '(';
-		SDL_itoa(c->speed + State->speedBonus, pom+hlp+1, 10);
-		hlp += 2+(int)log10f(c->speed + State->speedBonus);
-		pom[hlp] = ')'; pom[hlp+1] = '\0';
-	}
-	printTo(pom,276,175,FONT_SMALL,zwykly,bitmap);
-
-
-	//luck and morale
-	blitAt(graphics->morale42->ourImages[(State)?(State->morale+3):(3)].bitmap,24,189,bitmap);
-	blitAt(graphics->luck42->ourImages[(State)?(State->luck+3):(3)].bitmap,77,189,bitmap);
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	init(st.type, &st, st.count);
 
 	//print abilities text - if r-click popup
 	if(type)
@@ -2373,9 +2232,9 @@ CCreInfoWindow::CCreInfoWindow(int Cid, int Type, int creatureCount, StackState
 			bool enough = true;
 			for(std::set<std::pair<int,int> >::iterator i=ui->cost[0].begin(); i!=ui->cost[0].end(); i++) //calculate upgrade cost
 			{
-				if(LOCPLINT->cb->getResourceAmount(i->first) < i->second*creatureCount)
+				if(LOCPLINT->cb->getResourceAmount(i->first) < i->second*st.count)
 					enough = false;
-				upgResCost.push_back(new SComponent(SComponent::resource,i->first,i->second*creatureCount)); 
+				upgResCost.push_back(new SComponent(SComponent::resource,i->first,i->second*st.count)); 
 			}
 
 			if(enough)
@@ -2385,11 +2244,11 @@ CCreInfoWindow::CCreInfoWindow(int Cid, int Type, int creatureCount, StackState
 				fs[0] += boost::bind(&CCreInfoWindow::close,this);
 				CFunctionList<void()> cfl;
 				cfl = boost::bind(&CPlayerInterface::showYesNoDialog,LOCPLINT,CGI->generaltexth->allTexts[207],boost::ref(upgResCost),fs[0],fs[1],false);
-				upgrade = new AdventureMapButton("",CGI->generaltexth->zelp[446].second,cfl,pos.x+76,pos.y+237,"IVIEWCR.DEF",SDLK_u);
+				upgrade = new AdventureMapButton("",CGI->generaltexth->zelp[446].second,cfl,76,237,"IVIEWCR.DEF",SDLK_u);
 			}
 			else
 			{
-				upgrade = new AdventureMapButton("",CGI->generaltexth->zelp[446].second,boost::function<void()>(),pos.x+76,pos.y+237,"IVIEWCR.DEF");
+				upgrade = new AdventureMapButton("",CGI->generaltexth->zelp[446].second,boost::function<void()>(),76,237,"IVIEWCR.DEF");
 				upgrade->callback.funcs.clear();
 				upgrade->bitmapOffset = 2;
 			}
@@ -2403,53 +2262,124 @@ CCreInfoWindow::CCreInfoWindow(int Cid, int Type, int creatureCount, StackState
 			fs[0] += boost::bind(&CCreInfoWindow::close,this);//close this window
 			CFunctionList<void()> cfl;
 			cfl = boost::bind(&CPlayerInterface::showYesNoDialog,LOCPLINT,CGI->generaltexth->allTexts[12],std::vector<SComponent*>(),fs[0],fs[1],false);
-			dismiss = new AdventureMapButton("",CGI->generaltexth->zelp[445].second,cfl,pos.x+21,pos.y+237,"IVIEWCR2.DEF",SDLK_d);
+			dismiss = new AdventureMapButton("",CGI->generaltexth->zelp[445].second,cfl,21,237,"IVIEWCR2.DEF",SDLK_d);
 		}
-		ok = new AdventureMapButton("",CGI->generaltexth->zelp[445].second,boost::bind(&CCreInfoWindow::close,this),pos.x+216,pos.y+237,"IOKAY.DEF",SDLK_RETURN);
+		ok = new AdventureMapButton("",CGI->generaltexth->zelp[445].second,boost::bind(&CCreInfoWindow::close,this),216,237,"IOKAY.DEF",SDLK_RETURN);
 	}
 	else
 	{
-		printAtWB(c->abilityText,17,231,FONT_SMALL,35,zwykly,bitmap);
+		printAtWB(c->abilityText,17,231,FONT_SMALL,35,zwykly,*bitmap);
 	}
 
-	//spell effects
-	if(State)
+	//if we are displying window fo r stack in battle, there are several more things that we need to display
+	if(const CStack *battleStack = dynamic_cast<const CStack*>(&st))
 	{
+		//spell effects
 		int printed=0; //how many effect pics have been printed
-		for(std::set<int>::const_iterator it = State->effects.begin(); it!=State->effects.end(); ++it)
+		BOOST_FOREACH(const CStack::StackEffect &effect, battleStack->effects)
 		{
-			blitAt(graphics->spellEffectsPics->ourImages[*it + 1].bitmap, 127 + 52 * printed, 186, bitmap); 
+			blitAt(graphics->spellEffectsPics->ourImages[effect.id + 1].bitmap, 127 + 52 * printed, 186, *bitmap); 
 			++printed;
-			if(printed >= 3)
-			{
+			if(printed >= 3) //we can fit only 3 effects
 				break;
-			}
 		}
+
+		//print current health
+		printLine(5, CGI->generaltexth->allTexts[200], battleStack->firstHPleft);
+	}
+}
+
+
+
+void CCreInfoWindow::printLine(int nr, const std::string &text, int baseVal, int val/*=-1*/, bool range/*=false*/)
+{
+	printAt(text, 155, 48 + nr*19, FONT_SMALL, zwykly, *bitmap);
+
+	std::string hlp;
+	if(range && baseVal != val)
+		hlp = boost::str(boost::format("%d - %d") % baseVal % val);
+	else if(baseVal != val && val>=0)
+		hlp = boost::str(boost::format("%d (%d)") % baseVal % val);
+	else
+		hlp = boost::lexical_cast<std::string>(baseVal);
+
+	printTo(hlp, 276, 61 + nr*19, FONT_SMALL, zwykly, *bitmap);
+}
+
+void CCreInfoWindow::init(const CCreature *cre, const CStackInstance *stack, int creatureCount)
+{
+	const CBonusSystemNode *finalNode = NULL;
+	if(stack)
+		finalNode = stack;
+	else
+		finalNode = cre;
+
+	anf = 0;
+	c = cre;
+
+	bitmap = new CPicture("CRSTKPU.bmp");
+	graphics->blueToPlayersAdv(*bitmap, LOCPLINT->playerID);
+	bitmap->convertToScreenBPP();
+	pos = bitmap->center();
+
+	{
+		BLOCK_CAPTURING;
+		anim = new CCreaturePic(c);
+	}
+
+	if(!type) anim->anim->setType(2);
+
+	count = boost::lexical_cast<std::string>(creatureCount);
+
+
+	printAtMiddle(c->namePl,149,30,FONT_SMALL,tytulowy,*bitmap); //creature name
+	
+	printLine(0, CGI->generaltexth->primarySkillNames[0], cre->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK), finalNode->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK));
+	printLine(1, CGI->generaltexth->primarySkillNames[0], cre->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE), finalNode->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE));
+	if(c->shots)
+		printLine(2, CGI->generaltexth->allTexts[198], c->shots);
+
+	//TODO
+	int dmgMultiply = 1;
+	if(stack && stack->hasBonusOfType(Bonus::SIEGE_WEAPON))
+		dmgMultiply += stack->armyObj->Attack(); 
+
+	printLine(3, CGI->generaltexth->allTexts[199], c->damageMin * dmgMultiply, c->damageMax * dmgMultiply, true);
+	printLine(4, CGI->generaltexth->allTexts[388], cre->valOfBonuses(Bonus::STACK_HEALTH), finalNode->valOfBonuses(Bonus::STACK_HEALTH));
+	printLine(6, CGI->generaltexth->zelp[441].first, cre->valOfBonuses(Bonus::STACKS_SPEED), finalNode->valOfBonuses(Bonus::STACKS_SPEED));
+
+	//luck and morale
+	int luck = 3, morale = 3;
+	if(stack)
+	{
+		//add modifiers
+		luck += stack->LuckVal();
+		morale += stack->MoraleVal();
 	}
+
+	blitAt(graphics->morale42->ourImages[morale].bitmap, 24, 189, *bitmap);
+	blitAt(graphics->luck42->ourImages[luck].bitmap, 77, 189, *bitmap);
+}
+
+CCreInfoWindow::CCreInfoWindow(int Cid, int Type, int creatureCount)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	const CCreature *cre = CGI->creh->creatures[Cid];
+	init(cre, NULL, creatureCount);
 }
+
 CCreInfoWindow::~CCreInfoWindow()
 {
-	SDL_FreeSurface(bitmap);
 	delete anim;
-	delete upgrade;
-	delete ok;
-	delete dismiss;
 	for(int i=0; i<upgResCost.size();i++)
 		delete upgResCost[i];
 }
 
 void CCreInfoWindow::activate()
 {
-	//if(active) return;
-	//active = true;
+	CIntObject::activate();
 	if(!type)
 		activateRClick();
-	if(ok)
-		ok->activate();
-	if(dismiss)
-		dismiss->activate();
-	if(upgrade)
-		upgrade->activate();
 }
 
 void CCreInfoWindow::close()
@@ -2475,16 +2405,9 @@ void CCreInfoWindow::keyPressed (const SDL_KeyboardEvent & key)
 
 void CCreInfoWindow::deactivate()
 {
-	//if(!active) return;
-	//active = false;
 	if(!type)
 		deactivateRClick();
-	if(ok)
-		ok->deactivate();
-	if(dismiss)
-		dismiss->deactivate();
-	if(upgrade)
-		upgrade->deactivate();
+	CIntObject::deactivate();
 }
 
 void CLevelWindow::close()
@@ -4511,10 +4434,10 @@ void CExchangeWindow::prepareBackground()
 		printAtMiddle( makeNumberShort(heroInst[b]->mana), 155 + 490*b, 71, FONT_SMALL, zwykly, bg );
 
 		//setting morale
-		blitAt(graphics->morale30->ourImages[heroInst[b]->getCurrentMorale()+3].bitmap, 177 + 490*b, 45, bg);
+		blitAt(graphics->morale30->ourImages[heroInst[b]->MoraleVal()+3].bitmap, 177 + 490*b, 45, bg);
 
 		//setting luck
-		blitAt(graphics->luck30->ourImages[heroInst[b]->getCurrentLuck()+3].bitmap, 213 + 490*b, 45, bg);
+		blitAt(graphics->luck30->ourImages[heroInst[b]->LuckVal()+3].bitmap, 213 + 490*b, 45, bg);
 	}
 
 	//printing portraits
@@ -5096,16 +5019,16 @@ CThievesGuildWindow::~CThievesGuildWindow()
 
 
 
-void MoraleLuckBox::set( bool morale, const CGHeroInstance *hero, int slot /*= -1*/ )
+void MoraleLuckBox::set(bool morale, const CGHeroInstance *hero)
 {
 	int mrlv = -9, mrlt = -9;
-	std::vector<std::pair<int,std::string> > mrl;
+	TModDescr mrl;
 
 	if(morale)
 	{
 		//setting morale
-		mrl = hero->getCurrentMoraleModifiers(slot);
-		mrlv = hero->getCurrentMorale(slot);
+		hero->getModifiersWDescr(mrl, Bonus::MORALE);
+		mrlv = hero->MoraleVal();
 		mrlt = (mrlv>0)-(mrlv<0); //signum: -1 - bad morale, 0 - neutral, 1 - good
 		hoverText = CGI->generaltexth->heroscrn[4 - mrlt];
 		baseType = SComponent::morale;
@@ -5121,8 +5044,8 @@ void MoraleLuckBox::set( bool morale, const CGHeroInstance *hero, int slot /*= -
 	else
 	{
 		//setting luck
-		mrl = hero->getCurrentLuckModifiers(slot);
-		mrlv = hero->getCurrentLuck(slot);
+		hero->getModifiersWDescr(mrl, Bonus::LUCK);
+		mrlv = hero->LuckVal();
 		mrlt = (mrlv>0)-(mrlv<0); //signum: -1 - bad luck, 0 - neutral, 1 - good
 		hoverText = CGI->generaltexth->heroscrn[7 - mrlt];
 		baseType = SComponent::luck;

+ 11 - 6
client/GUIClasses.h

@@ -64,6 +64,7 @@ class CArtifactsOfHero;
 class CResDataBar;
 struct SPuzzleInfo;
 class CGGarrison;
+class CStackInstance;
 
 extern SDL_Color tytulowy, tlo, zwykly ;
 
@@ -199,7 +200,8 @@ class CGarrisonSlot : public CIntObject
 {
 public:
 	CGarrisonInt *owner;
-	const CCreature * creature; //creature in slot
+	const CStackInstance *myStack; //NULL if slot is empty
+	const CCreature *creature;
 	int count; //number of creatures
 	int upg; //0 - up garrison, 1 - down garrison
 	bool active; //TODO: comment me
@@ -211,7 +213,7 @@ public:
 	void activate();
 	void deactivate();
 	void show(SDL_Surface * to);
-	CGarrisonSlot(CGarrisonInt *Owner, int x, int y, int IID, int Upg=0, const CCreature * Creature=NULL, int Count=0);
+	CGarrisonSlot(CGarrisonInt *Owner, int x, int y, int IID, int Upg=0, const CStackInstance * Creature=NULL);
 	~CGarrisonSlot(); //d-tor
 };
 
@@ -700,7 +702,7 @@ class MoraleLuckBox : public LRClickableAreaWTextComp
 {
 public:
 	
-	void set(bool morale, const CGHeroInstance *hero, int slot = -1); //slot -1 means only hero modifiers
+	void set(bool morale, const CGHeroInstance *hero);
 };
 
 class LRClickableAreaOpenHero: public LRClickableAreaWTextComp
@@ -725,19 +727,22 @@ class CCreInfoWindow : public CIntObject
 public:
 	//bool active; //TODO: comment me
 	int type;//0 - rclick popup; 1 - normal window
-	SDL_Surface *bitmap; //background
+	CPicture *bitmap; //background
 	char anf; //animation counter
 	std::string count; //creature count in text format
 
 	boost::function<void()> dsm; //dismiss button callback
 	CCreaturePic *anim; //related creature's animation
-	CCreature *c; //related creature
+	const CCreature *c; //related creature
 	std::vector<SComponent*> upgResCost; //cost of upgrade (if not possible then empty)
 
 	//MoraleLuckBox *luck, *morale;
 
 	AdventureMapButton *dismiss, *upgrade, *ok;
-	CCreInfoWindow(int Cid, int Type, int creatureCount, StackState *State, boost::function<void()> Upg, boost::function<void()> Dsm, UpgradeInfo *ui); //c-tor
+	CCreInfoWindow(const CStackInstance &st, int Type = 0, boost::function<void()> Upg = 0, boost::function<void()> Dsm = 0, UpgradeInfo *ui = NULL); //c-tor
+	CCreInfoWindow(int Cid, int Type, int creatureCount); //c-tor
+	void init(const CCreature *cre, const CStackInstance *stack, int creatureCount);
+	void printLine(int nr, const std::string &text, int baseVal, int val=-1, bool range=false);
 	~CCreInfoWindow(); //d-tor
 	void activate();
 	void close();

+ 2 - 2
client/Graphics.cpp

@@ -51,7 +51,7 @@ 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 (TSlots::const_iterator i = curh.army.Slots().begin(); i!=curh.army.Slots().end();i++)
 	{
 		blitAt(graphics->smallImgs[(*i).second.type->idNumber],slotsPos[(*i).first].first+1,slotsPos[(*i).first].second+1,ret);
 		if(curh.details)
@@ -108,7 +108,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 (TSlots::const_iterator i=curh.army.Slots().begin(); i!=curh.army.Slots().end();i++)
 	{
 		//if(!i->second.second)
 		//	continue;

+ 6 - 6
client/NetPacksClient.cpp

@@ -314,11 +314,11 @@ void SetHeroArtifacts::applyCl( CClient *cl )
 
 	player->heroArtifactSetChanged(h);
 
-	BOOST_FOREACH(HeroBonus *bonus, gained)
+	BOOST_FOREACH(Bonus *bonus, gained)
 	{
 		player->heroBonusChanged(h,*bonus,true);
 	}
-	BOOST_FOREACH(HeroBonus *bonus, lost)
+	BOOST_FOREACH(Bonus *bonus, lost)
 	{
 		player->heroBonusChanged(h,*bonus,false);
 	}
@@ -427,13 +427,13 @@ void BattleStart::applyCl( CClient *cl )
 		def = NULL;
 
 
-	new CBattleInterface(&info->army1, &info->army2, info->heroes[0], info->heroes[1], genRect(600, 800, (conf.cc.resx - 800)/2, (conf.cc.resy - 600)/2), att, def);
+	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->army1, &info->army2, info->tile, info->heroes[0], info->heroes[1], 0);
+		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->army1, &info->army2, info->tile, info->heroes[0], info->heroes[1], 1);
+		cl->playerint[info->side2]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 1);
 }
 
 void BattleNextRound::applyCl( CClient *cl )
@@ -448,7 +448,7 @@ void BattleSetActiveStack::applyCl( CClient *cl )
 {
 	CStack * activated = GS(cl)->curB->getStack(stack);
 	int playerToCall = -1; //player that will move activated stack
-	if( activated->hasFeatureOfType(StackFeature::HYPNOTIZED) )
+	if( activated->hasBonusOfType(Bonus::HYPNOTIZED) )
 	{
 		playerToCall = ( GS(cl)->curB->side1 == activated->owner ? GS(cl)->curB->side2 : GS(cl)->curB->side1 );
 	}

+ 0 - 1
global.h

@@ -297,7 +297,6 @@ 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;

+ 140 - 138
hch/CArtHandler.cpp

@@ -318,21 +318,21 @@ void CArtHandler::sortArts()
 	}
 }
 
-void CArtHandler::giveArtBonus( int aid, HeroBonus::BonusType type, int val, int subtype )
+void CArtHandler::giveArtBonus( int aid, Bonus::BonusType type, int val, int subtype, int valType )
 {
-	artifacts[aid].bonuses.push_back(HeroBonus(HeroBonus::PERMANENT,type,HeroBonus::ARTIFACT,val,aid,subtype));
-
-	if(type == HeroBonus::MORALE || HeroBonus::LUCK || HeroBonus::MORALE_AND_LUCK)
-		artifacts[aid].bonuses.back().description = "\n" + artifacts[aid].Name()  + (val > 0 ? " +" : " ") 	
-													+ boost::lexical_cast<std::string>(val);
+	Bonus added(Bonus::PERMANENT,type,Bonus::ARTIFACT,val,aid,subtype);
+	added.valType = valType;
+	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);
 }
 
 void CArtHandler::addBonuses()
 {
-	#define ART_PRIM_SKILL(ID, whichSkill, val) giveArtBonus(ID,HeroBonus::PRIMARY_SKILL,val,whichSkill)
-	#define ART_MORALE(ID, val) giveArtBonus(ID,HeroBonus::MORALE,val)
-	#define ART_LUCK(ID, val) giveArtBonus(ID,HeroBonus::LUCK,val)
-	#define ART_MORALE_AND_LUCK(ID, val) giveArtBonus(ID,HeroBonus::MORALE_AND_LUCK,val)
+	#define ART_PRIM_SKILL(ID, whichSkill, val) giveArtBonus(ID,Bonus::PRIMARY_SKILL,val,whichSkill)
+	#define ART_MORALE(ID, val) giveArtBonus(ID,Bonus::MORALE,val)
+	#define ART_LUCK(ID, val) giveArtBonus(ID,Bonus::LUCK,val)
+	#define ART_MORALE_AND_LUCK(ID, val) giveArtBonus(ID,Bonus::MORALE_AND_LUCK,val)
 	#define ART_ALL_PRIM_SKILLS(ID, val) ART_PRIM_SKILL(ID,0,val); ART_PRIM_SKILL(ID,1,val); ART_PRIM_SKILL(ID,2,val); ART_PRIM_SKILL(ID,3,val)
 	#define ART_ATTACK_AND_DEFENSE(ID, val) ART_PRIM_SKILL(ID,0,val); ART_PRIM_SKILL(ID,1,val)
 	#define ART_POWER_AND_KNOWLEDGE(ID, val) ART_PRIM_SKILL(ID,2,val); ART_PRIM_SKILL(ID,3,val)
@@ -400,185 +400,187 @@ void CArtHandler::addBonuses()
 	ART_LUCK(47,+1); //Cards of Prophecy
 	ART_LUCK(48,+1); //Ladybird of Luck
 	ART_MORALE(49,+1); //Badge of Courage -> +1 morale and immunity to hostile mind spells:
-	giveArtBonus(49,HeroBonus::SPELL_IMMUNITY,50);//sorrow
-	giveArtBonus(49,HeroBonus::SPELL_IMMUNITY,59);//berserk
-	giveArtBonus(49,HeroBonus::SPELL_IMMUNITY,60);//hypnotize
-	giveArtBonus(49,HeroBonus::SPELL_IMMUNITY,61);//forgetfulness
-	giveArtBonus(49,HeroBonus::SPELL_IMMUNITY,62);//blind
+	giveArtBonus(49,Bonus::SPELL_IMMUNITY,50);//sorrow
+	giveArtBonus(49,Bonus::SPELL_IMMUNITY,59);//berserk
+	giveArtBonus(49,Bonus::SPELL_IMMUNITY,60);//hypnotize
+	giveArtBonus(49,Bonus::SPELL_IMMUNITY,61);//forgetfulness
+	giveArtBonus(49,Bonus::SPELL_IMMUNITY,62);//blind
 	ART_MORALE(50,+1); //Crest of Valor
 	ART_MORALE(51,+1); //Glyph of Gallantry
 
-	giveArtBonus(52,HeroBonus::SIGHT_RADIOUS,+1);//Speculum
-	giveArtBonus(53,HeroBonus::SIGHT_RADIOUS,+1);//Spyglass
+	giveArtBonus(52,Bonus::SIGHT_RADIOUS,+1);//Speculum
+	giveArtBonus(53,Bonus::SIGHT_RADIOUS,+1);//Spyglass
 
 	//necromancy bonus
-	giveArtBonus(54,HeroBonus::SECONDARY_SKILL_PREMY,+5,12);//Amulet of the Undertaker
-	giveArtBonus(55,HeroBonus::SECONDARY_SKILL_PREMY,+10,12);//Vampire's Cowl
-	giveArtBonus(56,HeroBonus::SECONDARY_SKILL_PREMY,+15,12);//Dead Man's Boots
+	giveArtBonus(54,Bonus::SECONDARY_SKILL_PREMY,+5,12);//Amulet of the Undertaker
+	giveArtBonus(55,Bonus::SECONDARY_SKILL_PREMY,+10,12);//Vampire's Cowl
+	giveArtBonus(56,Bonus::SECONDARY_SKILL_PREMY,+15,12);//Dead Man's Boots
 
-	giveArtBonus(57,HeroBonus::MAGIC_RESISTANCE,+5);//Garniture of Interference
-	giveArtBonus(58,HeroBonus::MAGIC_RESISTANCE,+10);//Surcoat of Counterpoise
-	giveArtBonus(59,HeroBonus::MAGIC_RESISTANCE,+15);//Boots of Polarity
+	giveArtBonus(57,Bonus::MAGIC_RESISTANCE,+5);//Garniture of Interference
+	giveArtBonus(58,Bonus::MAGIC_RESISTANCE,+10);//Surcoat of Counterpoise
+	giveArtBonus(59,Bonus::MAGIC_RESISTANCE,+15);//Boots of Polarity
 
 	//archery bonus
-	giveArtBonus(60,HeroBonus::SECONDARY_SKILL_PREMY,+5,1);//Bow of Elven Cherrywood
-	giveArtBonus(61,HeroBonus::SECONDARY_SKILL_PREMY,+10,1);//Bowstring of the Unicorn's Mane
-	giveArtBonus(62,HeroBonus::SECONDARY_SKILL_PREMY,+15,1);//Angel Feather Arrows
+	giveArtBonus(60,Bonus::SECONDARY_SKILL_PREMY,+5,1);//Bow of Elven Cherrywood
+	giveArtBonus(61,Bonus::SECONDARY_SKILL_PREMY,+10,1);//Bowstring of the Unicorn's Mane
+	giveArtBonus(62,Bonus::SECONDARY_SKILL_PREMY,+15,1);//Angel Feather Arrows
 
 	//eagle eye bonus
-	giveArtBonus(63,HeroBonus::SECONDARY_SKILL_PREMY,+5,11);//Bird of Perception
-	giveArtBonus(64,HeroBonus::SECONDARY_SKILL_PREMY,+10,11);//Stoic Watchman
-	giveArtBonus(65,HeroBonus::SECONDARY_SKILL_PREMY,+15,11);//Emblem of Cognizance
+	giveArtBonus(63,Bonus::SECONDARY_SKILL_PREMY,+5,11);//Bird of Perception
+	giveArtBonus(64,Bonus::SECONDARY_SKILL_PREMY,+10,11);//Stoic Watchman
+	giveArtBonus(65,Bonus::SECONDARY_SKILL_PREMY,+15,11);//Emblem of Cognizance
 
 	//reducing cost of surrendering
-	giveArtBonus(66,HeroBonus::SURRENDER_DISCOUNT,+10);//Statesman's Medal
-	giveArtBonus(67,HeroBonus::SURRENDER_DISCOUNT,+10);//Diplomat's Ring
-	giveArtBonus(68,HeroBonus::SURRENDER_DISCOUNT,+10);//Ambassador's Sash
-
-	giveArtBonus(69,HeroBonus::STACKS_SPEED,+1);//Ring of the Wayfarer
-
-	giveArtBonus(70,HeroBonus::LAND_MOVEMENT,+300);//Equestrian's Gloves
-	giveArtBonus(71,HeroBonus::SEA_MOVEMENT,+1000);//Necklace of Ocean Guidance
-	giveArtBonus(72,HeroBonus::FLYING_MOVEMENT,+1);//Angel Wings
-
-	giveArtBonus(73,HeroBonus::MANA_REGENERATION,+1);//Charm of Mana
-	giveArtBonus(74,HeroBonus::MANA_REGENERATION,+2);//Talisman of Mana
-	giveArtBonus(75,HeroBonus::MANA_REGENERATION,+3);//Mystic Orb of Mana
-
-	giveArtBonus(76,HeroBonus::SPELL_DURATION,+1);//Collar of Conjuring
-	giveArtBonus(77,HeroBonus::SPELL_DURATION,+2);//Ring of Conjuring
-	giveArtBonus(78,HeroBonus::SPELL_DURATION,+3);//Cape of Conjuring
-
-	giveArtBonus(79,HeroBonus::AIR_SPELL_DMG_PREMY,+50);//Orb of the Firmament
-	giveArtBonus(80,HeroBonus::EARTH_SPELL_DMG_PREMY,+50);//Orb of Silt
-	giveArtBonus(81,HeroBonus::FIRE_SPELL_DMG_PREMY,+50);//Orb of Tempestuous Fire
-	giveArtBonus(82,HeroBonus::WATER_SPELL_DMG_PREMY,+50);//Orb of Driving Rain
-
-	giveArtBonus(83,HeroBonus::BLOCK_SPELLS_ABOVE_LEVEL,3);//Recanter's Cloak
-	giveArtBonus(84,HeroBonus::BLOCK_MORALE,0);//Spirit of Oppression
-	giveArtBonus(85,HeroBonus::BLOCK_LUCK,0);//Hourglass of the Evil Hour
-
-	giveArtBonus(86,HeroBonus::FIRE_SPELLS,0);//Tome of Fire Magic
-	giveArtBonus(87,HeroBonus::AIR_SPELLS,0);//Tome of Air Magic
-	giveArtBonus(88,HeroBonus::WATER_SPELLS,0);//Tome of Water Magic
-	giveArtBonus(89,HeroBonus::EARTH_SPELLS,0);//Tome of Earth Magic
-
-	giveArtBonus(90,HeroBonus::WATER_WALKING,0);//Boots of Levitation
-	giveArtBonus(91,HeroBonus::NO_SHOTING_PENALTY,0);//Golden Bow
-	giveArtBonus(92,HeroBonus::SPELL_IMMUNITY,35);//Sphere of Permanence
-	giveArtBonus(93,HeroBonus::NEGATE_ALL_NATURAL_IMMUNITIES,0);//Orb of Vulnerability
-
-	giveArtBonus(94,HeroBonus::STACK_HEALTH,+1);//Ring of Vitality
-	giveArtBonus(95,HeroBonus::STACK_HEALTH,+1);//Ring of Life
-	giveArtBonus(96,HeroBonus::STACK_HEALTH,+2);//Vial of Lifeblood
-
-	giveArtBonus(97,HeroBonus::STACKS_SPEED,+1);//Necklace of Swiftness
-	giveArtBonus(98,HeroBonus::LAND_MOVEMENT,+600);//Boots of Speed
-	giveArtBonus(99,HeroBonus::STACKS_SPEED,+2);//Cape of Velocity
-
-	giveArtBonus(100,HeroBonus::SPELL_IMMUNITY,59);//Pendant of Dispassion
-	giveArtBonus(101,HeroBonus::SPELL_IMMUNITY,62);//Pendant of Second Sight
-	giveArtBonus(102,HeroBonus::SPELL_IMMUNITY,42);//Pendant of Holiness
-	giveArtBonus(103,HeroBonus::SPELL_IMMUNITY,24);//Pendant of Life
-	giveArtBonus(104,HeroBonus::SPELL_IMMUNITY,25);//Pendant of Death
-	giveArtBonus(105,HeroBonus::SPELL_IMMUNITY,60);//Pendant of Free Will
-	giveArtBonus(106,HeroBonus::SPELL_IMMUNITY,17);//Pendant of Negativity
-	giveArtBonus(107,HeroBonus::SPELL_IMMUNITY,61);//Pendant of Total Recall
-	giveArtBonus(108,HeroBonus::MORALE_AND_LUCK,+3);//Pendant of Courage
-
-	giveArtBonus(109,HeroBonus::GENERATE_RESOURCE,+1,4); //Everflowing Crystal Cloak
-	giveArtBonus(110,HeroBonus::GENERATE_RESOURCE,+1,5); //Ring of Infinite Gems
-	giveArtBonus(111,HeroBonus::GENERATE_RESOURCE,+1,1); //Everpouring Vial of Mercury
-	giveArtBonus(112,HeroBonus::GENERATE_RESOURCE,+1,2); //Inexhaustible Cart of Ore
-	giveArtBonus(113,HeroBonus::GENERATE_RESOURCE,+1,3); //Eversmoking Ring of Sulfur
-	giveArtBonus(114,HeroBonus::GENERATE_RESOURCE,+1,0); //Inexhaustible Cart of Lumber
-	giveArtBonus(115,HeroBonus::GENERATE_RESOURCE,+1000,6); //Endless Sack of Gold
-	giveArtBonus(116,HeroBonus::GENERATE_RESOURCE,+750,6); //Endless Bag of Gold
-	giveArtBonus(117,HeroBonus::GENERATE_RESOURCE,+500,6); //Endless Purse of Gold
-
-	giveArtBonus(118,HeroBonus::CREATURE_GROWTH,+5,1); //Legs of Legion
-	giveArtBonus(119,HeroBonus::CREATURE_GROWTH,+4,2); //Loins of Legion
-	giveArtBonus(120,HeroBonus::CREATURE_GROWTH,+3,3); //Torso of Legion
-	giveArtBonus(121,HeroBonus::CREATURE_GROWTH,+2,4); //Arms of Legion
-	giveArtBonus(122,HeroBonus::CREATURE_GROWTH,+1,5); //Head of Legion
+	giveArtBonus(66,Bonus::SURRENDER_DISCOUNT,+10);//Statesman's Medal
+	giveArtBonus(67,Bonus::SURRENDER_DISCOUNT,+10);//Diplomat's Ring
+	giveArtBonus(68,Bonus::SURRENDER_DISCOUNT,+10);//Ambassador's Sash
+
+	giveArtBonus(69,Bonus::STACKS_SPEED,+1);//Ring of the Wayfarer
+
+	giveArtBonus(70,Bonus::LAND_MOVEMENT,+300);//Equestrian's Gloves
+	giveArtBonus(71,Bonus::SEA_MOVEMENT,+1000);//Necklace of Ocean Guidance
+	giveArtBonus(72,Bonus::FLYING_MOVEMENT,+1);//Angel Wings
+
+	giveArtBonus(73,Bonus::MANA_REGENERATION,+1);//Charm of Mana
+	giveArtBonus(74,Bonus::MANA_REGENERATION,+2);//Talisman of Mana
+	giveArtBonus(75,Bonus::MANA_REGENERATION,+3);//Mystic Orb of Mana
+
+	giveArtBonus(76,Bonus::SPELL_DURATION,+1);//Collar of Conjuring
+	giveArtBonus(77,Bonus::SPELL_DURATION,+2);//Ring of Conjuring
+	giveArtBonus(78,Bonus::SPELL_DURATION,+3);//Cape of Conjuring
+
+	giveArtBonus(79,Bonus::AIR_SPELL_DMG_PREMY,+50);//Orb of the Firmament
+	giveArtBonus(80,Bonus::EARTH_SPELL_DMG_PREMY,+50);//Orb of Silt
+	giveArtBonus(81,Bonus::FIRE_SPELL_DMG_PREMY,+50);//Orb of Tempestuous Fire
+	giveArtBonus(82,Bonus::WATER_SPELL_DMG_PREMY,+50);//Orb of Driving Rain
+
+	giveArtBonus(83,Bonus::BLOCK_SPELLS_ABOVE_LEVEL,3);//Recanter's Cloak
+	giveArtBonus(84,Bonus::BLOCK_MORALE,0);//Spirit of Oppression
+	giveArtBonus(85,Bonus::BLOCK_LUCK,0);//Hourglass of the Evil Hour
+
+	giveArtBonus(86,Bonus::FIRE_SPELLS,0);//Tome of Fire Magic
+	giveArtBonus(87,Bonus::AIR_SPELLS,0);//Tome of Air Magic
+	giveArtBonus(88,Bonus::WATER_SPELLS,0);//Tome of Water Magic
+	giveArtBonus(89,Bonus::EARTH_SPELLS,0);//Tome of Earth Magic
+
+	giveArtBonus(90,Bonus::WATER_WALKING,0);//Boots of Levitation
+	giveArtBonus(91,Bonus::NO_SHOTING_PENALTY,0);//Golden Bow
+	giveArtBonus(92,Bonus::SPELL_IMMUNITY,35);//Sphere of Permanence
+	giveArtBonus(93,Bonus::NEGATE_ALL_NATURAL_IMMUNITIES,0);//Orb of Vulnerability
+
+	giveArtBonus(94,Bonus::STACK_HEALTH,+1);//Ring of Vitality
+	giveArtBonus(95,Bonus::STACK_HEALTH,+1);//Ring of Life
+	giveArtBonus(96,Bonus::STACK_HEALTH,+2);//Vial of Lifeblood
+
+	giveArtBonus(97,Bonus::STACKS_SPEED,+1);//Necklace of Swiftness
+	giveArtBonus(98,Bonus::LAND_MOVEMENT,+600);//Boots of Speed
+	giveArtBonus(99,Bonus::STACKS_SPEED,+2);//Cape of Velocity
+
+	giveArtBonus(100,Bonus::SPELL_IMMUNITY,59);//Pendant of Dispassion
+	giveArtBonus(101,Bonus::SPELL_IMMUNITY,62);//Pendant of Second Sight
+	giveArtBonus(102,Bonus::SPELL_IMMUNITY,42);//Pendant of Holiness
+	giveArtBonus(103,Bonus::SPELL_IMMUNITY,24);//Pendant of Life
+	giveArtBonus(104,Bonus::SPELL_IMMUNITY,25);//Pendant of Death
+	giveArtBonus(105,Bonus::SPELL_IMMUNITY,60);//Pendant of Free Will
+	giveArtBonus(106,Bonus::SPELL_IMMUNITY,17);//Pendant of Negativity
+	giveArtBonus(107,Bonus::SPELL_IMMUNITY,61);//Pendant of Total Recall
+	giveArtBonus(108,Bonus::MORALE,+3);//Pendant of Courage
+	giveArtBonus(108,Bonus::LUCK,+3);//Pendant of Courage
+
+	giveArtBonus(109,Bonus::GENERATE_RESOURCE,+1,4); //Everflowing Crystal Cloak
+	giveArtBonus(110,Bonus::GENERATE_RESOURCE,+1,5); //Ring of Infinite Gems
+	giveArtBonus(111,Bonus::GENERATE_RESOURCE,+1,1); //Everpouring Vial of Mercury
+	giveArtBonus(112,Bonus::GENERATE_RESOURCE,+1,2); //Inexhaustible Cart of Ore
+	giveArtBonus(113,Bonus::GENERATE_RESOURCE,+1,3); //Eversmoking Ring of Sulfur
+	giveArtBonus(114,Bonus::GENERATE_RESOURCE,+1,0); //Inexhaustible Cart of Lumber
+	giveArtBonus(115,Bonus::GENERATE_RESOURCE,+1000,6); //Endless Sack of Gold
+	giveArtBonus(116,Bonus::GENERATE_RESOURCE,+750,6); //Endless Bag of Gold
+	giveArtBonus(117,Bonus::GENERATE_RESOURCE,+500,6); //Endless Purse of Gold
+
+	giveArtBonus(118,Bonus::CREATURE_GROWTH,+5,1); //Legs of Legion
+	giveArtBonus(119,Bonus::CREATURE_GROWTH,+4,2); //Loins of Legion
+	giveArtBonus(120,Bonus::CREATURE_GROWTH,+3,3); //Torso of Legion
+	giveArtBonus(121,Bonus::CREATURE_GROWTH,+2,4); //Arms of Legion
+	giveArtBonus(122,Bonus::CREATURE_GROWTH,+1,5); //Head of Legion
 
 	//Sea Captain's Hat 
-	giveArtBonus(123,HeroBonus::WHIRLPOOL_PROTECTION,0); 
-	giveArtBonus(123,HeroBonus::SEA_MOVEMENT,+500); 
-	giveArtBonus(123,HeroBonus::SPELL,3,0); 
-	giveArtBonus(123,HeroBonus::SPELL,3,1); 
+	giveArtBonus(123,Bonus::WHIRLPOOL_PROTECTION,0); 
+	giveArtBonus(123,Bonus::SEA_MOVEMENT,+500); 
+	giveArtBonus(123,Bonus::SPELL,3,0); 
+	giveArtBonus(123,Bonus::SPELL,3,1); 
 
-	giveArtBonus(124,HeroBonus::SPELLS_OF_LEVEL,3,1); //Spellbinder's Hat
-	giveArtBonus(125,HeroBonus::ENEMY_CANT_ESCAPE,0); //Shackles of War
-	giveArtBonus(126,HeroBonus::BLOCK_SPELLS_ABOVE_LEVEL,0);//Orb of Inhibition
+	giveArtBonus(124,Bonus::SPELLS_OF_LEVEL,3,1); //Spellbinder's Hat
+	giveArtBonus(125,Bonus::ENEMY_CANT_ESCAPE,0); //Shackles of War
+	giveArtBonus(126,Bonus::BLOCK_SPELLS_ABOVE_LEVEL,0);//Orb of Inhibition
 
 	//Armageddon's Blade
-	giveArtBonus(128, HeroBonus::SPELL, 3, 26);
-	giveArtBonus(128, HeroBonus::SPELL_IMMUNITY, 26);
+	giveArtBonus(128, Bonus::SPELL, 3, 26);
+	giveArtBonus(128, Bonus::SPELL_IMMUNITY, 26);
 	ART_ATTACK_AND_DEFENSE(128, +3);
 	ART_PRIM_SKILL(128, 2, +3);
 	ART_PRIM_SKILL(128, 3, +6);
 
 	//Angelic Alliance
 	ART_ALL_PRIM_SKILLS(129, +21);
-	giveArtBonus(129, HeroBonus::NONEVIL_ALIGNMENT_MIX, 0);
-	giveArtBonus(129, HeroBonus::OPENING_BATTLE_SPELL, 10, 29); // Prayer
+	giveArtBonus(129, Bonus::NONEVIL_ALIGNMENT_MIX, 0);
+	giveArtBonus(129, Bonus::OPENING_BATTLE_SPELL, 10, 29); // Prayer
 
 	//Cloak of the Undead King
-	giveArtBonus(130, HeroBonus::SECONDARY_SKILL_PREMY, +30, 12);
-	giveArtBonus(130, HeroBonus::SECONDARY_SKILL_PREMY, +30, 12);
-	giveArtBonus(130, HeroBonus::IMPROVED_NECROMANCY, 0);
+	giveArtBonus(130, Bonus::SECONDARY_SKILL_PREMY, +30, 12);
+	giveArtBonus(130, Bonus::SECONDARY_SKILL_PREMY, +30, 12);
+	giveArtBonus(130, Bonus::IMPROVED_NECROMANCY, 0);
 
 	//Elixir of Life
-	giveArtBonus(131, HeroBonus::STACK_HEALTH, +4);
-	giveArtBonus(131, HeroBonus::STACK_HEALTH_PERCENT, +25);
-	giveArtBonus(131, HeroBonus::HP_REGENERATION, +50);
+	giveArtBonus(131, Bonus::STACK_HEALTH, +4);
+	giveArtBonus(131, Bonus::STACK_HEALTH, +25, -1, Bonus::PERCENT_TO_BASE);
+	giveArtBonus(131, Bonus::HP_REGENERATION, +50);
 
 	//Armor of the Damned
 	ART_ATTACK_AND_DEFENSE(132, +3);
 	ART_POWER_AND_KNOWLEDGE(132, +2);
-	giveArtBonus(132, HeroBonus::OPENING_BATTLE_SPELL, 50, 54); // Slow
-	giveArtBonus(132, HeroBonus::OPENING_BATTLE_SPELL, 50, 47); // Disrupting Ray
-	giveArtBonus(132, HeroBonus::OPENING_BATTLE_SPELL, 50, 45); // Weakness
-	giveArtBonus(132, HeroBonus::OPENING_BATTLE_SPELL, 50, 52); // Misfortune
+	giveArtBonus(132, Bonus::OPENING_BATTLE_SPELL, 50, 54); // Slow
+	giveArtBonus(132, Bonus::OPENING_BATTLE_SPELL, 50, 47); // Disrupting Ray
+	giveArtBonus(132, Bonus::OPENING_BATTLE_SPELL, 50, 45); // Weakness
+	giveArtBonus(132, Bonus::OPENING_BATTLE_SPELL, 50, 52); // Misfortune
 
 	// Statue of Legion - gives only 50% growth
-	giveArtBonus(133, HeroBonus::CREATURE_GROWTH_PERCENT, 50);
+	giveArtBonus(133, Bonus::CREATURE_GROWTH_PERCENT, 50);
 
 	//Power of the Dragon Father
 	ART_ALL_PRIM_SKILLS(134, +16);
-	giveArtBonus(134, HeroBonus::MORALE_AND_LUCK, +1);
-	giveArtBonus(134, HeroBonus::LEVEL_SPELL_IMMUNITY, 4);
+	giveArtBonus(134, Bonus::MORALE, +1);
+	giveArtBonus(134, Bonus::LUCK, +1);
+	giveArtBonus(134, Bonus::LEVEL_SPELL_IMMUNITY, 4);
 
 	//Titan's Thunder
 	// should also add a permanent spell book, somehow.
 	ART_ATTACK_AND_DEFENSE(135, +9);
 	ART_POWER_AND_KNOWLEDGE(135, +8);
-	giveArtBonus(135, HeroBonus::SPELL, 3, 57);
+	giveArtBonus(135, Bonus::SPELL, 3, 57);
 
 	//Admiral's Hat
-	giveArtBonus(136, HeroBonus::SEA_MOVEMENT, +1500);
-	giveArtBonus(136, HeroBonus::WHIRLPOOL_PROTECTION, 0);
-	giveArtBonus(136, HeroBonus::SPELL, 3, 0);
-	giveArtBonus(136, HeroBonus::SPELL, 3, 1);
-	giveArtBonus(136, HeroBonus::FREE_SHIP_BOARDING, 0);
+	giveArtBonus(136, Bonus::SEA_MOVEMENT, +1500);
+	giveArtBonus(136, Bonus::WHIRLPOOL_PROTECTION, 0);
+	giveArtBonus(136, Bonus::SPELL, 3, 0);
+	giveArtBonus(136, Bonus::SPELL, 3, 1);
+	giveArtBonus(136, Bonus::FREE_SHIP_BOARDING, 0);
 
 	//Bow of the Sharpshooter
-	giveArtBonus(137, HeroBonus::SECONDARY_SKILL_PREMY, +30, 1);
-	giveArtBonus(137, HeroBonus::NO_SHOTING_PENALTY, 0);
-	giveArtBonus(137, HeroBonus::FREE_SHOOTING, 0);
+	giveArtBonus(137, Bonus::SECONDARY_SKILL_PREMY, +30, 1);
+	giveArtBonus(137, Bonus::NO_SHOTING_PENALTY, 0);
+	giveArtBonus(137, Bonus::FREE_SHOOTING, 0);
 
 	//Wizard's Well
-	giveArtBonus(138, HeroBonus::FULL_MANA_REGENERATION, 0);
+	giveArtBonus(138, Bonus::FULL_MANA_REGENERATION, 0);
 
 	//Ring of the Magi
-	giveArtBonus(139, HeroBonus::SPELL_DURATION, +56);
+	giveArtBonus(139, Bonus::SPELL_DURATION, +56);
 
 	//Cornucopia
-	giveArtBonus(140, HeroBonus::GENERATE_RESOURCE, +5, 1);
-	giveArtBonus(140, HeroBonus::GENERATE_RESOURCE, +5, 3);
-	giveArtBonus(140, HeroBonus::GENERATE_RESOURCE, +5, 4);
-	giveArtBonus(140, HeroBonus::GENERATE_RESOURCE, +5, 5);
+	giveArtBonus(140, Bonus::GENERATE_RESOURCE, +5, 1);
+	giveArtBonus(140, Bonus::GENERATE_RESOURCE, +5, 3);
+	giveArtBonus(140, Bonus::GENERATE_RESOURCE, +5, 4);
+	giveArtBonus(140, Bonus::GENERATE_RESOURCE, +5, 5);
 }
 
 void CArtHandler::clear()

+ 2 - 2
hch/CArtHandler.h

@@ -35,7 +35,7 @@ public:
 	std::vector<ui32> * constituentOf; // Reverse map of constituents.
 	EartClass aClass;
 	ui32 id;
-	std::list<HeroBonus> bonuses; //bonuses given by artifact
+	std::list<Bonus> bonuses; //bonuses given by artifact
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -45,7 +45,7 @@ public:
 
 class DLL_EXPORT CArtHandler //handles artifacts
 {
-	void giveArtBonus(int aid, HeroBonus::BonusType type, int val, int subtype = -1);
+	void giveArtBonus(int aid, Bonus::BonusType type, int val, int subtype = -1, int valType = Bonus::BASE_NUMBER);
 public:
 	std::vector<CArtifact*> treasures, minors, majors, relics;
 	std::vector<CArtifact> artifacts;

+ 93 - 49
hch/CCreatureHandler.cpp

@@ -5,6 +5,7 @@
 #include <sstream>
 #include <boost/assign/std/set.hpp>
 #include <boost/assign/std/vector.hpp>
+#include <boost/assign/std/list.hpp>
 #include <boost/assign/list_of.hpp>
 #include <boost/algorithm/string.hpp>
 #include <boost/algorithm/string/find.hpp>
@@ -70,22 +71,22 @@ int CCreature::getQuantityID(const int & quantity)
 
 bool CCreature::isDoubleWide() const
 {
-	return vstd::contains(abilities, StackFeature::DOUBLE_WIDE);
+	return doubleWide;
 }
 
 bool CCreature::isFlying() const
 {
-	return vstd::contains(abilities, StackFeature::FLYING);
+	return vstd::contains(bonuses, Bonus::FLYING);
 }
 
 bool CCreature::isShooting() const
 {
-	return vstd::contains(abilities, StackFeature::SHOOTER);
+	return vstd::contains(bonuses, Bonus::SHOOTER);
 }
 
 bool CCreature::isUndead() const
 {
-	return vstd::contains(abilities, StackFeature::UNDEAD);
+	return vstd::contains(bonuses, Bonus::UNDEAD);
 }
 
 /**
@@ -116,6 +117,17 @@ si32 CCreature::maxAmount(const std::vector<si32> &res) const //how many creatur
 	return ret;
 }
 
+CCreature::CCreature()
+{
+	doubleWide = false;
+}
+
+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);
+}
+
 int readNumber(int & befi, int & i, int andame, std::string & buf) //helper function for void CCreatureHandler::loadCreatures() and loadUnitAnimInfo()
 {
 	befi=i;
@@ -199,7 +211,8 @@ void CCreatureHandler::loadCreatures()
 
 	while(i<buf.size())
 	{
-		CCreature ncre;
+		CCreature &ncre = *new CCreature;
+		ncre.idNumber = creatures.size();
 		ncre.cost.resize(RESOURCE_QUANTITY);
 		ncre.level=0;
 
@@ -229,12 +242,18 @@ void CCreatureHandler::loadCreatures()
 		ncre.AIValue = readNumber(befi, i, andame, buf);
 		ncre.growth = readNumber(befi, i, andame, buf);
 		ncre.hordeGrowth = readNumber(befi, i, andame, buf);
+
 		ncre.hitPoints = readNumber(befi, i, andame, buf);
+		ncre.addBonus(ncre.hitPoints, Bonus::STACK_HEALTH);
 		ncre.speed = readNumber(befi, i, andame, buf);
+		ncre.addBonus(ncre.speed, Bonus::STACKS_SPEED);
 		ncre.attack = readNumber(befi, i, andame, buf);
+		ncre.addBonus(ncre.attack, Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK);
 		ncre.defence = readNumber(befi, i, andame, buf);
+		ncre.addBonus(ncre.defence, Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE);
 		ncre.damageMin = readNumber(befi, i, andame, buf);
 		ncre.damageMax = readNumber(befi, i, andame, buf);
+
 		ncre.shots = readNumber(befi, i, andame, buf);
 		ncre.spells = readNumber(befi, i, andame, buf);
 		ncre.ammMin = readNumber(befi, i, andame, buf);
@@ -260,64 +279,69 @@ void CCreatureHandler::loadCreatures()
 		if(useCreAbilsFromZCRTRAIT)
 		{ //adding abilities from ZCRTRAIT.TXT
 			if(boost::algorithm::find_first(ncre.abilityRefs, "DOUBLE_WIDE"))
-				ncre.abilities.push_back(makeCreatureAbility(StackFeature::DOUBLE_WIDE, 0));
+				ncre.doubleWide = true;
 			if(boost::algorithm::find_first(ncre.abilityRefs, "FLYING_ARMY"))
-				ncre.abilities.push_back(makeCreatureAbility(StackFeature::FLYING, 0));
+				ncre.addBonus(0, Bonus::FLYING);
 			if(boost::algorithm::find_first(ncre.abilityRefs, "SHOOTING_ARMY"))
-				ncre.abilities.push_back(makeCreatureAbility(StackFeature::SHOOTER, 0));
+				ncre.addBonus(0, Bonus::SHOOTER);
 			if(boost::algorithm::find_first(ncre.abilityRefs, "SIEGE_WEAPON"))
-				ncre.abilities.push_back(makeCreatureAbility(StackFeature::SIEGE_WEAPON, 0));
+				ncre.addBonus(0, Bonus::SIEGE_WEAPON);
 			if(boost::algorithm::find_first(ncre.abilityRefs, "const_two_attacks"))
-				ncre.abilities.push_back(makeCreatureAbility(StackFeature::ADDITIONAL_ATTACK, 1));
+				ncre.addBonus(1, Bonus::ADDITIONAL_ATTACK);
 			if(boost::algorithm::find_first(ncre.abilityRefs, "const_free_attack"))
-				ncre.abilities.push_back(makeCreatureAbility(StackFeature::BLOCKS_RETALIATION, 0));
+				ncre.addBonus(0, Bonus::BLOCKS_RETALIATION);
 			if(boost::algorithm::find_first(ncre.abilityRefs, "IS_UNDEAD"))
-				ncre.abilities.push_back(makeCreatureAbility(StackFeature::UNDEAD, 0));
+				ncre.addBonus(0, Bonus::UNDEAD);
 			if(boost::algorithm::find_first(ncre.abilityRefs, "const_no_melee_penalty"))
-				ncre.abilities.push_back(makeCreatureAbility(StackFeature::NO_MELEE_PENALTY, 0));
+				ncre.addBonus(0, Bonus::NO_MELEE_PENALTY);
 			if(boost::algorithm::find_first(ncre.abilityRefs, "const_jousting"))
-				ncre.abilities.push_back(makeCreatureAbility(StackFeature::JOUSTING, 0));
+				ncre.addBonus(0, Bonus::JOUSTING);
 			if(boost::algorithm::find_first(ncre.abilityRefs, "const_raises_morale"))
-				ncre.abilities.push_back(makeCreatureAbility(StackFeature::RAISING_MORALE, 1));
+			{
+				ncre.addBonus(+1, Bonus::MORALE);;
+				ncre.bonuses.back().effectRange = Bonus::ONLY_ALLIED_ARMY;
+			}
 			if(boost::algorithm::find_first(ncre.abilityRefs, "const_lowers_morale"))
-				ncre.abilities.push_back(makeCreatureAbility(StackFeature::ENEMY_MORALE_DECREASING, 1));
+			{
+				ncre.addBonus(-1, Bonus::MORALE);;
+				ncre.bonuses.back().effectRange = Bonus::ONLY_ENEMY_ARMY;
+			}
 			if(boost::algorithm::find_first(ncre.abilityRefs, "KING_1"))
-				ncre.abilities.push_back(makeCreatureAbility(StackFeature::KING1, 0));
+				ncre.addBonus(0, Bonus::KING1);
 			if(boost::algorithm::find_first(ncre.abilityRefs, "KING_2"))
-				ncre.abilities.push_back(makeCreatureAbility(StackFeature::KING2, 0));
+				ncre.addBonus(0, Bonus::KING2);
 			if(boost::algorithm::find_first(ncre.abilityRefs, "KING_3"))
-				ncre.abilities.push_back(makeCreatureAbility(StackFeature::KING3, 0));
+				ncre.addBonus(0, Bonus::KING3);
 			if(boost::algorithm::find_first(ncre.abilityRefs, "const_no_wall_penalty"))
-				ncre.abilities.push_back(makeCreatureAbility(StackFeature::NO_WALL_PENALTY, 0));
+				ncre.addBonus(0, Bonus::NO_WALL_PENALTY);
 			if(boost::algorithm::find_first(ncre.abilityRefs, "CATAPULT"))
-				ncre.abilities.push_back(makeCreatureAbility(StackFeature::CATAPULT, 0));
+				ncre.addBonus(0, Bonus::CATAPULT);
 			if(boost::algorithm::find_first(ncre.abilityRefs, "MULTI_HEADED"))
-				ncre.abilities.push_back(makeCreatureAbility(StackFeature::ATTACKS_ALL_ADJACENT, 0));
+				ncre.addBonus(0, Bonus::ATTACKS_ALL_ADJACENT);
+
 			if(boost::algorithm::find_first(ncre.abilityRefs, "IMMUNE_TO_MIND_SPELLS"))
 			{
 				std::vector<int> mindSpells = getMindSpells();
 				for(int g=0; g<mindSpells.size(); ++g)
-				{
-					creatures[40].abilities += makeCreatureAbility(StackFeature::SPELL_IMMUNITY, 0, mindSpells[g]); //giants are immune to mind spells
-				}
+					ncre.addBonus(0, Bonus::SPELL_IMMUNITY, mindSpells[g]); //giants are immune to mind spells
 			}
 			if(boost::algorithm::find_first(ncre.abilityRefs, "IMMUNE_TO_FIRE_SPELLS"))
-				ncre.abilities.push_back(makeCreatureAbility(StackFeature::FIRE_IMMUNITY, 0));
+				ncre.addBonus(0, Bonus::FIRE_IMMUNITY);
 			if(boost::algorithm::find_first(ncre.abilityRefs, "HAS_EXTENDED_ATTACK"))
-				ncre.abilities.push_back(makeCreatureAbility(StackFeature::TWO_HEX_ATTACK_BREATH, 0));
+				ncre.addBonus(0, Bonus::TWO_HEX_ATTACK_BREATH);;
 		}
 
 		if(ncre.nameSing!=std::string("") && ncre.namePl!=std::string(""))
 		{
 			ncre.idNumber = creatures.size();
-			creatures.push_back(ncre);
+			creatures.push_back(&ncre);
 		}
 	}
 
 	// Map types names
-#define VCMI_CREATURE_ABILITY_NAME(x) ( #x, StackFeature::x )
-	static const std::map<std::string, int> type_list = map_list_of VCMI_CREATURE_ABILITY_LIST;
-#undef VCMI_CREATURE_ABILITY_NAME
+#define BONUS_NAME(x) ( #x, Bonus::x )
+	static const std::map<std::string, int> type_list = map_list_of BONUS_LIST;
+#undef BONUS_NAME
 
 	////second part of reading cr_abils.txt////
 	bool contReading = true;
@@ -332,28 +356,45 @@ void CCreatureHandler::loadCreatures()
 		case '+': //add new ability
 			{
 				int creatureID;
-				StackFeature nsf;
+				Bonus nsf;
 				si32 buf;
 				std::string type;
 
 				reader >> creatureID;
-
 				reader >> type;
+
 				std::map<std::string, int>::const_iterator it = type_list.find(type);
-				if (it == type_list.end()) {
-					tlog1 << "Error: invalid type " << type << " in cr_abils.txt" << std::endl;
+				CCreature *cre = creatures[creatureID];
+
+				if (it == type_list.end()) 
+				{
+					if(type == "DOUBLE_WIDE")
+						cre->doubleWide = true;
+					else if(type == "ENEMY_MORALE_DECREASING")
+					{
+						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;
+					}
+					else
+						tlog1 << "Error: invalid type " << type << " in cr_abils.txt" << std::endl;
 					break;
 				}
 				nsf.type = it->second;
 
-				reader >> buf; nsf.value = buf;
+				reader >> buf; nsf.val = buf;
 				reader >> buf; nsf.subtype = buf;
 				reader >> buf; nsf.additionalInfo = buf;
-				nsf.source = StackFeature::CREATURE_ABILITY;
-				nsf.duration = StackFeature::WHOLE_BATTLE;
+				nsf.source = Bonus::CREATURE_ABILITY;
+				nsf.id = cre->idNumber;
+				nsf.duration = Bonus::ONE_BATTLE;
 				nsf.turnsRemain = 0;
 
-				creatures[creatureID].abilities += nsf;
+				cre->bonuses += nsf;
 				break;
 			}
 		case '-': //remove ability
@@ -365,14 +406,17 @@ void CCreatureHandler::loadCreatures()
 				std::map<std::string, int>::const_iterator it = type_list.find(type);
 				if (it == type_list.end())
 				{
-					tlog1 << "Error: invalid type " << type << " in cr_abils.txt" << std::endl;
+					if(type == "DOUBLE_WIDE")
+						creatures[creatureID]->doubleWide = false;
+					else
+						tlog1 << "Error: invalid type " << type << " in cr_abils.txt" << std::endl;
 					break;
 				}
 				int typeNo = it->second;
 
-				StackFeature::ECombatFeatures ecf = static_cast<StackFeature::ECombatFeatures>(typeNo);
+				Bonus::BonusType ecf = static_cast<Bonus::BonusType>(typeNo);
 
-				creatures[creatureID].abilities -= ecf;
+				creatures[creatureID]->bonuses -= ecf;
 				break;
 			}
 		case '0': //end reading
@@ -401,7 +445,7 @@ void CCreatureHandler::loadCreatures()
 		if (tempi>=creatures.size())
 			break;
 		boost::assign::insert(nameToID)(temps,tempi);
-		creatures[tempi].nameRef=temps;
+		creatures[tempi]->nameRef=temps;
 	}
 	ifs.close();
 	ifs.clear();
@@ -417,8 +461,8 @@ void CCreatureHandler::loadCreatures()
 			ifs >> id >> lvl;
 			if(lvl>0)
 			{
-				creatures[id].level = lvl;
-				levelCreatures[lvl].push_back(&(creatures[id]));
+				creatures[id]->level = lvl;
+				levelCreatures[lvl].push_back(creatures[id]);
 			}
 		}
 	}
@@ -431,7 +475,7 @@ void CCreatureHandler::loadCreatures()
 	{
 		int id, fact;
 		ifs >> id >> fact;
-		creatures[id].faction = fact;
+		creatures[id]->faction = fact;
 	}
 	ifs.close();
 	ifs.clear();
@@ -442,7 +486,7 @@ void CCreatureHandler::loadCreatures()
 	{
 		int id, up;
 		ifs >> id >> up;
-		creatures[id].upgrades.insert(up);
+		creatures[id]->upgrades.insert(up);
 	}
 	ifs.close();
 	ifs.clear();
@@ -489,7 +533,7 @@ void CCreatureHandler::loadCreatures()
 				break;
 		}
 		std::string defName = buf.substr(befi, i-befi);
-		creatures[s].animDefName = defName;
+		creatures[s]->animDefName = defName;
 	}
 	tlog5 << "\t\tReading CRANIM.TXT.txt" << std::endl;
 	loadAnimationInfo();
@@ -546,7 +590,7 @@ void CCreatureHandler::loadAnimationInfo()
 	for(int dd=0; dd<creatures.size(); ++dd)
 	{
 		//tlog5 << "\t\t\tReading animation info for creature " << dd << std::endl;
-		loadUnitAnimInfo(creatures[dd], buf, i);
+		loadUnitAnimInfo(*creatures[dd], buf, i);
 	}
 	return;
 }

+ 16 - 14
hch/CCreatureHandler.h

@@ -7,7 +7,7 @@
 #include <set>
 
 #include "CSoundBase.h"
-#include "../lib/StackFeature.h"
+#include "../lib/HeroBonus.h"
 
 /*
  * CCreatureHandler.h, part of VCMI engine
@@ -20,14 +20,16 @@
  */
 
 class CLodHandler;
+class CCreatureHandler;
 
-class DLL_EXPORT CCreature
+class DLL_EXPORT CCreature : public CBonusSystemNode
 {
+	ui32 hitPoints, speed, attack, defence;
 public:
 	std::string namePl, nameSing, nameRef; //name in singular and plural form; and reference name
 	std::vector<ui32> cost; //cost[res_id] - amount of that resource
 	std::set<ui32> upgrades; // IDs of creatures to which this creature can be upgraded
-	ui32 fightValue, AIValue, growth, hordeGrowth, hitPoints, speed, attack, defence, shots, spells;
+	ui32 fightValue, AIValue, growth, hordeGrowth, shots, spells;
 	ui32 damageMin, damageMax;
 	ui32 ammMin, ammMax;
 	ui8 level; // 0 - unknown
@@ -35,8 +37,8 @@ public:
 	std::string abilityRefs; //references to abilities, in textformat
 	std::string animDefName;
 	ui32 idNumber;
-	std::vector<StackFeature> abilities;
 	si8 faction; //-1 = neutral
+	ui8 doubleWide;
 
 	///animation info
 	float timeBetweenFidgets, walkAnimationTime, attackAnimationTime, flightAnimationDistance;
@@ -54,6 +56,8 @@ public:
 	si32 maxAmount(const std::vector<si32> &res) const; //how many creatures can be bought
 	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
 
+	void addBonus(int val, int type, int subtype = -1);
+
 	template<typename RanGen>
 	int getRandomAmount(RanGen &ranGen)
 	{
@@ -65,12 +69,13 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
+		h & static_cast<CBonusSystemNode&>(*this);
 		h & namePl & nameSing & nameRef
 			& cost & upgrades 
 			& fightValue & AIValue & growth & hordeGrowth & hitPoints & speed & attack & defence & shots & spells
 			& damageMin & damageMax & ammMin & ammMax & level
 			& abilityText & abilityRefs & animDefName
-			& idNumber & abilities & faction
+			& idNumber & faction
 
 			& timeBetweenFidgets & walkAnimationTime & attackAnimationTime & flightAnimationDistance
 			& upperRightMissleOffsetX & rightMissleOffsetX & lowerRightMissleOffsetX & upperRightMissleOffsetY & rightMissleOffsetY & lowerRightMissleOffsetY
@@ -82,6 +87,10 @@ public:
 			h & snd;
 		}
 	}
+
+
+	CCreature();
+	friend CCreatureHandler;
 };
 
 
@@ -89,7 +98,7 @@ class DLL_EXPORT CCreatureHandler
 {
 public:
 	std::set<int> notUsedMonsters;
-	std::vector<CCreature> creatures; //creature ID -> creature info
+	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;
@@ -111,14 +120,7 @@ public:
 	{
 		//TODO: should be optimized, not all these informations needs to be serialized (same for ccreature)
 		h & notUsedMonsters & creatures & nameToID & idToProjectile & idToProjectileSpin & factionToTurretCreature;
-
-		if(!h.saving)
-		{
-			for (int i=0; i<creatures.size(); i++) //recreate levelCreatures map
-			{
-				levelCreatures[creatures[i].level].push_back(&creatures[i]);
-			}
-		}
+		h & levelCreatures;		
 	}
 };
 

+ 1 - 1
hch/CMusicHandler.cpp

@@ -148,7 +148,7 @@ soundBase::soundID CSoundHandler::getSoundID(std::string &fileName)
 		return it->second;
 }
 
-void CSoundHandler::initCreaturesSounds(std::vector<CCreature> &creatures)
+void CSoundHandler::initCreaturesSounds(std::vector<CCreature*> &creatures)
 {
 	tlog5 << "\t\tReading config/cr_sounds.txt" << std::endl;
 	std::ifstream ifs(DATA_DIR "/config/cr_sounds.txt");

+ 2 - 1
hch/CMusicHandler.h

@@ -5,6 +5,7 @@
 
 #include "CSoundBase.h"
 #include "CMusicBase.h"
+#include "CCreatureHandler.h"
 
 
 /*
@@ -79,7 +80,7 @@ public:
 	void init();
 	void release();
 
-	void initCreaturesSounds(std::vector<CCreature> &creatures);
+	void initCreaturesSounds(std::vector<CCreature*> &creatures);
 	void initSpellsSounds(std::vector<CSpell> &spells);
 	void setVolume(unsigned int percent);
 

File diff suppressed because it is too large
+ 187 - 269
hch/CObjectHandler.cpp


+ 32 - 22
hch/CObjectHandler.h

@@ -13,6 +13,7 @@
 #include "CTownHandler.h"
 #include "../lib/VCMI_Lib.h"
 #endif
+#include "../lib/CCreatureSet.h"
 
 /*
  * CObjectHandler.h, part of VCMI engine
@@ -24,6 +25,7 @@
  *
  */
 
+class BattleInfo;
 class IGameCallback;
 struct BattleResult;
 class CCPPObjectScript;
@@ -46,6 +48,7 @@ struct Component;
 struct BankConfig;
 class CGBoat;
 
+
 class DLL_EXPORT CCastleEvent
 {
 public:
@@ -141,7 +144,7 @@ class DLL_EXPORT CGObjectInstance : public IObjectInterface
 {
 protected:
 	void getNameVis(std::string &hname) const;
-	void giveDummyBonus(int heroID, ui8 duration = HeroBonus::ONE_DAY) const;
+	void giveDummyBonus(int heroID, ui8 duration = Bonus::ONE_DAY) const;
 public:
 	mutable std::string hoverName;
 	int3 pos; //h3m pos
@@ -216,23 +219,31 @@ public:
 	}
 };
 
-class DLL_EXPORT CArmedInstance: public CGObjectInstance
+class DLL_EXPORT CArmedInstance: public CGObjectInstance, public CBonusSystemNode, public CCreatureSet
 {
 public:
-	CCreatureSet army; //army
-	virtual bool needsLastStack() const; //true if last stack cannot be taken
-	int getArmyStrength() const; //sum of AI values of creatures
-	ui64 getPower (TSlot slot) const; //value of specific stack
-	std::string getRoughAmount (TSlot slot) const; //rought size of specific stack
+	BattleInfo *battle; //set to the current battle, if engaged
+
+	void setArmy(const CCreatureSet &src);
+	CCreatureSet getArmy() const;
+	void randomizeArmy(int type);
+
+	//////////////////////////////////////////////////////////////////////////
+	void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const;
+	void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;
+	//////////////////////////////////////////////////////////////////////////
+
+	CArmedInstance();
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CGObjectInstance&>(*this);
-		h & army;
+		h & static_cast<CBonusSystemNode&>(*this);
+		h & static_cast<CCreatureSet&>(*this);
 	}
 };
 
-class DLL_EXPORT CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CBonusSystemNode
+class DLL_EXPORT CGHeroInstance : public CArmedInstance, public IBoatGenerator
 {
 public:
 	//////////////////////////////////////////////////////////////////////////
@@ -251,7 +262,6 @@ public:
 	std::string biography; //if custom
 	si32 portrait; //may be custom
 	si32 mana; // remaining spell points
-	std::vector<si32> primSkills; //0-attack, 1-defence, 2-spell power, 3-knowledge
 	std::vector<std::pair<ui8,ui8> > secSkills; //first - ID of skill, second - level of skill (1 - basic, 2 - adv., 3 - expert); if hero has ability (-1, -1) it meansthat it should have default secondary abilities
 	si32 movement; //remaining movement points
 	ui8 sex;
@@ -280,7 +290,7 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CArmedInstance&>(*this);
-		h & exp & level & name & biography & portrait & mana & primSkills & secSkills & movement
+		h & exp & level & name & biography & portrait & mana & secSkills & movement
 			& sex & inTownGarrison & artifacts & artifWorn & spells & patrol & bonuses
 			& moveDir;
 
@@ -288,11 +298,11 @@ public:
 		//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;
+	//////////////////////////////////////////////////////////////////////////
 	int3 getSightCenter() const; //"center" tile from which the sight distance is calculated
 	int getSightRadious() const; //sight distance (should be used if player-owned structure)
-
-
 	//////////////////////////////////////////////////////////////////////////
 
 	int getBoatType() const; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral
@@ -311,12 +321,9 @@ public:
 	int getCurrentLuck(int stack=-1, bool town=false) const;
 	int getSpellCost(const CSpell *sp) const; //do not use during battles -> bonuses from army would be ignored
 
-
-	void getParents(TCNodes &out, const CBonusSystemNode *source = NULL) const;
-
-	std::vector<std::pair<int,std::string> > getCurrentLuckModifiers(int stack=-1, bool town=false) const; //args as above
+	TModDescr getCurrentLuckModifiers(int stack=-1, bool town=false) const; //args as above
 	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
-	std::vector<std::pair<int,std::string> > getCurrentMoraleModifiers(int stack=-1, bool town=false) const; //args as above
+	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
 	int maxMovePoints(bool onLand) const;
@@ -342,6 +349,7 @@ public:
 	void recreateArtBonuses();
 	void giveArtifact (ui32 aid);
 	void initHeroDefInfo();
+	void pushPrimSkill(int which, int val);
 
 	CGHeroInstance();
 	virtual ~CGHeroInstance();
@@ -472,7 +480,9 @@ public:
 		h & town;
 		//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;
 	//////////////////////////////////////////////////////////////////////////
 
 	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
@@ -541,7 +551,7 @@ public:
 	{
 		h & static_cast<CArmedInstance&>(*this);
 		h & message & gainedExp & manaDiff & moraleDiff & luckDiff & resources & primskills
-			& abilities & abilityLevels & artifacts & spells & creatures & army;
+			& abilities & abilityLevels & artifacts & spells & creatures;
 	}
 };
 
@@ -558,7 +568,7 @@ public:
 		h & static_cast<CArmedInstance&>(*this);
 		h & message & gainedExp & manaDiff & moraleDiff & luckDiff & resources & primskills
 			& abilities & abilityLevels & artifacts & spells & creatures & availableFor 
-			& computerActivate & humanActivate & army;
+			& computerActivate & humanActivate;
 	}
 	
 	void onHeroVisit(const CGHeroInstance * h) const;

+ 113 - 11
lib/CCreatureSet.cpp

@@ -3,6 +3,10 @@
 #include "../hch/CCreatureHandler.h"
 #include "VCMI_Lib.h"
 #include <assert.h>
+#include "../hch/CObjectHandler.h"
+#include "IGameCallback.h"
+#include "CGameState.h"
+#include "../hch/CGeneralTextHandler.h"
 
 const CStackInstance CCreatureSet::operator[](TSlot slot) const
 {
@@ -28,7 +32,7 @@ bool CCreatureSet::setCreature(TSlot slot, TCreature type, TQuantity quantity) /
 	if (quantity == 0)
 		slots.erase(slot);
 
-	if (slots.size() > 7) 
+	if (slots.size() > ARMY_SIZE) 
 		return false;
 	else 
 		return true;
@@ -62,12 +66,12 @@ int CCreatureSet::getAmount(TSlot slot) const
 		return 0; //TODO? consider issuing a warning
 }
 
-bool CCreatureSet::mergableStacks(std::pair<TSlot, TSlot> &out, TSlot preferable /*= -1*/) /*looks for two same stacks, returns slot positions */
+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))
 	{
-		const CCreature *cr = slots[preferable].type;
+		const CCreature *cr = slots.find(preferable)->second.type;
 		for(TSlots::const_iterator j=slots.begin(); j!=slots.end(); ++j)
 		{
 			if(cr == j->second.type && j->first != preferable)
@@ -107,18 +111,24 @@ void CCreatureSet::sweep()
 	}
 }
 
-void CCreatureSet::addToSlot(TSlot slot, TCreature cre, TQuantity count)
+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); //that slot was empty or contained same type creature
+	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;
 }
 
-void CCreatureSet::addToSlot(TSlot slot, const CStackInstance &stack)
+void CCreatureSet::addToSlot(TSlot slot, const CStackInstance &stack, bool allowMerging/* = true*/)
 {
-	addToSlot(slot, stack.type->idNumber, stack.count);
+	assert(stack.type == VLC->creh->creatures[stack.type->idNumber]);
+	addToSlot(slot, stack.type->idNumber, stack.count, allowMerging	);
 }
 
 bool CCreatureSet::validTypes(bool allowUnrandomized /*= false*/) const
@@ -129,7 +139,7 @@ bool CCreatureSet::validTypes(bool allowUnrandomized /*= false*/) const
 		if(!isRand)
 		{
 			assert(i->second.type);
-			assert(i->second.type == &VLC->creh->creatures[i->second.type->idNumber]);
+			assert(i->second.type == VLC->creh->creatures[i->second.type->idNumber]);
 		}
 		else
 			assert(allowUnrandomized);
@@ -137,16 +147,94 @@ bool CCreatureSet::validTypes(bool allowUnrandomized /*= false*/) const
 	return true;
 }
 
+bool CCreatureSet::slotEmpty(TSlot slot) const
+{
+	return !vstd::contains(slots, slot);
+}
+
+bool CCreatureSet::needsLastStack() const
+{
+	return false;
+}
+
+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;
+	return ret;
+}
+
+ui64 CCreatureSet::getPower (TSlot slot) const
+{
+	return getCreature(slot)->AIValue * getAmount(slot);
+}
+std::string CCreatureSet::getRoughAmount (TSlot slot) const
+{
+	return VLC->generaltexth->arraytxt[174 + 3*CCreature::getQuantityID(getAmount(slot))];
+}
+
+int CCreatureSet::stacksCount() const
+{
+	return slots.size();
+}
+
+void CCreatureSet::addStack(TSlot slot, const CStackInstance &stack)
+{
+	addToSlot(slot, stack, false);
+}
+
+void CCreatureSet::setFormation(bool tight)
+{
+	formation = tight;
+}
+
+void CCreatureSet::setStackCount(TSlot slot, TQuantity count)
+{
+	assert(vstd::contains(slots, slot));
+	slots[slot].count = count;
+}
+
+void CCreatureSet::clear()
+{
+	slots.clear();
+}
+
+const CStackInstance& CCreatureSet::getStack(TSlot slot) const
+{
+	assert(vstd::contains(slots, slot));
+	return slots.find(slot)->second;
+}
+
+void CCreatureSet::eraseStack(TSlot slot)
+{
+	assert(vstd::contains(slots, slot));
+	slots.erase(slot);
+}
+
+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)
+			return true;
+
+	return false;
+}
+
 CStackInstance::CStackInstance()
 {
 	init();
 }
 
-CStackInstance::CStackInstance(TCreature id, TQuantity Count)
+CStackInstance::CStackInstance(TCreature id, TQuantity Count, const CArmedInstance *ArmyObj)
 {
 	init();
 	setType(id);
 	count = Count;
+	armyObj = ArmyObj;
 }
 
 CStackInstance::CStackInstance(const CCreature *cre, TQuantity Count)
@@ -162,6 +250,7 @@ void CStackInstance::init()
 	count = 0;
 	type = NULL;
 	idRand = -1;
+	armyObj = NULL;
 }
 
 int CStackInstance::getQuantityID() const 
@@ -171,5 +260,18 @@ int CStackInstance::getQuantityID() const
 
 void CStackInstance::setType(int creID)
 {
-	type = &VLC->creh->creatures[creID];
+	type = VLC->creh->creatures[creID];
+}
+
+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);
 }

+ 36 - 8
lib/CCreatureSet.h

@@ -3,20 +3,26 @@
 
 #include "../global.h"
 #include <map>
+#include "HeroBonus.h"
 
 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 CStackInstance
+
+class DLL_EXPORT CStackInstance : public CBonusSystemNode
 {
 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;
 	ui32 experience; //TODO: handle
@@ -24,13 +30,17 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h/* & owner*/ & type & count & experience;
+		h & static_cast<CBonusSystemNode&>(*this);
+		h & armyObj & type & count & 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
+
 	int getQuantityID() const;
 	void init();
 	CStackInstance();
-	CStackInstance(TCreature id, TQuantity count);
+	CStackInstance(TCreature id, TQuantity count, const CArmedInstance *ArmyObj = NULL);
 	CStackInstance(const CCreature *cre, TQuantity count);
 	void setType(int creID);
 };
@@ -38,6 +48,8 @@ public:
 
 typedef std::map<TSlot, CStackInstance> TSlots;
 
+
+
 class DLL_EXPORT CCreatureSet //seven combined creatures
 {
 public:
@@ -46,15 +58,31 @@ public:
 
 	const CStackInstance operator[](TSlot slot) const; 
 
-	void addToSlot(TSlot slot, TCreature cre, TQuantity count); //Adds stack to slot. Slot must be empty or with same type creature
-	void addToSlot(TSlot slot, const CStackInstance &stack); //Adds stack to slot. Slot must be empty or with same type creature
+	const TSlots &Slots() const {return slots;}
 
+	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 setFormation(bool tight);
+	void setStackCount(TSlot slot, TQuantity count); //stack must exist!
+	void eraseStack(TSlot slot);
+	
+	const CStackInstance& getStack(TSlot slot) const; 
 	const CCreature* getCreature(TSlot slot) const; //workaround of map issue;
 	int getAmount (TSlot slot) const;
-	bool setCreature (TSlot slot, TCreature type, TQuantity quantity); //slots 0 to 6
-	TSlot getSlotFor(TCreature creature, ui32 slotsAmount=7) const; //returns -1 if no slot available
-	bool mergableStacks(std::pair<TSlot, TSlot> &out, TSlot preferable = -1); //looks for two same stacks, returns slot positions;
+	TSlot getSlotFor(TCreature creature, 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;
+	int stacksCount() const;
+	virtual bool needsLastStack() const; //true if last stack cannot be taken
+	int getArmyStrength() const; //sum of AI values of creatures
+	ui64 getPower (TSlot slot) const; //value of specific stack
+	std::string getRoughAmount (TSlot slot) const; //rought size of specific stack
+	
+	bool contains(const CStackInstance *stack) const;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{

+ 146 - 310
lib/CGameState.cpp

@@ -166,7 +166,7 @@ void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst
 	}
 	else if(type == CRE_PL_NAMES)
 	{
-		dst = VLC->creh->creatures[ser].namePl;
+		dst = VLC->creh->creatures[ser]->namePl;
 	}
 	else if(type == MINE_NAMES)
 	{
@@ -182,7 +182,7 @@ void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst
 	}
 	else if(type == CRE_SING_NAMES)
 	{
-		dst = VLC->creh->creatures[ser].nameSing;
+		dst = VLC->creh->creatures[ser]->nameSing;
 	}
 	else if(type == ART_DESCR)
 	{
@@ -394,8 +394,8 @@ CStack * BattleInfo::getStackT(int tileID, bool onlyAlive)
 	for(unsigned int g=0; g<stacks.size(); ++g)
 	{
 		if(stacks[g]->position == tileID 
-			|| (stacks[g]->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && stacks[g]->attackerOwned && stacks[g]->position-1 == tileID)
-			|| (stacks[g]->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && !stacks[g]->attackerOwned && stacks[g]->position+1 == 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())
 			{
@@ -428,7 +428,7 @@ void BattleInfo::getAccessibilityMap(bool *accessibility, bool twoHex, bool atta
 			continue;
 
 		accessibility[stacks[g]->position] = false;
-		if(stacks[g]->hasFeatureOfType(StackFeature::DOUBLE_WIDE)) //if it's a double hex creature
+		if(stacks[g]->doubleWide()) //if it's a double hex creature
 		{
 			if(stacks[g]->attackerOwned)
 				accessibility[stacks[g]->position-1] = false;
@@ -561,12 +561,12 @@ std::vector<int> BattleInfo::getAccessibility(int stackID, bool addOccupiable) c
 
 	std::set<int> occupyable;
 
-	getAccessibilityMap(ac, s->hasFeatureOfType(StackFeature::DOUBLE_WIDE), s->attackerOwned, addOccupiable, occupyable, s->hasFeatureOfType(StackFeature::FLYING), stackID);
+	getAccessibilityMap(ac, s->doubleWide(), s->attackerOwned, addOccupiable, occupyable, s->hasBonusOfType(Bonus::FLYING), stackID);
 
 	int pr[BFIELD_SIZE], dist[BFIELD_SIZE];
-	makeBFS(s->position, ac, pr, dist, s->hasFeatureOfType(StackFeature::DOUBLE_WIDE), s->attackerOwned, s->hasFeatureOfType(StackFeature::FLYING), false);
+	makeBFS(s->position, ac, pr, dist, s->doubleWide(), s->attackerOwned, s->hasBonusOfType(Bonus::FLYING), false);
 
-	if(s->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
+	if(s->doubleWide())
 	{
 		if(!addOccupiable)
 		{
@@ -597,7 +597,7 @@ std::vector<int> BattleInfo::getAccessibility(int stackID, bool addOccupiable) c
 	
 	for (int i=0; i < BFIELD_SIZE ; ++i) {
 		if(
-			( ( !addOccupiable && dist[i] <= s->Speed() && ac[i] ) || ( addOccupiable && dist[i] <= s->Speed() && isAccessible(i, ac, s->hasFeatureOfType(StackFeature::DOUBLE_WIDE), s->attackerOwned, s->hasFeatureOfType(StackFeature::FLYING), true) ) )//we can reach it
+			( ( !addOccupiable && dist[i] <= s->Speed() && ac[i] ) || ( addOccupiable && dist[i] <= s->Speed() && isAccessible(i, ac, s->doubleWide(), s->attackerOwned, s->hasBonusOfType(Bonus::FLYING), true) ) )//we can reach it
 			|| (vstd::contains(occupyable, i) && ( dist[ i + (s->attackerOwned ? 1 : -1 ) ] <= s->Speed() ) &&
 				ac[i + (s->attackerOwned ? 1 : -1 )] ) //it's occupyable and we can reach adjacent hex
 			)
@@ -611,7 +611,7 @@ std::vector<int> BattleInfo::getAccessibility(int stackID, bool addOccupiable) c
 bool BattleInfo::isStackBlocked(int ID)
 {
 	CStack *our = getStack(ID);
-	if(our->hasFeatureOfType(StackFeature::SIEGE_WEAPON)) //siege weapons cannot be blocked
+	if(our->hasBonusOfType(Bonus::SIEGE_WEAPON)) //siege weapons cannot be blocked
 		return false;
 
 	for(unsigned int i=0; i<stacks.size();i++)
@@ -620,7 +620,7 @@ bool BattleInfo::isStackBlocked(int ID)
 			|| stacks[i]->owner==our->owner
 		  )
 			continue; //we omit dead and allied stacks
-		if(stacks[i]->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
+		if(stacks[i]->doubleWide())
 		{
 			if( mutualPosition(stacks[i]->position, our->position) >= 0  
 			  || mutualPosition(stacks[i]->position + (stacks[i]->attackerOwned ? -1 : 1), our->position) >= 0)
@@ -689,72 +689,32 @@ std::pair< std::vector<int>, int > BattleInfo::getPath(int start, int dest, bool
 	return std::make_pair(path, dist[dest]);
 }
 
-int CStack::valOfFeatures(StackFeature::ECombatFeatures type, int subtype, int turn) const
+CStack::CStack(const CStackInstance *base, int O, int I, bool AO, int S)
+	: CStackInstance(*base), ID(I), owner(O), slot(S), attackerOwned(AO), position(-1),   
+	counterAttacks(1)
 {
-	int ret = 0;
-	if(subtype == -1024) //any subtype
-	{
-		for(std::vector<StackFeature>::const_iterator i=features.begin(); i != features.end(); i++)
-			if(i->type == type	&&  (!turn || i->turnsRemain > turn))
-				ret += i->value;
-	}
-	else //given subtype
-	{
-		for(std::vector<StackFeature>::const_iterator i=features.begin(); i != features.end(); i++)
-			if(i->type == type && i->subtype == subtype && (!turn || i->turnsRemain > turn))
-				ret += i->value;
-	}
-	return ret;
-}
-
-bool CStack::hasFeatureOfType(StackFeature::ECombatFeatures type, int subtype, int turn) const
-{
-	if(subtype == -1024) //any subtype
-	{
-		for(std::vector<StackFeature>::const_iterator i=features.begin(); i != features.end(); i++)
-			if(i->type == type && (!turn || i->turnsRemain > turn))
-				return true;
-	}
-	else //given subtype
-	{
-		for(std::vector<StackFeature>::const_iterator i=features.begin(); i != features.end(); i++)
-			if(i->type == type && i->subtype == subtype && (!turn || i->turnsRemain > turn))
-				return true;
-	}
-	return false;
-}
+	baseAmount = base->count;
+	firstHPleft = valOfBonuses(Bonus::STACK_HEALTH);
+	shots = type->shots;
+	counterAttacks += valOfBonuses(Bonus::ADDITIONAL_RETALIATION);
 
-CStack::CStack(CCreature * C, int A, int O, int I, bool AO, int S)
-	:ID(I), creature(C), amount(A), baseAmount(A), firstHPleft(C->hitPoints), owner(O), slot(S), attackerOwned(AO), position(-1),   
-	counterAttacks(1), shots(C->shots), features(C->abilities)
-{
-	//additional retaliations
-	for(int h=0; h<C->abilities.size(); ++h)
-	{
-		if(C->abilities[h].type == StackFeature::ADDITIONAL_RETALIATION)
-		{
-			counterAttacks += C->abilities[h].value;
-		}
-	}
 	//alive state indication
 	state.insert(ALIVE);
 }
 
 ui32 CStack::Speed( int turn /*= 0*/ ) const
 {
-	if(hasFeatureOfType(StackFeature::SIEGE_WEAPON, -1024, turn)) //war machnes cannot move
+	if(hasBonus(Selector::type(Bonus::SIEGE_WEAPON) && Selector::turns(turn))) //war machines cannot move
 		return 0;
 
-	int speed = creature->speed;
-
-	speed += valOfFeatures(StackFeature::SPEED_BONUS, -1024, turn);
+	int speed = valOfBonuses(Selector::type(Bonus::STACKS_SPEED) && Selector::turns(turn));
 
 	int percentBonus = 0;
-	for(int g=0; g<features.size(); ++g)
+	BOOST_FOREACH(const Bonus &b, bonuses)
 	{
-		if(features[g].type == StackFeature::SPEED_BONUS)
+		if(b.type == Bonus::STACKS_SPEED)
 		{
-			percentBonus += features[g].additionalInfo;
+			percentBonus += b.additionalInfo;
 		}
 	}
 
@@ -789,39 +749,6 @@ ui8 CStack::howManyEffectsSet(ui16 id) const
 	return ret;
 }
 
-si32 CStack::Attack() const
-{
-	si32 ret = creature->attack; //value to be returned
-
-	if(hasFeatureOfType(StackFeature::IN_FRENZY)) //frenzy for attacker
-	{
-		ret += si32(VLC->spellh->spells[56].powers[getEffect(56)->level]/100.0) * Defense(false);
-	}
-
-	ret += valOfFeatures(StackFeature::ATTACK_BONUS);
-
-	return ret;
-}
-
-si32 CStack::Defense(bool withFrenzy /*= true*/) const
-{
-	si32 ret = creature->defence;
-
-	if(withFrenzy && getEffect(56)) //frenzy for defender
-	{
-		return 0;
-	}
-
-	ret += valOfFeatures(StackFeature::DEFENCE_BONUS);
-
-	return ret;
-}
-
-ui16 CStack::MaxHealth() const
-{
-	return creature->hitPoints + valOfFeatures(StackFeature::HP_BONUS);
-}
-
 bool CStack::willMove(int turn /*= 0*/) const
 {
 	return ( turn ? true : !vstd::contains(state, DEFENDING) )
@@ -832,7 +759,7 @@ bool CStack::willMove(int turn /*= 0*/) const
 bool CStack::canMove( int turn /*= 0*/ ) const
 {
 	return alive()
-		&& !hasFeatureOfType(StackFeature::NOT_ACTIVE, -1024, turn); //eg. Ammo Cart
+		&& !hasBonus(Selector::type(Bonus::NOT_ACTIVE) && Selector::turns(turn)); //eg. Ammo Cart or blinded creature
 }
 
 bool CStack::moved( int turn /*= 0*/ ) const
@@ -843,6 +770,11 @@ bool CStack::moved( int turn /*= 0*/ ) const
 		return false;
 }
 
+bool CStack::doubleWide() const
+{
+	return type->doubleWide;
+}
+
 CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, int player, const CTown *town, std::map<ui32,CGHeroInstance *> &available) const
 {
 	CGHeroInstance *ret = NULL;
@@ -1135,24 +1067,6 @@ std::pair<int,int> CGameState::pickObject (CGObjectInstance *obj)
 	return std::pair<int,int>(-1,-1);
 }
 
-void randomizeArmy(CArmedInstance * army, int type)
-{
-	int max = VLC->creh->creatures.size();
-	for (TSlots::iterator j=army->army.slots.begin(); j!=army->army.slots.end();j++)
-	{
-		if(j->second.idRand > max)
-		{
-			if(j->second.idRand % 2)
-				j->second.setType(VLC->townh->towns[type].basicCreatures[(j->second.idRand-197) / 2 -1]);
-			else
-				j->second.setType(VLC->townh->towns[type].upgradedCreatures[(j->second.idRand-197) / 2 -1]);
-
-			j->second.idRand = -1;
-		}
-	}
-	return;
-}
-
 void CGameState::randomizeObject(CGObjectInstance *cur)
 {		
 	std::pair<int,int> ran = pickObject(cur);
@@ -1178,7 +1092,7 @@ void CGameState::randomizeObject(CGObjectInstance *cur)
 		cur->ID = ran.first;
 		h->portrait = cur->subID = ran.second;
 		h->type = VLC->heroh->heroes[ran.second];
-		randomizeArmy(h, h->type->heroType/2);
+		h->randomizeArmy(h->type->heroType/2);
 		map->heroes.push_back(h);
 		return; //TODO: maybe we should do something with definfo?
 	}
@@ -1195,7 +1109,7 @@ void CGameState::randomizeObject(CGObjectInstance *cur)
 			t->defInfo = forts[t->subID];
 		else
 			t->defInfo = villages[t->subID]; 
-		randomizeArmy(t, t->subID);
+		t->randomizeArmy(t->subID);
 		map->towns.push_back(t);
 		return;
 	}
@@ -1655,8 +1569,8 @@ bool CGameState::battleCanFlee(int player)
 	if(!curB) //there is no battle
 		return false;
 
-	if(curB->heroes[0]->hasBonusOfType(HeroBonus::ENEMY_CANT_ESCAPE) //eg. one of heroes is wearing shakles of war
-		|| curB->heroes[0]->hasBonusOfType(HeroBonus::ENEMY_CANT_ESCAPE))
+	if(curB->heroes[0]->hasBonusOfType(Bonus::ENEMY_CANT_ESCAPE) //eg. one of heroes is wearing shakles of war
+		|| curB->heroes[0]->hasBonusOfType(Bonus::ENEMY_CANT_ESCAPE))
 		return false;
 
 	return true;
@@ -1669,7 +1583,7 @@ int CGameState::battleGetStack(int pos, bool onlyAlive)
 	for(unsigned int g=0; g<curB->stacks.size(); ++g)
 	{
 		if((curB->stacks[g]->position == pos 
-			  || (curB->stacks[g]->hasFeatureOfType(StackFeature::DOUBLE_WIDE) 
+			  || (curB->stacks[g]->doubleWide() 
 					&&( (curB->stacks[g]->attackerOwned && curB->stacks[g]->position-1 == pos) 
 					||	(!curB->stacks[g]->attackerOwned && curB->stacks[g]->position+1 == pos)	)
 			 ))
@@ -1758,7 +1672,7 @@ const CGHeroInstance * CGameState::battleGetOwner(int stackID)
 UpgradeInfo CGameState::getUpgradeInfo(const CArmedInstance *obj, int stackPos)
 {
 	UpgradeInfo ret;
-	const CCreature *base = obj->army.slots.find(stackPos)->second.type;
+	const CCreature *base = obj->getCreature(stackPos);
 	if((obj->ID == TOWNI_TYPE)  ||  ((obj->ID == HEROI_TYPE) && static_cast<const CGHeroInstance*>(obj)->visitedTown))
 	{
 		const CGTownInstance * t;
@@ -1777,7 +1691,7 @@ UpgradeInfo CGameState::getUpgradeInfo(const CArmedInstance *obj, int stackPos)
 					ret.cost.push_back(std::set<std::pair<int,int> >());
 					for(int j=0;j<RESOURCE_QUANTITY;j++)
 					{
-						int dif = VLC->creh->creatures[nid].cost[j] - base->cost[j];
+						int dif = VLC->creh->creatures[nid]->cost[j] - base->cost[j];
 						if(dif)
 							ret.cost[ret.cost.size()-1].insert(std::make_pair(j,dif));
 					}
@@ -2378,10 +2292,10 @@ bool CGameState::checkForVisitableDir( const int3 & src, const TerrainTile *pom,
 std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky )
 {
 	float additiveBonus=1.0f, multBonus=1.0f,
-		minDmg = attacker->creature->damageMin * attacker->amount, 
-		maxDmg = attacker->creature->damageMax * attacker->amount;
+		minDmg = attacker->type->damageMin * attacker->count, 
+		maxDmg = attacker->type->damageMax * attacker->count;
 
-	if(attacker->creature->idNumber == 149) //arrow turret
+	if(attacker->type->idNumber == 149) //arrow turret
 	{
 		switch(attacker->position)
 		{
@@ -2396,16 +2310,16 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
 		}
 	}
 
-	if(attacker->hasFeatureOfType(StackFeature::SIEGE_WEAPON) && attacker->creature->idNumber != 149) //any siege weapon, but only ballista can attack (second condition - not arrow turret)
+	if(attacker->hasBonusOfType(Bonus::SIEGE_WEAPON) && attacker->type->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->hasFeatureOfType(StackFeature::GENERAL_ATTACK_REDUCTION))
+	if(attacker->hasBonusOfType(Bonus::GENERAL_ATTACK_REDUCTION))
 	{
-		float multAttackReduction = attacker->valOfFeatures(StackFeature::GENERAL_ATTACK_REDUCTION, -1024) / 100.0f;
+		float multAttackReduction = attacker->valOfBonuses(Bonus::GENERAL_ATTACK_REDUCTION, -1024) / 100.0f;
 		attackDefenceDifference = attacker->Attack() * multAttackReduction;
 	}
 	else
@@ -2413,9 +2327,9 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
 		attackDefenceDifference = attacker->Attack();
 	}
 
-	if(attacker->hasFeatureOfType(StackFeature::ENEMY_DEFENCE_REDUCTION))
+	if(attacker->hasBonusOfType(Bonus::ENEMY_DEFENCE_REDUCTION))
 	{
-		float multDefenceReduction = (100.0f - attacker->valOfFeatures(StackFeature::ENEMY_DEFENCE_REDUCTION, -1024)) / 100.0f;
+		float multDefenceReduction = (100.0f - attacker->valOfBonuses(Bonus::ENEMY_DEFENCE_REDUCTION, -1024)) / 100.0f;
 		attackDefenceDifference -= defender->Defense() * multDefenceReduction;
 	}
 	else
@@ -2425,15 +2339,11 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
 
 	//calculating total attack/defense skills modifier
 
-	if(!shooting && attacker->hasFeatureOfType(StackFeature::ATTACK_BONUS, 0)) //bloodlust handling (etc.)
-	{
-		attackDefenceDifference += attacker->valOfFeatures(StackFeature::ATTACK_BONUS, 0);
-	}
+	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(shooting && attacker->hasFeatureOfType(StackFeature::ATTACK_BONUS, 1)) //precision handling (etc.)
-	{
-		attackDefenceDifference += attacker->valOfFeatures(StackFeature::ATTACK_BONUS, 1);
-	}
 
 	if(attacker->getEffect(55)) //slayer handling
 	{
@@ -2442,11 +2352,11 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
 
 		for(int g = 0; g < VLC->creh->creatures.size(); ++g)
 		{
-			for (int d=0; d<VLC->creh->creatures[g].abilities.size(); ++d)
+			BOOST_FOREACH(const Bonus &b, VLC->creh->creatures[g]->bonuses)
 			{
-				if ( (VLC->creh->creatures[g].abilities[d].type == StackFeature::KING3 && spLevel >= 3) || //expert
-					(VLC->creh->creatures[g].abilities[d].type == StackFeature::KING2 && spLevel >= 2) || //adv +
-					(VLC->creh->creatures[g].abilities[d].type == StackFeature::KING1 && spLevel >= 0) ) //none or basic +
+				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;
@@ -2456,7 +2366,7 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
 
 		for(unsigned int g=0; g<affectedIds.size(); ++g)
 		{
-			if(defender->creature->idNumber == affectedIds[g])
+			if(defender->type->idNumber == affectedIds[g])
 			{
 				attackDefenceDifference += VLC->spellh->spells[55].powers[attacker->getEffect(55)->level];
 				break;
@@ -2492,7 +2402,7 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
 	
 
 	//applying jousting bonus
-	if( attacker->hasFeatureOfType(StackFeature::JOUSTING) && !defender->hasFeatureOfType(StackFeature::CHARGE_IMMUNITY) )
+	if( attacker->hasBonusOfType(Bonus::JOUSTING) && !defender->hasBonusOfType(Bonus::CHARGE_IMMUNITY) )
 		additiveBonus += charge * 0.05f;
 
 	
@@ -2517,7 +2427,7 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
 			if(attackerHero->getSecSkillLevel(1) > 0) //non-none level
 			{
 				//apply artifact premy to archery
-				additiveBonus += attackerHero->valOfBonuses(HeroBonus::SECONDARY_SKILL_PREMY, 1) / 100.0f;
+				additiveBonus += attackerHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 1) / 100.0f;
 			}
 		}
 		else
@@ -2554,7 +2464,7 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
 	}
 
 	//handling hate effect
-	if( attacker->hasFeatureOfType(StackFeature::HATE, defender->creature->idNumber) )
+	if( attacker->hasBonusOfType(Bonus::HATE, defender->type->idNumber) )
 		additiveBonus += 0.5f;
 
 	//luck bonus
@@ -2564,13 +2474,13 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
 	}
 
 	//handling spell effects
-	if(!shooting && defender->hasFeatureOfType(StackFeature::GENERAL_DAMAGE_REDUCTION, 0)) //eg. shield
+	if(!shooting && defender->hasBonusOfType(Bonus::GENERAL_DAMAGE_REDUCTION, 0)) //eg. shield
 	{
-		multBonus *= float(defender->valOfFeatures(StackFeature::GENERAL_DAMAGE_REDUCTION, 0)) / 100.0f;
+		multBonus *= float(defender->valOfBonuses(Bonus::GENERAL_DAMAGE_REDUCTION, 0)) / 100.0f;
 	}
-	else if(shooting && defender->hasFeatureOfType(StackFeature::GENERAL_DAMAGE_REDUCTION, 1)) //eg. air shield
+	else if(shooting && defender->hasBonusOfType(Bonus::GENERAL_DAMAGE_REDUCTION, 1)) //eg. air shield
 	{
-		multBonus *= float(defender->valOfFeatures(StackFeature::GENERAL_DAMAGE_REDUCTION, 1)) / 100.0f;
+		multBonus *= float(defender->valOfBonuses(Bonus::GENERAL_DAMAGE_REDUCTION, 1)) / 100.0f;
 	}
 	if(attacker->getEffect(42)) //curse handling (partial, the rest is below)
 	{
@@ -2636,7 +2546,7 @@ ui32 BattleInfo::calculateDmg( const CStack* attacker, const CStack* defender, c
 	if(range.first != range.second)
 	{
 		int valuesToAverage[10];
-		int howManyToAv = std::min<ui32>(10, attacker->amount);
+		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);
@@ -2653,10 +2563,10 @@ 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->amount : st->baseAmount);
+		si32 killed = (st->alive() ? st->baseAmount - st->count : st->baseAmount);
 		amax(killed, 0);
 		if(killed)
-			casualties[!st->attackerOwned][st->creature->idNumber] += killed;
+			casualties[!st->attackerOwned][st->type->idNumber] += killed;
 	}
 }
 
@@ -2673,16 +2583,16 @@ si8 CGameState::battleMaxSpellLevel()
 	const CGHeroInstance *h1 =  curB->heroes[0];
 	if(h1)
 	{
-		for(std::list<HeroBonus>::const_iterator i = h1->bonuses.begin(); i != h1->bonuses.end(); i++)
-			if(i->type == HeroBonus::BLOCK_SPELLS_ABOVE_LEVEL)
+		for(std::list<Bonus>::const_iterator i = h1->bonuses.begin(); i != h1->bonuses.end(); i++)
+			if(i->type == Bonus::BLOCK_SPELLS_ABOVE_LEVEL)
 				amin(levelLimit, i->val);
 	}
 
 	const CGHeroInstance *h2 = curB->heroes[1];
 	if(h2)
 	{
-		for(std::list<HeroBonus>::const_iterator i = h2->bonuses.begin(); i != h2->bonuses.end(); i++)
-			if(i->type == HeroBonus::BLOCK_SPELLS_ABOVE_LEVEL)
+		for(std::list<Bonus>::const_iterator i = h2->bonuses.begin(); i != h2->bonuses.end(); i++)
+			if(i->type == Bonus::BLOCK_SPELLS_ABOVE_LEVEL)
 				amin(levelLimit, i->val);
 	}
 
@@ -2700,8 +2610,8 @@ std::set<CStack*> BattleInfo::getAttackedCreatures( const CSpell * s, int skillL
 	{
 		for(int it=0; it<stacks.size(); ++it)
 		{
-			if((s->id == 24 && !stacks[it]->creature->isUndead()) //death ripple
-				|| (s->id == 25 && stacks[it]->creature->isUndead()) //destroy undead
+			if((s->id == 24 && !stacks[it]->type->isUndead()) //death ripple
+				|| (s->id == 25 && stacks[it]->type->isUndead()) //destroy undead
 				|| (s->id == 26) //Armageddon
 				)
 			{
@@ -2761,51 +2671,21 @@ int BattleInfo::calculateSpellDuration(const CSpell * spell, const CGHeroInstanc
 	case 56: //frenzy
 		return 1;
 	default: //other spells
-		return caster->getPrimSkillLevel(2) + caster->valOfBonuses(HeroBonus::SPELL_DURATION);
+		return caster->getPrimSkillLevel(2) + caster->valOfBonuses(Bonus::SPELL_DURATION);
 	}
 }
 
-CStack * BattleInfo::generateNewStack(const CGHeroInstance * owner, int creatureID, int amount, int stackID, bool attackerOwned, int slot, int /*TerrainTile::EterrainType*/ terrain, int position) const
+CStack * BattleInfo::generateNewStack(const CStackInstance &base, int stackID, bool attackerOwned, int slot, int /*TerrainTile::EterrainType*/ terrain, int position) const
 {
-	CStack * ret = new CStack(&VLC->creh->creatures[creatureID], amount, attackerOwned ? side1 : side2, stackID, attackerOwned, slot);
-	if(owner)
-	{
-		ret->features.push_back(makeFeature(StackFeature::SPEED_BONUS, StackFeature::WHOLE_BATTLE, 0, owner->valOfBonuses(HeroBonus::STACKS_SPEED), StackFeature::BONUS_FROM_HERO));
-		//base luck/morale calculations
-		ret->morale = owner->getCurrentMorale(slot, false);
-		ret->luck = owner->getCurrentLuck(slot, false);
-		//other bonuses
-		ret->features.push_back(makeFeature(StackFeature::ATTACK_BONUS, StackFeature::WHOLE_BATTLE, 0, owner->getPrimSkillLevel(0), StackFeature::BONUS_FROM_HERO));
-		ret->features.push_back(makeFeature(StackFeature::DEFENCE_BONUS, StackFeature::WHOLE_BATTLE, 0, owner->getPrimSkillLevel(1), StackFeature::BONUS_FROM_HERO));
-
-		if ( owner->hasBonusOfType(HeroBonus::STACK_HEALTH_PERCENT) ) // e.g. Elixir of Life
-			ret->features.push_back(makeFeature(StackFeature::HP_BONUS, StackFeature::WHOLE_BATTLE, 0, 
-				(ret->creature->hitPoints * owner->valOfBonuses(HeroBonus::STACK_HEALTH_PERCENT)) / 100, 
-				StackFeature::BONUS_FROM_HERO));
-		if (owner->hasBonusOfType(HeroBonus::HP_REGENERATION)) // e.g. Elixir of Life
-			ret->features.push_back(makeFeature(StackFeature::HP_REGENERATION, StackFeature::WHOLE_BATTLE, 0,
-				owner->valOfBonuses(HeroBonus::HP_REGENERATION), StackFeature::BONUS_FROM_HERO));
-
-		if (owner->hasBonusOfType(HeroBonus::LEVEL_SPELL_IMMUNITY)) // e.g. Power of the Dragon Father
-			ret->features.push_back(makeFeature(StackFeature::LEVEL_SPELL_IMMUNITY, StackFeature::WHOLE_BATTLE, 0,
-				owner->valOfBonuses(HeroBonus::LEVEL_SPELL_IMMUNITY), StackFeature::BONUS_FROM_HERO));
-
-		ret->features.push_back(makeFeature(StackFeature::HP_BONUS, StackFeature::WHOLE_BATTLE, 0, owner->valOfBonuses(HeroBonus::STACK_HEALTH), StackFeature::BONUS_FROM_HERO));
-		ret->firstHPleft = ret->MaxHealth();
-	}
-	else
-	{
-		ret->morale = 0;
-		ret->luck = 0;
-	}
+	CStack * ret = new CStack(&base, attackerOwned ? side1 : side2, stackID, attackerOwned, slot);
 
 	//native terrain bonuses
-	int faction = ret->creature->faction;
+	int faction = ret->type->faction;
 	if(faction >= 0 && VLC->heroh->nativeTerrains[faction] == terrain)
 	{
-		ret->features.push_back(makeFeature(StackFeature::SPEED_BONUS, StackFeature::WHOLE_BATTLE, 0, 1, StackFeature::OTHER_SOURCE));
-		ret->features.push_back(makeFeature(StackFeature::ATTACK_BONUS, StackFeature::WHOLE_BATTLE, 0, 1, StackFeature::OTHER_SOURCE));
-		ret->features.push_back(makeFeature(StackFeature::DEFENCE_BONUS, StackFeature::WHOLE_BATTLE, 0, 1, StackFeature::OTHER_SOURCE));
+		ret->bonuses.push_back(makeFeature(Bonus::STACKS_SPEED, Bonus::ONE_BATTLE, 0, 1, Bonus::TERRAIN_NATIVE));
+		ret->bonuses.push_back(makeFeature(Bonus::PRIMARY_SKILL, Bonus::ONE_BATTLE, PrimarySkill::ATTACK, 1, Bonus::TERRAIN_NATIVE));
+		ret->bonuses.push_back(makeFeature(Bonus::PRIMARY_SKILL, Bonus::ONE_BATTLE, PrimarySkill::DEFENSE, 1, Bonus::TERRAIN_NATIVE));
 	}
 
 	ret->position = position;
@@ -2823,13 +2703,13 @@ ui32 BattleInfo::getSpellCost(const CSpell * sp, const CGHeroInstance * caster)
 	si32 manaIncrease = 0;
 	for(int g=0; g<stacks.size(); ++g)
 	{
-		if( stacks[g]->owner == caster->tempOwner && stacks[g]->hasFeatureOfType(StackFeature::CHANGES_SPELL_COST_FOR_ALLY) )
+		if( stacks[g]->owner == caster->tempOwner && stacks[g]->hasBonusOfType(Bonus::CHANGES_SPELL_COST_FOR_ALLY) )
 		{
-			amin(manaReduction, stacks[g]->valOfFeatures(StackFeature::CHANGES_SPELL_COST_FOR_ALLY));
+			amin(manaReduction, stacks[g]->valOfBonuses(Bonus::CHANGES_SPELL_COST_FOR_ALLY));
 		}
-		if( stacks[g]->owner != caster->tempOwner && stacks[g]->hasFeatureOfType(StackFeature::CHANGES_SPELL_COST_FOR_ENEMY) )
+		if( stacks[g]->owner != caster->tempOwner && stacks[g]->hasBonusOfType(Bonus::CHANGES_SPELL_COST_FOR_ENEMY) )
 		{
-			amax(manaIncrease, stacks[g]->valOfFeatures(StackFeature::CHANGES_SPELL_COST_FOR_ENEMY));
+			amax(manaIncrease, stacks[g]->valOfBonuses(Bonus::CHANGES_SPELL_COST_FOR_ENEMY));
 		}
 	}
 
@@ -2866,10 +2746,10 @@ std::pair<const CStack *, int> BattleInfo::getNearestStack(const CStack * closes
 	bool ac[BFIELD_SIZE];
 	std::set<int> occupyable;
 
-	getAccessibilityMap(ac, closest->hasFeatureOfType(StackFeature::DOUBLE_WIDE), closest->attackerOwned, false, occupyable, closest->hasFeatureOfType(StackFeature::FLYING), closest->ID);
+	getAccessibilityMap(ac, closest->doubleWide(), closest->attackerOwned, false, occupyable, closest->hasBonusOfType(Bonus::FLYING), closest->ID);
 
 	int predecessor[BFIELD_SIZE], dist[BFIELD_SIZE];
-	makeBFS(closest->position, ac, predecessor, dist, closest->hasFeatureOfType(StackFeature::DOUBLE_WIDE), closest->attackerOwned, closest->hasFeatureOfType(StackFeature::FLYING), true);
+	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)
@@ -2944,59 +2824,59 @@ ui32 BattleInfo::calculateSpellDmg( const CSpell * sp, const CGHeroInstance * ca
 		}
 	}
 	//applying hero bonuses
-	if(sp->air && caster && caster->valOfBonuses(HeroBonus::AIR_SPELL_DMG_PREMY) != 0)
+	if(sp->air && caster && caster->valOfBonuses(Bonus::AIR_SPELL_DMG_PREMY) != 0)
 	{
-		ret *= (100.0f + caster->valOfBonuses(HeroBonus::AIR_SPELL_DMG_PREMY)) / 100.0f;
+		ret *= (100.0f + caster->valOfBonuses(Bonus::AIR_SPELL_DMG_PREMY)) / 100.0f;
 	}
-	else if(sp->fire && caster && caster->valOfBonuses(HeroBonus::FIRE_SPELL_DMG_PREMY) != 0)
+	else if(sp->fire && caster && caster->valOfBonuses(Bonus::FIRE_SPELL_DMG_PREMY) != 0)
 	{
-		ret *= (100.0f + caster->valOfBonuses(HeroBonus::FIRE_SPELL_DMG_PREMY)) / 100.0f;
+		ret *= (100.0f + caster->valOfBonuses(Bonus::FIRE_SPELL_DMG_PREMY)) / 100.0f;
 	}
-	else if(sp->water && caster && caster->valOfBonuses(HeroBonus::WATER_SPELL_DMG_PREMY) != 0)
+	else if(sp->water && caster && caster->valOfBonuses(Bonus::WATER_SPELL_DMG_PREMY) != 0)
 	{
-		ret *= (100.0f + caster->valOfBonuses(HeroBonus::WATER_SPELL_DMG_PREMY)) / 100.0f;
+		ret *= (100.0f + caster->valOfBonuses(Bonus::WATER_SPELL_DMG_PREMY)) / 100.0f;
 	}
-	else if(sp->earth && caster && caster->valOfBonuses(HeroBonus::EARTH_SPELL_DMG_PREMY) != 0)
+	else if(sp->earth && caster && caster->valOfBonuses(Bonus::EARTH_SPELL_DMG_PREMY) != 0)
 	{
-		ret *= (100.0f + caster->valOfBonuses(HeroBonus::EARTH_SPELL_DMG_PREMY)) / 100.0f;
+		ret *= (100.0f + caster->valOfBonuses(Bonus::EARTH_SPELL_DMG_PREMY)) / 100.0f;
 	}
 
 	//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->hasFeatureOfType(StackFeature::SPELL_DAMAGE_REDUCTION, 0)) //air spell & protection from air
+		if(sp->air && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, 0)) //air spell & protection from air
 		{
-			ret *= affectedCreature->valOfFeatures(StackFeature::SPELL_DAMAGE_REDUCTION, 0);
+			ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, 0);
 			ret /= 100;
 		}
-		else if(sp->fire && affectedCreature->hasFeatureOfType(StackFeature::SPELL_DAMAGE_REDUCTION, 1)) //fire spell & protection from fire
+		else if(sp->fire && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, 1)) //fire spell & protection from fire
 		{
-			ret *= affectedCreature->valOfFeatures(StackFeature::SPELL_DAMAGE_REDUCTION, 1);
+			ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, 1);
 			ret /= 100;
 		}
-		else if(sp->water && affectedCreature->hasFeatureOfType(StackFeature::SPELL_DAMAGE_REDUCTION, 2)) //water spell & protection from water
+		else if(sp->water && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, 2)) //water spell & protection from water
 		{
-			ret *= affectedCreature->valOfFeatures(StackFeature::SPELL_DAMAGE_REDUCTION, 2);
+			ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, 2);
 			ret /= 100;
 		}
-		else if (sp->earth && affectedCreature->hasFeatureOfType(StackFeature::SPELL_DAMAGE_REDUCTION, 3)) //earth spell & protection from earth
+		else if (sp->earth && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, 3)) //earth spell & protection from earth
 		{
-			ret *= affectedCreature->valOfFeatures(StackFeature::SPELL_DAMAGE_REDUCTION, 3);
+			ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, 3);
 			ret /= 100;
 		}
 
 		//general spell dmg reduction
-		if(sp->air && affectedCreature->hasFeatureOfType(StackFeature::SPELL_DAMAGE_REDUCTION, -1)) //air spell & protection from air
+		if(sp->air && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, -1)) //air spell & protection from air
 		{
-			ret *= affectedCreature->valOfFeatures(StackFeature::SPELL_DAMAGE_REDUCTION, -1);
+			ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, -1);
 			ret /= 100;
 		}
 
 		//dmg increasing
-		if( affectedCreature->hasFeatureOfType(StackFeature::MORE_DAMAGE_FROM_SPELL, sp->id) )
+		if( affectedCreature->hasBonusOfType(Bonus::MORE_DAMAGE_FROM_SPELL, sp->id) )
 		{
-			ret *= 100 + affectedCreature->valOfFeatures(StackFeature::MORE_DAMAGE_FROM_SPELL, sp->id);
+			ret *= 100 + affectedCreature->valOfBonuses(Bonus::MORE_DAMAGE_FROM_SPELL, sp->id);
 			ret /= 100;
 		}
 	}
@@ -3016,16 +2896,16 @@ bool CGameState::battleCanShoot(int ID, int dest)
 
 	const CGHeroInstance * ourHero = battleGetOwner(our->ID);
 
-	if(our->hasFeatureOfType(StackFeature::FORGETFULL)) //forgetfulness
+	if(our->hasBonusOfType(Bonus::FORGETFULL)) //forgetfulness
 		return false;
 
-	if(our->creature->idNumber == 145 && dst) //catapult cannot attack creatures
+	if(our->type->idNumber == 145 && dst) //catapult cannot attack creatures
 		return false;
 
-	if(our->hasFeatureOfType(StackFeature::SHOOTER)//it's shooter
+	if(our->hasBonusOfType(Bonus::SHOOTER)//it's shooter
 		&& our->owner != dst->owner
 		&& dst->alive()
-		&& (!curB->isStackBlocked(ID)  ||  NBonus::hasOfType(ourHero, HeroBonus::FREE_SHOOTING))
+		&& (!curB->isStackBlocked(ID)  ||  NBonus::hasOfType(ourHero, Bonus::FREE_SHOOTING))
 		&& our->shots
 		)
 		return true;
@@ -3062,7 +2942,7 @@ int CGameState::victoryCheck( ui8 player ) const
 						&& map->objects[i]->tempOwner == player //object controlled by player
 						&&  (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
+						for(TSlots::const_iterator i=ai->Slots().begin(); i!=ai->Slots().end(); ++i) //iterate through army
 							if(i->second.type->idNumber == map->victoryCondition.ID) //it's searched creature
 								total += i->second.count;
 					}
@@ -3291,7 +3171,7 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
 			const CGHeroInstance * best = statsHLP::findBestHero(this, g->second.color);
 			InfoAboutHero iah;
 			iah.initFromHero(best, level >= 8);
-			iah.army.slots.clear();
+			iah.army.clear();
 			tgi.colorToBestHero[g->second.color] = iah;
 		}
 	}
@@ -3354,10 +3234,10 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
 			int bestCre = -1; //best creature's ID
 			for(int b=0; b<g->second.heroes.size(); ++b)
 			{
-				for(TSlots::const_iterator it = g->second.heroes[b]->army.slots.begin(); it != g->second.heroes[b]->army.slots.end(); ++it)
+				for(TSlots::const_iterator it = g->second.heroes[b]->Slots().begin(); it != g->second.heroes[b]->Slots().end(); ++it)
 				{
 					int toCmp = it->second.type->idNumber; //ID of creature we should compare with the best one
-					if(bestCre == -1 || VLC->creh->creatures[bestCre].AIValue < VLC->creh->creatures[toCmp].AIValue)
+					if(bestCre == -1 || VLC->creh->creatures[bestCre]->AIValue < VLC->creh->creatures[toCmp]->AIValue)
 					{
 						bestCre = toCmp;
 					}
@@ -3508,7 +3388,7 @@ void BattleInfo::getStackQueue( std::vector<const CStack *> &out, int howMany, i
 			else
 				p = 3;
 		}
-		else if(s->creature->idNumber == 145  ||  s->creature->idNumber == 149) //catapult and turrets are first
+		else if(s->type->idNumber == 145  ||  s->type->idNumber == 149) //catapult and turrets are first
 		{
 			p = 0;
 		}
@@ -3566,77 +3446,6 @@ void BattleInfo::getStackQueue( std::vector<const CStack *> &out, int howMany, i
 	}
 }
 
-si8 BattleInfo::Morale( const CStack * st ) const
-{
-	si8 ret = st->morale;
-
-	if(st->hasFeatureOfType(StackFeature::NON_LIVING) || st->hasFeatureOfType(StackFeature::UNDEAD) ||
-		st->hasFeatureOfType(StackFeature::NO_MORALE) || st->hasFeatureOfType(StackFeature::SIEGE_WEAPON))
-		return 0;
-
-	ret += st->valOfFeatures(StackFeature::MORALE_BONUS); //mirth & sorrow & other
-
-	//decreasing / increasing morale from  other stacks
-	for (int g=0; g<stacks.size(); ++g)
-	{
-		if (stacks[g]->owner == st->owner) //ally
-		{
-			if (stacks[g]->hasFeatureOfType(StackFeature::RAISING_MORALE))
-			{
-				ret += stacks[g]->valOfFeatures(StackFeature::RAISING_MORALE);
-			}
-		}
-		else //enemy
-		{
-			if (stacks[g]->hasFeatureOfType(StackFeature::ENEMY_MORALE_DECREASING))
-			{
-				ret -= stacks[g]->valOfFeatures(StackFeature::ENEMY_MORALE_DECREASING);
-			}
-		}
-	}
-
-	if(st->hasFeatureOfType(StackFeature::SELF_MORALE)) //eg. minotaur
-	{
-		ret = std::max<si8>(ret, +1);
-	}
-
-	if(ret > 3) ret = 3;
-	if(ret < -3) ret = -3;
-	return ret;
-}
-
-si8 BattleInfo::Luck( const CStack * st ) const
-{
-	si8 ret = st->luck;
-
-	if(st->hasFeatureOfType(StackFeature::NO_LUCK))
-		return 0;
-
-	ret += st->valOfFeatures(StackFeature::LUCK_BONUS); //fortune & misfortune & other
-
-	//decreasing / increasing morale from  other stacks
-	for (int g=0; g<stacks.size(); ++g)
-	{
-		if (stacks[g]->owner == st->owner) //ally
-		{
-			//no such feature (yet)
-		}
-		else //enemy
-		{
-			ret -= stacks[g]->valOfFeatures(StackFeature::ENEMY_LUCK_DECREASING);
-		}
-	}
-
-	if(st->hasFeatureOfType(StackFeature::SELF_LUCK)) //eg. halfling
-	{
-		ret = std::max<si8>(ret, +1);
-	}
-
-	if(ret > 3) ret = 3;
-	if(ret < -3) ret = -3;
-	return ret;
-}
-
 si8 BattleInfo::hasDistancePenalty( int stackID, int destHex )
 {
 	const CStack * stack = getStack(stackID);
@@ -3644,7 +3453,7 @@ si8 BattleInfo::hasDistancePenalty( int stackID, int destHex )
 	int distance = std::abs(destHex % BFIELD_WIDTH - stack->position % BFIELD_WIDTH);
 
 	//I hope it's approximately correct
-	return distance > 8 && !stack->hasFeatureOfType(StackFeature::NO_DISTANCE_PENALTY);
+	return distance > 8 && !stack->hasBonusOfType(Bonus::NO_DISTANCE_PENALTY);
 }
 
 si8 BattleInfo::hasWallPenalty( int stackID, int destHex )
@@ -3654,7 +3463,7 @@ si8 BattleInfo::hasWallPenalty( int stackID, int destHex )
 		return false;
 	}
 	const CStack * stack = getStack(stackID);
-	if (stack->hasFeatureOfType(StackFeature::NO_WALL_PENALTY));
+	if (stack->hasBonusOfType(Bonus::NO_WALL_PENALTY));
 	{
 		return false;
 	}
@@ -3667,6 +3476,28 @@ si8 BattleInfo::hasWallPenalty( int stackID, int destHex )
 	return stackLeft != destLeft;
 }
 
+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);
+		}
+	}
+}
+
 int3 CPath::startPos() const
 {
 	return nodes[nodes.size()-1].coord;
@@ -3766,7 +3597,7 @@ bool CMP_stack::operator()( const CStack* a, const CStack* b )
 	switch(phase)
 	{
 	case 0: //catapult moves after turrets
-		return a->creature->idNumber < b->creature->idNumber; //catapult is 145 and turrets are 149
+		return a->type->idNumber < b->type->idNumber; //catapult is 145 and turrets are 149
 		//TODO? turrets order
 	case 1: //fastest first, upper slot first
 		{
@@ -3805,6 +3636,11 @@ PlayerState::PlayerState()
 
 }
 
+void PlayerState::getParents(TCNodes &out, const CBonusSystemNode *root /*= NULL*/) const
+{
+	//TODO: global effects
+}
+
 InfoAboutHero::InfoAboutHero()
 {
 	details = NULL;
@@ -3830,14 +3666,14 @@ void InfoAboutHero::initFromHero( const CGHeroInstance *h, bool detailed )
 	hclass = h->type->heroClass;
 	name = h->name;
 	portrait = h->portrait;
-	army = h->army; 
+	army = h->getArmy(); 
 
 	if(detailed) 
 	{
 		//include details about hero
 		details = new Details;
-		details->luck = h->getCurrentLuck();
-		details->morale = h->getCurrentMorale();
+		details->luck = h->LuckVal();
+		details->morale = h->MoraleVal();
 		details->mana = h->mana;
 		details->primskills.resize(PRIMARY_SKILLS);
 
@@ -3849,9 +3685,9 @@ void InfoAboutHero::initFromHero( const CGHeroInstance *h, bool detailed )
 	else
 	{
 		//hide info about hero stacks counts using descriptives names ids
-		for(TSlots::iterator i = army.slots.begin(); i != army.slots.end(); ++i)
+		for(TSlots::const_iterator i = army.Slots().begin(); i != army.Slots().end(); ++i)
 		{
-			i->second.count = i->second.getQuantityID();
+			army.setStackCount(i->first, i->second.getQuantityID());
 		}
 	}
 }

+ 32 - 40
lib/CGameState.h

@@ -2,22 +2,26 @@
 #define __CGAMESTATE_H__
 #include "../global.h"
 #include <cassert>
+
 #ifndef _MSC_VER
 #include "../hch/CCreatureHandler.h"
 #include "VCMI_Lib.h"
 #include "map.h"
 #endif
+
 #include <set>
 #include <vector>
 #include <list>
-#include "StackFeature.h"
 #include "HeroBonus.h"
+#include "CCreatureSet.h"
+
 #ifdef _WIN32
 #include <tchar.h>
 #else
 #include "../tchar_amigaos4.h"
 #endif
 
+
 /*
  * CGameState.h, part of VCMI engine
  *
@@ -123,11 +127,13 @@ public:
 	ui8 daysWithoutCastle;
 
 	PlayerState();
+	virtual void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & color & serial & human & currentSelection & fogOfWarMap & resources & status;
 		h & heroes & towns & availableHeroes & dwellings & bonuses & status & daysWithoutCastle;
+		h & static_cast<CBonusSystemNode&>(*this);
 	}
 };
 
@@ -153,7 +159,7 @@ struct DLL_EXPORT SiegeInfo
 	}
 };
 
-struct DLL_EXPORT BattleInfo
+struct DLL_EXPORT BattleInfo : public CBonusSystemNode
 {
 	ui8 side1, side2; //side1 - attacker, side2 - defender
 	si32 round, activeStack;
@@ -161,7 +167,7 @@ struct DLL_EXPORT BattleInfo
 	si32 tid; //used during town siege - id of attacked town; -1 if not town defence
 	int3 tile; //for background and bonuses
 	CGHeroInstance *heroes[2];
-	CCreatureSet army1, army2;
+	CArmedInstance *belligerents[2]; //may be same as heroes
 	std::vector<CStack*> stacks;
 	std::vector<CObstacleInstance> obstacles;
 	ui8 castSpells[2]; //[0] - attacker, [1] - defender
@@ -169,10 +175,16 @@ struct DLL_EXPORT BattleInfo
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & side1 & side2 & round & activeStack & siege & tid & tile & stacks & army1 & army2 & obstacles
+		h & side1 & side2 & round & activeStack & siege & tid & tile & stacks & belligerents & obstacles
 			& castSpells & si;
 		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);
@@ -185,9 +197,6 @@ struct DLL_EXPORT BattleInfo
 	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)
 
-	si8 Morale(const CStack * st) const; //get morale of stack with all modificators
-	si8 Luck(const CStack * st) const; //get luck of stack with all modificators
-
 	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);
@@ -196,7 +205,7 @@ struct DLL_EXPORT BattleInfo
 	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);
-	CStack * generateNewStack(const CGHeroInstance * owner, int creatureID, int amount, 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
+	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
@@ -206,27 +215,24 @@ struct DLL_EXPORT BattleInfo
 	si8 hasWallPenalty(int stackID, int destHex); //determines if given stack has wall penalty shooting given pos
 };
 
-class DLL_EXPORT CStack
+class DLL_EXPORT CStack : public CStackInstance
 { 
 public:
 	ui32 ID; //unique ID of stack
-	CCreature * creature;
-	ui32 amount, baseAmount;
+	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
-	si8 morale, luck; //base stack luck/morale
 
-	std::vector<StackFeature> features;
 	std::set<ECombatInfo> state;
 	struct StackEffect
 	{
 		ui16 id; //spell id
 		ui8 level; //skill level
-		ui16 turnsRemain; 
+		si16 turnsRemain; 
 		template <typename Handler> void serialize(Handler &h, const int version)
 		{
 			h & id & level & turnsRemain;
@@ -234,39 +240,25 @@ public:
 	};
 	std::vector<StackEffect> effects;
 
-	int valOfFeatures(StackFeature::ECombatFeatures type, int subtype = -1024, int turn = 0) const;//subtype -> subtype of bonus, if -1024 then any
-	bool hasFeatureOfType(StackFeature::ECombatFeatures type, int subtype = -1024, int turn = 0) const; //determines if stack has a bonus of given type (and optionally subtype)
+	//overrides
+	const CCreature* getCreature() const {return type;}
 
-	CStack(CCreature * C, int A, int O, int I, bool AO, int S); //c-tor
-	CStack() : ID(-1), creature(NULL), amount(-1), baseAmount(-1), firstHPleft(-1), owner(255), slot(255), attackerOwned(true), position(-1), counterAttacks(1) {} //c-tor
+	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 StackEffect * 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
-	si32 Attack() const; //get attack of stack with all modificators
-	si32 Defense(bool withFrenzy = true) const; //get defense of stack with all modificators
-	ui16 MaxHealth() const; //get max HP of stack with all modifiers
-	template <typename Handler> void save(Handler &h, const int version)
-	{
-		h & creature->idNumber;
-	}
-	template <typename Handler> void load(Handler &h, const int version)
-	{
-		ui32 id;
-		h & id;
-		creature = &VLC->creh->creatures[id];
-		//features = creature->abilities;
-	}
+
+	bool doubleWide() const;
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & ID & amount & baseAmount & firstHPleft & owner & slot & attackerOwned & position & state & counterAttacks
-			& shots & morale & luck & features;
-		if(h.saving)
-			save(h,version);
-		else
-			load(h,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
 	{
@@ -363,6 +355,7 @@ public:
 	std::map<ui8, PlayerState> players; //ID <-> player state
 	std::map<int, CGDefInfo*> villages, forts, capitols; //def-info for town graphics
 	std::vector<ui32> resVals; //default values of resources in gold
+	CBonusSystemNode globalEffects;
 
 	struct DLL_EXPORT HeroesPool
 	{
@@ -378,7 +371,6 @@ public:
 	} hpool; //we have here all heroes available on this map that are not hired
 
 	boost::shared_mutex *mx;
-
 	PlayerState *getPlayer(ui8 color, bool verbose = true);
 	const PlayerState *getPlayer(ui8 color, bool verbose = true) const;
 	void init(StartInfo * si, Mapa * map, int Seed);
@@ -424,7 +416,7 @@ public:
 	int getDate(int mode=0) const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & scenarioOps & seed & currentPlayer & day & map & players & resVals & hpool;
+		h & scenarioOps & seed & currentPlayer & day & map & players & resVals & hpool & globalEffects;
 		if(!h.saving)
 		{
 			loadTownDInfos();

+ 17 - 0
lib/Connection.cpp

@@ -14,6 +14,8 @@
 #include "CGameState.h"
 #include "map.h"
 #include "../hch/CObjectHandler.h"
+#include "../hch/CCreatureHandler.h"
+#include "VCMI_Lib.h"
 
 
 /*
@@ -202,6 +204,7 @@ void CConnection::close()
 	}
 }
 
+
 CGObjectInstance *CConnection::loadObject()
 {
 	assert(gs);
@@ -218,6 +221,20 @@ void CConnection::saveObject( const CGObjectInstance *data )
 	*this << data->id;
 }
 
+CCreature * CConnection::loadCreature()
+{
+	si32 id;
+	*this >> id;
+	assert(id >= 0 && id < VLC->creh->creatures.size());
+	return VLC->creh->creatures[id];
+}
+
+void CConnection::saveCreature(const CCreature *data)
+{
+	assert(data);
+	*this << data->idNumber;
+}
+
 void CConnection::setGS( CGameState *state )
 {
 	gs = state;

+ 28 - 2
lib/Connection.h

@@ -25,6 +25,7 @@ const ui32 version = 719;
 class CConnection;
 class CGObjectInstance;
 class CGameState;
+class CCreature;
 namespace mpl = boost::mpl;
 
 /*
@@ -740,6 +741,8 @@ public:
 
 	CGObjectInstance *loadObject(); //reads id from net and returns that obj
 	void saveObject(const CGObjectInstance *data);
+	CCreature *loadCreature(); //reads id from net and returns that obj
+	void saveCreature(const CCreature *data);
 
 
 	template<typename T>
@@ -751,6 +754,14 @@ public:
 		}
 	};
 	template<typename T>
+	struct loadCreatureHelper
+	{
+		static void invoke(CConnection &s, T &data, ui16 tid)
+		{
+			data = static_cast<T>(s.loadCreature());
+		}
+	};
+	template<typename T>
 	struct loadRestHelper
 	{
 		static void invoke(CConnection &s, T &data, ui16 tid)
@@ -768,6 +779,15 @@ public:
 		}
 	};
 	template<typename T>
+	struct saveCreatureHelper
+	{
+		static void invoke(CConnection &s, const T &data, ui16 tid)
+		{
+			//CGObjectInstance *&hlp = const_cast<CGObjectInstance*&>(data); //for loading pointer to const obj we must remove the qualifier
+			s.saveCreature(data);
+		}
+	};
+	template<typename T>
 	struct saveRestHelper
 	{
 		static void invoke(CConnection &s, const T &data, ui16 tid)
@@ -785,9 +805,12 @@ public:
 			//if
 			mpl::eval_if< boost::is_base_of<CGObjectInstance, typename boost::remove_pointer<T>::type>,
 			mpl::identity<loadObjectHelper<T> >,
+			//else if
+			mpl::eval_if< boost::is_base_of<CCreature, typename boost::remove_pointer<T>::type>,
+			mpl::identity<loadCreatureHelper<T> >,
 			//else
 			mpl::identity<loadRestHelper<T> >
-			>::type typex;
+			> >::type typex;
 		typex::invoke(*this, data, tid);
 	}
 
@@ -799,9 +822,12 @@ public:
 			//if
 			mpl::eval_if< boost::is_base_of<CGObjectInstance, typename boost::remove_pointer<T>::type>,
 				mpl::identity<saveObjectHelper<T> >,
+			//else if
+			mpl::eval_if< boost::is_base_of<CCreature, typename boost::remove_pointer<T>::type>,
+			mpl::identity<saveCreatureHelper<T> >,
 			//else
 				mpl::identity<saveRestHelper<T> >
-			>::type typex;
+			> >::type typex;
 		typex::invoke(*this, data, tid);
 	}
 };

+ 266 - 89
lib/HeroBonus.cpp

@@ -1,156 +1,265 @@
 #define VCMI_DLL
 #include "HeroBonus.h"
 #include <boost/foreach.hpp>
+#include "VCMI_Lib.h"
+#include "../hch/CSpellHandler.h"
+#include <sstream>
+#include "../hch/CCreatureHandler.h"
 
-#define FOREACH_PARENT(pname) 	TCNodes parents; getParents(parents); BOOST_FOREACH(const CBonusSystemNode *pname, parents)
+#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)
 
-int BonusList::valOfBonuses( HeroBonus::BonusType type, int subtype /*= -1*/ ) const /*subtype -> subtype of bonus, if -1 then any */
+int DLL_EXPORT BonusList::totalValue() const
 {
-	if(!this) //to avoid null-checking in maany places -> no bonus list means 0 bonus value
-		return 0;
+	int base = 0;
+	int percentToBase = 0;
+	int percentToAll = 0;
+	int additive = 0;
 
-	int ret = 0;
-	if(subtype == -1)
-	{
-		for(const_iterator i = begin(); i != end(); i++)
-			if(i->type == type)
-				ret += i->val;
-	}
-	else
+	for(const_iterator i = begin(); i != end(); i++)
 	{
-		for(const_iterator i = begin(); i != end(); i++)
-			if(i->type == type && i->subtype == subtype)
-				ret += i->val;
+		switch(i->valType)
+		{
+		case Bonus::BASE_NUMBER:
+			base += i->val;
+			break;
+		case Bonus::PERCENT_TO_ALL:
+			percentToAll += i->val;
+			break;
+		case Bonus::PERCENT_TO_BASE:
+			percentToBase += i->val;
+			break;
+		case Bonus::ADDITIVE_VALUE:
+			additive += i->val;
+			break;
+		}
 	}
-	return ret;
+	int modifiedBase = base + (base * percentToBase) / 100;
+	modifiedBase += additive;
+	return (modifiedBase * (100 + percentToAll)) / 100;
 }
 
-bool BonusList::hasBonusOfType( HeroBonus::BonusType type, int subtype /*= -1*/ ) const 
+const DLL_EXPORT Bonus * BonusList::getFirst(const CSelector &selector) const
 {
-	if(!this) //to avoid null-checking in maany places -> no bonus list means there is no searched bonus
-		return 0;
+	for (const_iterator i = begin(); i != end(); i++)
+		if(selector(*i))
+			return &*i;
+	return NULL;
+}
 
-	if(subtype == -1) //any subtype
-	{
-		for(const_iterator i = begin(); i != end(); i++)
-			if(i->type == type)
-				return true;
-	}
-	else //given subtype
-	{
-		for(const_iterator i = begin(); i != end(); i++)
-			if(i->type == type && i->subtype == subtype)
-				return true;
-	}
-	return false;
+DLL_EXPORT Bonus * BonusList::getFirst(const CSelector &select)
+{
+	for (iterator i = begin(); i != end(); i++)
+		if(select(*i))
+			return &*i;
+	return NULL;
 }
 
-const HeroBonus * BonusList::getBonus( int from, int id ) const
+void DLL_EXPORT BonusList::getModifiersWDescr(TModDescr &out) const
 {
-	if(!this) //to avoid null-checking in maany places -> no bonus list means bonus cannot be retreived
-		return NULL;
+	for(const_iterator i = begin(); i != end(); i++)
+		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);
+}
+
+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);
+}
+
+int CBonusSystemNode::valOfBonuses(Bonus::BonusType type, int subtype /*= -1*/) const
+{
+	CSelector s = Selector::type(type);
+	if(subtype != -1)
+		s = s && Selector::subtype(subtype);
+
+	return valOfBonuses(s);
+}
+
+int CBonusSystemNode::valOfBonuses(Bonus::BonusType type, const CSelector &selector) const
+{
+	return valOfBonuses(Selector::type(type) && selector);
+}
+
+int CBonusSystemNode::valOfBonuses(const CSelector &selector, const CBonusSystemNode *root/* = NULL*/) const
+{
+	BonusList hlp;
+	getBonuses(hlp, selector, root);
+	return hlp.totalValue();
+}
+
+bool CBonusSystemNode::hasBonus(const CSelector &selector, const CBonusSystemNode *root/* = NULL*/) const
+{
+	return getBonuses(selector).size() > 0;
+}
+
+bool CBonusSystemNode::hasBonusOfType(Bonus::BonusType type, int subtype /*= -1*/) const
+{
+	CSelector s = Selector::type(type);
+	if(subtype != -1)
+		s = s && Selector::subtype(subtype);
+
+	return hasBonus(s);
+}
+
+Bonus * CBonusSystemNode::getBonus(const CSelector &selector)
+{
+	Bonus *ret = bonuses.getFirst(selector);
+	if(ret)
+		return ret;
+
+	FOREACH_PARENT(p, this)
+		if(ret = p->getBonus(selector))
+			return ret;
 
-	for (const_iterator i = begin(); i != end(); i++)
-		if(i->source == from  &&  i->id == id)
-			return &*i;
 	return NULL;
 }
 
-void BonusList::getModifiersWDescr( std::vector<std::pair<int,std::string> > &out, HeroBonus::BonusType type, int subtype /*= -1 */ ) const
+void CBonusSystemNode::getModifiersWDescr(TModDescr &out, Bonus::BonusType type, int subtype /*= -1 */) const
 {
-	if(!this) //to avoid null-checking in maany places -> no bonus list means nothing has to be done here
-		return;
+	getModifiersWDescr(out, Selector::typeSybtype(type, subtype));
+}
 
-	if(subtype == -1)
-	{
-		for(const_iterator i = begin(); i != end(); i++)
-			if(i->type == type)
-				out.push_back(std::make_pair(i->val, i->description));
-	}
-	else
-	{
-		for(const_iterator i = begin(); i != end(); i++)
-			if(i->type == type && i->subtype == subtype)
-				out.push_back(std::make_pair(i->val, i->description));
-	}
+void CBonusSystemNode::getModifiersWDescr(TModDescr &out, const CSelector &selector, const CBonusSystemNode *root /*= NULL*/) const
+{
+	getBonuses(selector).getModifiersWDescr(out);
+}
+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
+{
+	return getBonuses(selector, root).size();
+}
+
+void CBonusSystemNode::getParents(TCNodes &out, const CBonusSystemNode *root) const /*retreives list of parent nodes (nodes to inherit bonuses from) */
+{
+	return;
+}
+
+void CBonusSystemNode::getParents(TNodes &out, const CBonusSystemNode *root /*= NULL*/)
+{
+	//de-constify above
+	TCNodes hlp;
+	getParents(hlp, root);
+	BOOST_FOREACH(const CBonusSystemNode *pname, hlp)
+		out.insert(const_cast<CBonusSystemNode*>(pname));
+}
+
+void CBonusSystemNode::getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root /*= NULL*/) const
+{
+	bonuses.getBonuses(out, selector);
+	FOREACH_CONST_PARENT(p, root ? root : this)
+		p->getBonuses(out, selector, root ? root : this);
 }
 
-int CBonusSystemNode::valOfBonuses(HeroBonus::BonusType type, int subtype /*= -1*/) const
+BonusList CBonusSystemNode::getBonuses(const CSelector &selector, const CBonusSystemNode *root /*= NULL*/) const
 {
-	int ret = bonuses.valOfBonuses(type, subtype);
+	BonusList ret;
+	getBonuses(ret, selector, root);
+	return ret;
+}
 
-	FOREACH_PARENT(p)
-		ret += p->valOfBonuses(type, subtype);
+void CBonusSystemNode::getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= NULL*/) const
+{
+	bonuses.getBonuses(out, selector, limit);
+	FOREACH_CONST_PARENT(p, root ? root : this)
+		p->getBonuses(out, selector, limit, root ? root : this);
+}
 
+BonusList CBonusSystemNode::getBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= NULL*/) const
+{
+	BonusList ret;
+	getBonuses(ret, selector, limit, root);
 	return ret;
 }
 
-bool CBonusSystemNode::hasBonusOfType(HeroBonus::BonusType type, int subtype /*= -1*/) const
+bool CBonusSystemNode::hasBonusFrom(ui8 source, ui32 sourceID) const
+{
+	return hasBonus(Selector::source(source,sourceID));
+}
+
+int CBonusSystemNode::MoraleVal() const
 {
-	if(!this) //to allow calls on NULL and avoid checking duplication
-		return false; //if hero doesn't exist then bonus neither can
+	if(hasBonusOfType(Bonus::NON_LIVING) || hasBonusOfType(Bonus::UNDEAD) ||
+		hasBonusOfType(Bonus::NO_MORALE) || hasBonusOfType(Bonus::SIEGE_WEAPON))
+		return 0;
 
-	if(bonuses.hasBonusOfType(type, subtype))
-		return true;
+	int ret = valOfBonuses(Selector::type(Bonus::MORALE));
 
-	FOREACH_PARENT(p)
-		if(p->hasBonusOfType(type, subtype))
-			return true;
+	if(hasBonusOfType(Bonus::SELF_MORALE)) //eg. minotaur
+		amax(ret, +1);
 
-	return false;
+	return abetw(ret, -3, +3);
 }
 
-const HeroBonus * CBonusSystemNode::getBonus(int from, int id) const
+int CBonusSystemNode::LuckVal() const
 {
-	return bonuses.getBonus(from, id);
+	if(hasBonusOfType(Bonus::NO_LUCK))
+		return 0;
+
+	int ret = valOfBonuses(Selector::type(Bonus::LUCK));
+	
+	if(hasBonusOfType(Bonus::SELF_LUCK)) //eg. halfling
+		amax(ret, +1);
+
+	return abetw(ret, -3, +3);
 }
 
-void CBonusSystemNode::getModifiersWDescr(std::vector<std::pair<int,std::string> > &out, HeroBonus::BonusType type, int subtype /*= -1 */) const
+si32 CBonusSystemNode::Attack() const
 {
-	bonuses.getModifiersWDescr(out, type, subtype);
+	si32 ret = valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK);
+
+	if(int frenzyPower = valOfBonuses(Bonus::IN_FRENZY)) //frenzy for attacker
+	{
+		ret += frenzyPower * Defense(false);
+	}
 
-	FOREACH_PARENT(p)
-		p->getModifiersWDescr(out, type, subtype);
+	return ret;
 }
 
-int CBonusSystemNode::getBonusesCount(int from, int id) const
+si32 CBonusSystemNode::Defense(bool withFrenzy /*= true*/) const
 {
-	int ret = 0;
+	si32 ret = valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE);
 
-	BOOST_FOREACH(const HeroBonus &hb, bonuses)
-		if(hb.source == from  &&  hb.id == id)
-			ret++;
+	if(withFrenzy && hasBonusOfType(Bonus::IN_FRENZY)) //frenzy for defender
+	{
+		return 0;
+	}
 
 	return ret;
 }
 
-void CBonusSystemNode::getParents(TCNodes &out, const CBonusSystemNode *source) const /*retreives list of parent nodes (nodes to inherit bonuses from) */
+ui16 CBonusSystemNode::MaxHealth() const
 {
-	return;
+	return valOfBonuses(Bonus::STACK_HEALTH);
 }
 
-int NBonus::valOf(const CBonusSystemNode *obj, HeroBonus::BonusType type, int subtype /*= -1*/)
+int NBonus::valOf(const CBonusSystemNode *obj, Bonus::BonusType type, int subtype /*= -1*/)
 {
 	if(obj)
 		return obj->valOfBonuses(type, subtype);
 	return 0;
 }
 
-bool NBonus::hasOfType(const CBonusSystemNode *obj, HeroBonus::BonusType type, int subtype /*= -1*/)
+bool NBonus::hasOfType(const CBonusSystemNode *obj, Bonus::BonusType type, int subtype /*= -1*/)
 {
 	if(obj)
 		return obj->hasBonusOfType(type, subtype);
 	return false;
 }
 
-const HeroBonus * NBonus::get(const CBonusSystemNode *obj, int from, int id)
-{
-	if(obj)
-		return obj->getBonus(from, id);
-	return NULL;
-}
-
-void NBonus::getModifiersWDescr(const CBonusSystemNode *obj, std::vector<std::pair<int,std::string> > &out, HeroBonus::BonusType type, int subtype /*= -1 */)
+void NBonus::getModifiersWDescr(const CBonusSystemNode *obj, TModDescr &out, Bonus::BonusType type, int subtype /*= -1 */)
 {
 	if(obj)
 		return obj->getModifiersWDescr(out, type, subtype);
@@ -161,4 +270,72 @@ int NBonus::getCount(const CBonusSystemNode *obj, int from, int id)
 	if(obj)
 		return obj->getBonusesCount(from, id);
 	return 0;
+}
+
+const CSpell * Bonus::sourceSpell() const
+{
+	if(source == SPELL_EFFECT)
+		return &VLC->spellh->spells[id];
+	return NULL;
+}
+
+std::string Bonus::Description() const
+{
+	if(description.size())
+		return description;
+
+	std::ostringstream str;
+	if(val < 0)
+		str << '-';
+	else if(val > 0)
+		str << '+';
+
+	str << val << " ";
+
+	switch(source)
+	{
+	case CREATURE_ABILITY:
+		str << VLC->creh->creatures[id]->namePl;
+		break;
+	}
+	
+	return str.str();
+}
+
+CSelector DLL_EXPORT operator&&(const CSelector &first, const CSelector &second)
+{
+	return CSelectorsConjunction(first, second);
+}
+
+
+namespace Selector
+{
+	DLL_EXPORT CSelectFieldEqual<TBonusType> type(&Bonus::type, 0);
+	DLL_EXPORT CSelectFieldEqual<TBonusSubtype> subtype(&Bonus::subtype, 0);
+	DLL_EXPORT CSelectFieldEqual<si32> info(&Bonus::additionalInfo, 0);
+	DLL_EXPORT CSelectFieldEqual<ui8> sourceType(&Bonus::source, 0);
+	DLL_EXPORT CSelectFieldEqual<ui8> effectRange(&Bonus::effectRange, Bonus::NO_LIMIT);
+	DLL_EXPORT CWillLastTurns turns;;
+
+	CSelector DLL_EXPORT typeSybtype(TBonusType Type, TBonusSubtype Subtype)
+	{
+		return type(Type) && subtype(Subtype);
+	}
+
+	CSelector DLL_EXPORT typeSybtypeInfo(TBonusType type, TBonusSubtype subtype, si32 info)
+	{
+		return CSelectFieldEqual<TBonusType>(&Bonus::type, type) && CSelectFieldEqual<TBonusSubtype>(&Bonus::subtype, subtype) && CSelectFieldEqual<si32>(&Bonus::additionalInfo, info);
+	}
+
+	CSelector DLL_EXPORT source(ui8 source, ui32 sourceID)
+	{
+		return CSelectFieldEqual<ui8>(&Bonus::source, source) && CSelectFieldEqual<ui32>(&Bonus::id, sourceID);
+	}
+
+	bool matchesType(const CSelector &sel, TBonusType type)
+	{
+		Bonus dummy;
+		dummy.type = type;
+		return sel(dummy);
+	}
 }

+ 363 - 88
lib/HeroBonus.h

@@ -2,6 +2,8 @@
 #include "../global.h"
 #include <string>
 #include <list>
+#include <set>
+#include <boost/function.hpp>
 
 /*
  * HeroBonus.h, part of VCMI engine
@@ -13,68 +15,226 @@
  *
  */
 
-struct DLL_EXPORT HeroBonus
+
+typedef ui8 TBonusType;
+typedef si32 TBonusSubtype;
+
+
+class CSpell;
+struct Bonus;
+class CBonusSystemNode;
+
+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;
+
+
+namespace PrimarySkill
+{
+	enum { ATTACK, DEFENSE, SPELL_POWER, KNOWLEDGE};
+}
+
+#define BONUS_LIST										\
+	BONUS_NAME(NONE) 									\
+	BONUS_NAME(MOVEMENT) /*both water/land*/			\
+	BONUS_NAME(LAND_MOVEMENT) \
+	BONUS_NAME(SEA_MOVEMENT) \
+	BONUS_NAME(MORALE) \
+	BONUS_NAME(LUCK) \
+	BONUS_NAME(PRIMARY_SKILL) /*uses subtype to pick skill; additional info if set: 1 - only melee, 2 - only distance*/  \
+	BONUS_NAME(SIGHT_RADIOUS) \
+	BONUS_NAME(MANA_REGENERATION) /*points per turn apart from normal (1 + mysticism)*/  \
+	BONUS_NAME(FULL_MANA_REGENERATION) /*all mana points are replenished every day*/  \
+	BONUS_NAME(NONEVIL_ALIGNMENT_MIX) /*good and neutral creatures can be mixed without morale penalty*/  \
+	BONUS_NAME(SECONDARY_SKILL_PREMY) /*%*/  \
+	BONUS_NAME(SURRENDER_DISCOUNT) /*%*/  \
+	BONUS_NAME(STACKS_SPEED)  /*additional info - percent of speed bonus applied after direct bonuses; >0 - added, <0 - substracted to this part*/ \
+	BONUS_NAME(FLYING_MOVEMENT) \
+	BONUS_NAME(SPELL_DURATION) \
+	BONUS_NAME(AIR_SPELL_DMG_PREMY) \
+	BONUS_NAME(EARTH_SPELL_DMG_PREMY) \
+	BONUS_NAME(FIRE_SPELL_DMG_PREMY) \
+	BONUS_NAME(WATER_SPELL_DMG_PREMY) \
+	BONUS_NAME(BLOCK_SPELLS_ABOVE_LEVEL) \
+	BONUS_NAME(WATER_WALKING) \
+	BONUS_NAME(NO_SHOTING_PENALTY) \
+	BONUS_NAME(DISPEL_IMMUNITY) \
+	BONUS_NAME(NEGATE_ALL_NATURAL_IMMUNITIES) \
+	BONUS_NAME(STACK_HEALTH) \
+	BONUS_NAME(BLOCK_MORALE) \
+	BONUS_NAME(BLOCK_LUCK) \
+	BONUS_NAME(FIRE_SPELLS) \
+	BONUS_NAME(AIR_SPELLS) \
+	BONUS_NAME(WATER_SPELLS) \
+	BONUS_NAME(EARTH_SPELLS) \
+	BONUS_NAME(GENERATE_RESOURCE) /*daily value, uses subtype (resource type)*/  \
+	BONUS_NAME(CREATURE_GROWTH) /*for legion artifacts: value - week growth bonus, subtype - monster level*/  \
+	BONUS_NAME(WHIRLPOOL_PROTECTION) /*hero won't lose army when teleporting through whirlpool*/  \
+	BONUS_NAME(SPELL) /*hero knows spell, val - skill level (0 - 3), subtype - spell id*/  \
+	BONUS_NAME(SPELLS_OF_LEVEL) /*hero knows all spells of given level, val - skill level; subtype - level*/  \
+	BONUS_NAME(ENEMY_CANT_ESCAPE) /*for shackles of war*/ \
+	BONUS_NAME(MAGIC_SCHOOL_SKILL) /* //eg. for magic plains terrain, subtype: school of magic (0 - all, 1 - fire, 2 - air, 4 - water, 8 - earth), value - level*/ \
+	BONUS_NAME(FREE_SHOOTING) /*stacks can shoot even if otherwise blocked (sharpshooter's bow effect)*/ \
+	BONUS_NAME(OPENING_BATTLE_SPELL) /*casts a spell at expert level at beginning of battle, val - spell power, subtype - spell id*/ \
+	BONUS_NAME(IMPROVED_NECROMANCY) /*allows Necropolis units other than skeletons to be raised by necromancy*/ \
+	BONUS_NAME(CREATURE_GROWTH_PERCENT) /*increases growth of all units in all towns, val - percentage*/ \
+	BONUS_NAME(FREE_SHIP_BOARDING) /*movement points preserved with ship boarding and landing*/  \
+	BONUS_NAME(NO_TYPE)									\
+	BONUS_NAME(FLYING)									\
+	BONUS_NAME(SHOOTER)									\
+	BONUS_NAME(CHARGE_IMMUNITY)							\
+	BONUS_NAME(ADDITIONAL_ATTACK)						\
+	BONUS_NAME(UNLIMITED_RETALIATIONS)					\
+	BONUS_NAME(NO_MELEE_PENALTY)						\
+	BONUS_NAME(JOUSTING) /*for champions*/				\
+	BONUS_NAME(HATE) /*eg. angels hate devils, subtype - ID of hated creature*/ \
+	BONUS_NAME(KING1)									\
+	BONUS_NAME(KING2)									\
+	BONUS_NAME(KING3)									\
+	BONUS_NAME(MAGIC_RESISTANCE) /*in % (value)*/		\
+	BONUS_NAME(CHANGES_SPELL_COST_FOR_ALLY) /*in mana points (value) , eg. mage*/ \
+	BONUS_NAME(CHANGES_SPELL_COST_FOR_ENEMY) /*in mana points (value) , eg. pegasus */ \
+	BONUS_NAME(SPELL_AFTER_ATTACK) /* subtype - spell id, value - spell level, (additional info)%1000 - chance in %; eg. dendroids, (additional info)/1000 -> [0 - all attacks, 1 - shot only, 2 - melee only*/ \
+	BONUS_NAME(SPELL_RESISTANCE_AURA) /*eg. unicorns, value - resistance bonus in % for adjacent creatures*/ \
+	BONUS_NAME(LEVEL_SPELL_IMMUNITY) /*creature is immune to all spell with level below or equal to value of this bonus*/ \
+	BONUS_NAME(TWO_HEX_ATTACK_BREATH) /*eg. dragons*/	\
+	BONUS_NAME(SPELL_DAMAGE_REDUCTION) /*eg. golems; value - reduction in %, subtype - spell school; -1 - all, 0 - air, 1 - fire, 2 - water, 3 - earth*/ \
+	BONUS_NAME(NO_WALL_PENALTY)							\
+	BONUS_NAME(NON_LIVING) /*eg. gargoyle*/				\
+	BONUS_NAME(RANDOM_GENIE_SPELLCASTER) /*eg. master genie*/ \
+	BONUS_NAME(BLOCKS_RETALIATION) /*eg. naga*/			\
+	BONUS_NAME(SPELL_IMMUNITY) /*subid - spell id*/		\
+	BONUS_NAME(MANA_CHANNELING) /*value in %, eg. familiar*/ \
+	BONUS_NAME(SPELL_LIKE_ATTACK) /*value - spell id; range is taken from spell, but damage from creature; eg. magog*/ \
+	BONUS_NAME(THREE_HEADED_ATTACK) /*eg. cerberus*/	\
+	BONUS_NAME(DAEMON_SUMMONING) /*pit lord*/			\
+	BONUS_NAME(FIRE_IMMUNITY)							\
+	BONUS_NAME(FIRE_SHIELD)								\
+	BONUS_NAME(UNDEAD)									\
+	BONUS_NAME(HP_REGENERATION) /*creature regenerates val HP every new round*/					\
+	BONUS_NAME(FULL_HP_REGENERATION) /*first creature regenerates all HP every new round; subtype 0 - animation 4 (trolllike), 1 - animation 47 (wightlike)*/		\
+	BONUS_NAME(MANA_DRAIN) /*value - spell points per turn*/ \
+	BONUS_NAME(LIFE_DRAIN)								\
+	BONUS_NAME(DOUBLE_DAMAGE_CHANCE) /*value in %, eg. dread knight*/ \
+	BONUS_NAME(RETURN_AFTER_STRIKE)						\
+	BONUS_NAME(SELF_MORALE) /*eg. minotaur*/			\
+	BONUS_NAME(SPELLCASTER) /*subtype - spell id, value - level of school, additional info - spell power*/ \
+	BONUS_NAME(CATAPULT)								\
+	BONUS_NAME(ENEMY_DEFENCE_REDUCTION) /*in % (value) eg. behemots*/ \
+	BONUS_NAME(GENERAL_DAMAGE_REDUCTION) /* shield / air shield effect */ \
+	BONUS_NAME(GENERAL_ATTACK_REDUCTION) /*eg. while stoned or blinded - in %, subtype: -1 - any damage, 0 - melee damage, 1 - ranged damage*/ \
+	BONUS_NAME(ATTACKS_ALL_ADJACENT) /*eg. hydra*/		\
+	BONUS_NAME(MORE_DAMAGE_FROM_SPELL) /*value - damage increase in %, subtype - spell id*/ \
+	BONUS_NAME(CASTS_SPELL_WHEN_KILLED) /*similar to spell after attack*/ \
+	BONUS_NAME(FEAR)									\
+	BONUS_NAME(FEARLESS)								\
+	BONUS_NAME(NO_DISTANCE_PENALTY)						\
+	BONUS_NAME(NO_OBSTACLES_PENALTY)					\
+	BONUS_NAME(SELF_LUCK) /*halfling*/					\
+	BONUS_NAME(ENCHANTER)								\
+	BONUS_NAME(HEALER)									\
+	BONUS_NAME(SIEGE_WEAPON)							\
+	BONUS_NAME(HYPNOTIZED)								\
+	BONUS_NAME(ADDITIONAL_RETALIATION) /*value - number of additional retaliations*/ \
+	BONUS_NAME(MAGIC_MIRROR) /* value - chance of redirecting in %*/ \
+	BONUS_NAME(ALWAYS_MINIMUM_DAMAGE) /*unit does its minimum damage from range; subtype: -1 - any attack, 0 - melee, 1 - ranged, value: additional damage, additional info - multiplicative anti-bonus for dmg in % [eg 20 means that creature will inflict 80% of normal dmg]*/ \
+	BONUS_NAME(ALWAYS_MAXIMUM_DAMAGE) /*eg. bless effect, subtype: -1 - any attack, 0 - melee, 1 - ranged, value: additional damage, additional info - multiplicative bonus for dmg in %*/ \
+	BONUS_NAME(ATTACKS_NEAREST_CREATURE) /*while in berserk*/ \
+	BONUS_NAME(IN_FRENZY) /*value - level*/				\
+	BONUS_NAME(SLAYER) /*value - level*/				\
+	BONUS_NAME(FORGETFULL) /*forgetfulness spell effect, value - level*/ \
+	BONUS_NAME(NOT_ACTIVE)								\
+	BONUS_NAME(NO_LUCK) /*eg. when fighting on cursed ground*/	\
+	BONUS_NAME(NO_MORALE) /*eg. when fighting on cursed ground*/
+
+struct DLL_EXPORT Bonus
 {
 	enum BonusType
 	{
-		//handled
-		NONE, 
-		MOVEMENT, //both water/land
-		LAND_MOVEMENT, 
-		SEA_MOVEMENT, 
-		MORALE, 
-		LUCK, 
-		MORALE_AND_LUCK, 
-		PRIMARY_SKILL, //uses subtype to pick skill
-		SIGHT_RADIOUS, 
-		MANA_REGENERATION, //points per turn apart from normal (1 + mysticism)
-		FULL_MANA_REGENERATION, //all mana points are replenished every day
-		NONEVIL_ALIGNMENT_MIX, //good and neutral creatures can be mixed without morale penalty
-		HP_REGENERATION, //regenerates a certain amount of hp for the top of each stack every turn, val - hp regained
-		LEVEL_SPELL_IMMUNITY, //val - spell level creatures become immune to and below
-		//might not be handled yet:
-		MAGIC_RESISTANCE, // %
-		SECONDARY_SKILL_PREMY, //%
-		SURRENDER_DISCOUNT, //%
-		STACKS_SPEED,
-		FLYING_MOVEMENT, SPELL_DURATION, AIR_SPELL_DMG_PREMY, EARTH_SPELL_DMG_PREMY, FIRE_SPELL_DMG_PREMY, 
-		WATER_SPELL_DMG_PREMY, BLOCK_SPELLS_ABOVE_LEVEL, WATER_WALKING, NO_SHOTING_PENALTY, DISPEL_IMMUNITY, 
-		NEGATE_ALL_NATURAL_IMMUNITIES, STACK_HEALTH, STACK_HEALTH_PERCENT, //the second one of stack health - value in % of base HP to be added to overall stack HP
-		SPELL_IMMUNITY, BLOCK_MORALE, BLOCK_LUCK, FIRE_SPELLS,
-		AIR_SPELLS, WATER_SPELLS, EARTH_SPELLS, 
-		GENERATE_RESOURCE, //daily value, uses subtype (resource type)
-		CREATURE_GROWTH, //for legion artifacts: value - week growth bonus, subtype - monster level
-		WHIRLPOOL_PROTECTION, //hero won't lose army when teleporting through whirlpool
-		SPELL, //hero knows spell, val - skill level (0 - 3), subtype - spell id
-		SPELLS_OF_LEVEL, //hero knows all spells of given level, val - skill level; subtype - level
-		ENEMY_CANT_ESCAPE, //for shackles of war
-		MAGIC_SCHOOL_SKILL, //eg. for magic plains terrain, subtype: school of magic (0 - all, 1 - fire, 2 - air, 4 - water, 8 - earth), value - level
-		FREE_SHOOTING, //stacks can shoot even if otherwise blocked (sharpshooter's bow effect)
-		OPENING_BATTLE_SPELL, //casts a spell at expert level at beginning of battle, val - spell power, subtype - spell id
-		IMPROVED_NECROMANCY, //allows Necropolis units other than skeletons to be raised by necromancy
-		CREATURE_GROWTH_PERCENT, //increases growth of all units in all towns, val - percentage
-		FREE_SHIP_BOARDING //movement points preserved with ship boarding and landing
+#define BONUS_NAME(x) x,
+		BONUS_LIST
+#undef BONUS_NAME
+	};
+	enum BonusDuration //when bonus is automatically removed
+	{
+		PERMANENT = 1,
+		ONE_BATTLE = 2, //at the end of battle 
+		ONE_DAY = 4,   //at the end of day
+		ONE_WEEK = 8, //at the end of week (bonus lasts till the end of week, thats NOT 7 days
+		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*/
+	};
+	enum BonusSource
+	{
+		ARTIFACT, 
+		OBJECT, 
+		CASTED_SPELL,
+		CREATURE_ABILITY,
+		TERRAIN_NATIVE,
+		TERRAIN_OVERLAY,
+		SPELL_EFFECT,
+		TOWN_STRUCTURE,
+		HERO_BASE_SKILL,
+		SECONDARY_SKILL,
+		ARMY
+	};
+
+	enum LimitEffect
+	{
+		NO_LIMIT = 0, 
+		ONLY_DISTANCE_FIGHT=1, ONLY_MELEE_FIGHT, //used to mark bonuses for attack/defense primary skills from spells like Precision (distance only)
+		ONLY_ALLIED_ARMY, ONLY_ENEMY_ARMY
+	};
+
+	enum ValueType
+	{
+		ADDITIVE_VALUE,
+		BASE_NUMBER,
+		PERCENT_TO_ALL,
+		PERCENT_TO_BASE
 	};
-	enum BonusDuration{PERMANENT, ONE_BATTLE, ONE_DAY, ONE_WEEK};
-	enum BonusSource{ARTIFACT, OBJECT, CASTED_SPELL};
 
 	ui8 duration; //uses BonusDuration values
-	ui8 type; //uses BonusType values - says to what is this bonus
-	si32 subtype; //-1 if not applicable
-	ui8 source;//uses BonusSource values - what gave that bonus
-	si32 val;//for morale/luck [-3,+3], others any
-	ui32 id; //id of object/artifact
+	si16 turnsRemain; //used if duration is N_TURNS or N_DAYS
+
+	TBonusType type; //uses BonusType values - says to what is this bonus - 1 byte
+	TBonusSubtype subtype; //-1 if not applicable - 4 bytes
+
+	ui8 source;//source type" uses BonusSource values - what gave that bonus
+	ui32 id; //source id: id of object/artifact/spell
+
+	ui8 valType; 
+	si32 val;
+	si32 additionalInfo;
+	ui8 effectRange; //if not NO_LIMIT, bonus will be ommitted by default
+
 	std::string description; 
 
-	HeroBonus(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, std::string Desc, si32 Subtype=-1)
 		:duration(Dur), type(Type), subtype(Subtype), source(Src), val(Val), id(ID), description(Desc) 
-	{}
-	HeroBonus(ui8 Dur, ui8 Type, ui8 Src, si32 Val, ui32 ID, si32 Subtype=-1)
-		:duration(Dur), type(Type), subtype(Subtype), source(Src), val(Val), id(ID) 
-	{}
-	HeroBonus()
+	{
+		additionalInfo = -1;
+		turnsRemain = 0;
+		valType = ADDITIVE_VALUE;
+		effectRange = NO_LIMIT;
+	}
+	Bonus(ui8 Dur, ui8 Type, ui8 Src, si32 Val, ui32 ID, si32 Subtype=-1, ui8 ValType = ADDITIVE_VALUE)
+		:duration(Dur), type(Type), subtype(Subtype), source(Src), val(Val), id(ID), valType(ValType)
+	{
+		additionalInfo = -1;
+		turnsRemain = 0;
+		effectRange = NO_LIMIT;
+	}
+	Bonus()
 	{
 		subtype = -1;
+		additionalInfo = -1;
+		turnsRemain = 0;
+		valType = ADDITIVE_VALUE;
+		effectRange = NO_LIMIT;
 	}
 
 // 	//comparison
@@ -91,47 +251,57 @@ struct DLL_EXPORT HeroBonus
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & duration & type & subtype & source & val & id & description;
+		h & duration & type & subtype & source & val & id & description & additionalInfo & turnsRemain & valType & effectRange;
 	}
 
-	static bool OneDay(const HeroBonus &hb)
+	static bool OneDay(const Bonus &hb)
+	{
+		return hb.duration & Bonus::ONE_DAY;
+	}
+	static bool OneWeek(const Bonus &hb)
+	{
+		return hb.duration & Bonus::ONE_WEEK;
+	}
+	static bool OneBattle(const Bonus &hb)
 	{
-		return hb.duration==HeroBonus::ONE_DAY;
+		return hb.duration & Bonus::ONE_BATTLE;
 	}
-	static bool OneWeek(const HeroBonus &hb)
+	static bool UntilAttack(const Bonus &hb)
 	{
-		return hb.duration==HeroBonus::ONE_WEEK;
+		return hb.duration & Bonus::UNTIL_ATTACK;
 	}
-	static bool OneBattle(const HeroBonus &hb)
+	static bool UntilBeingAttacked(const Bonus &hb)
 	{
-		return hb.duration==HeroBonus::ONE_BATTLE;
+		return hb.duration & Bonus::UNITL_BEING_ATTACKED;
 	}
-	static bool IsFrom(const HeroBonus &hb, ui8 source, ui32 id) //if id==0xffffff then id doesn't matter
+	static bool IsFrom(const Bonus &hb, ui8 source, ui32 id) //if id==0xffffff then id doesn't matter
 	{
 		return hb.source==source && (id==0xffffff  ||  hb.id==id);
 	}
+ 	inline bool operator == (const BonusType & cf) const
+ 	{
+ 		return type == cf;
+ 	}
+	const CSpell * sourceSpell() const;
 
+	std::string Description() const;
 };
 
-class CBonusSystemNode;
-
-static const HeroBonus::BonusType MORALE_AFFECTING[] =  {HeroBonus::MORALE, HeroBonus::MORALE_AND_LUCK};
-static const HeroBonus::BonusType LUCK_AFFECTING[] =  {HeroBonus::LUCK, HeroBonus::MORALE_AND_LUCK};
-typedef std::vector<std::pair<int,std::string> > TModDescr; //modifiers values and their descriptions
-typedef std::list<CBonusSystemNode*> TNodes;
-typedef std::list<const CBonusSystemNode*> TCNodes;
-
-class BonusList : public std::list<HeroBonus>
+class BonusList : public std::list<Bonus>
 {
 public:
-	int DLL_EXPORT valOfBonuses(HeroBonus::BonusType type, int subtype = -1) const; //subtype -> subtype of bonus, if -1 then any
-	bool DLL_EXPORT hasBonusOfType(HeroBonus::BonusType type, int subtype = -1) const;
-	const DLL_EXPORT HeroBonus * getBonus( int from, int id ) const;
-	void DLL_EXPORT getModifiersWDescr( std::vector<std::pair<int,std::string> > &out, HeroBonus::BonusType type, int subtype = -1 ) const;
+	int DLL_EXPORT totalValue() const; //subtype -> subtype of bonus, if -1 then any
+	void DLL_EXPORT getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *source = NULL) const;
+	void DLL_EXPORT getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *source = NULL) const;
+	void DLL_EXPORT getModifiersWDescr(TModDescr &out) const;
+
+	//special find functions
+	DLL_EXPORT Bonus * getFirst(const CSelector &select);
+	DLL_EXPORT const Bonus * getFirst(const CSelector &select) const;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & static_cast<std::list<HeroBonus>&>(*this);
+		h & static_cast<std::list<Bonus>&>(*this);
 	}
 };
 
@@ -140,19 +310,39 @@ class DLL_EXPORT CBonusSystemNode
 public:
 	BonusList bonuses;
 
-	virtual void getParents(TCNodes &out, const CBonusSystemNode *source = NULL) const;  //retreives list of parent nodes (nodes to inherit bonuses from), source is the prinary asker
+	//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;
 
-	int valOfBonuses(HeroBonus::BonusType type, int subtype = -1) const; //subtype -> subtype of bonus, if -1 then any
-	bool hasBonusOfType(HeroBonus::BonusType type, int subtype = -1) const;//determines if hero has a bonus of given type (and optionally subtype)
-	const HeroBonus * getBonus( int from, int id ) const;
-	void getModifiersWDescr( std::vector<std::pair<int,std::string> > &out, HeroBonus::BonusType type, int subtype = -1 ) const;  //out: pairs<modifier value, modifier description>
+	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>
+
+	//////////////////////////////////////////////////////////////////////////
+	//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 any
+	bool hasBonusOfType(Bonus::BonusType type, int subtype = -1) const;//determines if hero has a bonus of given type (and optionally subtype)
+	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;
 
-	template<int N> void getModifiersWDescr(std::vector<std::pair<int,std::string> > &out, const HeroBonus::BonusType (&types)[N]) const //retreive array of types
-	{
-		for (int i = 0; i < N; i++)
-			getModifiersWDescr(out, types[i]);
-	}
+	int MoraleVal() const; //range [-3, +3]
+	int LuckVal() const; //range [-3, +3]
+	si32 Attack() const; //get attack of stack with all modificators
+	si32 Defense(bool withFrenzy = true) const; //get defense of stack with all modificators
+	ui16 MaxHealth() const; //get max HP of stack with all modifiers
+
+
+	//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
+	Bonus *getBonus(const CSelector &selector);
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -163,9 +353,94 @@ public:
 namespace NBonus
 {
 	//set of methods that may be safely called with NULL objs
-	DLL_EXPORT int valOf(const CBonusSystemNode *obj, HeroBonus::BonusType type, int subtype = -1); //subtype -> subtype of bonus, if -1 then any
-	DLL_EXPORT bool hasOfType(const CBonusSystemNode *obj, HeroBonus::BonusType type, int subtype = -1);//determines if hero has a bonus of given type (and optionally subtype)
-	DLL_EXPORT const HeroBonus * get(const CBonusSystemNode *obj, int from, int id );
-	DLL_EXPORT void getModifiersWDescr(const CBonusSystemNode *obj, std::vector<std::pair<int,std::string> > &out, HeroBonus::BonusType type, int subtype = -1 );  //out: pairs<modifier value, modifier description>
+	DLL_EXPORT int valOf(const CBonusSystemNode *obj, Bonus::BonusType type, int subtype = -1); //subtype -> subtype of bonus, if -1 then any
+	DLL_EXPORT bool hasOfType(const CBonusSystemNode *obj, Bonus::BonusType type, int subtype = -1);//determines if hero has a bonus of given type (and optionally subtype)
+	//DLL_EXPORT const HeroBonus * get(const CBonusSystemNode *obj, int from, int id );
+	DLL_EXPORT void getModifiersWDescr(const CBonusSystemNode *obj, TModDescr &out, Bonus::BonusType type, int subtype = -1 );  //out: pairs<modifier value, modifier description>
 	DLL_EXPORT int getCount(const CBonusSystemNode *obj, int from, int id);
-};
+};
+
+//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)
+{
+	Bonus sf;
+	sf.type = type;
+	sf.duration = duration;
+	sf.source = source;
+	sf.turnsRemain = turnsRemain;
+	sf.subtype = subtype;
+	sf.val = value;
+	sf.additionalInfo = additionalInfo;
+
+	return sf;
+}
+
+class DLL_EXPORT CSelectorsConjunction
+{
+	const CSelector first, second;
+public:
+	CSelectorsConjunction(const CSelector &First, const CSelector &Second)
+		:first(First), second(Second)
+	{
+	}
+	bool operator()(const Bonus &bonus) const
+	{
+		return first(bonus) && second(bonus);
+	}
+};
+CSelector DLL_EXPORT operator&&(const CSelector &first, const CSelector &second);
+
+
+template<typename T>
+class CSelectFieldEqual
+{
+	T Bonus::*ptr;
+	T val;
+public:
+	CSelectFieldEqual(T Bonus::*Ptr, const T &Val)
+		: ptr(Ptr), val(Val)
+	{
+	}
+	bool operator()(const Bonus &bonus) const
+	{
+		return bonus.*ptr == val;
+	}
+	CSelectFieldEqual& operator()(const T &setVal)
+	{
+		val = setVal;
+		return *this;
+	}
+};
+
+class CWillLastTurns
+{
+public:
+	int turnsRequested;
+
+	bool operator()(const Bonus &bonus) const
+	{
+		return !turnsRequested 
+				||   bonus.turnsRemain > turnsRequested  &&  bonus.duration == Bonus::N_TURNS;
+	}
+	CWillLastTurns& operator()(const int &setVal)
+	{
+		turnsRequested = setVal;
+		return *this;
+	}
+};
+
+namespace Selector
+{
+	extern DLL_EXPORT CSelectFieldEqual<TBonusType> type;
+	extern DLL_EXPORT CSelectFieldEqual<TBonusSubtype> subtype;
+	extern DLL_EXPORT CSelectFieldEqual<si32> info;
+	extern DLL_EXPORT CSelectFieldEqual<ui8> sourceType;
+	extern DLL_EXPORT CSelectFieldEqual<ui8> effectRange;
+	extern DLL_EXPORT CWillLastTurns turns;
+
+	CSelector DLL_EXPORT typeSybtype(TBonusType Type, TBonusSubtype Subtype);
+	CSelector DLL_EXPORT typeSybtypeInfo(TBonusType type, TBonusSubtype subtype, si32 info);
+	CSelector DLL_EXPORT source(ui8 source, ui32 sourceID);
+
+	bool DLL_EXPORT matchesType(const CSelector &sel, TBonusType type);
+}

+ 1 - 0
lib/IGameCallback.h

@@ -5,6 +5,7 @@
 #include <vector>
 #include <set>
 #include "../client/FunctionList.h"
+#include "CCreatureSet.h"
 
 /*
  * IGameCallback.h, part of VCMI engine

+ 11 - 4
lib/NetPacks.h

@@ -5,6 +5,7 @@
 #include "BattleAction.h"
 #include "HeroBonus.h"
 #include <set>
+#include "CCreatureSet.h"
 
 /*
  * NetPacks.h, part of VCMI engine
@@ -77,7 +78,7 @@ public:
 
 	std::vector<ui8> message; //vector of EMessage
 
-	std::vector<std::pair<ui8,ui32> > localStrings; //pairs<text handler type, text number>; types: 1 - generaltexthandler->all; 2 - objh->xtrainfo; 3 - objh->names; 4 - objh->restypes; 5 - arth->artifacts[id].name; 6 - generaltexth->arraytxt; 7 - creh->creatures[os->subID].namePl; 8 - objh->creGens; 9 - objh->mines[ID].first; 10 - objh->mines[ID].second; 11 - objh->advobtxt
+	std::vector<std::pair<ui8,ui32> > localStrings; //pairs<text handler type, text number>; types: 1 - generaltexthandler->all; 2 - objh->xtrainfo; 3 - objh->names; 4 - objh->restypes; 5 - arth->artifacts[id].name; 6 - generaltexth->arraytxt; 7 - creh->creatures[os->subID].namePl; 8 - objh->creGens; 9 - objh->mines[ID]->first; 10 - objh->mines[ID]->second; 11 - objh->advobtxt
 	std::vector<std::string> exactStrings;
 	std::vector<si32> numbers;
 
@@ -370,7 +371,7 @@ struct GiveBonus :  public CPackForClient //115
 	enum {HERO, PLAYER};
 	ui8 who; //who receives bonus, uses enum above
 	ui32 id; //hero or player id
-	HeroBonus bonus;
+	Bonus bonus;
 	MetaString bdescr;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
@@ -440,7 +441,7 @@ struct RemoveBonus :  public CPackForClient //118
 	ui32 id; //source id
 
 	//used locally: copy of removed bonus
-	HeroBonus bonus;
+	Bonus bonus;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -573,7 +574,7 @@ struct SetHeroArtifacts : public CPackForClient //509
 		h & hid & artifacts & artifWorn;
 	}
 
-	std::vector<HeroBonus*> gained, lost; //used locally as hlp when applying
+	std::vector<Bonus*> gained, lost; //used locally as hlp when applying
 };   
 
 struct HeroRecruited : public CPackForClient //515
@@ -712,6 +713,12 @@ struct InfoWindow : public CPackForClient //103  - displays simple info window
 	}
 };
 
+namespace ObjProperty
+{
+	//TODO: move non general properties out to the appropriate objs classes
+	enum {OWNER = 1, BLOCKVIS = 2, PRIMARY_STACK_COUNT = 3, VISITORS = 4, VISITED = 5, ID = 6};
+}
+
 struct SetObjectProperty : public CPackForClient//1001
 {
 	DLL_EXPORT void applyGs(CGameState *gs);

+ 128 - 126
lib/NetPacksLib.cpp

@@ -52,12 +52,17 @@ DLL_EXPORT void SetResource::applyGs( CGameState *gs )
 DLL_EXPORT void SetPrimSkill::applyGs( CGameState *gs )
 {
 	CGHeroInstance *hero = gs->getHero(id);
+	assert(hero);
+
 	if(which <4)
 	{
+		Bonus *skill = hero->getBonus(Selector::type(Bonus::PRIMARY_SKILL) && Selector::subtype(which) && Selector::sourceType(Bonus::HERO_BASE_SKILL));
+		assert(skill);
+		
 		if(abs)
-			hero->primSkills[which] = val;
+			skill->val = val;
 		else
-			hero->primSkills[which] += val;
+			skill->val += val;
 	}
 	else if(which == 4) //XP
 	{
@@ -171,16 +176,16 @@ DLL_EXPORT void SetAvailableHeroes::applyGs( CGameState *gs )
 	gs->getPlayer(player)->availableHeroes.push_back(h);
 	if(h  &&  flags & 1)
 	{
-		h->army.slots.clear();
-		h->army.slots[0] = CStackInstance(VLC->creh->nameToID[h->type->refTypeStack[0]],1);
+		h->clear();
+		h->addStack(0, CStackInstance(VLC->creh->nameToID[h->type->refTypeStack[0]],1));
 	}
 
 	h = (hid2>=0 ?  gs->hpool.heroesPool[hid2] : NULL);
 	gs->getPlayer(player)->availableHeroes.push_back(h);
 	if(flags & 2)
 	{
-		h->army.slots.clear();
-		h->army.slots[0] = CStackInstance(VLC->creh->nameToID[h->type->refTypeStack[0]],1);
+		h->clear();
+		h->addStack(0, CStackInstance(VLC->creh->nameToID[h->type->refTypeStack[0]],1));
 	}
 }
 
@@ -209,8 +214,8 @@ DLL_EXPORT void GiveBonus::applyGs( CGameState *gs )
 	std::string &descr = bonuses->back().description;
 
 	if(!bdescr.message.size() 
-		&& bonus.source == HeroBonus::OBJECT 
-		&& (bonus.type == HeroBonus::LUCK || bonus.type == HeroBonus::MORALE || bonus.type == HeroBonus::MORALE_AND_LUCK)
+		&& bonus.source == Bonus::OBJECT 
+		&& (bonus.type == Bonus::LUCK || bonus.type == Bonus::MORALE)
 		&& gs->map->objects[bonus.id]->ID == EVENTI_TYPE) //it's morale/luck bonus from an event without description
 	{
 		descr = VLC->generaltexth->arraytxt[bonus.val > 0 ? 110 : 109]; //+/-%d Temporary until next battle"
@@ -243,9 +248,9 @@ DLL_EXPORT void PlayerEndsGame::applyGs( CGameState *gs )
 
 DLL_EXPORT void RemoveBonus::applyGs( CGameState *gs )
 {
-	std::list<HeroBonus> &bonuses = (who == HERO ? gs->getHero(whoID)->bonuses : gs->getPlayer(whoID)->bonuses);
+	std::list<Bonus> &bonuses = (who == HERO ? gs->getHero(whoID)->bonuses : gs->getPlayer(whoID)->bonuses);
 
-	for(std::list<HeroBonus>::iterator i = bonuses.begin(); i != bonuses.end(); i++)
+	for(std::list<Bonus>::iterator i = bonuses.begin(); i != bonuses.end(); i++)
 	{
 		if(i->source == source && i->id == id)
 		{
@@ -377,14 +382,14 @@ DLL_EXPORT void SetGarrisons::applyGs( CGameState *gs )
 	for(std::map<ui32,CCreatureSet>::iterator i = garrs.begin(); i!=garrs.end(); i++)
 	{
 		CArmedInstance *ai = static_cast<CArmedInstance*>(gs->map->objects[i->first]);
-		ai->army = i->second;
+		ai->setArmy(i->second);
 		if(ai->ID==TOWNI_TYPE && (static_cast<CGTownInstance*>(ai))->garrisonHero) //if there is a hero in garrison then we must update also his army
-			const_cast<CGHeroInstance*>((static_cast<CGTownInstance*>(ai))->garrisonHero)->army = i->second;
+			const_cast<CGHeroInstance*>((static_cast<CGTownInstance*>(ai))->garrisonHero)->setArmy(i->second);
 		else if(ai->ID==HEROI_TYPE)
 		{
 			CGHeroInstance *h =  static_cast<CGHeroInstance*>(ai);
 			if(h->visitedTown && h->inTownGarrison)
-				h->visitedTown->army = i->second;
+				h->visitedTown->setArmy(i->second);
 		}
 	}
 }
@@ -456,7 +461,7 @@ DLL_EXPORT void SetHeroArtifacts::applyGs( CGameState *gs )
 			continue;
 
 		CArtifact &art = VLC->arth->artifacts[id];
-		for(std::list<HeroBonus>::iterator i = art.bonuses.begin(); i != art.bonuses.end(); i++)
+		for(std::list<Bonus>::iterator i = art.bonuses.begin(); i != art.bonuses.end(); i++)
 		{
 			gained.push_back(&*i);
 			h->bonuses.push_back(*i);
@@ -476,7 +481,7 @@ DLL_EXPORT void SetHeroArtifacts::applyGs( CGameState *gs )
 
 		while(1)
 		{
-			std::list<HeroBonus>::iterator hlp = std::find_if(h->bonuses.begin(),h->bonuses.end(),boost::bind(HeroBonus::IsFrom,_1,HeroBonus::ARTIFACT,id));
+			std::list<Bonus>::iterator hlp = std::find_if(h->bonuses.begin(),h->bonuses.end(),boost::bind(Bonus::IsFrom,_1,Bonus::ARTIFACT,id));
 			if(hlp != h->bonuses.end())
 			{
 				lost.push_back(&*hlp);
@@ -613,11 +618,11 @@ DLL_EXPORT void NewTurn::applyGs( CGameState *gs )
 		t->builded = 0;
 
 	BOOST_FOREACH(CGHeroInstance *h, gs->map->heroes)
-		h->bonuses.remove_if(HeroBonus::OneDay);
+		h->bonuses.remove_if(Bonus::OneDay);
 
 	if(gs->getDate(1) == 7) //new week
 		BOOST_FOREACH(CGHeroInstance *h, gs->map->heroes)
-			h->bonuses.remove_if(HeroBonus::OneWeek);
+			h->bonuses.remove_if(Bonus::OneWeek);
 
 	//count days without town
 	for( std::map<ui8, PlayerState>::iterator i=gs->players.begin() ; i!=gs->players.end();i++)
@@ -627,9 +632,9 @@ DLL_EXPORT void NewTurn::applyGs( CGameState *gs )
 		else
 			i->second.daysWithoutCastle++;
 
-		i->second.bonuses.remove_if(HeroBonus::OneDay);
+		i->second.bonuses.remove_if(Bonus::OneDay);
 		if(gs->getDate(1) == 7) //new week
-			i->second.bonuses.remove_if(HeroBonus::OneWeek);
+			i->second.bonuses.remove_if(Bonus::OneWeek);
 	}
 }
 
@@ -642,7 +647,7 @@ DLL_EXPORT void SetObjectProperty::applyGs( CGameState *gs )
 		return;
 	}
 
-	if(what == 1)
+	if(what == ObjProperty::OWNER)
 	{
 		if(obj->ID == TOWNI_TYPE)
 		{
@@ -672,6 +677,7 @@ DLL_EXPORT void HeroLevelUp::applyGs( CGameState *gs )
 DLL_EXPORT void BattleStart::applyGs( CGameState *gs )
 {
 	gs->curB = info;
+	info->belligerents[0]->battle = info->belligerents[1]->battle = info;
 }
 
 DLL_EXPORT void BattleNextRound::applyGs( CGameState *gs )
@@ -685,12 +691,12 @@ DLL_EXPORT void BattleNextRound::applyGs( CGameState *gs )
 		s->state -= WAITING;
 		s->state -= MOVED;
 		s->state -= HAD_MORALE;
-		s->counterAttacks = 1 + s->valOfFeatures(StackFeature::ADDITIONAL_RETALIATION);
+		s->counterAttacks = 1 + s->valOfBonuses(Bonus::ADDITIONAL_RETALIATION);
 
 		//regeneration
-		if( s->hasFeatureOfType(StackFeature::HP_REGENERATION) && s->alive() )
-			s->firstHPleft = std::min<ui32>( s->MaxHealth(), s->valOfFeatures(StackFeature::HP_REGENERATION) );
-		if( s->hasFeatureOfType(StackFeature::FULL_HP_REGENERATION) && s->alive() )
+		if( s->hasBonusOfType(Bonus::HP_REGENERATION) && s->alive() )
+			s->firstHPleft = std::min<ui32>( s->MaxHealth(), s->valOfBonuses(Bonus::HP_REGENERATION) );
+		if( s->hasBonusOfType(Bonus::FULL_HP_REGENERATION) && s->alive() )
 			s->firstHPleft = s->MaxHealth();
 
 		//remove effects and restore only those with remaining turns in duration
@@ -704,19 +710,20 @@ DLL_EXPORT void BattleNextRound::applyGs( CGameState *gs )
 		}
 
 		//the same as above for features
-		std::vector<StackFeature> tmpFeatures = s->features;
-		s->features.clear();
-		for(int i=0; i < tmpFeatures.size(); i++)
+		BonusList tmpFeatures = s->bonuses;
+		s->bonuses.clear();
+
+		BOOST_FOREACH(Bonus &b, tmpFeatures)
 		{
-			if((tmpFeatures[i].duration & StackFeature::N_TURNS) != 0)
+			if((b.duration & Bonus::N_TURNS) != 0)
 			{
-				tmpFeatures[i].turnsRemain--;
-				if(tmpFeatures[i].turnsRemain > 0)
-					s->features.push_back(tmpFeatures[i]);
+				b.turnsRemain--;
+				if(b.turnsRemain > 0)
+					s->bonuses.push_back(b);
 			}
 			else
 			{
-				s->features.push_back(tmpFeatures[i]);
+				s->bonuses.push_back(b);
 			}
 		}
 	}
@@ -739,12 +746,13 @@ void BattleResult::applyGs( CGameState *gs )
 	CGHeroInstance *h;
 	h = gs->curB->heroes[0];
 	if(h)
-		h->bonuses.remove_if(HeroBonus::OneBattle);
+		h->bonuses.remove_if(Bonus::OneBattle);
 
 	h = gs->curB->heroes[1];
 	if(h) 
-		h->bonuses.remove_if(HeroBonus::OneBattle);
+		h->bonuses.remove_if(Bonus::OneBattle);
 
+	gs->curB->belligerents[0]->battle = gs->curB->belligerents[1]->battle = NULL;
 	delete gs->curB;
 	gs->curB = NULL;
 }
@@ -757,7 +765,7 @@ void BattleStackMoved::applyGs( CGameState *gs )
 DLL_EXPORT void BattleStackAttacked::applyGs( CGameState *gs )
 {
 	CStack * at = gs->curB->getStack(stackAttacked);
-	at->amount = newAmount;
+	at->count = newAmount;
 	at->firstHPleft = newHP;
 	if(killed())
 		at->state -= ALIVE;
@@ -773,27 +781,12 @@ DLL_EXPORT void BattleAttack::applyGs( CGameState *gs )
 	BOOST_FOREACH(BattleStackAttacked stackAttacked, bsa)
 		stackAttacked.applyGs(gs);
 
-	for(int g=0; g<attacker->features.size(); ++g)
-	{
-		if((attacker->features[g].duration & StackFeature::UNTIL_ATTACK) != 0)
-		{
-			attacker->features.erase(attacker->features.begin() + g);
-			g = 0;
-		}
-	}
+	attacker->bonuses.remove_if(Bonus::UntilAttack);
 
 	for(std::vector<BattleStackAttacked>::const_iterator it = bsa.begin(); it != bsa.end(); ++it)
 	{
 		CStack * stack = gs->curB->getStack(it->stackAttacked, false);
-
-		for(int g=0; g<stack->features.size(); ++g)
-		{
-			if((stack->features[g].duration & StackFeature::UNITL_BEING_ATTACKED) != 0)
-			{
-				stack->features.erase(stack->features.begin() + g);
-				g = 0;
-			}
-		}
+		stack->bonuses.remove_if(Bonus::UntilBeingAttacked);
 	}
 }
 
@@ -823,6 +816,7 @@ DLL_EXPORT void StartAction::applyGs( CGameState *gs )
 
 DLL_EXPORT void SpellCast::applyGs( CGameState *gs )
 {
+	assert(gs->curB);
 	CGHeroInstance *h = (side) ? gs->curB->heroes[1] : gs->curB->heroes[0];
 	if(h)
 	{
@@ -844,7 +838,7 @@ DLL_EXPORT void SpellCast::applyGs( CGameState *gs )
 	}
 
 
-	if(gs->curB && (id == 35 || id == 78)) //dispel and dispel helpful spells
+	if(id == 35 || id == 78) //dispel and dispel helpful spells
 	{
 		bool onlyHelpful = id == 78;
 		for(std::set<ui32>::const_iterator it = affectedCres.begin(); it != affectedCres.end(); ++it)
@@ -865,14 +859,13 @@ DLL_EXPORT void SpellCast::applyGs( CGameState *gs )
 				s->effects = remainingEff; //assigning effects that should remain
 
 				//removing all features from spells
-				std::vector<StackFeature> tmpFeatures = s->features;
-				s->features.clear();
-				for(int i=0; i < tmpFeatures.size(); i++)
+				BonusList tmpFeatures = s->bonuses;
+				s->bonuses.clear();
+				BOOST_FOREACH(Bonus &b, tmpFeatures)
 				{
-					if(tmpFeatures[i].source != StackFeature::SPELL_EFFECT || tmpFeatures[i].positiveness != 1)
-					{
-						s->features.push_back(tmpFeatures[i]);
-					}
+					const CSpell *sp = b.sourceSpell();
+					if(sp && sp->positiveness != 1) //if(b.source != HeroBonus::SPELL_EFFECT || b.positiveness != 1)
+						s->bonuses.push_back(b);
 				}
 			}
 		}
@@ -904,8 +897,8 @@ DLL_EXPORT void SpellCast::applyGs( CGameState *gs )
 
 		bool ac[BFIELD_SIZE];
 		std::set<int> occupyable;
-		bool twoHex = vstd::contains(VLC->creh->creatures[creID].abilities, StackFeature::DOUBLE_WIDE);
-		bool flying = vstd::contains(VLC->creh->creatures[creID].abilities, StackFeature::FLYING);
+		bool twoHex = VLC->creh->creatures[creID]->isDoubleWide();
+		bool flying = vstd::contains(VLC->creh->creatures[creID]->bonuses, Bonus::FLYING);
 		gs->curB->getAccessibilityMap(ac, twoHex, !side, true, occupyable, flying);
 		for(int g=0; g<BFIELD_SIZE; ++g)
 		{
@@ -916,121 +909,126 @@ DLL_EXPORT void SpellCast::applyGs( CGameState *gs )
 			}
 		}
 
-		CStack * summonedStack = gs->curB->generateNewStack(h, creID, h->getPrimSkillLevel(2) * VLC->spellh->spells[id].powers[skill], gs->curB->stacks.size(), !side, 255, ter, pos);
-		summonedStack->features.push_back( makeFeature(StackFeature::SUMMONED, StackFeature::WHOLE_BATTLE, 0, 0, StackFeature::BONUS_FROM_HERO) );
+		CStack * summonedStack = gs->curB->generateNewStack(CStackInstance(creID, h->getPrimSkillLevel(2) * VLC->spellh->spells[id].powers[skill], h), gs->curB->stacks.size(), !side, 255, ter, pos);
+		summonedStack->state.insert(SUMMONED);
+		//summonedStack->bonuses.push_back( makeFeature(HeroBonus::SUMMONED, HeroBonus::ONE_BATTLE, 0, 0, HeroBonus::BONUS_FROM_HERO) );
 
 		gs->curB->stacks.push_back(summonedStack);
 	}
 }
 
-static inline StackFeature featureGenerator(StackFeature::ECombatFeatures type, si16 subtype, si32 value, ui16 turnsRemain, si32 additionalInfo = 0)
+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;
+	return hb;
+}
+
+static inline Bonus featureGeneratorVT(Bonus::BonusType type, si16 subtype, si32 value, ui16 turnsRemain, ui8 valType)
 {
-	return makeFeature(type, StackFeature::N_TURNS, subtype, value, StackFeature::SPELL_EFFECT, turnsRemain, additionalInfo);
+	Bonus ret(makeFeature(type, Bonus::N_TURNS, subtype, value, Bonus::SPELL_EFFECT, turnsRemain));
+	ret.valType = valType;
+	return ret;
 }
 
-static std::vector<StackFeature> stackEffectToFeature(const CStack::StackEffect & sse)
+static BonusList stackEffectToFeature(const CStack::StackEffect & sse)
 {
-	std::vector<StackFeature> sf;
+	BonusList sf;
+	si32 power = VLC->spellh->spells[sse.id].powers[sse.level];
 	switch(sse.id)
 	{
 	case 27: //shield 
-		sf.push_back(featureGenerator(StackFeature::GENERAL_DAMAGE_REDUCTION, 0, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::GENERAL_DAMAGE_REDUCTION, 0, power, sse.turnsRemain));
 		break;
 	case 28: //air shield
-		sf.push_back(featureGenerator(StackFeature::GENERAL_DAMAGE_REDUCTION, 1, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::GENERAL_DAMAGE_REDUCTION, 1, power, sse.turnsRemain));
 		break;
 	case 29: //fire shield
-		sf.push_back(featureGenerator(StackFeature::FIRE_SHIELD, 0, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::FIRE_SHIELD, 0, power, sse.turnsRemain));
 		break;
 	case 30: //protection from air
-		sf.push_back(featureGenerator(StackFeature::SPELL_DAMAGE_REDUCTION, 0, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 0, power, sse.turnsRemain));
 		break;
 	case 31: //protection from fire
-		sf.push_back(featureGenerator(StackFeature::SPELL_DAMAGE_REDUCTION, 1, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 1, power, sse.turnsRemain));
 		break;
 	case 32: //protection from water
-		sf.push_back(featureGenerator(StackFeature::SPELL_DAMAGE_REDUCTION, 2, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 2, power, sse.turnsRemain));
 		break;
 	case 33: //protection from earth
-		sf.push_back(featureGenerator(StackFeature::SPELL_DAMAGE_REDUCTION, 3, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 3, power, sse.turnsRemain));
 		break;
 	case 34: //anti-magic
-		sf.push_back(featureGenerator(StackFeature::LEVEL_SPELL_IMMUNITY, 0, VLC->spellh->spells[sse.id].powers[sse.level] - 1, sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::LEVEL_SPELL_IMMUNITY, 0, power - 1, sse.turnsRemain));
 		break;
 	case 41: //bless
-		sf.push_back(featureGenerator(StackFeature::ALWAYS_MAXIMUM_DAMAGE, -1, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::ALWAYS_MAXIMUM_DAMAGE, -1, power, sse.turnsRemain));
 		break;
 	case 42: //curse
-		sf.push_back(featureGenerator(StackFeature::ALWAYS_MINIMUM_DAMAGE, -1, -1 * VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain, sse.level >= 2 ? 20 : 0));
+		sf.push_back(featureGenerator(Bonus::ALWAYS_MINIMUM_DAMAGE, -1, -1 * power, sse.turnsRemain, sse.level >= 2 ? 20 : 0));
 		break;
 	case 43: //bloodlust
-		sf.push_back(featureGenerator(StackFeature::ATTACK_BONUS, 0, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, sse.turnsRemain, 0, Bonus::ONLY_MELEE_FIGHT));
 		break;
 	case 44: //precision
-		sf.push_back(featureGenerator(StackFeature::ATTACK_BONUS, 1, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, sse.turnsRemain, 0, Bonus::ONLY_DISTANCE_FIGHT));
 		break;
 	case 45: //weakness
-		sf.push_back(featureGenerator(StackFeature::ATTACK_BONUS, -1, -1 * VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, -1 * power, sse.turnsRemain));
 		break;
 	case 46: //stone skin
-		sf.push_back(featureGenerator(StackFeature::DEFENCE_BONUS, -1, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, power, sse.turnsRemain));
 		break;
 	case 47: //disrupting ray
-		sf.push_back(featureGenerator(StackFeature::DEFENCE_BONUS, -1, -1 * VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, -1 * power, sse.turnsRemain));
 		break;
 	case 48: //prayer
-		sf.push_back(featureGenerator(StackFeature::ATTACK_BONUS, 0, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
-		sf.push_back(featureGenerator(StackFeature::DEFENCE_BONUS, 0, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
-		sf.push_back(featureGenerator(StackFeature::SPEED_BONUS, 0, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, power, sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::STACKS_SPEED, 0, power, sse.turnsRemain));
 		break;
 	case 49: //mirth
-		sf.push_back(featureGenerator(StackFeature::MORALE_BONUS, 0, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::MORALE, 0, power, sse.turnsRemain));
 		break;
 	case 50: //sorrow
-		sf.push_back(featureGenerator(StackFeature::MORALE_BONUS, 0, -1 * VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::MORALE, 0, -1 * power, sse.turnsRemain));
 		break;
 	case 51: //fortune
-		sf.push_back(featureGenerator(StackFeature::LUCK_BONUS, 0, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::LUCK, 0, power, sse.turnsRemain));
 		break;
 	case 52: //misfortune
-		sf.push_back(featureGenerator(StackFeature::LUCK_BONUS, 0, -1 * VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::LUCK, 0, -1 * power, sse.turnsRemain));
 		break;
 	case 53: //haste
-		sf.push_back(featureGenerator(StackFeature::SPEED_BONUS, 0, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::STACKS_SPEED, 0, power, sse.turnsRemain));
 		break;
 	case 54: //slow
-		sf.push_back(featureGenerator(StackFeature::SPEED_BONUS, 0, 0, sse.turnsRemain, -1 * ( 100 - VLC->spellh->spells[sse.id].powers[sse.level] ) ));
+		sf.push_back(featureGeneratorVT(Bonus::STACKS_SPEED, 0, -1 * ( 100 - power ), sse.turnsRemain, Bonus::PERCENT_TO_ALL));
 		break;
 	case 55: //slayer
-		sf.push_back(featureGenerator(StackFeature::SLAYER, 0, sse.level, sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::SLAYER, 0, sse.level, sse.turnsRemain));
 		break;
 	case 56: //frenzy
-		sf.push_back(featureGenerator(StackFeature::IN_FRENZY, 0, sse.level, sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::IN_FRENZY, 0, VLC->spellh->spells[56].powers[sse.level]/100.0, sse.turnsRemain));
 		break;
 	case 58: //counterstrike
-		sf.push_back(featureGenerator(StackFeature::ADDITIONAL_RETALIATION, 0, VLC->spellh->spells[sse.id].powers[sse.level], sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::ADDITIONAL_RETALIATION, 0, power, sse.turnsRemain));
 		break;
 	case 59: //bersek
-		sf.push_back(featureGenerator(StackFeature::ATTACKS_NEAREST_CREATURE, 0, sse.level, sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::ATTACKS_NEAREST_CREATURE, 0, sse.level, sse.turnsRemain));
 		break;
 	case 60: //hypnotize
-		sf.push_back(featureGenerator(StackFeature::HYPNOTIZED, 0, sse.level, sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::HYPNOTIZED, 0, sse.level, sse.turnsRemain));
 		break;
 	case 61: //forgetfulness
-		sf.push_back(featureGenerator(StackFeature::FORGETFULL, 0, sse.level, sse.turnsRemain));
+		sf.push_back(featureGenerator(Bonus::FORGETFULL, 0, sse.level, sse.turnsRemain));
 		break;
 	case 62: //blind
-		sf.push_back(makeFeature(StackFeature::NOT_ACTIVE, StackFeature::UNITL_BEING_ATTACKED | StackFeature::N_TURNS, 0, 0, StackFeature::SPELL_EFFECT, sse.turnsRemain, 0));
-		sf.push_back(makeFeature(StackFeature::GENERAL_ATTACK_REDUCTION, StackFeature::UNTIL_ATTACK | StackFeature::N_TURNS, 0, VLC->spellh->spells[sse.id].powers[sse.level], StackFeature::SPELL_EFFECT, sse.turnsRemain, 0));
+		sf.push_back(makeFeature(Bonus::NOT_ACTIVE, Bonus::UNITL_BEING_ATTACKED | Bonus::N_TURNS, 0, 0, Bonus::SPELL_EFFECT, sse.turnsRemain));
+		sf.push_back(makeFeature(Bonus::GENERAL_ATTACK_REDUCTION, Bonus::UNTIL_ATTACK | Bonus::N_TURNS, 0, power, Bonus::SPELL_EFFECT, sse.turnsRemain));
 		break;
 	}
 
-	//setting positiveness
-	for(int g=0; g<sf.size(); ++g)
-	{
-		sf[g].positiveness = VLC->spellh->spells[sse.id].positiveness;
-	}
-
 	return sf;
 }
 
@@ -1045,14 +1043,15 @@ void actualizeEffect(CStack * s, CStack::StackEffect & ef)
 		}
 	}
 	//actualizing features vector
-	std::vector<StackFeature> sf = stackEffectToFeature(ef);
-	for(int b=0; b<sf.size(); ++b)
+	BonusList sf = stackEffectToFeature(ef);
+
+	BOOST_FOREACH(const Bonus &fromEffect, sf)
 	{
-		for(int g=0; g<s->features.size(); ++g)
+		BOOST_FOREACH(Bonus &stackBonus, s->bonuses)
 		{
-			if(s->features[g].source == StackFeature::SPELL_EFFECT && s->features[g].type == sf[b].type && s->features[g].subtype == sf[b].subtype)
+			if(stackBonus.source == Bonus::SPELL_EFFECT && stackBonus.type == fromEffect.type && stackBonus.subtype == fromEffect.subtype)
 			{
-				s->features[g].turnsRemain = std::max(s->features[g].turnsRemain, ef.turnsRemain);
+				stackBonus.turnsRemain = std::max(stackBonus.turnsRemain, ef.turnsRemain);
 			}
 		}
 	}
@@ -1079,10 +1078,10 @@ DLL_EXPORT void SetStackEffect::applyGs( CGameState *gs )
 			if(effect.id == 42 || !containsEff(s->effects, effect.id))//disrupting ray or not on the list - just add
 			{
 				s->effects.push_back(effect);
-				std::vector<StackFeature> sf = stackEffectToFeature(effect);
-				for(int n=0; n<sf.size(); ++n)
+				BonusList sf = stackEffectToFeature(effect);
+				BOOST_FOREACH(const Bonus &fromEffect, sf)
 				{
-					s->features.push_back(sf[n]);
+					s->bonuses.push_back(fromEffect);
 				}
 			}
 			else //just actualize
@@ -1115,8 +1114,8 @@ DLL_EXPORT void StacksHealedOrResurrected::applyGs( CGameState *gs )
 		for(int h=0; h<access.size(); ++h)
 			acc[access[h]] = true;
 		if(!changedStack->alive() && !gs->curB->isAccessible(changedStack->position, acc,
-			changedStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE), changedStack->attackerOwned,
-			changedStack->hasFeatureOfType(StackFeature::FLYING), true))
+			changedStack->doubleWide(), changedStack->attackerOwned,
+			changedStack->hasBonusOfType(Bonus::FLYING), true))
 			return; //position is already occupied
 
 		//applying changes
@@ -1125,18 +1124,19 @@ DLL_EXPORT void StacksHealedOrResurrected::applyGs( CGameState *gs )
 		{
 			changedStack->state.insert(ALIVE);
 			if(healedStacks[g].lowLevelResurrection)
-				changedStack->features.push_back( makeFeature(StackFeature::SUMMONED, StackFeature::WHOLE_BATTLE, 0, 0, StackFeature::BONUS_FROM_HERO) );
+				changedStack->state.insert(SUMMONED);
+				//changedStack->bonuses.push_back( makeFeature(HeroBonus::SUMMONED, HeroBonus::ONE_BATTLE, 0, 0, HeroBonus::BONUS_FROM_HERO) );
 		}
 		int missingHPfirst = changedStack->MaxHealth() - changedStack->firstHPleft;
-		int res = std::min( healedStacks[g].healedHP / changedStack->MaxHealth() , changedStack->baseAmount - changedStack->amount );
-		changedStack->amount += res;
+		int res = std::min( healedStacks[g].healedHP / changedStack->MaxHealth() , changedStack->baseAmount - changedStack->count );
+		changedStack->count += res;
 		changedStack->firstHPleft += healedStacks[g].healedHP - res * changedStack->MaxHealth();
 		if(changedStack->firstHPleft > changedStack->MaxHealth())
 		{
 			changedStack->firstHPleft -= changedStack->MaxHealth();
-			if(changedStack->baseAmount > changedStack->amount)
+			if(changedStack->baseAmount > changedStack->count)
 			{
-				changedStack->amount += 1;
+				changedStack->count += 1;
 			}
 		}
 		amin(changedStack->firstHPleft, changedStack->MaxHealth());
@@ -1152,13 +1152,15 @@ DLL_EXPORT void StacksHealedOrResurrected::applyGs( CGameState *gs )
 			}
 			
 			//removing all features from negative spells
-			std::vector<StackFeature> tmpFeatures = changedStack->features;
-			changedStack->features.clear();
-			for(int i=0; i < tmpFeatures.size(); i++)
+			BonusList tmpFeatures = changedStack->bonuses;
+			changedStack->bonuses.clear();
+
+			BOOST_FOREACH(Bonus &b, tmpFeatures)
 			{
-				if(tmpFeatures[i].source != StackFeature::SPELL_EFFECT || tmpFeatures[i].positiveness >= 0)
+				const CSpell *s = b.sourceSpell();
+				if(s && s->positiveness >= 0)
 				{
-					changedStack->features.push_back(tmpFeatures[i]);
+					changedStack->bonuses.push_back(b);
 				}
 			}
 		}

+ 0 - 153
lib/StackFeature.h

@@ -1,153 +0,0 @@
-#ifndef __STACK_FEATURE_H__
-#define __STACK_FEATURE_H__
-
-struct StackFeature
-{
-#define VCMI_CREATURE_ABILITY_LIST										\
-	VCMI_CREATURE_ABILITY_NAME(NO_TYPE)									\
-	VCMI_CREATURE_ABILITY_NAME(DOUBLE_WIDE)								\
-	VCMI_CREATURE_ABILITY_NAME(FLYING)									\
-	VCMI_CREATURE_ABILITY_NAME(SHOOTER)									\
-	VCMI_CREATURE_ABILITY_NAME(CHARGE_IMMUNITY)							\
-	VCMI_CREATURE_ABILITY_NAME(ADDITIONAL_ATTACK)						\
-	VCMI_CREATURE_ABILITY_NAME(UNLIMITED_RETALIATIONS)					\
-	VCMI_CREATURE_ABILITY_NAME(NO_MELEE_PENALTY)						\
-	VCMI_CREATURE_ABILITY_NAME(JOUSTING) /*for champions*/				\
-	VCMI_CREATURE_ABILITY_NAME(RAISING_MORALE) /*value - how much raises*/ \
-	VCMI_CREATURE_ABILITY_NAME(HATE) /*eg. angels hate devils, subtype - ID of hated creature*/ \
-	VCMI_CREATURE_ABILITY_NAME(KING1)									\
-	VCMI_CREATURE_ABILITY_NAME(KING2)									\
-	VCMI_CREATURE_ABILITY_NAME(KING3)									\
-	VCMI_CREATURE_ABILITY_NAME(MAGIC_RESISTANCE) /*in % (value)*/		\
-	VCMI_CREATURE_ABILITY_NAME(CHANGES_SPELL_COST_FOR_ALLY) /*in mana points (value) , eg. mage*/ \
-	VCMI_CREATURE_ABILITY_NAME(CHANGES_SPELL_COST_FOR_ENEMY) /*in mana points (value) , eg. pegasus */ \
-	VCMI_CREATURE_ABILITY_NAME(SPELL_AFTER_ATTACK) /* subtype - spell id, value - spell level, (additional info)%1000 - chance in %; eg. dendroids, (additional info)/1000 -> [0 - all attacks, 1 - shot only, 2 - melee only*/ \
-	VCMI_CREATURE_ABILITY_NAME(SPELL_RESISTANCE_AURA) /*eg. unicorns, value - resistance bonus in % for adjacent creatures*/ \
-	VCMI_CREATURE_ABILITY_NAME(LEVEL_SPELL_IMMUNITY) /*creature is immune to all spell with level below or equal to value of this bonus*/ \
-	VCMI_CREATURE_ABILITY_NAME(TWO_HEX_ATTACK_BREATH) /*eg. dragons*/	\
-	VCMI_CREATURE_ABILITY_NAME(SPELL_DAMAGE_REDUCTION) /*eg. golems; value - reduction in %, subtype - spell school; -1 - all, 0 - air, 1 - fire, 2 - water, 3 - earth*/ \
-	VCMI_CREATURE_ABILITY_NAME(NO_WALL_PENALTY)							\
-	VCMI_CREATURE_ABILITY_NAME(NON_LIVING) /*eg. gargoyle*/				\
-	VCMI_CREATURE_ABILITY_NAME(RANDOM_GENIE_SPELLCASTER) /*eg. master genie*/ \
-	VCMI_CREATURE_ABILITY_NAME(BLOCKS_RETALIATION) /*eg. naga*/			\
-	VCMI_CREATURE_ABILITY_NAME(SPELL_IMMUNITY) /*subid - spell id*/		\
-	VCMI_CREATURE_ABILITY_NAME(MANA_CHANNELING) /*value in %, eg. familiar*/ \
-	VCMI_CREATURE_ABILITY_NAME(SPELL_LIKE_ATTACK) /*value - spell id; range is taken from spell, but damage from creature; eg. magog*/ \
-	VCMI_CREATURE_ABILITY_NAME(THREE_HEADED_ATTACK) /*eg. cerberus*/	\
-	VCMI_CREATURE_ABILITY_NAME(DAEMON_SUMMONING) /*pit lord*/			\
-	VCMI_CREATURE_ABILITY_NAME(FIRE_IMMUNITY)							\
-	VCMI_CREATURE_ABILITY_NAME(FIRE_SHIELD)								\
-	VCMI_CREATURE_ABILITY_NAME(ENEMY_MORALE_DECREASING) /*value - how much it decreases*/ \
-	VCMI_CREATURE_ABILITY_NAME(ENEMY_LUCK_DECREASING)					\
-	VCMI_CREATURE_ABILITY_NAME(UNDEAD)									\
-	VCMI_CREATURE_ABILITY_NAME(HP_REGENERATION) /*creature regenerates val HP every new round*/					\
-	VCMI_CREATURE_ABILITY_NAME(FULL_HP_REGENERATION) /*first creature regenerates all HP every new round; subtype 0 - animation 4 (trolllike), 1 - animation 47 (wightlike)*/		\
-	VCMI_CREATURE_ABILITY_NAME(MANA_DRAIN) /*value - spell points per turn*/ \
-	VCMI_CREATURE_ABILITY_NAME(LIFE_DRAIN)								\
-	VCMI_CREATURE_ABILITY_NAME(DOUBLE_DAMAGE_CHANCE) /*value in %, eg. dread knight*/ \
-	VCMI_CREATURE_ABILITY_NAME(RETURN_AFTER_STRIKE)						\
-	VCMI_CREATURE_ABILITY_NAME(SELF_MORALE) /*eg. minotaur*/			\
-	VCMI_CREATURE_ABILITY_NAME(SPELLCASTER) /*subtype - spell id, value - level of school, additional info - spell power*/ \
-	VCMI_CREATURE_ABILITY_NAME(CATAPULT)								\
-	VCMI_CREATURE_ABILITY_NAME(ENEMY_DEFENCE_REDUCTION) /*in % (value) eg. behemots*/ \
-	VCMI_CREATURE_ABILITY_NAME(GENERAL_DAMAGE_REDUCTION) /* shield / air shield effect */ \
-	VCMI_CREATURE_ABILITY_NAME(GENERAL_ATTACK_REDUCTION) /*eg. while stoned or blinded - in %, subtype: -1 - any damage, 0 - melee damage, 1 - ranged damage*/ \
-	VCMI_CREATURE_ABILITY_NAME(ATTACKS_ALL_ADJACENT) /*eg. hydra*/		\
-	VCMI_CREATURE_ABILITY_NAME(MORE_DAMAGE_FROM_SPELL) /*value - damage increase in %, subtype - spell id*/ \
-	VCMI_CREATURE_ABILITY_NAME(CASTS_SPELL_WHEN_KILLED) /*similar to spell after attack*/ \
-	VCMI_CREATURE_ABILITY_NAME(FEAR)									\
-	VCMI_CREATURE_ABILITY_NAME(FEARLESS)								\
-	VCMI_CREATURE_ABILITY_NAME(NO_DISTANCE_PENALTY)						\
-	VCMI_CREATURE_ABILITY_NAME(NO_OBSTACLES_PENALTY)					\
-	VCMI_CREATURE_ABILITY_NAME(SELF_LUCK) /*halfling*/					\
-	VCMI_CREATURE_ABILITY_NAME(ATTACK_BONUS) /*subtype: -1 - any attack, 0 - melee, 1 - ranged*/ \
-	VCMI_CREATURE_ABILITY_NAME(DEFENCE_BONUS) /*subtype: -1 - any attack, 0 - melee, 1 - ranged*/ \
-	VCMI_CREATURE_ABILITY_NAME(SPEED_BONUS) /*additional info - percent of speed bonus applied after direct bonuses; >0 - added, <0 - substracted to this part*/ \
-	VCMI_CREATURE_ABILITY_NAME(HP_BONUS)								\
-	VCMI_CREATURE_ABILITY_NAME(ENCHANTER)								\
-	VCMI_CREATURE_ABILITY_NAME(HEALER)									\
-	VCMI_CREATURE_ABILITY_NAME(SIEGE_WEAPON)							\
-	VCMI_CREATURE_ABILITY_NAME(LUCK_BONUS)								\
-	VCMI_CREATURE_ABILITY_NAME(MORALE_BONUS)							\
-	VCMI_CREATURE_ABILITY_NAME(HYPNOTIZED)								\
-	VCMI_CREATURE_ABILITY_NAME(ADDITIONAL_RETALIATION) /*value - number of additional retaliations*/ \
-	VCMI_CREATURE_ABILITY_NAME(MAGIC_MIRROR) /* value - chance of redirecting in %*/ \
-	VCMI_CREATURE_ABILITY_NAME(SUMMONED)								\
-	VCMI_CREATURE_ABILITY_NAME(ALWAYS_MINIMUM_DAMAGE) /*unit does its minimum damage from range; subtype: -1 - any attack, 0 - melee, 1 - ranged, value: additional damage, additional info - multiplicative anti-bonus for dmg in % [eg 20 means that creature will inflict 80% of normal dmg]*/ \
-	VCMI_CREATURE_ABILITY_NAME(ALWAYS_MAXIMUM_DAMAGE) /*eg. bless effect, subtype: -1 - any attack, 0 - melee, 1 - ranged, value: additional damage, additional info - multiplicative bonus for dmg in %*/ \
-	VCMI_CREATURE_ABILITY_NAME(ATTACKS_NEAREST_CREATURE) /*while in berserk*/ \
-	VCMI_CREATURE_ABILITY_NAME(IN_FRENZY) /*value - level*/				\
-	VCMI_CREATURE_ABILITY_NAME(SLAYER) /*value - level*/				\
-	VCMI_CREATURE_ABILITY_NAME(FORGETFULL) /*forgetfullnes spell effect, value - level*/ \
-	VCMI_CREATURE_ABILITY_NAME(CLONED)									\
-	VCMI_CREATURE_ABILITY_NAME(NOT_ACTIVE)								\
-	VCMI_CREATURE_ABILITY_NAME(NO_LUCK) /*eg. when fighting on cursed ground*/	\
-	VCMI_CREATURE_ABILITY_NAME(NO_MORALE) /*eg. when fighting on cursed ground*/
-	
-	//general list of stack abilities and effects
-	enum ECombatFeatures
-	{
-#define VCMI_CREATURE_ABILITY_NAME(x) x,
-		VCMI_CREATURE_ABILITY_LIST
-#undef VCMI_CREATURE_ABILITY_NAME
-	};
-
-	enum EDuration
-	{
-		WHOLE_BATTLE = 1,
-		N_TURNS = 2,
-		UNITL_BEING_ATTACKED = 4,/*removed after attack and counterattacks are performed*/
-		UNTIL_ATTACK = 8 /*removed after attack and counterattacks are performed*/
-	};
-
-	enum ESource
-	{
-		CREATURE_ABILITY,
-		BONUS_FROM_HERO,
-		SPELL_EFFECT,
-		OTHER_SOURCE /*eg. bonus from terrain if native*/
-	};
-
-	ui8 type;//ECombatFeatures
-	ui8 duration;//EDuration //bitfield
-	ui8 source;//ESource
-	si8 positiveness; //+1 - positive, 0 - neutral, -1 - negative; used mostly for spell features
-	ui16 turnsRemain; //if duration is N_TURNS it describes how long the effect will last
-	si16 subtype; //subtype of bonus/feature
-	si32 value;
-	si32 additionalInfo;
-
-	inline bool operator == (const ECombatFeatures & cf) const
-	{
-		return type == cf;
-	}
-	StackFeature() : type(NO_TYPE)
-	{}
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & type & duration & source & positiveness & turnsRemain & subtype & value & additionalInfo;
-	}
-};
-
-//generates StackFeature from given data
-inline StackFeature makeFeature(StackFeature::ECombatFeatures type, ui8 duration, si16 subtype, si32 value, StackFeature::ESource source, ui16 turnsRemain = 0, si32 additionalInfo = 0)
-{
-	StackFeature sf;
-	sf.type = type;
-	sf.duration = duration;
-	sf.source = source;
-	sf.turnsRemain = turnsRemain;
-	sf.subtype = subtype;
-	sf.value = value;
-	sf.additionalInfo = additionalInfo;
-
-	return sf;
-}
-
-inline StackFeature makeCreatureAbility(StackFeature::ECombatFeatures type, si32 value, si16 subtype = 0, si32 additionalInfo = 0)
-{
-	return makeFeature(type, StackFeature::WHOLE_BATTLE, subtype, value, StackFeature::CREATURE_ABILITY, 0, additionalInfo);
-}
-
-#endif //__STACK_FEATURE_H__

+ 24 - 18
lib/map.cpp

@@ -152,18 +152,21 @@ static CCreatureSet readCreatureSet(const unsigned char * bufor, int &i, int num
 	{
 		int creID = readNormalNr(bufor,i+ir*bytesPerCre, idBytes);
 		int count = readNormalNr(bufor,i+ir*bytesPerCre+idBytes, 2);
-
 		if(creID == maxID) //empty slot
 			continue;
+
+		CStackInstance hlp;
+		hlp.count = count;
+
 		if(creID > maxID - 0xf)
 		{
 			creID = maxID + 1 - creID + VLC->creh->creatures.size();//this will happen when random object has random army
-			ret.slots[ir].idRand = creID;
+			hlp.idRand = creID;
 		}
 		else
-			ret.slots[ir].setType(creID);
+			hlp.setType(creID);
 
-		ret.slots[ir].count = count;
+		ret.slots[ir] = hlp;
 	}
 	i+=number*bytesPerCre;
 	
@@ -704,8 +707,8 @@ void Mapa::loadTown( CGObjectInstance * &nobj, const unsigned char * bufor, int
 	if(readChar(bufor,i)) //has name
 		nt->name = readString(bufor,i);
 	if(readChar(bufor,i))//true if garrison isn't empty
-		nt->army = readCreatureSet(bufor,i,7,(version>RoE));
-	nt->army.formation = bufor[i]; ++i;
+		nt->setArmy(readCreatureSet(bufor, i, 7, version > RoE));
+	nt->formation = bufor[i]; ++i;
 	if(readChar(bufor,i)) //custom buildings info
 	{
 		//built buildings
@@ -889,9 +892,9 @@ 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->setArmy(readCreatureSet(bufor, i, 7, version > RoE));
 
-	nhi->army.formation =bufor[i]; ++i; //formation
+	nhi->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;
 	int artidlen = version == RoE ? 1 : 2;
@@ -988,9 +991,8 @@ void Mapa::loadHero( CGObjectInstance * &nobj, const unsigned char * bufor, int
 	{
 		if(readChar(bufor,i))//customPrimSkills
 		{
-			nhi->primSkills.resize(4);
 			for(int xx=0;xx<4;xx++)
-				nhi->primSkills[xx] = bufor[i++];
+				nhi->pushPrimSkill(xx, bufor[i++]);
 		}
 	}
 	i+=16;
@@ -1197,9 +1199,8 @@ void Mapa::readPredefinedHeroes( const unsigned char * bufor, int &i)
 				}
 				if(readChar(bufor,i))//customPrimSkills
 				{
-					cgh->primSkills.resize(4);
 					for(int xx=0;xx<4;xx++)
-						cgh->primSkills[xx] = bufor[i++];
+						cgh->pushPrimSkill(xx, bufor[i++]);
 				}
 				predefinedHeroes.push_back(cgh);
 			}
@@ -1375,7 +1376,7 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
 					}
 					if(bufor[i++])
 					{
-						evnt->army = readCreatureSet(bufor,i,7,(version>RoE)); 
+						evnt->setArmy(readCreatureSet(bufor, i, 7, version > RoE)); 
 					}
 					i+=4;
 				}
@@ -1485,7 +1486,12 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
 					cre->identifier = readNormalNr(bufor,i); i+=4;
 					monsters[cre->identifier] = cre;
 				}
-				cre->army.slots[0].count = readNormalNr(bufor,i, 2); i+=2;
+
+				CStackInstance hlp;
+				hlp.count =  readNormalNr(bufor,i, 2); i+=2;
+				//type will be set during initialization
+				cre->slots[0] = hlp;
+
 				cre->character = bufor[i]; ++i;
 				bool isMesTre = bufor[i]; ++i; //true if there is message or treasury
 				if(isMesTre)
@@ -1575,7 +1581,7 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
 				nobj = gar;
 				nobj->setOwner(bufor[i++]);
 				i+=3;
-				gar->army = readCreatureSet(bufor,i,7,(version>RoE));
+				gar->setArmy(readCreatureSet(bufor, i, 7, version > RoE));
 				if(version > RoE)
 				{
 					gar->removableUnits = bufor[i]; ++i;
@@ -1599,7 +1605,7 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
 					bool areGuards = bufor[i]; ++i;
 					if(areGuards)
 					{
-						art->army = readCreatureSet(bufor,i,7,(version>RoE));
+						art->setArmy(readCreatureSet(bufor, i, 7, version > RoE));
 					}
 					i+=4;
 				}
@@ -1621,7 +1627,7 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
 					res->message = readString(bufor,i);
 					if(bufor[i++])
 					{
-						res->army = readCreatureSet(bufor,i,7,(version>RoE));
+						res->setArmy(readCreatureSet(bufor, i, 7, version > RoE));
 					}
 					i+=4;
 				}
@@ -1675,7 +1681,7 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
 					box->message = readString(bufor,i);
 					if(bufor[i++])
 					{
-						box->army = readCreatureSet(bufor,i,7,(version>RoE));
+						box->setArmy(readCreatureSet(bufor, i, 7, version > RoE));
 					}
 					i+=4;
 				}

+ 161 - 151
server/CGameHandler.cpp

@@ -101,7 +101,7 @@ static void giveExp(BattleResult &r)
 	r.exp[1] = 0;
 	for(std::map<ui32,si32>::iterator i = r.casualties[!r.winner].begin(); i!=r.casualties[!r.winner].end(); i++)
 	{
-		r.exp[r.winner] += VLC->creh->creatures[i->first].hitPoints * i->second;
+		r.exp[r.winner] += VLC->creh->creatures[i->first]->valOfBonuses(Bonus::STACK_HEALTH) * i->second;
 	}
 }
 
@@ -302,19 +302,20 @@ static CCreatureSet takeCasualties(int color, const CCreatureSet &set, BattleInf
 {
 	if(color == 254)
 		color = 255;
+
 	CCreatureSet ret(set);
 	for(int i=0; i<bat->stacks.size();i++)
 	{
-		if(bat->stacks[i]->hasFeatureOfType(StackFeature::SUMMONED)) //don't take into account sumoned stacks
+		CStack *st = bat->stacks[i];
+		if(vstd::contains(st->state, SUMMONED)) //don't take into account sumoned stacks
 			continue;
 
-		CStack *st = bat->stacks[i];
-		if(st->owner==color && vstd::contains(set.slots,st->slot) && st->amount < set.slots.find(st->slot)->second.count)
+		if(st->owner==color && !set.slotEmpty(st->slot) && st->count < set.getAmount(st->slot))
 		{
 			if(st->alive())
-				ret.slots[st->slot].count = st->amount;
+				ret.setStackCount(st->slot, st->count);
 			else
-				ret.slots.erase(st->slot);
+				ret.eraseStack(st->slot);
 		}
 	}
 	return ret;
@@ -331,7 +332,7 @@ void CGameHandler::startBattle(const CArmedInstance *army1, const CArmedInstance
 		curB->side2 = army2->tempOwner;
 		if(curB->side2 == 254) 
 			curB->side2 = 255;
-		setupBattle(curB, tile, army1->army, army2->army, hero1, hero2, creatureBank, town); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces
+		setupBattle(curB, tile, army1, army2, hero1, hero2, creatureBank, town); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces
 	}
 
 	NEW_ROUND;
@@ -360,11 +361,12 @@ void CGameHandler::startBattle(const CArmedInstance *army1, const CArmedInstance
 		{
 
 			//check for bad morale => freeze
-			if( curB.Morale(next) < 0 &&
-				!(NBonus::hasOfType(hero1, HeroBonus::BLOCK_MORALE) || NBonus::hasOfType(hero2, HeroBonus::BLOCK_MORALE)) //checking if heroes have (or don't have) morale blocking bonuses)
+			int nextStackMorale = next->MoraleVal();
+			if( nextStackMorale < 0 &&
+				!(NBonus::hasOfType(hero1, Bonus::BLOCK_MORALE) || NBonus::hasOfType(hero2, Bonus::BLOCK_MORALE)) //checking if heroes have (or don't have) morale blocking bonuses)
 				)
 			{
-				if( rand()%24   <   (-curB.Morale(next))*2 )
+				if( rand()%24   <   -2 * nextStackMorale)
 				{
 					//unit loses its turn - empty freeze action
 					BattleAction ba;
@@ -379,7 +381,7 @@ void CGameHandler::startBattle(const CArmedInstance *army1, const CArmedInstance
 				}
 			}
 
-			if(next->hasFeatureOfType(StackFeature::ATTACKS_NEAREST_CREATURE)) //while in berserk
+			if(next->hasBonusOfType(Bonus::ATTACKS_NEAREST_CREATURE)) //while in berserk
 			{
 				std::pair<const CStack *, int> attackInfo = curB.getNearestStack(next, boost::logic::indeterminate);
 				if(attackInfo.first != NULL)
@@ -402,7 +404,7 @@ void CGameHandler::startBattle(const CArmedInstance *army1, const CArmedInstance
 			const CGHeroInstance * curOwner = gs->battleGetOwner(next->ID);
 
 			if( (next->position < 0 && (!curOwner || curOwner->getSecSkillLevel(10) == 0)) //arrow turret, hero has no ballistics
-				|| (next->creature->idNumber == 146 && curOwner->getSecSkillLevel(20) == 0)) //ballista, hero has no artillery
+				|| (next->type->idNumber == 146 && curOwner->getSecSkillLevel(20) == 0)) //ballista, hero has no artillery
 			{
 				BattleAction attack;
 				attack.actionType = 7;
@@ -424,7 +426,7 @@ void CGameHandler::startBattle(const CArmedInstance *army1, const CArmedInstance
 				continue;
 			}
 
-			if(next->creature->idNumber == 145 && (!curOwner || curOwner->getSecSkillLevel(10) == 0)) //catapult, hero has no ballistics
+			if(next->type->idNumber == 145 && (!curOwner || curOwner->getSecSkillLevel(10) == 0)) //catapult, hero has no ballistics
 			{
 				BattleAction attack;
 				static const int wallHexes[] = {50, 183, 182, 130, 62, 29, 12, 95};
@@ -439,7 +441,7 @@ void CGameHandler::startBattle(const CArmedInstance *army1, const CArmedInstance
 				continue;
 			}
 
-			if(next->creature->idNumber == 147 && (!curOwner || curOwner->getSecSkillLevel(27) == 0)) //first aid tent, hero has no first aid
+			if(next->type->idNumber == 147 && (!curOwner || curOwner->getSecSkillLevel(27) == 0)) //first aid tent, hero has no first aid
 			{
 				BattleAction heal;
 
@@ -503,14 +505,15 @@ askInterfaceForMove:
 			checkForBattleEnd(stacks); //check if this action ended the battle
 
 			//check for good morale
+			nextStackMorale = next->MoraleVal();
 			if(!vstd::contains(next->state,HAD_MORALE)  //only one extra move per turn possible
 				&& !vstd::contains(next->state,DEFENDING)
 				&& !vstd::contains(next->state,WAITING)
 				&&  next->alive()
-				&&  curB.Morale(next) > 0
-				&& !(NBonus::hasOfType(hero1, HeroBonus::BLOCK_MORALE) || NBonus::hasOfType(hero2, HeroBonus::BLOCK_MORALE)) //checking if heroes have (or don't have) morale blocking bonuses
+				&&  nextStackMorale > 0
+				&& !(NBonus::hasOfType(hero1, Bonus::BLOCK_MORALE) || NBonus::hasOfType(hero2, Bonus::BLOCK_MORALE)) //checking if heroes have (or don't have) morale blocking bonuses
 			)
-				if(rand()%24 < curB.Morale(next)) //this stack hasn't got morale this turn
+				if(rand()%24 < nextStackMorale) //this stack hasn't got morale this turn
 					goto askInterfaceForMove; //move this stack once more
 		}
 	}
@@ -533,8 +536,8 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
 
 	//casualties among heroes armies
 	SetGarrisons sg;
-	sg.garrs[bEndArmy1->id] = takeCasualties(bEndArmy1->tempOwner, bEndArmy1->army, gs->curB);
-	sg.garrs[bEndArmy2->id] = takeCasualties(bEndArmy2->tempOwner, bEndArmy2->army, gs->curB);
+	sg.garrs[bEndArmy1->id] = takeCasualties(bEndArmy1->tempOwner, *bEndArmy1, gs->curB);
+	sg.garrs[bEndArmy2->id] = takeCasualties(bEndArmy2->tempOwner, *bEndArmy2, gs->curB);
 	sendAndApply(&sg);
 
 	ui8 sides[2];
@@ -581,17 +584,18 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
 		// Give raised units to winner and show dialog, if any were raised.
 		if (raisedStack.type) 
 		{
-			int slot = winnerHero->army.getSlotFor(raisedStack.type->idNumber);
+			int slot = winnerHero->getSlotFor(raisedStack.type->idNumber);
 
 			if (slot != -1) 
 			{
 				SetGarrisons sg;
+				sg.garrs[winnerHero->id] = winnerHero->getArmy();
+				sg.garrs[winnerHero->id].addToSlot(slot, raisedStack);
 
-				sg.garrs[winnerHero->id] = winnerHero->army;
-				if (vstd::contains(winnerHero->army.slots, slot)) // Add to existing stack.
-					sg.garrs[winnerHero->id].slots[slot].count += raisedStack.count;
-				else // Create a new stack.
-					sg.garrs[winnerHero->id].slots[slot] = raisedStack;
+// 				if (vstd::contains(winnerHero->slots, slot)) // Add to existing stack.
+// 					sg.garrs[winnerHero->id].slots[slot].count += raisedStack.count;
+// 				else // Create a new stack.
+// 					sg.garrs[winnerHero->id].slots[slot] = raisedStack;
 				winnerHero->showNecromancyDialog(raisedStack);
 				sendAndApply(&sg);
 			}
@@ -617,15 +621,15 @@ void CGameHandler::prepareAttacked(BattleStackAttacked &bsa, const CStack *def)
 		bsa.newHP = def->firstHPleft - damageFirst;
 	}
 
-	if(def->amount <= bsa.killedAmount) //stack killed
+	if(def->count <= bsa.killedAmount) //stack killed
 	{
 		bsa.newAmount = 0;
 		bsa.flags |= 1;
-		bsa.killedAmount = def->amount; //we cannot kill more creatures than we have
+		bsa.killedAmount = def->count; //we cannot kill more creatures than we have
 	}
 	else
 	{
-		bsa.newAmount = def->amount - bsa.killedAmount;
+		bsa.newAmount = def->count - bsa.killedAmount;
 	}
 }
 
@@ -637,7 +641,8 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
 	BattleStackAttacked *bsa = &bat.bsa.back();
 	bsa->stackAttacked = def->ID;
 	bsa->attackerID = att->ID;
-	if(gs->curB->Luck(att) > 0  &&  rand()%24 < gs->curB->Luck(att))
+	int attackerLuck = att->LuckVal();
+	if(attackerLuck > 0  &&  rand()%24 < attackerLuck) //TODO?: negative luck option?
 	{
 		bsa->damageAmount *= 2;
 		bat.flags |= 4;
@@ -650,7 +655,7 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
 	prepareAttacked(*bsa, def);
 
 	//fire shield handling
-	if ( !bat.shot() && def->hasFeatureOfType(StackFeature::FIRE_SHIELD) && !bsa->killed() )
+	if ( !bat.shot() && def->hasBonusOfType(Bonus::FIRE_SHIELD) && !bsa->killed() )
 	{
 		bat.bsa.push_back(BattleStackAttacked());
 		BattleStackAttacked *bsa = &bat.bsa.back();
@@ -659,7 +664,7 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
 		bsa->flags |= 2;
 		bsa->effect = 11;
 
-		bsa->damageAmount = (dmg * def->valOfFeatures(StackFeature::FIRE_SHIELD)) / 100;
+		bsa->damageAmount = (dmg * def->valOfBonuses(Bonus::FIRE_SHIELD)) / 100;
 		prepareAttacked(*bsa, att);
 	}
 }
@@ -739,7 +744,7 @@ int CGameHandler::moveStack(int stack, int dest)
 	}
 
 	//shifting destination (if we have double wide stack and we can occupy dest but not be exactly there)
-	if(!stackAtEnd && curStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && !accessibility[dest])
+	if(!stackAtEnd && curStack->doubleWide() && !accessibility[dest])
 	{
 		if(curStack->attackerOwned)
 		{
@@ -770,11 +775,11 @@ int CGameHandler::moveStack(int stack, int dest)
 	//if(dists[dest] > curStack->creature->speed && !(stackAtEnd && dists[dest] == curStack->creature->speed+1)) //we can attack a stack if we can go to adjacent hex
 	//	return false;
 
-	std::pair< std::vector<int>, int > path = gs->curB->getPath(curStack->position, dest, accessibilityWithOccupyable, curStack->hasFeatureOfType(StackFeature::FLYING), curStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE), curStack->attackerOwned);
+	std::pair< std::vector<int>, int > path = gs->curB->getPath(curStack->position, dest, accessibilityWithOccupyable, curStack->hasBonusOfType(Bonus::FLYING), curStack->doubleWide(), curStack->attackerOwned);
 
 	ret = path.second;
 
-	if(curStack->hasFeatureOfType(StackFeature::FLYING))
+	if(curStack->hasBonusOfType(Bonus::FLYING))
 	{
 		if(path.second <= curStack->Speed() && path.first.size() > 0)
 		{
@@ -912,8 +917,8 @@ void CGameHandler::newTurn()
 					break;
 				}
 
-				for(std::list<HeroBonus>::iterator  j = h->bonuses.begin(); j != h->bonuses.end(); j++)
-					if(j->type == HeroBonus::GENERATE_RESOURCE)
+				for(std::list<Bonus>::iterator  j = h->bonuses.begin(); j != h->bonuses.end(); j++)
+					if(j->type == Bonus::GENERATE_RESOURCE)
 						n.res[i->first][j->subtype] += j->val;
 
 				//TODO player bonuses
@@ -939,7 +944,7 @@ void CGameHandler::newTurn()
 				{
 					sac.creatures[k].first += (**j).creatureGrowth(k);
 					if(!gs->getDate(0)) //first day of game: use only basic growths
-						amin(sac.creatures[k].first, VLC->creh->creatures[(*j)->town->basicCreatures[k]].growth);
+						amin(sac.creatures[k].first, VLC->creh->creatures[(*j)->town->basicCreatures[k]]->growth);
 				}
 			}
 			n.cres.push_back(sac);
@@ -1007,7 +1012,8 @@ void CGameHandler::newTurn()
 	}
 }
 void CGameHandler::run(bool resume)
-{	
+{
+	using namespace boost::posix_time;
 	BOOST_FOREACH(CConnection *cc, conns)
 	{//init conn.
 		ui8 quantity, pom;
@@ -1050,7 +1056,6 @@ void CGameHandler::run(bool resume)
 		resume = false;
 		for(; i != gs->players.end(); i++)
 		{
-
 			if(i->second.towns.size()==0 && i->second.heroes.size()==0
 				|| i->second.color<0 
 				|| i->first>=PLAYER_LIMIT  
@@ -1070,8 +1075,7 @@ void CGameHandler::run(bool resume)
 			boost::unique_lock<boost::mutex> lock(states.mx);
 			while(states.players[i->first].makingTurn && !end2)
 			{
-				boost::posix_time::time_duration p;
-				p = boost::posix_time::milliseconds(200);
+				static time_duration p = milliseconds(200);
 				states.cv.timed_wait(lock,p); 
 			}
 		}
@@ -1099,14 +1103,14 @@ namespace CGH
 	}
 }
 
-void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet &army1, const CCreatureSet &army2, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool creatureBank, const CGTownInstance *town)
+void CGameHandler::setupBattle(BattleInfo * curB, int3 tile, const CArmedInstance *army1, const CArmedInstance *army2, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool creatureBank, const CGTownInstance *town)
 {
 	battleResult.set(NULL);
 	std::vector<CStack*> & stacks = (curB->stacks);
 
 	curB->tile = tile;
-	curB->army1=army1;
-	curB->army2=army2;
+	curB->belligerents[0] = const_cast<CArmedInstance*>(army1);
+	curB->belligerents[1] = const_cast<CArmedInstance*>(army2);
 	curB->heroes[0] = const_cast<CGHeroInstance*>(hero1);
 	curB->heroes[1] = const_cast<CGHeroInstance*>(hero2);
 	curB->round = -2;
@@ -1150,42 +1154,42 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 	//battleStartpos read
 
 	int k = 0; //stack serial 
-	for(TSlots::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)
-			pos = attackerCreBank[army1.slots.size()-1][k];
-		else if(army1.formation)
-			pos = attackerTight[army1.slots.size()-1][k];
+			pos = attackerCreBank[army1->stacksCount()-1][k];
+		else if(army1->formation)
+			pos = attackerTight[army1->stacksCount()-1][k];
 		else
-			pos = attackerLoose[army1.slots.size()-1][k];
+			pos = attackerLoose[army1->stacksCount()-1][k];
 
-		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);
+		CStack * stack = curB->generateNewStack(i->second, stacks.size(), true, i->first, gs->map->terrain[tile.x][tile.y][tile.z].tertype, pos);
 		stacks.push_back(stack);
 	}
 	
 	k = 0;
-	for(TSlots::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)
-			pos = defenderCreBank[army2.slots.size()-1][k];
-		else if(army2.formation)
-			pos = defenderTight[army2.slots.size()-1][k];
+			pos = defenderCreBank[army2->stacksCount()-1][k];
+		else if(army2->formation)
+			pos = defenderTight[army2->stacksCount()-1][k];
 		else
-			pos = defenderLoose[army2.slots.size()-1][k];
+			pos = defenderLoose[army2->stacksCount()-1][k];
 
-		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);
+		CStack * stack = curB->generateNewStack(i->second, stacks.size(), false, i->first, gs->map->terrain[tile.x][tile.y][tile.z].tertype, pos);
 		stacks.push_back(stack);
 	}
 
 	for(unsigned g=0; g<stacks.size(); ++g) //shifting positions of two-hex creatures
 	{
-		if((stacks[g]->position%17)==1 && stacks[g]->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && stacks[g]->attackerOwned)
+		if((stacks[g]->position%17)==1 && stacks[g]->doubleWide() && stacks[g]->attackerOwned)
 		{
 			stacks[g]->position += 1;
 		}
-		else if((stacks[g]->position%17)==15 && stacks[g]->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && !stacks[g]->attackerOwned)
+		else if((stacks[g]->position%17)==15 && stacks[g]->doubleWide() && !stacks[g]->attackerOwned)
 		{
 			stacks[g]->position -= 1;
 		}
@@ -1196,17 +1200,17 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 	{
 		if(hero1->getArt(13)) //ballista
 		{
-			CStack * stack = curB->generateNewStack(hero1, 146, 1, stacks.size(), true, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 52);
+			CStack * stack = curB->generateNewStack(CStackInstance(146, 1, hero1), stacks.size(), true, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 52);
 			stacks.push_back(stack);
 		}
 		if(hero1->getArt(14)) //ammo cart
 		{
-			CStack * stack = curB->generateNewStack(hero1, 148, 1, stacks.size(), true, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 18);
+			CStack * stack = curB->generateNewStack(CStackInstance(148, 1, hero1), stacks.size(), true, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 18);
 			stacks.push_back(stack);
 		}
 		if(hero1->getArt(15)) //first aid tent
 		{
-			CStack * stack = curB->generateNewStack(hero1, 147, 1, stacks.size(), true, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 154);
+			CStack * stack = curB->generateNewStack(CStackInstance(147, 1, hero1), stacks.size(), true, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 154);
 			stacks.push_back(stack);
 		}
 	}
@@ -1214,23 +1218,23 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 	{
 		if(hero2->getArt(13)) //ballista
 		{
-			CStack * stack = curB->generateNewStack(hero2, 146, 1, stacks.size(), false, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 66);
+			CStack * stack = curB->generateNewStack(CStackInstance(146, 1, hero2),  stacks.size(), false, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 66);
 			stacks.push_back(stack);
 		}
 		if(hero2->getArt(14)) //ammo cart
 		{
-			CStack * stack = curB->generateNewStack(hero2, 148, 1, stacks.size(), false, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 32);
+			CStack * stack = curB->generateNewStack(CStackInstance(148, 1, hero1), stacks.size(), false, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 32);
 			stacks.push_back(stack);
 		}
 		if(hero2->getArt(15)) //first aid tent
 		{
-			CStack * stack = curB->generateNewStack(hero2, 147, 1, stacks.size(), false, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 168);
+			CStack * stack = curB->generateNewStack(CStackInstance(147, 1, hero2), stacks.size(), false, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 168);
 			stacks.push_back(stack);
 		}
 	}
 	if(town && hero1 && town->hasFort()) //catapult
 	{
-		CStack * stack = curB->generateNewStack(hero1, 145, 1, stacks.size(), true, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 120);
+		CStack * stack = curB->generateNewStack(CStackInstance(145, 1, hero1), stacks.size(), true, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, 120);
 		stacks.push_back(stack);
 	}
 	//war machines added
@@ -1240,14 +1244,14 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 		
 	case 3: //castle
 		{//lower tower / upper tower
-			CStack * stack = curB->generateNewStack(hero2, 149, 1, stacks.size(), false, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, -4);
+			CStack * stack = curB->generateNewStack(CStackInstance(149, 1, hero2), stacks.size(), false, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, -4);
 			stacks.push_back(stack);
-			stack = curB->generateNewStack(hero2, 149, 1, stacks.size(), false, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, -3);
+			stack = curB->generateNewStack(CStackInstance(149, 1, hero2), stacks.size(), false, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, -3);
 			stacks.push_back(stack);
 		}
 	case 2: //citadel
 		{//main tower
-			CStack * stack = curB->generateNewStack(hero2, 149, 1, stacks.size(), false, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, -2);
+			CStack * stack = curB->generateNewStack(CStackInstance(149, 1, hero2), stacks.size(), false, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, -2);
 			stacks.push_back(stack);
 		}
 	}
@@ -1335,7 +1339,7 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 				if (val)
 				{
 					GiveBonus gs;
-					gs.bonus = HeroBonus(HeroBonus::ONE_BATTLE, HeroBonus::PRIMARY_SKILL, HeroBonus::OBJECT, val, -1, "", i);
+					gs.bonus = Bonus(Bonus::ONE_BATTLE, Bonus::PRIMARY_SKILL, Bonus::OBJECT, val, -1, "", i);
 					gs.id = hero2->id;
 					sendAndApply(&gs);
 				}
@@ -1345,15 +1349,15 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 		{
 			if(town->subID == 0  &&  vstd::contains(town->builtBuildings,22)) //castle, brotherhood of sword built
 				for(int g=0; g<stacks.size(); ++g)
-					stacks[g]->features.push_back(makeFeature(StackFeature::MORALE_BONUS, StackFeature::WHOLE_BATTLE, 0, 2, StackFeature::OTHER_SOURCE));
+					stacks[g]->bonuses.push_back(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]->features.push_back(makeFeature(StackFeature::MORALE_BONUS, StackFeature::WHOLE_BATTLE, 0, 1, StackFeature::OTHER_SOURCE));
+					stacks[g]->bonuses.push_back(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]->features.push_back(makeFeature(StackFeature::LUCK_BONUS, StackFeature::WHOLE_BATTLE, 0, 2, StackFeature::OTHER_SOURCE));
+					stacks[g]->bonuses.push_back(makeFeature(Bonus::LUCK, Bonus::ONE_BATTLE, 0, 2, Bonus::TOWN_STRUCTURE));
 		}
 	}
 
@@ -1393,7 +1397,7 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 				if(cHero == NULL) continue;
 
 				GiveBonus gs;
-				gs.bonus = HeroBonus(HeroBonus::ONE_BATTLE, HeroBonus::MAGIC_SCHOOL_SKILL, HeroBonus::OBJECT, 3, -1, "", bonusSubtype);
+				gs.bonus = Bonus(Bonus::ONE_BATTLE, Bonus::MAGIC_SCHOOL_SKILL, Bonus::OBJECT, 3, -1, "", bonusSubtype);
 				gs.id = cHero->id;
 
 				sendAndApply(&gs);
@@ -1406,10 +1410,10 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 		{
 			for(int g=0; g<stacks.size(); ++g) //+1 morale bonus for good creatures, -1 morale bonus for evil creatures
 			{
-				if (stacks[g]->creature->isGood())
-					stacks[g]->features.push_back(makeFeature(StackFeature::MORALE_BONUS, StackFeature::WHOLE_BATTLE, 0, 1, StackFeature::OTHER_SOURCE));
-				else if (stacks[g]->creature->isEvil())
-					stacks[g]->features.push_back(makeFeature(StackFeature::MORALE_BONUS, StackFeature::WHOLE_BATTLE, 0, -1, StackFeature::OTHER_SOURCE));
+				if (stacks[g]->type->isGood())
+					stacks[g]->bonuses.push_back(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, 1, Bonus::TERRAIN_OVERLAY));
+				else if (stacks[g]->type->isEvil())
+					stacks[g]->bonuses.push_back(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, -1, Bonus::TERRAIN_OVERLAY));
 			}
 			break;
 		}
@@ -1417,9 +1421,9 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 		{
 			for(int g=0; g<stacks.size(); ++g)
 			{
-				if(stacks[g]->creature->faction == -1) //+2 luck bonus for neutral creatures
+				if(stacks[g]->type->faction == -1) //+2 luck bonus for neutral creatures
 				{
-					stacks[g]->features.push_back(makeFeature(StackFeature::LUCK_BONUS, StackFeature::WHOLE_BATTLE, 0, 2, StackFeature::OTHER_SOURCE));
+					stacks[g]->bonuses.push_back(makeFeature(Bonus::LUCK, Bonus::ONE_BATTLE, 0, 2, Bonus::TERRAIN_OVERLAY));
 				}
 			}
 			break;
@@ -1428,10 +1432,10 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 		{
 			for(int g=0; g<stacks.size(); ++g) //-1 morale bonus for good creatures, +1 morale bonus for evil creatures
 			{
-				if (stacks[g]->creature->isGood())
-					stacks[g]->features.push_back(makeFeature(StackFeature::MORALE_BONUS, StackFeature::WHOLE_BATTLE, 0, -1, StackFeature::OTHER_SOURCE));
-				else if (stacks[g]->creature->isEvil())
-					stacks[g]->features.push_back(makeFeature(StackFeature::MORALE_BONUS, StackFeature::WHOLE_BATTLE, 0, 1, StackFeature::OTHER_SOURCE));
+				if (stacks[g]->type->isGood())
+					stacks[g]->bonuses.push_back(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, -1, Bonus::TERRAIN_OVERLAY));
+				else if (stacks[g]->type->isEvil())
+					stacks[g]->bonuses.push_back(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, 1, Bonus::TERRAIN_OVERLAY));
 			}
 			break;
 		}
@@ -1439,8 +1443,8 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 		{
 			for(int g=0; g<stacks.size(); ++g) //no luck nor morale
 			{
-				stacks[g]->features.push_back(makeFeature(StackFeature::NO_MORALE, StackFeature::WHOLE_BATTLE, 0, 0, StackFeature::OTHER_SOURCE));
-				stacks[g]->features.push_back(makeFeature(StackFeature::NO_LUCK, StackFeature::WHOLE_BATTLE, 0, 0, StackFeature::OTHER_SOURCE));
+				stacks[g]->bonuses.push_back(makeFeature(Bonus::NO_MORALE, Bonus::ONE_BATTLE, 0, 0, Bonus::TERRAIN_OVERLAY));
+				stacks[g]->bonuses.push_back(makeFeature(Bonus::NO_LUCK, Bonus::ONE_BATTLE, 0, 0, Bonus::TERRAIN_OVERLAY));
 			}
 
 			const CGHeroInstance * cHero = NULL;
@@ -1452,7 +1456,7 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 				if(cHero == NULL) continue;
 
 				GiveBonus gs;
-				gs.bonus = HeroBonus(HeroBonus::ONE_BATTLE, HeroBonus::BLOCK_SPELLS_ABOVE_LEVEL, HeroBonus::OBJECT, 1, -1, "", bonusSubtype);
+				gs.bonus = Bonus(Bonus::ONE_BATTLE, Bonus::BLOCK_SPELLS_ABOVE_LEVEL, Bonus::OBJECT, 1, -1, "", bonusSubtype);
 				gs.id = cHero->id;
 
 				sendAndApply(&gs);
@@ -1478,7 +1482,7 @@ void CGameHandler::checkForBattleEnd( std::vector<CStack*> &stacks )
 	hasStack[0] = hasStack[1] = false;
 	for(int b = 0; b<stacks.size(); ++b)
 	{
-		if(stacks[b]->alive() && !stacks[b]->hasFeatureOfType(StackFeature::SIEGE_WEAPON))
+		if(stacks[b]->alive() && !stacks[b]->hasBonusOfType(Bonus::SIEGE_WEAPON))
 		{
 			hasStack[1-stacks[b]->attackerOwned] = true;
 		}
@@ -1772,19 +1776,19 @@ void CGameHandler::giveResource(int player, int which, int val)
 }
 void CGameHandler::giveCreatures (int objid, const CGHeroInstance * h, CCreatureSet creatures)
 {
-	if (creatures.slots.size() <= 0)
+	if (creatures.stacksCount() <= 0)
 		return;
-	CCreatureSet heroArmy = h->army;
-	while (creatures.slots.size() > 0)
+	CCreatureSet heroArmy = h->getArmy();
+	while (creatures.stacksCount() > 0)
 	{
-		int slot = heroArmy.getSlotFor(creatures.slots.begin()->second.type->idNumber);
+		int slot = heroArmy.getSlotFor(creatures.Slots().begin()->second.type->idNumber);
 		if (slot < 0)
 			break;
 		heroArmy.addToSlot(slot, creatures.slots.begin()->second);
 		creatures.slots.erase (creatures.slots.begin());
 	}
 
-	if (creatures.slots.size() == 0) //all creatures can be moved to hero army - do that
+	if (creatures.stacksCount() == 0) //all creatures can be moved to hero army - do that
 	{
 		SetGarrisons sg;
 		sg.garrs[h->id] = heroArmy;
@@ -1804,7 +1808,7 @@ void CGameHandler::takeCreatures (int objid, TSlots creatures) //probably we cou
 	if (creatures.size() <= 0)
 		return;
 	const CArmedInstance* obj = static_cast<const CArmedInstance*>(getObj(objid));
-	CCreatureSet newArmy = obj->army;
+	CCreatureSet newArmy = obj->getArmy();
 	while (creatures.size() > 0)
 	{
 		int slot = newArmy.getSlotFor(creatures.begin()->second.type->idNumber);
@@ -2227,7 +2231,7 @@ bool CGameHandler::arrangeStacks( si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2,
 {
 	CArmedInstance *s1 = static_cast<CArmedInstance*>(gs->map->objects[id1]),
 		*s2 = static_cast<CArmedInstance*>(gs->map->objects[id2]);
-	CCreatureSet temp1 = s1->army, temp2 = s2->army,
+	CCreatureSet temp1 = s1->getArmy(), temp2 = s2->getArmy(),
 		&S1 = temp1, &S2 = (s1!=s2)?(temp2):(temp1);
 
 	if(!isAllowedExchange(id1,id2))
@@ -2295,8 +2299,8 @@ bool CGameHandler::arrangeStacks( si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2,
 		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!
-		|| (s2->needsLastStack() && !S2.slots.size())
+	if((s1->needsLastStack() && !S1.stacksCount()) //it's not allowed to take last stack from hero army!
+		|| (s2->needsLastStack() && !S2.stacksCount())
 	)
 	{
 		complain("Cannot take the last stack!");
@@ -2339,14 +2343,14 @@ int CGameHandler::getPlayerAt( CConnection *c ) const
 bool CGameHandler::disbandCreature( si32 id, ui8 pos )
 {
 	CArmedInstance *s1 = static_cast<CArmedInstance*>(gs->map->objects[id]);
-	if(!vstd::contains(s1->army.slots,pos))
+	if(!vstd::contains(s1->slots,pos))
 	{
 		complain("Illegal call to disbandCreature - no such stack in army!");
 		return false;
 	}
-	s1->army.slots.erase(pos);
+	s1->slots.erase(pos);
 	SetGarrisons sg;
-	sg.garrs[id] = s1->army;
+	sg.garrs[id] = s1->getArmy();
 	sendAndApply(&sg);
 	return true;
 }
@@ -2398,7 +2402,7 @@ bool CGameHandler::buildStructure( si32 tid, si32 bid )
 		SetAvailableCreatures ssi;
 		ssi.tid = tid;
 		ssi.creatures = t->creatures;
-		ssi.creatures[bid-30].first = VLC->creh->creatures[crid].growth;
+		ssi.creatures[bid-30].first = VLC->creh->creatures[crid]->growth;
 		ssi.creatures[bid-30].second.push_back(crid);
 		sendAndApply(&ssi);
 	}
@@ -2495,10 +2499,10 @@ bool CGameHandler::recruitCreatures( si32 objid, ui32 crid, ui32 cram )
 			break;
 		}
 	}
-	int slot = dst->army.getSlotFor(crid);
+	int slot = dst-> getSlotFor(crid);
 
 	if(!found && complain("Cannot recruit: no such creatures!")
-		|| cram > VLC->creh->creatures[crid].maxAmount(gs->getPlayer(dst->tempOwner)->resources) && complain("Cannot recruit: lack of resources!")
+		|| cram > VLC->creh->creatures[crid]->maxAmount(gs->getPlayer(dst->tempOwner)->resources) && complain("Cannot recruit: lack of resources!")
 		|| cram<=0	&& complain("Cannot recruit: cram <= 0!")
 		|| slot<0  && complain("Cannot recruit: no available slot!")) 
 	{
@@ -2509,7 +2513,7 @@ bool CGameHandler::recruitCreatures( si32 objid, ui32 crid, ui32 cram )
 	SetResources sr;
 	sr.player = dst->tempOwner;
 	for(int i=0;i<RESOURCE_QUANTITY;i++)
-		sr.res[i]  =  gs->getPlayer(dst->tempOwner)->resources[i] - (VLC->creh->creatures[crid].cost[i] * cram);
+		sr.res[i]  =  gs->getPlayer(dst->tempOwner)->resources[i] - (VLC->creh->creatures[crid]->cost[i] * cram);
 
 	SetAvailableCreatures sac;
 	sac.tid = objid;
@@ -2517,7 +2521,7 @@ bool CGameHandler::recruitCreatures( si32 objid, ui32 crid, ui32 cram )
 	sac.creatures[level].first -= cram;
 
 	SetGarrisons sg;
-	sg.garrs[dst->id] = dst->army;
+	sg.garrs[dst->id] = dst->getArmy();
 	sg.garrs[dst->id] .addToSlot(slot, crid, cram);
 
 	sendAndApply(&sr); 
@@ -2531,7 +2535,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].count;
+	int crQuantity = obj->slots[pos].count;
 
 	//check if upgrade is possible
 	if((ui.oldID<0 || !vstd::contains(ui.newID,upgID)) && complain("That upgrade is not possible!")) 
@@ -2567,7 +2571,7 @@ bool CGameHandler::upgradeCreature( ui32 objid, ui8 pos, ui32 upgID )
 
 	//upgrade creature
 	SetGarrisons sg;
-	sg.garrs[objid] = obj->army;
+	sg.garrs[objid] = obj->getArmy();
 	sg.garrs[objid].slots[pos].setType(upgID);
 	sendAndApply(&sg);	
 	return true;
@@ -2578,7 +2582,7 @@ bool CGameHandler::garrisonSwap( si32 tid )
 	CGTownInstance *town = gs->getTown(tid);
 	if(!town->garrisonHero && town->visitingHero) //visiting => garrison, merge armies: town army => hero army
 	{
-		CCreatureSet csn = town->visitingHero->army, cso = town->army;
+		CCreatureSet csn = town->visitingHero->getArmy(), cso = town->getArmy();
 		while(!cso.slots.empty())//while there are unmoved creatures
 		{
 			int pos = csn.getSlotFor(cso.slots.begin()->second.type->idNumber);
@@ -2645,8 +2649,8 @@ bool CGameHandler::garrisonSwap( si32 tid )
 	else if (town->garrisonHero && town->visitingHero) //swap visiting and garrison hero
 	{
 		SetGarrisons sg;
-		sg.garrs[town->id] = town->visitingHero->army;
-		sg.garrs[town->garrisonHero->id] = town->garrisonHero->army;
+		sg.garrs[town->id] = town->visitingHero->getArmy();;
+		sg.garrs[town->garrisonHero->id] = town->garrisonHero->getArmy();
 
 		SetHeroesInTown intown;
 		intown.tid = tid;
@@ -2915,7 +2919,7 @@ bool CGameHandler::tradeResources( ui32 val, ui8 player, ui32 id1, ui32 id2 )
 
 bool CGameHandler::setFormation( si32 hid, ui8 formation )
 {
-	gs->getHero(hid)->army.formation = formation;
+	gs->getHero(hid)-> formation = formation;
 	return true;
 }
 
@@ -3032,12 +3036,12 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 				*stackAtEnd = gs->curB->getStackT(ba.additionalInfo);
 
 			if(curStack->position != ba.destinationTile //we wasn't able to reach destination tile
-				&& !(curStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE)
+				&& !(curStack->doubleWide()
 					&&  ( curStack->position == ba.destinationTile + (curStack->attackerOwned ?  +1 : -1 ) )
 						) //nor occupy specified hex
 				) 
 			{
-				std::string problem = "We cannot move this stack to its destination " + curStack->creature->namePl;
+				std::string problem = "We cannot move this stack to its destination " + curStack->type->namePl;
 				tlog3 << problem << std::endl;
 				complain(problem);
 				ok = false;
@@ -3068,11 +3072,11 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 
 			if( !(
 				(BattleInfo::mutualPosition(curpos, enemypos) >= 0)						//front <=> front
-				|| (curStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE)									//back <=> front
+				|| (curStack->doubleWide()									//back <=> front
 					&& BattleInfo::mutualPosition(curpos + (curStack->attackerOwned ? -1 : 1), enemypos) >= 0)
-				|| (stackAtEnd->hasFeatureOfType(StackFeature::DOUBLE_WIDE)									//front <=> back
+				|| (stackAtEnd->doubleWide()									//front <=> back
 					&& BattleInfo::mutualPosition(curpos, enemypos + (stackAtEnd->attackerOwned ? -1 : 1)) >= 0)
-				|| (stackAtEnd->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && curStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE)//back <=> back
+				|| (stackAtEnd->doubleWide() && curStack->doubleWide()//back <=> back
 					&& BattleInfo::mutualPosition(curpos + (curStack->attackerOwned ? -1 : 1), enemypos + (stackAtEnd->attackerOwned ? -1 : 1)) >= 0)
 				)
 				)
@@ -3089,11 +3093,11 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 			handleAfterAttackCasting(bat);
 
 			//counterattack
-			if(!curStack->hasFeatureOfType(StackFeature::BLOCKS_RETALIATION)
+			if(!curStack->hasBonusOfType(Bonus::BLOCKS_RETALIATION)
 				&& stackAtEnd->alive()
-				&& ( stackAtEnd->counterAttacks > 0 || stackAtEnd->hasFeatureOfType(StackFeature::UNLIMITED_RETALIATIONS) )
-				&& !stackAtEnd->hasFeatureOfType(StackFeature::SIEGE_WEAPON)
-				&& !stackAtEnd->hasFeatureOfType(StackFeature::HYPNOTIZED))
+				&& ( stackAtEnd->counterAttacks > 0 || stackAtEnd->hasBonusOfType(Bonus::UNLIMITED_RETALIATIONS) )
+				&& !stackAtEnd->hasBonusOfType(Bonus::SIEGE_WEAPON)
+				&& !stackAtEnd->hasBonusOfType(Bonus::HYPNOTIZED))
 			{
 				prepareAttack(bat, stackAtEnd, curStack, 0);
 				bat.flags |= 2;
@@ -3102,8 +3106,8 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 			}
 
 			//second attack
-			if(curStack->valOfFeatures(StackFeature::ADDITIONAL_ATTACK) > 0
-				&& !curStack->hasFeatureOfType(StackFeature::SHOOTER)
+			if(curStack->valOfBonuses(Bonus::ADDITIONAL_ATTACK) > 0
+				&& !curStack->hasBonusOfType(Bonus::SHOOTER)
 				&& curStack->alive()
 				&& stackAtEnd->alive()  )
 			{
@@ -3114,7 +3118,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 			}
 
 			//return
-			if(curStack->hasFeatureOfType(StackFeature::RETURN_AFTER_STRIKE) && startingPos != curStack->position && curStack->alive())
+			if(curStack->hasBonusOfType(Bonus::RETURN_AFTER_STRIKE) && startingPos != curStack->position && curStack->alive())
 			{
 				moveStack(ba.stackNumber, startingPos);
 				//NOTE: curStack->ID == ba.stackNumber (rev 1431)
@@ -3136,7 +3140,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 			bat.flags |= 1;
 			sendAndApply(&bat);
 
-			if(curStack->valOfFeatures(StackFeature::ADDITIONAL_ATTACK) > 0 //if unit shots twice let's make another shot
+			if(curStack->valOfBonuses(Bonus::ADDITIONAL_ATTACK) > 0 //if unit shots twice let's make another shot
 				&& curStack->alive()
 				&& destStack->alive()
 				&& curStack->shots
@@ -3260,7 +3264,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 			CStack *healer = gs->curB->getStack(ba.stackNumber),
 				*destStack = gs->curB->getStackT(ba.destinationTile);
 
-			if(healer == NULL || destStack == NULL || !healer->hasFeatureOfType(StackFeature::HEALER))
+			if(healer == NULL || destStack == NULL || !healer->hasBonusOfType(Bonus::HEALER))
 			{
 				complain("There is either no healer, no destination, or healer cannot heal :P");
 			}
@@ -3337,24 +3341,30 @@ void CGameHandler::playerMessage( ui8 player, const std::string &message )
 	}
 	else if(message == "vcmiainur") //gives 5 archangels into each slot
 	{
-		SetGarrisons sg;
 		CGHeroInstance *hero = gs->getHero(gs->getPlayer(player)->currentSelection);
 		if(!hero) return;
-		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] = CStackInstance(13,5);
+
+		SetGarrisons sg;
+		CCreatureSet &newArmy = sg.garrs[hero->id];
+
+		newArmy = hero->getArmy();
+		for(int i=0; i<ARMY_SIZE; i++)
+			if(newArmy.slotEmpty(i))
+				newArmy.addToSlot(i, CStackInstance(13,5));
 		sendAndApply(&sg);
 	}
 	else if(message == "vcmiangband") //gives 10 black knight into each slot
 	{
-		SetGarrisons sg;
 		CGHeroInstance *hero = gs->getHero(gs->getPlayer(player)->currentSelection);
 		if(!hero) return;
-		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] = CStackInstance(66,10);
+
+		SetGarrisons sg;
+		CCreatureSet &newArmy = sg.garrs[hero->id];
+
+		newArmy = hero->getArmy();
+		for(int i=0; i<ARMY_SIZE; i++)
+			if(newArmy.slotEmpty(i))
+				newArmy.addToSlot(i, CStackInstance(66,10));
 		sendAndApply(&sg);
 	}
 	else if(message == "vcminoldor") //all war machines
@@ -3447,9 +3457,9 @@ static std::vector<ui32> calculateResistedStacks(const CSpell * sp, const CGHero
 	std::vector<ui32> ret;
 	for(std::set<CStack*>::const_iterator it = affectedCreatures.begin(); it != affectedCreatures.end(); ++it)
 	{
-		if ((*it)->hasFeatureOfType(StackFeature::SPELL_IMMUNITY, sp->id) //100% sure spell immunity
-			|| ( (*it)->hasFeatureOfType(StackFeature::LEVEL_SPELL_IMMUNITY) &&
-				(*it)->valOfFeatures(StackFeature::LEVEL_SPELL_IMMUNITY) >= sp->level) ) //some creature abilities have level 0
+		if ((*it)->hasBonusOfType(Bonus::SPELL_IMMUNITY, sp->id) //100% sure spell immunity
+			|| ( (*it)->hasBonusOfType(Bonus::LEVEL_SPELL_IMMUNITY) &&
+				(*it)->valOfBonuses(Bonus::LEVEL_SPELL_IMMUNITY) >= sp->level) ) //some creature abilities have level 0
 		{
 			ret.push_back((*it)->ID);
 			continue;
@@ -3465,11 +3475,11 @@ static std::vector<ui32> calculateResistedStacks(const CSpell * sp, const CGHero
 		else
 			bonusHero = hero2;
 
-		int prob = (*it)->valOfFeatures(StackFeature::MAGIC_RESISTANCE); //probability of resistance in %
+		int prob = (*it)->valOfBonuses(Bonus::MAGIC_RESISTANCE); //probability of resistance in %
 		if(bonusHero)
 		{
 			//bonusHero's resistance support (secondary skils and artifacts)
-			prob += bonusHero->valOfBonuses(HeroBonus::MAGIC_RESISTANCE);
+			prob += bonusHero->valOfBonuses(Bonus::MAGIC_RESISTANCE);
 
 			switch(bonusHero->getSecSkillLevel(26)) //resistance
 			{
@@ -3496,8 +3506,8 @@ static std::vector<ui32> calculateResistedStacks(const CSpell * sp, const CGHero
 	{
 		for(std::set<CStack*>::const_iterator it = affectedCreatures.begin(); it != affectedCreatures.end(); ++it)
 		{
-			if( (*it)->hasFeatureOfType(StackFeature::SPELL_IMMUNITY, sp->id) //100% sure spell immunity
-				|| ( (*it)->amount - 1 ) * (*it)->MaxHealth() + (*it)->firstHPleft 
+			if( (*it)->hasBonusOfType(Bonus::SPELL_IMMUNITY, sp->id) //100% sure spell immunity
+				|| ( (*it)->count - 1 ) * (*it)->MaxHealth() + (*it)->firstHPleft 
 				> 
 				caster->getPrimSkillLevel(2) * 25 + sp->powers[caster->getSpellSchoolLevel(sp)]
 			)
@@ -3630,7 +3640,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, int destinatio
 			for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
 			{
 				if(vstd::contains(sc.resisted, (*it)->ID) //this creature resisted the spell
-					|| (spellID == 39 && !(*it)->hasFeatureOfType(StackFeature::UNDEAD)) //we try to cast animate dead on living stack
+					|| (spellID == 39 && !(*it)->hasBonusOfType(Bonus::UNDEAD)) //we try to cast animate dead on living stack
 					) 
 					continue;
 				StacksHealedOrResurrected::HealInfo hi;
@@ -3690,7 +3700,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
 				|| (h->mana < gs->curB->getSpellCost(s, h)) //not enough mana
 				|| (ba.additionalInfo < 10) //it's adventure spell (not combat)
 				|| (gs->curB->castSpells[ba.side]) //spell has been cast
-				|| (NBonus::hasOfType(secondHero, HeroBonus::SPELL_IMMUNITY, s->id)) //non - casting hero provides immunity for this spell 
+				|| (NBonus::hasOfType(secondHero, Bonus::SPELL_IMMUNITY, s->id)) //non - casting hero provides immunity for this spell 
 				|| (gs->battleMaxSpellLevel() < s->level) //non - casting hero stops caster from casting this spell
 				)
 			{
@@ -4159,12 +4169,12 @@ bool CGameHandler::dig( const CGHeroInstance *h )
 void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
 {
 	const CStack * attacker = gs->curB->getStack(bat.stackAttacking);
-	if( attacker->hasFeatureOfType(StackFeature::SPELL_AFTER_ATTACK) )
+	if( attacker->hasBonusOfType(Bonus::SPELL_AFTER_ATTACK) )
 	{
-		for (int it=0; it<attacker->features.size(); ++it)
+
+		BOOST_FOREACH(const Bonus & sf, attacker->bonuses)
 		{
-			const StackFeature & sf = attacker->features[it];
-			if (sf.type == StackFeature::SPELL_AFTER_ATTACK)
+			if (sf.type == Bonus::SPELL_AFTER_ATTACK)
 			{
 				const CStack * oneOfAttacked = NULL;
 				for(int g=0; g<bat.bsa.size(); ++g)
@@ -4179,7 +4189,7 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
 					return;
 
 				int spellID = sf.subtype;
-				int spellLevel = sf.value;
+				int spellLevel = sf.val;
 				int chance = sf.additionalInfo % 1000;
 				int meleeRanged = sf.additionalInfo / 1000;
 				int destination = oneOfAttacked->position;
@@ -4188,7 +4198,7 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
 					continue;
 
 				//casting
-				handleSpellCasting(spellID, spellLevel, destination, !attacker->attackerOwned, attacker->owner, NULL, NULL, attacker->amount);
+				handleSpellCasting(spellID, spellLevel, destination, !attacker->attackerOwned, attacker->owner, NULL, NULL, attacker->count);
 			}
 		}
 	}
@@ -4304,7 +4314,7 @@ bool CGameHandler::castSpell(const CGHeroInstance *h, int spellID, const int3 &p
 				COMPLAIN_RET("Destination tile doesn't exist!");
 			if(!h->movement)
 				COMPLAIN_RET("Hero needs movement points to cast Dimension Door!");
-			if(h->getBonusesCount(HeroBonus::CASTED_SPELL, Spells::DIMENSION_DOOR) >= s->powers[schoolLevel]) //limit casts per turn
+			if(h->getBonusesCount(Bonus::CASTED_SPELL, Spells::DIMENSION_DOOR) >= s->powers[schoolLevel]) //limit casts per turn
 			{
 				InfoWindow iw;
 				iw.player = h->tempOwner;
@@ -4316,7 +4326,7 @@ bool CGameHandler::castSpell(const CGHeroInstance *h, int spellID, const int3 &p
 
 			GiveBonus gb;
 			gb.id = h->id;
-			gb.bonus = HeroBonus(HeroBonus::ONE_DAY, HeroBonus::NONE, HeroBonus::CASTED_SPELL, 0, Spells::DIMENSION_DOOR);
+			gb.bonus = Bonus(Bonus::ONE_DAY, Bonus::NONE, Bonus::CASTED_SPELL, 0, Spells::DIMENSION_DOOR);
 			sendAndApply(&gb);
 			
 			if(!dest->isClear(curr)) //wrong dest tile

+ 1 - 1
server/CGameHandler.h

@@ -101,7 +101,7 @@ public:
 	void prepareAttack(BattleAttack &bat, const CStack *att, const CStack *def, int distance); //distance - number of hexes travelled before attacking
 	void prepareAttacked(BattleStackAttacked &bsa, const CStack *def);
 	void checkForBattleEnd( std::vector<CStack*> &stacks );
-	void setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet &army1, const CCreatureSet &army2, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool creatureBank, const CGTownInstance *town);
+	void setupBattle( BattleInfo * curB, int3 tile, const CArmedInstance *army1, const CArmedInstance *army2, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool creatureBank, const CGTownInstance *town);
 
 	CGameHandler(void);
 	~CGameHandler(void);

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