Browse Source

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

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

+ 8 - 4
client/Client.h

@@ -113,10 +113,14 @@ 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 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){};
+	bool changeStackType(const StackLocation &sl, CCreature *c){return false;};
+	bool changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue = false){return false;};
+	bool insertNewStack(const StackLocation &sl, const CCreature *c, TQuantity count){return false;};
+	bool eraseStack(const StackLocation &sl){return false;};
+	bool swapStacks(const StackLocation &sl1, const StackLocation &sl2){return false;}
+	bool addToSlot(const StackLocation &sl, const CCreature *c, TQuantity count){return false;}
+	void tryJoiningArmy(const CArmedInstance *src, const CArmedInstance *dst, bool removeObjWhenFinished){}
+	bool moveStack(const StackLocation &src, const StackLocation &dst, TQuantity count = -1){return false;}
 	void showCompInfo(ShowInInfobox * comp){};
 	void heroVisitCastle(int obj, int heroID){};
 	void stopHeroVisitCastle(int obj, int heroID){};

+ 34 - 0
client/NetPacksClient.cpp

@@ -122,6 +122,40 @@ void SetAvailableHeroes::applyCl( CClient *cl )
 	//TODO: inform interface?
 }
 
+void ChangeStackCount::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(sl.army->tempOwner,garrisonChanged,sl.army);
+}
+
+void SetStackType::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(sl.army->tempOwner,garrisonChanged,sl.army);
+}
+
+void EraseStack::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(sl.army->tempOwner,garrisonChanged,sl.army);
+}
+
+void SwapStacks::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(sl1.army->tempOwner,garrisonChanged,sl1.army);
+	if(sl1.army != sl2.army)
+		INTERFACE_CALL_IF_PRESENT(sl2.army->tempOwner,garrisonChanged,sl2.army);
+}
+
+void InsertNewStack::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(sl.army->tempOwner,garrisonChanged,sl.army);
+}
+
+void RebalanceStacks::applyCl( CClient *cl )
+{
+	INTERFACE_CALL_IF_PRESENT(src.army->tempOwner,garrisonChanged,src.army);
+	if(src.army != dst.army)
+		INTERFACE_CALL_IF_PRESENT(dst.army->tempOwner,garrisonChanged,dst.army);
+}
+
 void GiveBonus::applyCl( CClient *cl )
 {
 	switch(who)

+ 5 - 0
hch/CCreatureHandler.cpp

@@ -139,6 +139,11 @@ bool CCreature::isMyUpgrade(const CCreature *anotherCre) const
 	return vstd::contains(upgrades, anotherCre->idNumber);
 }
 
+bool CCreature::valid() const
+{
+	return this == VLC->creh->creatures[idNumber];
+}
+
 int readNumber(int & befi, int & i, int andame, std::string & buf) //helper function for void CCreatureHandler::loadCreatures() and loadUnitAnimInfo()
 {
 	befi=i;

+ 2 - 0
hch/CCreatureHandler.h

@@ -61,6 +61,8 @@ public:
 	static int getQuantityID(const int & quantity); //0 - a few, 1 - several, 2 - pack, 3 - lots, 4 - horde, 5 - throng, 6 - swarm, 7 - zounds, 8 - legion
 	bool isMyUpgrade(const CCreature *anotherCre) const;
 
+	bool valid() const;
+
 	void addBonus(int val, int type, int subtype = -1);
 	//void getParents(TCNodes &out, const CBonusSystemNode *root /*= NULL*/) const;
 

+ 24 - 25
hch/CObjectHandler.cpp

@@ -1766,10 +1766,11 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h, ui32 answer ) co
 
 	int crid = creatures[0].second[0];
 	CCreature *crs = VLC->creh->creatures[crid];
+	TQuantity count = creatures[0].first;
 
 	if(crs->level == 1  &&  ID != 78) //first level - creatures are for free
 	{
-		if(creatures[0].first) //there are available creatures
+		if(count) //there are available creatures
 		{
 			int slot = h->getSlotFor(crid);
 			if(slot < 0) //no available slot
@@ -1787,19 +1788,16 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h, ui32 answer ) co
 				sac.creatures = creatures;
 				sac.creatures[0].first = 0;
 
-				SetGarrisons sg;
-				sg.garrs[h->id] = *h;
-				sg.garrs[h->id].addToSlot(slot, crid, creatures[0].first);
 
 				InfoWindow iw;
 				iw.player = h->tempOwner;
 				iw.text.addTxt(MetaString::GENERAL_TXT, 423); //%d %s join your army.
-				iw.text.addReplacement(creatures[0].first);
+				iw.text.addReplacement(count);
 				iw.text.addReplacement(MetaString::CRE_PL_NAMES, crid);
 
 				cb->showInfoDialog(&iw);
 				cb->sendAndApply(&sac);
-				cb->sendAndApply(&sg);
+				cb->addToSlot(StackLocation(h, slot), crs, count);
 			}
 		}
 		else //there no creatures
