浏览代码

Stacks #2 -> towards new system of stack operations.

Michał W. Urbańczyk 15 年之前
父节点
当前提交
7c3f3d20c3

+ 1 - 1
AI/GeniusAI/CGeniusAI.cpp

@@ -317,7 +317,7 @@ float CGeniusAI::TownObjective::getValue() const
 	  case upgradeCreatures:
 		  UpgradeInfo ui = AI->m_cb->getUpgradeInfo(whichTown->t,which);
 		  ID = whichTown->creaturesInGarrison.getCreature(which)->idNumber;
-		  howMany = whichTown->creaturesInGarrison.getAmount(which);
+		  howMany = whichTown->creaturesInGarrison.getStackCount(which);
 
 		  newID = ui.newID.back();
 		  int upgrade_serial = ui.newID.size() - 1;

+ 4 - 1
client/Client.h

@@ -113,7 +113,10 @@ public:
 	void giveCreatures (int objid, const CGHeroInstance * h, CCreatureSet creatures, bool remove) {};
 	void takeCreatures (int objid, TSlots creatures){};
 	void takeCreatures (int objid, std::vector<CStackBasicDescriptor> creatures){};
-	void changeCreatureType (int objid, TSlot slot, TCreature creature){};
+	void changeStackType(const StackLocation &sl, CCreature *c){};
+	void changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue = false){};
+	void insertNewStack(const StackLocation &sl, CCreature *c, TQuantity count){};
+	void eraseStack(const StackLocation &sl){};
 	void showCompInfo(ShowInInfobox * comp){};
 	void heroVisitCastle(int obj, int heroID){};
 	void stopHeroVisitCastle(int obj, int heroID){};

+ 10 - 10
client/GUIClasses.cpp

@@ -2919,7 +2919,7 @@ void CTradeWindow::initSubs(bool Left)
 			switch(itemsType[1])
 			{
 			case CREATURE:
-				t->subtitle = boost::lexical_cast<std::string>(hero->getAmount(t->serial));
+				t->subtitle = boost::lexical_cast<std::string>(hero->getStackCount(t->serial));
 				break;
 			case RESOURCE:
 				t->subtitle = boost::lexical_cast<std::string>(LOCPLINT->cb->getResourceAmount(t->serial));
@@ -2992,7 +2992,7 @@ void CTradeWindow::removeItem(CTradeableItem * t)
 void CTradeWindow::getEmptySlots(std::set<CTradeableItem *> &toRemove)
 {
 	BOOST_FOREACH(CTradeableItem *t, items[1])
-		if(!hero->getAmount(t->serial))
+		if(!hero->getStackCount(t->serial))
 			toRemove.insert(t);
 }
 
@@ -3225,7 +3225,7 @@ void CMarketplaceWindow::selectionChanged(bool side)
 		if(itemsType[1] == RESOURCE)
 			newAmount = LOCPLINT->cb->getResourceAmount(hLeft->id);
 		else if(itemsType[1] ==  CREATURE)
-			newAmount = hero->getAmount(hLeft->serial) - (hero->Slots().size() == 1  &&  hero->needsLastStack());
+			newAmount = hero->getStackCount(hLeft->serial) - (hero->Slots().size() == 1  &&  hero->needsLastStack());
 		else
 			assert(0);
 
@@ -3598,7 +3598,7 @@ void CAltarWindow::SacrificeAll()
 	{
 		bool movedAnything = false;
 		BOOST_FOREACH(CTradeableItem *t, items[1])
-			sacrificedUnits[t->serial] = hero->getAmount(t->serial);
+			sacrificedUnits[t->serial] = hero->getStackCount(t->serial);
 
 		sacrificedUnits[items[1].front()->serial]--;
 
@@ -3637,10 +3637,10 @@ void CAltarWindow::selectionChanged(bool side)
 
 	int stackCount = 0;
 	for (int i = 0; i < ARMY_SIZE; i++)
-		if(hero->getAmount(i) > sacrificedUnits[i])
+		if(hero->getStackCount(i) > sacrificedUnits[i])
 			stackCount++;
 
-	slider->setAmount(hero->getAmount(hLeft->serial) - (stackCount == 1));
+	slider->setAmount(hero->getStackCount(hLeft->serial) - (stackCount == 1));
 	slider->block(!slider->amount);
 	slider->value = sacrificedUnits[hLeft->serial];
 	max->block(!slider->amount);
@@ -5783,7 +5783,7 @@ CTransformerWindow::CTransformerWindow(const CGHeroInstance * _hero, const CGTow
 	
 	for (int i=0; i<7; i++ )
 		if ( army->getCreature(i) )
-			items.push_back(new CItem(this, army->getAmount(i), i));
+			items.push_back(new CItem(this, army->getStackCount(i), i));
 			
 	all    = new AdventureMapButton(CGI->generaltexth->zelp[590],boost::bind(&CTransformerWindow::addAll,this),     146,416,"ALTARMY.DEF",SDLK_a);
 	convert= new AdventureMapButton(CGI->generaltexth->zelp[591],boost::bind(&CTransformerWindow::makeDeal,this),   269,416,"ALTSACR.DEF",SDLK_RETURN);
@@ -6024,7 +6024,7 @@ void CHillFortWindow::updateGarrisons()
 			if (info.newID.size())//we have upgrades here - update costs
 				for(std::set<std::pair<int,int> >::iterator it=info.cost[0].begin(); it!=info.cost[0].end(); it++)
 				{
-					std::pair<int, int> pair = std::make_pair(it->first, it->second * hero->getAmount(i) );
+					std::pair<int, int> pair = std::make_pair(it->first, it->second * hero->getStackCount(i) );
 					costs[i].insert(pair);
 					totalSumm[pair.first] += pair.second;
 				}
@@ -6137,7 +6137,7 @@ std::string CHillFortWindow::getTextForSlot(int slot)
 		return "";
 
 	std::string str = CGI->generaltexth->allTexts[318];
-	int amount = hero->getAmount(slot);
+	int amount = hero->getStackCount(slot);
 	if ( amount == 1 )
 		boost::algorithm::replace_first(str,"%s",hero->getCreature(slot)->nameSing);
 	else
@@ -6172,7 +6172,7 @@ int CHillFortWindow::getState(int slot)
 		return 1;
 
 	for(std::set<std::pair<int,int> >::iterator it=info.cost[0].begin(); it!=info.cost[0].end(); it++)
-		if(LOCPLINT->cb->getResourceAmount(it->first) < it->second * hero->getAmount(slot))
+		if(LOCPLINT->cb->getResourceAmount(it->first) < it->second * hero->getStackCount(slot))
 			return 0;
 	return 2;//can upgrade
 }

+ 24 - 42
hch/CObjectHandler.cpp

@@ -893,7 +893,7 @@ void CGHeroInstance::initArmy(CCreatureSet *dst /*= NULL*/)
 			}
 		}
 		else
