Browse Source

CGTownInstance: implement armies merging on siege. Fix issue 169

Merging algorithm is replicate one from original game.
Arseniy Shestakov 9 years ago
parent
commit
d15106bf63
2 changed files with 59 additions and 1 deletions
  1. 58 1
      lib/mapObjects/CGTownInstance.cpp
  2. 1 0
      lib/mapObjects/CGTownInstance.h

+ 58 - 1
lib/mapObjects/CGTownInstance.cpp

@@ -535,8 +535,12 @@ void CGTownInstance::onHeroVisit(const CGHeroInstance * h) const
 
 			bool outsideTown = (defendingHero == visitingHero && garrisonHero);
 
-			//TODO
 			//"borrowing" army from garrison to visiting hero
+			if(!outsideTown && armedGarrison() &&
+				visitingHero && defendingHero == visitingHero)
+			{
+				mergeGarrisonOnSiege();
+			}
 
 			cb->startBattlePrimary(h, defendingArmy, getSightCenter(), h, defendingHero, false, (outsideTown ? nullptr : this));
 		}
@@ -725,6 +729,59 @@ void CGTownInstance::getOutOffsets( std::vector<int3> &offsets ) const
 	offsets = {int3(-1,2,0), int3(-3,2,0)};
 }
 
+void CGTownInstance::mergeGarrisonOnSiege() const
+{
+	auto getWeakestStackSlot = [&](int powerLimit)
+	{
+		std::vector<SlotID> weakSlots;
+		auto stacksList = visitingHero->stacks;
+		std::pair<SlotID, CStackInstance *> pair;
+		while(stacksList.size())
+		{
+			pair = *vstd::minElementByFun(stacksList, [&](std::pair<SlotID, CStackInstance *> elem)
+			{
+				return elem.second->getPower();
+			});
+			if(powerLimit > pair.second->getPower() &&
+				(weakSlots.empty() || pair.second->getPower() == visitingHero->getStack(weakSlots.front()).getPower()))
+			{
+				weakSlots.push_back(pair.first);
+				stacksList.erase(pair.first);
+			}
+			else
+				break;
+		}
+
+		if(weakSlots.size())
+			return *std::max_element(weakSlots.begin(), weakSlots.end());
+
+		return SlotID();
+	};
+
+	int count = stacks.size();
+	for(int i = 0; i < count; i++)
+	{
+		auto pair = *vstd::maxElementByFun(stacks, [&](std::pair<SlotID, CStackInstance *> elem)
+		{
+			ui64 power = elem.second->getPower();
+			auto dst = visitingHero->getSlotFor(elem.second->getCreatureID());
+			if(dst.validSlot() && visitingHero->hasStackAtSlot(dst))
+				power += visitingHero->getStack(dst).getPower();
+
+			return power;
+		});
+		auto dst = visitingHero->getSlotFor(pair.second->getCreatureID());
+		if(dst.validSlot())
+			cb->moveStack(StackLocation(this, pair.first), StackLocation(visitingHero, dst), -1);
+		else
+		{
+			dst = getWeakestStackSlot(pair.second->getPower());
+			if(dst.validSlot())
+				cb->swapStacks(StackLocation(this, pair.first), StackLocation(visitingHero, dst));
+		}
+	}
+}
+
 void CGTownInstance::removeCapitols (PlayerColor owner) const
 {
 	if (hasCapitol()) // search if there's an older capitol

+ 1 - 0
lib/mapObjects/CGTownInstance.h

@@ -237,6 +237,7 @@ public:
 
 	CBuilding::TRequired genBuildingRequirements(BuildingID build, bool includeUpgrade=true) const;
 
+	void mergeGarrisonOnSiege() const; // merge garrison into army of visiting hero
 	void removeCapitols (PlayerColor owner) const;
 	void addHeroToStructureVisitors(const CGHeroInstance *h, si32 structureInstanceID) const; //hero must be visiting or garrisoned in town