@@ -3102,20 +3100,21 @@ void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) co
 		if(cost)
 			cb->giveResource(h->tempOwner,6,-cost);
 
-		int slot = h->getSlotFor(subID);
-		if(slot >= 0) //there is place
-		{
-			//add creatures
-			SetGarrisons sg;
-			sg.garrs[h->id] = h->getArmy();
-			sg.garrs[h->id].addToSlot(slot, subID, getStackCount(0));
-			cb->sendAndApply(&sg);
-			cb->removeObject(id);
-		}
-		else
-		{
-			cb->showGarrisonDialog(id,h->id,true,boost::bind(&IGameCallback::removeObject,cb,id)); //show garrison window and remove ourselves from map when player ends
-		}
+		cb->tryJoiningArmy(this, h, true);
+// 		int slot = h->getSlotFor(subID);
+// 		if(slot >= 0) //there is place
+// 		{
+// 			//add creatures
+// 			SetGarrisons sg;
+// 			sg.garrs[h->id] = h->getArmy();
+// 			sg.garrs[h->id].addToSlot(slot, subID, getStackCount(0));
+// 			cb->sendAndApply(&sg);
+// 			cb->removeObject(id);
+// 		}
+// 		else
+// 		{
+// 			cb->showGarrisonDialog(id,h->id,true,boost::bind(&IGameCallback::removeObject,cb,id)); //show garrison window and remove ourselves from map when player ends
+// 		}
 	}
 }
 
@@ -6158,14 +6157,13 @@ void CGSirens::onHeroVisit( const CGHeroInstance * h ) const
 	{
 		giveDummyBonus(h->id, Bonus::ONE_BATTLE);
 		int xp = 0;
-		SetGarrisons sg;
-		sg.garrs[h->id] = h->getArmy();
+
 		for (TSlots::const_iterator i = h->Slots().begin(); i != h->Slots().end(); i++)
 		{
-			int drown = (int)(i->second->count * 0.3);
+			TQuantity drown = i->second->count * 0.3;
 			if(drown)
 			{
-				sg.garrs[h->id].setStackCount(i->first, i->second->count - drown);
+				cb->changeStackCount(StackLocation(h, i->first), -drown);
 				xp += drown * i->second->type->valOfBonuses(Bonus::STACK_HEALTH);
 			}
 		}
@@ -6174,7 +6172,6 @@ void CGSirens::onHeroVisit( const CGHeroInstance * h ) const
 		{
 			iw.text.addTxt(11,132);
 			iw.text.addReplacement(xp);
-			cb->sendAndApply(&sg);
 		}
 		else
 		{
@@ -6182,6 +6179,8 @@ void CGSirens::onHeroVisit( const CGHeroInstance * h ) const
 		}
 	}
 	cb->showInfoDialog(&iw);
+	//////////////////////////////////////////////////////////////////////////
+	///TODO: WHAT WITH EXP? 
 }
 
 //bool IShipyard::validLocation() const

+ 27 - 1
lib/CCreatureSet.cpp

@@ -49,9 +49,16 @@ bool CCreatureSet::setCreature(TSlot slot, TCreature type, TQuantity quantity) /
 
 TSlot CCreatureSet::getSlotFor(TCreature creature, ui32 slotsAmount/*=7*/) const /*returns -1 if no slot available */
 {
+	return getSlotFor(VLC->creh->creatures[creature], slotsAmount);
+}
+
+TSlot CCreatureSet::getSlotFor(const CCreature *c, ui32 slotsAmount/*=ARMY_SIZE*/) const
+{
+	assert(c->valid());
 	for(TSlots::const_iterator i=slots.begin(); i!=slots.end(); ++i)
 	{
-		if(i->second->type->idNumber == creature)
+		assert(i->second->type->valid());
+		if(i->second->type == c)
 		{
 			return i->first; //if there is already such creature we return its slot id
 		}
@@ -317,6 +324,7 @@ CStackInstance * CCreatureSet::detachStack(TSlot slot)
 	if(CArmedInstance *armedObj = castToArmyObj())
 		ret->detachFrom(armedObj);
 
+	slots.erase(slot);
 	return ret;
 }
 
@@ -327,6 +335,24 @@ void CCreatureSet::setStackType(TSlot slot, const CCreature *type)
 	s->setType(type->idNumber);
 }
 