-			dst->addStack(stackNo-warMachinesGiven, new CStackInstance(creID, count));
+			dst->putStack(stackNo-warMachinesGiven, new CStackInstance(creID, count));
 	}
 }
 void CGHeroInstance::initHeroDefInfo()
@@ -1587,7 +1587,7 @@ void CGDwelling::initObj()
 			creatures[0].second.push_back(crid);
 			hoverName = VLC->generaltexth->creGens[subID];
 			if(crs->level > 4)
-				addStack(0, new CStackInstance(crs, (crs->growth) * 3));
+				putStack(0, new CStackInstance(crs, (crs->growth) * 3));
 			if (getOwner() != 255)
 				cb->gameState()->players[getOwner()].dwellings.push_back (this);
 		}
@@ -1602,8 +1602,8 @@ void CGDwelling::initObj()
 			creatures[2].second.push_back(116); //Gold Golem
 			creatures[3].second.push_back(117); //Diamond Golem
 			//guards
-			addStack(0, new CStackInstance(116, 9));
-			addStack(1, new CStackInstance(117, 6));
+			putStack(0, new CStackInstance(116, 9));
+			putStack(1, new CStackInstance(117, 6));
 		}
 		else if(subID == 0) // Elemental Conflux 
 		{
@@ -1612,7 +1612,7 @@ void CGDwelling::initObj()
 			creatures[2].second.push_back(113); //Earth Elemental
 			creatures[3].second.push_back(115); //Water Elemental
 			//guards
-			addStack(0, new CStackInstance(113, 12));
+			putStack(0, new CStackInstance(113, 12));
 		}
 		else
 		{
@@ -2170,38 +2170,34 @@ void CGTownInstance::newTurn() const
 				if (nativeCrits.size())
 				{
 					TSlot pos = nativeCrits[rand() % nativeCrits.size()];
+					StackLocation sl(this, pos);
+
 					const CCreature *c = getCreature(pos);
 					if (rand()%100 < 90 || c->upgrades.empty()) //increase number if no upgrade avaliable
 					{
-						SetGarrisons sg;
-						sg.garrs[id] = getArmy();
-						sg.garrs[id].slots[pos]->count += c->growth;
-						cb->sendAndApply(&sg);	
+						cb->changeStackCount(sl, c->growth);
 					}
 					else //upgrade
 					{
-						SetGarrisons sg; //somewhat better upgrade pack would come in handy
-						sg.garrs[id] = getArmy();
-						//////////////////////////////////////////////////////////////////////////
-						//sg.garrs[id].t (pos, *c->upgrades.begin(), );
-						//sg.garrs[id].setCreature(pos, *c->upgrades.begin(), slt[pos].count);
-						cb->sendAndApply(&sg);	
+						cb->changeStackType(sl, VLC->creh->creatures[*c->upgrades.begin()]);
 					}
 				}
 				if ((stacksCount() < ARMY_SIZE && rand()%100 < 25) || Slots().empty()) //add new stack
 				{
-					int n, i = rand() % std::min (ARMY_SIZE, cb->getDate(3)<<1);	
+					int i = rand() % std::min (ARMY_SIZE, cb->getDate(3)<<1);
+					TCreature c = town->basicCreatures[i];
+					TSlot n = -1;
+					TQuantity count = creatureGrowth(i);
+
 					{//no lower tiers or above current month
 
-						if ((n = getSlotFor(town->basicCreatures[i], ARMY_SIZE))>=0)
+						if ((n = getSlotFor(c))>=0)
 						{ 
-							SetGarrisons sg;
-							sg.garrs[id] = getArmy();
+							StackLocation sl(this, n);
 							if (slotEmpty(n))
-								sg.garrs[id].setCreature (n, town->basicCreatures[i], creatureGrowth(i)); //if the stack is not yet present
-							else
-								sg.garrs[id].addToSlot(n, town->basicCreatures[i], creatureGrowth(i)); //add to existing
-							cb->sendAndApply(&sg);
+								cb->insertNewStack(sl, VLC->creh->creatures[c], count);
+							else //add to existing
+								cb->changeStackCount(sl, count);
 						}
 					}
 				}		
@@ -2896,7 +2892,7 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
 			BlockingDialog ynd(true,false);
 			ynd.player = h->tempOwner;
 			std::string tmp = VLC->generaltexth->advobtxt[90];
-			boost::algorithm::replace_first(tmp,"%d",boost::lexical_cast<std::string>(getAmount(0)));
+			boost::algorithm::replace_first(tmp,"%d",boost::lexical_cast<std::string>(getStackCount(0)));
 			boost::algorithm::replace_first(tmp,"%d",boost::lexical_cast<std::string>(action));
 			boost::algorithm::replace_first(tmp,"%s",VLC->creh->creatures[subID]->namePl);
 			ynd.text << tmp;
@@ -3040,7 +3036,7 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
 			if(h->getSecSkillLevel(4) + sympathy + 1 >= character)
 				return 0; //join for free
 			else if(h->getSecSkillLevel(4) * 2  +  sympathy  +  1 >= character)
-				return VLC->creh->creatures[subID]->cost[6] * getAmount(0); //join for gold
+				return VLC->creh->creatures[subID]->cost[6] * getStackCount(0); //join for gold
 		}
 	}
 