+bool CCreatureSet::canBeMergedWith(const CCreatureSet &cs) const
+{
+	std::set<const CCreature*> cres;
+
+	//get types of creatures that need their own slot
+	for(TSlots::const_iterator i = cs.slots.begin(); i != cs.slots.end(); i++)
+		cres.insert(i->second->type);
+	for(TSlots::const_iterator i = slots.begin(); i != slots.end(); i++)
+		cres.insert(i->second->type);
+
+	return cres.size() <= ARMY_SIZE;
+}
+
+bool CCreatureSet::hasStackAtSlot(TSlot slot) const
+{
+	return vstd::contains(slots, slot);
+}
+
 CStackInstance::CStackInstance()
 	: armyObj(_armyObj)
 {

+ 3 - 1
lib/CCreatureSet.h

@@ -106,6 +106,7 @@ public:
 	int getStackCount (TSlot slot) const;
 	TSlot findStack(const CStackInstance *stack) const; //-1 if none
 	TSlot getSlotFor(TCreature creature, ui32 slotsAmount=ARMY_SIZE) const; //returns -1 if no slot available
+	TSlot getSlotFor(const CCreature *c, ui32 slotsAmount=ARMY_SIZE) const; //returns -1 if no slot available
 	bool mergableStacks(std::pair<TSlot, TSlot> &out, TSlot preferable = -1) const; //looks for two same stacks, returns slot positions;
 	bool validTypes(bool allowUnrandomized = false) const; //checks if all types of creatures are set properly
 	bool slotEmpty(TSlot slot) const;
@@ -114,9 +115,10 @@ public:
 	int getArmyStrength() const; //sum of AI values of creatures
 	ui64 getPower (TSlot slot) const; //value of specific stack
 	std::string getRoughAmount (TSlot slot) const; //rough size of specific stack
+	bool hasStackAtSlot(TSlot slot) const;
 	
 	bool contains(const CStackInstance *stack) const;
-
+	bool canBeMergedWith(const CCreatureSet &cs) const;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{

+ 8 - 4
lib/IGameCallback.h

@@ -90,10 +90,14 @@ 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 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 bool changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue = false) =0;
+	virtual bool changeStackType(const StackLocation &sl, CCreature *c) =0;
+	virtual bool insertNewStack(const StackLocation &sl, const CCreature *c, TQuantity count = -1) =0; //count -1 => moves whole stack
+	virtual bool eraseStack(const StackLocation &sl) =0;
+	virtual bool swapStacks(const StackLocation &sl1, const StackLocation &sl2) =0;
+	virtual bool addToSlot(const StackLocation &sl, const CCreature *c, TQuantity count) =0; //makes new stack or increases count of already existing
+	virtual void tryJoiningArmy(const CArmedInstance *src, const CArmedInstance *dst, bool removeObjWhenFinished) =0; //merges army from src do dst or opens a garrison window
+	virtual bool moveStack(const StackLocation &src, const StackLocation &dst, TQuantity count) = 0;
 	virtual void showCompInfo(ShowInInfobox * comp)=0;
 	virtual void heroVisitCastle(int obj, int heroID)=0;
 	virtual void stopHeroVisitCastle(int obj, int heroID)=0;

+ 16 - 6
lib/NetPacks.h

@@ -736,12 +736,17 @@ struct StackLocation
 	}
 };
 
-struct ChangeStackCount : CPackForClient  //521
+struct CGarrisonOperationPack : CPackForClient
+{
+};
+
+struct ChangeStackCount : CGarrisonOperationPack  //521
 {
 	StackLocation sl;
 	TQuantity count;
 	ui8 absoluteValue; //if not -> count will be added (or subtracted if negative)
 
+	void applyCl(CClient *cl);
 	DLL_EXPORT void applyGs(CGameState *gs);
 
 	template <typename Handler> void serialize(Handler &h, const int version)
@@ -750,11 +755,12 @@ struct ChangeStackCount : CPackForClient  //521
 	}
 };
 
-struct SetStackType : CPackForClient  //522
+struct SetStackType : CGarrisonOperationPack  //522
 {
 	StackLocation sl;
 	CCreature *type;
 
+	void applyCl(CClient *cl);
 	DLL_EXPORT void applyGs(CGameState *gs);
 
 	template <typename Handler> void serialize(Handler &h, const int version)
@@ -763,10 +769,11 @@ struct SetStackType : CPackForClient  //522
 	}
 };
 
-struct EraseStack : CPackForClient  //523
+struct EraseStack : CGarrisonOperationPack  //523
 {
 	StackLocation sl;
 
+	void applyCl(CClient *cl);
 	DLL_EXPORT void applyGs(CGameState *gs);
 
 	template <typename Handler> void serialize(Handler &h, const int version)
@@ -775,10 +782,11 @@ struct EraseStack : CPackForClient  //523
 	}
 };
 