@@ -3112,7 +3108,7 @@ void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) co
 			//add creatures
 			SetGarrisons sg;
 			sg.garrs[h->id] = h->getArmy();
-			sg.garrs[h->id].addToSlot(slot, subID, getAmount(0));
+			sg.garrs[h->id].addToSlot(slot, subID, getStackCount(0));
 			cb->sendAndApply(&sg);
 			cb->removeObject(id);
 		}
@@ -3180,7 +3176,7 @@ void CGMine::initObj()
 		//set guardians
 		int howManyTroglodytes = 100 + ran()%100;
 		CStackInstance *troglodytes = new CStackInstance(70, howManyTroglodytes);
-		addStack(0, troglodytes);
+		putStack(0, troglodytes);
 
 		//after map reading tempOwner placeholds bitmask for allowed resources
 		std::vector<int> possibleResources;
@@ -4673,7 +4669,7 @@ void CGBonusingObject::onHeroVisit( const CGHeroInstance * h ) const
 			iw.components.push_back(Component(Component::CREATURE,11,0,1));
 			for (TSlots::const_iterator i = creatures.slots.begin(); i != creatures.slots.end(); ++i)
 			{
-				cb->changeCreatureType(h->id, i->first, 11);
+				cb->changeStackType(StackLocation(h, i->first), VLC->creh->creatures[11]);
 			}
 		}
 		else
@@ -6525,20 +6521,6 @@ void CGLighthouse::giveBonusTo( ui8 player ) const
 	gb.bonus.source = Bonus::OBJECT;
 	gb.bonus.id = id;
 	cb->sendAndApply(&gb);
-
-}
-
-void CArmedInstance::setArmy(const CCreatureSet &src)
-{
-	assert(0);
-// 	slots.clear();
-// 
-// 	for(TSlots::const_iterator i = src.Slots().begin(); i != src.Slots().end(); i++)
-// 	{
-// 		CStackInstance &inserted = slots[i->first];
-// 		inserted = i->second;
-// 		inserted.setArmyObj(this);
-// 	}
 }
 
 CCreatureSet& CArmedInstance::getArmy() const

+ 0 - 1
hch/CObjectHandler.h

@@ -222,7 +222,6 @@ class DLL_EXPORT CArmedInstance: public CGObjectInstance, public CBonusSystemNod
 public:
 	BattleInfo *battle; //set to the current battle, if engaged
 
-	void setArmy(const CCreatureSet &src);
 	CCreatureSet& getArmy() const;
 	void randomizeArmy(int type);
 

+ 137 - 37
lib/CCreatureSet.cpp

@@ -17,7 +17,7 @@ const CStackInstance &CCreatureSet::operator[](TSlot slot) const
 		throw std::string("That slot is empty!");
 }
 
-const CCreature* CCreatureSet::getCreature(TSlot slot) const /*workaround of map issue */
+const CCreature* CCreatureSet::getCreature(TSlot slot) const
 {
 	TSlots::const_iterator i = slots.find(slot);
 	if (i != slots.end())
@@ -37,16 +37,14 @@ bool CCreatureSet::setCreature(TSlot slot, TCreature type, TQuantity quantity) /
 	{
 		tlog2 << "Using set creature to delete stack?\n";
 		eraseStack(slot);
+		return true;
 	}
 
 	if(vstd::contains(slots, slot)) //remove old creature
-	{
 		eraseStack(slot);
-	}
 
-	CStackInstance *stack = new CStackInstance(type, quantity);
-	stack->armyObj = castToArmyObj(); //brutal force
-	slots[slot] = stack;
+	putStack(slot, new CStackInstance(type, quantity));
+	return true;
 }
 
 TSlot CCreatureSet::getSlotFor(TCreature creature, ui32 slotsAmount/*=7*/) const /*returns -1 if no slot available */
@@ -68,7 +66,7 @@ TSlot CCreatureSet::getSlotFor(TCreature creature, ui32 slotsAmount/*=7*/) const
 	return -1; //no slot available
 }
 
-int CCreatureSet::getAmount(TSlot slot) const
+int CCreatureSet::getStackCount(TSlot slot) const
 {
 	TSlots::const_iterator i = slots.find(slot);
 	if (i != slots.end())
@@ -130,24 +128,31 @@ void CCreatureSet::addToSlot(TSlot slot, TCreature cre, TQuantity count, bool al
 	{
 		setCreature(slot, cre, count);
 	}
-	else if(slots[slot]->type == c && allowMerging); //that slot was empty or contained same type creature
+	else if(getCreature(slot) == c && allowMerging) //that slot was empty or contained same type creature
 	{
-		setStackCount(slot, slots[slot]->count + count);
+		setStackCount(slot, getStackCount(slot) + count);
+	}
+	else
+	{
+		tlog1 << "Failed adding to slot!\n";
 	}
 }
 
 void CCreatureSet::addToSlot(TSlot slot, CStackInstance *stack, bool allowMerging/* = true*/)
 {
-	assert(stack->type == VLC->creh->creatures[stack->type->idNumber]);
+	assert(stack->valid(true));
 
 	if(!vstd::contains(slots, slot))
 	{
-		slots[slot] = stack;
-		stack->setArmyObj(castToArmyObj());
+		putStack(slot, stack);
+	}
+	else if(allowMerging && stack->type == getCreature(slot))
+	{
+		joinStack(slot, stack);
 	}
 	else
 	{
-		addToSlot(slot, stack->type->idNumber, stack->count, allowMerging);
+		tlog1 << "Cannot add to slot " << slot << " stack " << *stack << std::endl;
 	}
 }
 
@@ -155,14 +160,8 @@ bool CCreatureSet::validTypes(bool allowUnrandomized /*= false*/) const
 {
 	for(TSlots::const_iterator i=slots.begin(); i!=slots.end(); ++i)
 	{
-		bool isRand = (i->second->idRand != -1);
-		if(!isRand)
-		{
-			assert(i->second->type);
-			assert(i->second->type == VLC->creh->creatures[i->second->type->idNumber]);
-		}
-		else
-			assert(allowUnrandomized);
+		if(!i->second->valid(allowUnrandomized))
+			return false;
 	}
 	return true;
 }
@@ -187,11 +186,11 @@ int CCreatureSet::getArmyStrength() const
 
 ui64 CCreatureSet::getPower (TSlot slot) const
 {
-	return getCreature(slot)->AIValue * getAmount(slot);
+	return getCreature(slot)->AIValue * getStackCount(slot);
 }
 std::string CCreatureSet::getRoughAmount (TSlot slot) const
 {
-	return VLC->generaltexth->arraytxt[174 + 3*CCreature::getQuantityID(getAmount(slot))];
+	return VLC->generaltexth->arraytxt[174 + 3*CCreature::getQuantityID(getStackCount(slot))];
 }
 
 int CCreatureSet::stacksCount() const
@@ -199,11 +198,6 @@ int CCreatureSet::stacksCount() const
 	return slots.size();
 }
 
-void CCreatureSet::addStack(TSlot slot, CStackInstance *stack)
-{
-	addToSlot(slot, stack, false);
-}
-
 void CCreatureSet::setFormation(bool tight)
 {
 	formation = tight;
@@ -218,7 +212,10 @@ void CCreatureSet::setStackCount(TSlot slot, TQuantity count)
 
 void CCreatureSet::clear()
 {
-	slots.clear();
+	while(!slots.empty())
+	{
+		eraseStack(slots.begin()->first);
+	}
 }
 
 const CStackInstance& CCreatureSet::getStack(TSlot slot) const
@@ -230,6 +227,7 @@ const CStackInstance& CCreatureSet::getStack(TSlot slot) const
 void CCreatureSet::eraseStack(TSlot slot)
 {
 	assert(vstd::contains(slots, slot));
+	delNull(slots[slot]);
 	slots.erase(slot);
 }
 
@@ -262,20 +260,89 @@ CArmedInstance * CCreatureSet::castToArmyObj()
 	return dynamic_cast<CArmedInstance *>(this);
 }
 
+void CCreatureSet::putStack(TSlot slot, CStackInstance *stack)
+{
+	assert(!vstd::contains(slots, slot));
+	slots[slot] = stack;
+	stack->setArmyObj(castToArmyObj());
+}
+
+void CCreatureSet::joinStack(TSlot slot, CStackInstance * stack)
+{
+	const CCreature *c = getCreature(slot);
+	assert(c == stack->type);
+	assert(c);
+
+	//TODO move stuff 
+	changeStackCount(slot, stack->count);
+	delNull(stack);
+}
+
+void CCreatureSet::changeStackCount(TSlot slot, TQuantity toAdd)
+{
+	setStackCount(slot, getStackCount(slot) + toAdd);
+}
+
+CCreatureSet::CCreatureSet()
+{
+	formation = false;
+}
+
+CCreatureSet::~CCreatureSet()
+{
+	clear();
+}
+
+void CCreatureSet::setToArmy(CCreatureSet &src)
+{
+	clear();
+	while(src)
+	{
+		TSlots::iterator i = src.slots.begin();
+
+		assert(i->second->type);
+		assert(i->second->valid(false));
+		assert(i->second->armyObj == NULL);
+
+		putStack(i->first, i->second);
+		src.slots.erase(i);
+	}
+}
+
+CStackInstance * CCreatureSet::detachStack(TSlot slot)
+{
+	assert(vstd::contains(slots, slot));
+	CStackInstance *ret = slots[slot];
+
+	if(CArmedInstance *armedObj = castToArmyObj())
+		ret->detachFrom(armedObj);
+
+	return ret;
+}
+
+void CCreatureSet::setStackType(TSlot slot, const CCreature *type)
+{
+	assert(vstd::contains(slots, slot));
+	CStackInstance *s = slots[slot];
+	s->setType(type->idNumber);
+}
+
 CStackInstance::CStackInstance()
+	: armyObj(_armyObj)
 {
 	init();
 }
 