-struct SwapStacks : CPackForClient  //524
+struct SwapStacks : CGarrisonOperationPack  //524
 {
 	StackLocation sl1, sl2;
 
+	void applyCl(CClient *cl);
 	DLL_EXPORT void applyGs(CGameState *gs);
 
 	template <typename Handler> void serialize(Handler &h, const int version)
@@ -787,11 +795,12 @@ struct SwapStacks : CPackForClient  //524
 	}
 };
 
-struct InsertNewStack : CPackForClient  //525
+struct InsertNewStack : CGarrisonOperationPack //525
 {
 	StackLocation sl;
 	CStackInstance *stack;
 
+	void applyCl(CClient *cl);
 	DLL_EXPORT void applyGs(CGameState *gs);
 
 	template <typename Handler> void serialize(Handler &h, const int version)
@@ -801,11 +810,12 @@ struct InsertNewStack : CPackForClient  //525
 };
 
 //moves creatures from src stack to dst slot, may be used for merging/splittint/moving stacks
-struct RebalanceStacks : CPackForClient  //525
+struct RebalanceStacks : CGarrisonOperationPack  //525
 {
 	StackLocation src, dst;
 	TQuantity count;
 
+	void applyCl(CClient *cl);
 	DLL_EXPORT void applyGs(CGameState *gs);
 
 	template <typename Handler> void serialize(Handler &h, const int version)

+ 4 - 0
lib/NetPacksLib.cpp

@@ -625,7 +625,11 @@ DLL_EXPORT void EraseStack::applyGs( CGameState *gs )
 
 DLL_EXPORT void SwapStacks::applyGs( CGameState *gs )
 {
+	CStackInstance *s1 = sl1.army->detachStack(sl1.slot),
+		*s2 = sl2.army->detachStack(sl2.slot);
 
+	sl2.army->putStack(sl2.slot, s1);
+	sl1.army->putStack(sl1.slot, s2);
 }
 
 DLL_EXPORT void InsertNewStack::applyGs( CGameState *gs )

+ 198 - 141
server/CGameHandler.cpp

@@ -640,20 +640,13 @@ 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->getSlotFor(raisedStack.type->idNumber);
+			TSlot slot = winnerHero->getSlotFor(raisedStack.type);
 
 			if (slot != -1) 
 			{
-				SetGarrisons sg;
-				sg.garrs[winnerHero->id] = winnerHero->getArmy();
-				sg.garrs[winnerHero->id].addToSlot(slot, raisedStack.type->idNumber, raisedStack.count);
-
-// 				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);
+				addToSlot(StackLocation(winnerHero, slot), raisedStack.type, raisedStack.count);
 			}
 		}
 	}
@@ -926,6 +919,7 @@ int CGameHandler::moveStack(int stack, int dest)
 
 	return ret;
 }
+
 CGameHandler::CGameHandler(void)
 {
 	QID = 1;
@@ -2624,8 +2618,8 @@ 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->getArmy(), temp2 = s2->getArmy(),
-		&S1 = temp1, &S2 = (s1!=s2)?(temp2):(temp1);
+	CCreatureSet &S1 = *s1, &S2 = *s2;
+	StackLocation sl1(s1, p1), sl2(s2, p2);
 
 	if(!isAllowedExchange(id1,id2))
 	{
@@ -2641,14 +2635,8 @@ bool CGameHandler::arrangeStacks( si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2,
 			complain("Can't take troops from another player!");
 			return false;
 		}
-		
-		std::swap(S1.slots[p1], S2.slots[p2]); //swap slots
 
-		//if one of them is empty, remove entry
-		if(!S1.slots[p1]->count)
-			S1.slots.erase(p1);
-		if(!S2.slots[p2]->count)
-			S2.slots.erase(p2);
+		swapStacks(sl1, sl2);
 	}
 	else if(what==2)//merge
 	{
@@ -2656,11 +2644,17 @@ bool CGameHandler::arrangeStacks( si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2,
 		|| ((s1->tempOwner != player && s1->tempOwner != 254) && S2.slots[p2]->count) && complain("Can't take troops from another player!"))
 			return false; 
 
-		S2.slots[p2]->count += S1.slots[p1]->count;
-		S1.slots.erase(p1);
+		moveStack(sl1, sl2);
 	}
 	else if(what==3) //split
 	{
+		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;
+		}
+
 		//general conditions checking
 		if((!vstd::contains(S1.slots,p1) && complain("no creatures to split"))
 			|| (val<1  && complain("no creatures to split"))  )
@@ -2679,8 +2673,9 @@ bool CGameHandler::arrangeStacks( si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2,
 				return false; 
 			}
 			
-			S2.slots[p2]->count = val;
-			S1.slots[p1]->count = total - val;
+			moveStack(sl1, sl2, val - S2.slots[p2]->count);
+			//S2.slots[p2]->count = val;
+			//S1.slots[p1]->count = total - val;
 		}
 		else //split one stack to the two
 		{
@@ -2689,35 +2684,12 @@ bool CGameHandler::arrangeStacks( si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2,
 				complain("Cannot split that stack, not enough creatures!");
 				return false; 
 			}
-			S2.slots[p2]->type = S1.slots[p1]->type;
-			S2.slots[p2]->count = val;
-			S1.slots[p1]->count -= val;
-		}
 