-CStackInstance::CStackInstance(TCreature id, TQuantity Count, const CArmedInstance *ArmyObj)
+CStackInstance::CStackInstance(TCreature id, TQuantity Count)
+	: armyObj(_armyObj)
 {
 	init();
 	setType(id);
-	setArmyObj(ArmyObj);
 	count = Count;
 }
 
 CStackInstance::CStackInstance(const CCreature *cre, TQuantity Count)
+	: armyObj(_armyObj)
 {
 	init();
 	type = cre;
@@ -288,7 +355,7 @@ void CStackInstance::init()
 	count = 0;
 	type = NULL;
 	idRand = -1;
-	armyObj = NULL;
+	_armyObj = NULL;
 	nodeType = STACK;
 }
 
@@ -298,23 +365,31 @@ int CStackInstance::getQuantityID() const
 }
 
 void CStackInstance::setType(int creID)
+{
+	setType(VLC->creh->creatures[creID]);
+}
+
+void CStackInstance::setType(const CCreature *c)
 {
 	if(type)
 		detachFrom(const_cast<CCreature*>(type));
 
-	type = VLC->creh->creatures[creID];
+	type = c;
 
 	attachTo(const_cast<CCreature*>(type));
 }
 
 void CStackInstance::setArmyObj(const CArmedInstance *ArmyObj)
 {
-	if(armyObj)
-		detachFrom(const_cast<CArmedInstance*>(armyObj));
+	if(_armyObj)
+		detachFrom(const_cast<CArmedInstance*>(_armyObj));
 
-	armyObj = ArmyObj;
+	if(ArmyObj)
+	{
+		_armyObj = ArmyObj;
 
-	attachTo(const_cast<CArmedInstance*>(armyObj));
+		attachTo(const_cast<CArmedInstance*>(_armyObj));
+	}
 }
 // void CStackInstance::getParents(TCNodes &out, const CBonusSystemNode *source /*= NULL*/) const
 // {
@@ -334,6 +409,17 @@ std::string CStackInstance::getQuantityTXT(bool capitalized /*= true*/) const
 	return VLC->generaltexth->arraytxt[174 + getQuantityID()*3 + 2 - capitalized];
 }
 
+bool CStackInstance::valid(bool allowUnrandomized) const
+{
+	bool isRand = (idRand != -1);
+	if(!isRand)
+	{
+		return (type  &&  type == VLC->creh->creatures[type->idNumber]);
+	}
+	else
+		return allowUnrandomized;
+}
+
 CStackBasicDescriptor::CStackBasicDescriptor()
 {
 	type = NULL;
@@ -343,4 +429,18 @@ CStackBasicDescriptor::CStackBasicDescriptor()
 CStackBasicDescriptor::CStackBasicDescriptor(TCreature id, TQuantity Count)
 	: type (VLC->creh->creatures[id]), count(Count)
 {
+}
+
+DLL_EXPORT std::ostream & operator<<(std::ostream & str, const CStackInstance & sth)
+{
+	if(!sth.valid(true))
+		str << "an invalid stack!";
+
+	str << "stack with " << sth.count << " of ";
+	if(sth.type)
+		str << sth.type->namePl;
+	else
+		str << sth.idRand;
+
+	return str;
 }

+ 24 - 8
lib/CCreatureSet.h

@@ -33,10 +33,11 @@ public:
 
 class DLL_EXPORT CStackInstance : public CBonusSystemNode, public CStackBasicDescriptor
 {
+	const CArmedInstance *_armyObj; //stack must be part of some army, army must be part of some object
 public:
 	int idRand; //hlp variable used during loading game -> "id" placeholder for randomization
 
-	const CArmedInstance *armyObj; //stack must be part of some army, army must be part of some object
+	const CArmedInstance * const & armyObj; //stack must be part of some army, army must be part of some object
 	ui32 experience; //TODO: handle
 	//TODO: stack artifacts
 
@@ -44,7 +45,7 @@ public:
 	{
 		h & static_cast<CBonusSystemNode&>(*this);
 		h & static_cast<CStackBasicDescriptor&>(*this);
-		h & armyObj & experience;
+		h & _armyObj & experience;
 	}
 
 	//overrides CBonusSystemNode
@@ -54,12 +55,15 @@ public:
 	std::string getQuantityTXT(bool capitalized = true) const;
 	void init();
 	CStackInstance();
-	CStackInstance(TCreature id, TQuantity count, const CArmedInstance *ArmyObj = NULL);
+	CStackInstance(TCreature id, TQuantity count);
 	CStackInstance(const CCreature *cre, TQuantity count);
 	void setType(int creID);
+	void setType(const CCreature *c);
 	void setArmyObj(const CArmedInstance *ArmyObj);
+	bool valid(bool allowUnrandomized) const;
 };
 
+DLL_EXPORT std::ostream & operator<<(std::ostream & str, const CStackInstance & sth);
 
 typedef std::map<TSlot, CStackInstance*> TSlots;
 
@@ -71,22 +75,35 @@ public:
 	TSlots slots; //slots[slot_id]->> pair(creature_id,creature_quantity)
 	ui8 formation; //false - wide, true - tight
 
+	CCreatureSet();
+	virtual ~CCreatureSet();
+
 	const CStackInstance &operator[](TSlot slot) const; 
 
 	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, CStackInstance *stack, bool allowMerging = true); //Adds stack to slot. Slot must be empty or with same type creature
-	void addStack(TSlot slot, 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);
+	CArmedInstance *castToArmyObj();
+
+	//basic operations
+	void eraseStack(TSlot slot); //slot must be occupied
+	void putStack(TSlot slot, CStackInstance *stack); //adds new stack to the army, slot must be empty
+	void joinStack(TSlot slot, CStackInstance * stack); //adds new stack to the existing stack of the same type
 	void setStackCount(TSlot slot, TQuantity count); //stack must exist!
-	void eraseStack(TSlot slot);
+	CStackInstance *detachStack(TSlot slot); //removes stack from army but doesn't destroy it (so it can be moved somewhere else)
+	void setStackType(TSlot slot, const CCreature *type);
+
+	//derivative 
+	void changeStackCount(TSlot slot, TQuantity toAdd); //stack must exist!
+	bool setCreature (TSlot slot, TCreature type, TQuantity quantity); //replaces creature in stack; slots 0 to 6, if quantity=0 erases stack
+	void setToArmy(CCreatureSet &src); //erases all our army and moves stacks from src to us; src MUST NOT be an armed object! WARNING: use it wisely. Or better do not use at all.
 	
 	const CStackInstance& getStack(TSlot slot) const; 
 	const CCreature* getCreature(TSlot slot) const; //workaround of map issue;
-	int getAmount (TSlot slot) const;
+	int getStackCount (TSlot slot) const;
 	TSlot findStack(const CStackInstance *stack) const; //-1 if none
 	TSlot getSlotFor(TCreature creature, ui32 slotsAmount=ARMY_SIZE) const; //returns -1 if no slot available
 	bool mergableStacks(std::pair<TSlot, TSlot> &out, TSlot preferable = -1) const; //looks for two same stacks, returns slot positions;
@@ -100,7 +117,6 @@ public:
 	
 	bool contains(const CStackInstance *stack) const;
 
-	CArmedInstance *castToArmyObj();
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{

+ 6 - 0
lib/CGameState.cpp

@@ -1019,6 +1019,12 @@ std::vector<si32> CStack::activeSpells() const
 	return ret;
 }
 
+CStack::~CStack()
+{
+	if(vstd::contains(state, SUMMONED))
+		delNull(base);
+}
+
 CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, int player, const CTown *town, std::map<ui32,CGHeroInstance *> &available, const CHeroClass *bannedClass /*= NULL*/) const
 {
 	CGHeroInstance *ret = NULL;

+ 1 - 0
lib/CGameState.h

@@ -272,6 +272,7 @@ public:
 	CStack(const CStackInstance *base, int O, int I, bool AO, int S); //c-tor
 	CStack(const CStackBasicDescriptor *stack, int O, int I, bool AO, int S = 255); //c-tor
 	CStack(); //c-tor
+	~CStack();
 
 	void init(); //set initial (invalid) values
 	void postInit(); //used to finish initialization when inheriting creature parameters is working

+ 7 - 1
lib/HeroBonus.cpp

@@ -109,7 +109,7 @@ void DLL_EXPORT BonusList::removeSpells(Bonus::BonusSource sourceType)
 
 void BonusList::limit(const CBonusSystemNode &node)
 {
-	remove_if(boost::bind(&CBonusSystemNode::isLimitedOnUs, node, _1));
+	remove_if(boost::bind(&CBonusSystemNode::isLimitedOnUs, boost::ref(node), _1));
 }
 
 int CBonusSystemNode::valOfBonuses(Bonus::BonusType type, const CSelector &selector) const
@@ -300,7 +300,13 @@ CBonusSystemNode::CBonusSystemNode()
 
 CBonusSystemNode::~CBonusSystemNode()
 {
+	while(parents.size())
+		detachFrom(parents.front());
 
+	if(children.size())
+	{
+		tlog2 << "Warning: an orphaned child!\n";
+	}
 }
 
 void CBonusSystemNode::attachTo(CBonusSystemNode *parent)

+ 5 - 1
lib/IGameCallback.h

@@ -37,6 +37,7 @@ class CArmedInstance;
 struct TerrainTile;
 struct PlayerState;
 class CTown;
+struct StackLocation;
 
 class DLL_EXPORT IGameCallback
 {
@@ -89,7 +90,10 @@ public:
 	virtual void giveCreatures (int objid, const CGHeroInstance * h, CCreatureSet creatures, bool remove) =0;
 	virtual void takeCreatures (int objid, TSlots creatures) =0;
 	virtual void takeCreatures (int objid, std::vector<CStackBasicDescriptor> creatures) =0;
-	virtual void changeCreatureType (int objid, TSlot slot, TCreature creature) =0;
+	virtual void changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue = false) =0;
+	virtual void changeStackType(const StackLocation &sl, CCreature *c) =0;
+	virtual void insertNewStack(const StackLocation &sl, CCreature *c, TQuantity count) =0;
+	virtual void eraseStack(const StackLocation &sl) =0;
 	virtual void showCompInfo(ShowInInfobox * comp)=0;
 	virtual void heroVisitCastle(int obj, int heroID)=0;
 	virtual void stopHeroVisitCastle(int obj, int heroID)=0;

+ 102 - 1
lib/NetPacks.h

@@ -527,7 +527,8 @@ struct TryMoveHero : public CPackForClient //501
 	{
 		h & id & result & start & end & movePoints & fowRevealed & attackedFrom;
 	}
-}; 
+};
+
 struct SetGarrisons : public CPackForClient //502
 {
 	SetGarrisons(){type = 502;};
@@ -713,6 +714,106 @@ struct NewArtifact : public CPackForClient
 	}
 };
 