-		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;
+
+			moveStack(sl1, sl2, val);
 		}
 
-		if(!S1.slots[p1]->count) //if we've moved all creatures
-			S1.slots.erase(p1);
-	}
-	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!");
-		return false; //leave without applying changes to garrison
 	}
-
-	//apply changes
-	SetGarrisons sg;
-	sg.garrs[id1] = S1;
-	if(s1 != s2)
-		sg.garrs[id2] = S2;
-	sendAndApply(&sg);
 	return true;
 }
 
@@ -2753,10 +2725,8 @@ bool CGameHandler::disbandCreature( si32 id, ui8 pos )
 		complain("Illegal call to disbandCreature - no such stack in army!");
 		return false;
 	}
-	s1->slots.erase(pos);
-	SetGarrisons sg;
-	sg.garrs[id] = s1->getArmy();
-	sendAndApply(&sg);
+
+	eraseStack(StackLocation(s1, pos));
 	return true;
 }
 
@@ -2983,10 +2953,7 @@ bool CGameHandler::recruitCreatures( si32 objid, ui32 crid, ui32 cram, si32 from
 	}
 	else
 	{
-		SetGarrisons sg;
-		sg.garrs[dst->id] = dst->getArmy();
-		sg.garrs[dst->id] .addToSlot(slot, crid, cram);
-		sendAndApply(&sg);
+		addToSlot(StackLocation(dst, slot), c, cram);
 	}
 	return true;
 }
@@ -2994,6 +2961,7 @@ bool CGameHandler::recruitCreatures( si32 objid, ui32 crid, ui32 cram, si32 from
 bool CGameHandler::upgradeCreature( ui32 objid, ui8 pos, ui32 upgID )
 {
 	CArmedInstance *obj = static_cast<CArmedInstance*>(gs->map->objects[objid]);
+	assert(obj->hasStackAtSlot(pos));
 	UpgradeInfo ui = gs->getUpgradeInfo(obj->getStack(pos));
 	int player = obj->tempOwner;
 	int crQuantity = obj->slots[pos]->count;
@@ -3027,21 +2995,20 @@ bool CGameHandler::upgradeCreature( ui32 objid, ui8 pos, ui32 upgID )
 	}
 	
 	//upgrade creature
-	SetGarrisons sg;
-	sg.garrs[objid] = obj->getArmy();
-	sg.garrs[objid].slots[pos]->setType(upgID);
-	sendAndApply(&sg);	
+	changeStackType(StackLocation(obj, pos), VLC->creh->creatures[upgID]);
 	return true;
 }
 
-void CGameHandler::changeStackType(const StackLocation &sl, CCreature *c)
+bool CGameHandler::changeStackType(const StackLocation &sl, CCreature *c)
 {
-	assert(sl.army->getCreature(sl.slot));
+	if(!sl.army->hasStackAtSlot(sl.slot))
+		COMPLAIN_RET("Cannot find a stack to change type");
 
 	SetStackType sst;
 	sst.sl = sl;
 	sst.type = c;
 	sendAndApply(&sst);	
+	return true;
 }
 
 bool CGameHandler::garrisonSwap( si32 tid )