+struct StackLocation
+{
+	CArmedInstance *army;
+	TSlot slot;
+
+	StackLocation()
+	{
+		army = NULL;
+		slot = -1;
+	}
+	StackLocation(const CArmedInstance *Army, TSlot Slot)
+	{
+		army = const_cast<CArmedInstance*>(Army); //we are allowed here to const cast -> change will go through one of our packages... do not abuse!
+		slot = Slot;
+	}
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & army & slot;
+	}
+};
+
+struct ChangeStackCount : CPackForClient  //521
+{
+	StackLocation sl;
+	TQuantity count;
+	ui8 absoluteValue; //if not -> count will be added (or subtracted if negative)
+
+	DLL_EXPORT void applyGs(CGameState *gs);
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & sl & count;
+	}
+};
+
+struct SetStackType : CPackForClient  //522
+{
+	StackLocation sl;
+	CCreature *type;
+
+	DLL_EXPORT void applyGs(CGameState *gs);
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & sl & type;
+	}
+};
+
+struct EraseStack : CPackForClient  //523
+{
+	StackLocation sl;
+
+	DLL_EXPORT void applyGs(CGameState *gs);
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & sl;
+	}
+};
+
+struct SwapStacks : CPackForClient  //524
+{
+	StackLocation sl1, sl2;
+
+	DLL_EXPORT void applyGs(CGameState *gs);
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & sl1 & sl2;
+	}
+};
+
+struct InsertNewStack : CPackForClient  //525
+{
+	StackLocation sl;
+	CStackInstance *stack;
+
+	DLL_EXPORT void applyGs(CGameState *gs);
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & sl & stack;
+	}
+};
+
+//moves creatures from src stack to dst slot, may be used for merging/splittint/moving stacks
+struct RebalanceStacks : CPackForClient  //525
+{
+	StackLocation src, dst;
+	TQuantity count;
+
+	DLL_EXPORT void applyGs(CGameState *gs);
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & src & dst & count;
+	}
+};
+
 struct NewTurn : public CPackForClient //101
 {
 	enum weekType {NORMAL, DOUBLE_GROWTH, BONUS_GROWTH, DEITYOFFIRE, PLAGUE, CUSTOM, NO_ACTION, NONE};

+ 39 - 6
lib/NetPacksLib.cpp

@@ -178,7 +178,7 @@ DLL_EXPORT void SetAvailableHeroes::applyGs( CGameState *gs )
 	{
 		CGHeroInstance *h = (hid[i]>=0 ?  gs->hpool.heroesPool[hid[i]] : NULL);
 		if(h && army[i])
-			h->setArmy(*army[i]);
+			h->setToArmy(*army[i]);
 		p->availableHeroes.push_back(h);
 	}
 }
@@ -380,15 +380,15 @@ 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->setArmy(i->second);
+		ai->setToArmy(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)->setArmy(i->second);
+			const_cast<CGHeroInstance*>((static_cast<CGTownInstance*>(ai))->garrisonHero)->setToArmy(i->second);
 		else if(ai->ID==HEROI_TYPE)
 		{
 			CGHeroInstance *h =  static_cast<CGHeroInstance*>(ai);
 			CGTownInstance *t = const_cast<CGTownInstance *>(h->visitedTown);
 			if(t && h->inTownGarrison)
-				t->setArmy(i->second);
+			t->setToArmy(i->second);
 		}
 	}
 }
@@ -605,6 +605,38 @@ DLL_EXPORT void NewArtifact::applyGs( CGameState *gs )
 	gs->map->artInstances.push_back(art);
 }
 
+DLL_EXPORT void ChangeStackCount::applyGs( CGameState *gs )
+{
+	if(absoluteValue)
+		sl.army->setStackCount(sl.slot, count);
+	else
+		sl.army->changeStackCount(sl.slot, count);
+}
+
+DLL_EXPORT void SetStackType::applyGs( CGameState *gs )
+{
+	sl.army->setStackType(sl.slot, type);
+}
+
+DLL_EXPORT void EraseStack::applyGs( CGameState *gs )
+{
+	sl.army->eraseStack(sl.slot);
+}
+
+DLL_EXPORT void SwapStacks::applyGs( CGameState *gs )
+{
+
+}
+
+DLL_EXPORT void InsertNewStack::applyGs( CGameState *gs )
+{
+	sl.army->putStack(sl.slot, stack);
+}
+
+DLL_EXPORT void RebalanceStacks::applyGs( CGameState *gs )
+{
+}
+
 DLL_EXPORT void SetAvailableArtifacts::applyGs( CGameState *gs )
 {
 	if(id >= 0)
@@ -1007,10 +1039,11 @@ DLL_EXPORT void BattleSpellCast::applyGs( CGameState *gs )
 			}
 		}
 
-		CStack * summonedStack = gs->curB->generateNewStack(CStackInstance(creID, h->getPrimSkillLevel(2) * VLC->spellh->spells[id].powers[skill], h), gs->curB->stacks.size(), !side, 255, pos);
+		CStackInstance *csi = new CStackInstance(creID, h->getPrimSkillLevel(2) * VLC->spellh->spells[id].powers[skill]); //deleted by d-tor of summoned stack
+		csi->setArmyObj(h);
+		CStack * summonedStack = gs->curB->generateNewStack(*csi, gs->curB->stacks.size(), !side, 255, pos);
 		summonedStack->state.insert(SUMMONED);
 		//summonedStack->addNewBonus( makeFeature(HeroBonus::SUMMONED, HeroBonus::ONE_BATTLE, 0, 0, HeroBonus::BONUS_FROM_HERO) );
-
 		gs->curB->stacks.push_back(summonedStack);
 	}
 }

+ 6 - 0
lib/RegisterTypes.cpp

@@ -142,6 +142,12 @@ void registerTypes2(Serializer &s)
 	s.template registerType<OpenWindow>();
 	s.template registerType<NewObject>();
 	s.template registerType<NewArtifact>();
+	s.template registerType<ChangeStackCount>();
+	s.template registerType<SetStackType>();
+	s.template registerType<EraseStack>();
+	s.template registerType<SwapStacks>();
+	s.template registerType<InsertNewStack>();
+	s.template registerType<RebalanceStacks>();
 	s.template registerType<SetAvailableArtifacts>();
 
 	s.template registerType<SaveGame>();

+ 1 - 1
lib/map.cpp

@@ -106,7 +106,7 @@ void readCreatureSet(CCreatureSet *out, const unsigned char * bufor, int &i, int
 		else
 			hlp->setType(creID);
 
-		out->addStack(ir, hlp);
+		out->putStack(ir, hlp);
 	}
 	i+=number*bytesPerCre;
 	

+ 35 - 15
server/CGameHandler.cpp

@@ -322,7 +322,7 @@ static CCreatureSet takeCasualties(int color, const CCreatureSet &set, BattleInf
 		if(vstd::contains(st->state, SUMMONED)) //don't take into account sumoned stacks
 			continue;
 
-		if(st->owner==color && !set.slotEmpty(st->slot) && st->count < set.getAmount(st->slot))
+		if(st->owner==color && !set.slotEmpty(st->slot) && st->count < set.getStackCount(st->slot))
 		{
 			if(st->alive())
 				ret.setStackCount(st->slot, st->count);
@@ -2694,8 +2694,8 @@ bool CGameHandler::arrangeStacks( si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2,
 			S1.slots[p1]->count -= val;
 		}
 
-		if ( (s1->tempOwner != player && S1.slots[p1]->count < s1->getArmy().getAmount(p1) )
-		  || (s2->tempOwner != player && S2.slots[p2]->count < s2->getArmy().getAmount(p2) ) )
+		if ( (s1->tempOwner != player && S1.slots[p1]->count < s1->getArmy().getStackCount(p1) )
+		  || (s2->tempOwner != player && S2.slots[p2]->count < s2->getArmy().getStackCount(p2) ) )
 		{
 			complain("Can't move troops of another player!");
 			return false;
@@ -3034,18 +3034,14 @@ bool CGameHandler::upgradeCreature( ui32 objid, ui8 pos, ui32 upgID )
 	return true;
 }
 
-void CGameHandler::changeCreatureType (int objid, TSlot slot, TCreature creature)
+void CGameHandler::changeStackType(const StackLocation &sl, CCreature *c)
 {
-	CArmedInstance *obj = static_cast<CArmedInstance*>(gs->map->objects[objid]);
-	if (obj)
-	{
-		SetGarrisons sg;
-		sg.garrs[objid] = obj->getArmy();
-		sg.garrs[objid].slots[slot]->setType(creature);
-		sendAndApply(&sg);
-	}
-	else
-		tlog2 <<"Illegal call of changeCreatureType for non-armed instance!\n";	
+	assert(sl.army->getCreature(sl.slot));
+
+	SetStackType sst;
+	sst.sl = sl;
+	sst.type = c;
+	sendAndApply(&sst);	
 }
 
 bool CGameHandler::garrisonSwap( si32 tid )
@@ -5247,7 +5243,7 @@ bool CGameHandler::tryAttackingGuard(const int3 &guardPos, const CGHeroInstance
 
 bool CGameHandler::sacrificeCreatures(const IMarket *market, const CGHeroInstance *hero, TSlot slot, ui32 count)
 {
-	int oldCount = hero->getAmount(slot);
+	int oldCount = hero->getStackCount(slot);
 	int newCount = oldCount - count;
 
 	if(oldCount < count)
@@ -5283,3 +5279,27 @@ bool CGameHandler::sacrificeArtifact(const IMarket * m, const CGHeroInstance * h
 	changePrimSkill(hero->id, 4, expToGive);
 	return true;
 }
+
+void CGameHandler::insertNewStack(const StackLocation &sl, CCreature *c, TQuantity count)
+{
+	InsertNewStack ins;
+	ins.sl = sl;
+	ins.stack = new CStackInstance(c, count);
+	sendAndApply(&ins);
+}
+
+void CGameHandler::eraseStack(const StackLocation &sl)
+{
+	EraseStack es;
+	es.sl = sl;
+	sendAndApply(&es);
+}
+
+void CGameHandler::changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue /*= false*/)
+{
+	ChangeStackCount csc;
+	csc.sl = sl;
+	csc.count = count;
+	csc.absoluteValue = absoluteValue;
+	sendAndApply(&csc);
+}

+ 4 - 1
server/CGameHandler.h

@@ -138,7 +138,10 @@ public:
 	void giveCreatures (int objid, const CGHeroInstance * h, CCreatureSet creatures, bool remove);
 	void takeCreatures (int objid, TSlots creatures);
 	void takeCreatures (int objid, std::vector<CStackBasicDescriptor> creatures);
-	void changeCreatureType (int objid, TSlot slot, TCreature creature);
+	void changeStackType(const StackLocation &sl, CCreature *c);
+	void changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue = false);
+	void insertNewStack(const StackLocation &sl, CCreature *c, TQuantity count);
+	void eraseStack(const StackLocation &sl);
 	void showCompInfo(ShowInInfobox * comp);
 	void heroVisitCastle(int obj, int heroID);
 	void vistiCastleObjects (const CGTownInstance *t, const CGHeroInstance *h);