@@ -3049,42 +3016,43 @@ 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->getArmy(), cso = town->getArmy();
+
+		if(!town->visitingHero->canBeMergedWith(*town))
+		{
+			complain("Cannot make garrison swap, not enough free slots!");
+			return false;
+		}
+
+		const CCreatureSet &cso = *town;
+		const CCreatureSet &csn = *town->visitingHero;
+
 		while(!cso.slots.empty())//while there are unmoved creatures
 		{
-			int pos = csn.getSlotFor(cso.slots.begin()->second->type->idNumber);
-			if(pos<0)
+			TSlots::const_iterator i = cso.slots.begin(); //iterator to stack to move
+			StackLocation sl(town, i->first); //location of stack to move
+
+			TSlot pos = csn.getSlotFor(i->second->type);
+			if(pos < 0)
 			{
 				//try to merge two other stacks to make place
 				std::pair<TSlot, TSlot> toMerge;
-				if(csn.mergableStacks(toMerge, cso.slots.begin()->first))
+				if(csn.mergableStacks(toMerge, i->first))
 				{
-					//merge
-					csn.slots[toMerge.second]->count += csn.slots[toMerge.first]->count;
-					csn.slots[toMerge.first] = cso.slots.begin()->second;
+					moveStack(StackLocation(town->visitingHero, toMerge.first), StackLocation(town->visitingHero, toMerge.second)); //merge toMerge.first into toMerge.second
+					moveStack(sl, StackLocation(town->visitingHero, toMerge.first)); //move stack to freed slot
 				}
 				else
 				{
-					complain("Cannot make garrison swap, not enough free slots!");
+					complain("Unexpected failure during an attempt to merge town and visiting hero armies!");
 					return false;
 				}
 			}
-			else if(csn.slots.find(pos) != csn.slots.end()) //add creatures to the existing stack
-			{
-				csn.slots[pos]->count += cso.slots.begin()->second->count;
-			}
-			else //move stack on the free pos
+			else
 			{
-				csn.slots[pos]->type = cso.slots.begin()->second->type;
-				csn.slots[pos]->count = cso.slots.begin()->second->count;
+				moveStack(sl, StackLocation(town->visitingHero, pos));
 			}
-			cso.slots.erase(cso.slots.begin());
 		}
-		SetGarrisons sg;
-		sg.garrs[town->visitingHero->id] = csn;
-		sg.garrs[town->id] = csn;
-		sendAndApply(&sg);
-
+		
 		SetHeroesInTown intown;
 		intown.tid = tid;
 		intown.visiting = -1;
@@ -3106,25 +3074,14 @@ bool CGameHandler::garrisonSwap( si32 tid )
 		intown.garrison = -1;
 		intown.visiting =  town->garrisonHero->id;
 		sendAndApply(&intown);
-
-		//town will be empty
-		SetGarrisons sg;
-		sg.garrs[tid] = CCreatureSet();
-		sendAndApply(&sg);
-		return true;
 	}
 	else if (town->garrisonHero && town->visitingHero) //swap visiting and garrison hero
 	{
-		SetGarrisons sg;
-		sg.garrs[town->id] = town->visitingHero->getArmy();;
-		sg.garrs[town->garrisonHero->id] = town->garrisonHero->getArmy();
-
 		SetHeroesInTown intown;
 		intown.tid = tid;
 		intown.garrison = town->visitingHero->id;
 		intown.visiting =  town->garrisonHero->id;
 		sendAndApply(&intown);
-		sendAndApply(&sg);
 		return true;
 	}
 	else
@@ -3537,14 +3494,7 @@ bool CGameHandler::sellCreatures(ui32 count, const IMarket *market, const CGHero
  		assert(0);
  	}
  
-
-	SetGarrisons sg;
-	sg.garrs[hero->id] = hero->getArmy();
-	if(s.count > count)
-		sg.garrs[hero->id].setStackCount(slot, s.count - count);
-	else
-		sg.garrs[hero->id].eraseStack(slot);
-	sendAndApply(&sg);
+	changeStackCount(StackLocation(hero, slot), -count);
 
  	SetResource sr;
  	sr.player = hero->tempOwner;
@@ -3561,13 +3511,14 @@ bool CGameHandler::transformInUndead(const IMarket *market, const CGHeroInstance
 	if (hero)
 		army = hero;
 	else
-	{
 		army = dynamic_cast<const CGTownInstance *>(market->o);
-	}
+
 	if (!army)
 		COMPLAIN_RET("Incorrect call to transform in undead!");
-	if(!vstd::contains(army->Slots(), slot))
+	if(!army->hasStackAtSlot(slot))
 		COMPLAIN_RET("Army doesn't have any creature in that slot!");
+
+
 	const CStackInstance &s = army->getStack(slot);
 	int resCreature;//resulting creature - bone dragons or skeletons
 	
@@ -3575,10 +3526,8 @@ bool CGameHandler::transformInUndead(const IMarket *market, const CGHeroInstance
 		resCreature = 68;
 	else
 		resCreature = 56;
-	SetGarrisons sg;
-	sg.garrs[army->id] = army->getArmy();
-	sg.garrs[army->id].setCreature(slot, resCreature, s.count);
-	sendAndApply(&sg);
+
+	changeStackType(StackLocation(army, slot), VLC->creh->creatures[resCreature]);
 	return true;
 }
 
@@ -4054,30 +4003,22 @@ void CGameHandler::playerMessage( ui8 player, const std::string &message )
 	else if(message == "vcmiainur") //gives 5 archangels into each slot
 	{
 		CGHeroInstance *hero = gs->getHero(gs->getPlayer(player)->currentSelection);
+		const CCreature *archangel = VLC->creh->creatures[13];
 		if(!hero) return;
 
-		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, 13, 5);
-		sendAndApply(&sg);
+		for(int i = 0; i < ARMY_SIZE; i++)
+			if(!hero->hasStackAtSlot(i))
+				insertNewStack(StackLocation(hero, i), archangel, 10);
 	}
 	else if(message == "vcmiangband") //gives 10 black knight into each slot
 	{
 		CGHeroInstance *hero = gs->getHero(gs->getPlayer(player)->currentSelection);
+		const CCreature *blackKnight = VLC->creh->creatures[66];
 		if(!hero) return;
 
-		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, 66, 10);
-		sendAndApply(&sg);
+		for(int i = 0; i < ARMY_SIZE; i++)
+			if(!hero->hasStackAtSlot(i))
+				insertNewStack(StackLocation(hero, i), blackKnight, 10);
 	}
 	else if(message == "vcminoldor") //all war machines
 	{
@@ -5244,7 +5185,6 @@ bool CGameHandler::tryAttackingGuard(const int3 &guardPos, const CGHeroInstance
 bool CGameHandler::sacrificeCreatures(const IMarket *market, const CGHeroInstance *hero, TSlot slot, ui32 count)
 {
 	int oldCount = hero->getStackCount(slot);
-	int newCount = oldCount - count;
 
 	if(oldCount < count)
 		COMPLAIN_RET("Not enough creatures to sacrifice!")
@@ -5253,18 +5193,12 @@ bool CGameHandler::sacrificeCreatures(const IMarket *market, const CGHeroInstanc
 
 	int crid = hero->getStack(slot).type->idNumber;
 	
-	SetGarrisons sg;
-	sg.garrs[hero->id] = hero->getArmy();
-	if(newCount)
-		sg.garrs[hero->id].setStackCount(slot, newCount);
-	else
-		sg.garrs[hero->id].eraseStack(slot);
-	sendAndApply(&sg);
+	changeStackCount(StackLocation(hero, slot), -count);
 
 	int dump, exp;
 	market->getOffer(crid, 0, dump, exp, CREATURE_EXP);
 	exp *= count;
-	changePrimSkill	(hero->id, 4, exp*(100+hero->getSecSkillLevel(21)*5)/100.0f);
+	changePrimSkill(hero->id, 4, exp*(100+hero->getSecSkillLevel(21)*5)/100.0f);
 
 	return true;
 }
@@ -5280,26 +5214,149 @@ bool CGameHandler::sacrificeArtifact(const IMarket * m, const CGHeroInstance * h
 	return true;
 }
 
-void CGameHandler::insertNewStack(const StackLocation &sl, CCreature *c, TQuantity count)
+bool CGameHandler::insertNewStack(const StackLocation &sl, const CCreature *c, TQuantity count)
 {
+	if(sl.army->hasStackAtSlot(sl.slot))
+		COMPLAIN_RET("Slot is already taken!");
+
 	InsertNewStack ins;
 	ins.sl = sl;
 	ins.stack = new CStackInstance(c, count);
 	sendAndApply(&ins);
+	return true;
 }
 
-void CGameHandler::eraseStack(const StackLocation &sl)
+bool CGameHandler::eraseStack(const StackLocation &sl)
 {
+	if(!sl.army->hasStackAtSlot(sl.slot))
+		COMPLAIN_RET("Cannot find a stack to erase");
+
+	if(sl.army->Slots().size() == 1 //from the last stack
+		&& sl.army->needsLastStack()) //that must be left
+	{
+		COMPLAIN_RET("Cannot erase the last stack!");
+	}
+
 	EraseStack es;
 	es.sl = sl;
 	sendAndApply(&es);
+	return true;
+}
+
+bool CGameHandler::changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue /*= false*/)
+{
+	TQuantity currentCount = sl.army->getStackCount(sl.slot);
+	if(absoluteValue && count < 0
+		|| !absoluteValue && -count > currentCount)
+	{
+		COMPLAIN_RET("Cannot take more stacks than present!");
+	}
+
+	if(currentCount == -count  &&  !absoluteValue
+		|| !count && absoluteValue)
+	{
+		eraseStack(sl);
+	}
+	else
+	{
+		ChangeStackCount csc;
+		csc.sl = sl;
+		csc.count = count;
+		csc.absoluteValue = absoluteValue;
+		sendAndApply(&csc);
+	}
+	return true;
+}
+
+bool CGameHandler::addToSlot(const StackLocation &sl, const CCreature *c, TQuantity count)
+{
+	const CCreature *slotC = sl.army->getCreature(sl.slot);
+	if(!slotC) //slot is empty
+		insertNewStack(sl, c, count);
+	else if(c == slotC)
+		changeStackCount(sl, count);
+	else
+	{
+		tlog1 << "Cannot add " << c->namePl << " to slot " << sl.slot << std::endl;
+		COMPLAIN_RET("Cannot add stack to slot!");
+	}
+	return true;
+}
+
+void CGameHandler::tryJoiningArmy(const CArmedInstance *src, const CArmedInstance *dst, bool removeObjWhenFinished)
+{
+	if(!dst->canBeMergedWith(*src))
+	{
+		boost::function<void()> removeOrNot = 0;
+		if(removeObjWhenFinished) 
+			removeOrNot = boost::bind(&IGameCallback::removeObject,this,src->id);
+		showGarrisonDialog(src->id, dst->id, true, removeOrNot); //show garrison window and optionally remove ourselves from map when player ends
+	}
+	else //merge
+	{
+		while(src->slots.size()) //there are not moved cres
+		{
+			TSlots::const_iterator i = src->slots.begin();
+
+			TSlot dstSlot = dst->getSlotFor(i->second->type);
+			if(dstSlot >= 0) //there is place
+			{
+				moveStack(StackLocation(src, i->first), StackLocation(dst, dstSlot), i->second->count);
+			}
+			else
+			{
+				tlog1 << "Unexpected Failure on merging armies!\n";
+				return;
+			}
+		}
+
+		if(removeObjWhenFinished)
+			removeObject(src->id);
+	}
 }
 
-void CGameHandler::changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue /*= false*/)
+bool CGameHandler::moveStack(const StackLocation &src, const StackLocation &dst, TQuantity count)
 {
-	ChangeStackCount csc;
-	csc.sl = sl;
-	csc.count = count;
-	csc.absoluteValue = absoluteValue;
-	sendAndApply(&csc);
+	if(!src.army->hasStackAtSlot(src.slot))
+		COMPLAIN_RET("No stack to move!");
+
+	if(dst.army->hasStackAtSlot(dst.slot) && dst.army->getCreature(dst.slot) != src.army->getCreature(src.slot))
+		COMPLAIN_RET("Cannot move: stack of different type at destination pos!");
+
+	if(count == -1)
+	{
+		count = src.army->getStackCount(src.slot);
+	}
+
+	if(src.army != dst.army  //moving away
+		&&  count == src.army->getStackCount(src.slot) //all creatures
+		&& src.army->Slots().size() == 1 //from the last stack
+		&& src.army->needsLastStack()) //that must be left
+	{
+		COMPLAIN_RET("Cannot move away the alst creature!");
+	}
+
+	RebalanceStacks rs;
+	rs.src = src;
+	rs.dst = dst;
+	rs.count = count;
+	sendAndApply(&rs);
+	return true;
+}
+
+bool CGameHandler::swapStacks(const StackLocation &sl1, const StackLocation &sl2)
+{
+
+	if(!sl1.army->hasStackAtSlot(sl1.slot))
+		return moveStack(sl2, sl1);
+	else if(!sl2.army->hasStackAtSlot(sl2.slot))
+		return moveStack(sl1, sl2);
+	else
+	{
+		SwapStacks ss;
+		ss.sl1 = sl1;
+		ss.sl2 = sl2;
+		sendAndApply(&ss);
+		return true;
+	}
 }

+ 8 - 4
server/CGameHandler.h

@@ -138,10 +138,14 @@ 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 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);
+	bool changeStackType(const StackLocation &sl, CCreature *c);
+	bool changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue = false);
+	bool insertNewStack(const StackLocation &sl, const CCreature *c, TQuantity count);
+	bool eraseStack(const StackLocation &sl);
+	bool swapStacks(const StackLocation &sl1, const StackLocation &sl2);
+	bool addToSlot(const StackLocation &sl, const CCreature *c, TQuantity count);
+	void tryJoiningArmy(const CArmedInstance *src, const CArmedInstance *dst, bool removeObjWhenFinished);
+	bool moveStack(const StackLocation &src, const StackLocation &dst, TQuantity count = -1);
 	void showCompInfo(ShowInInfobox * comp);
 	void heroVisitCastle(int obj, int heroID);
 	void vistiCastleObjects (const CGTownInstance *t, const CGHeroInstance *h);

+ 1 - 0
server/NetPacksServer.cpp

@@ -22,6 +22,7 @@
  *
  */
 
+
 CGameState* CPackForServer::GS(CGameHandler *gh)
 {
 	return gh->gs;