2
0
Эх сурвалжийг харах

Improved handling buildings with mode "auto":
* they will properly processed (new creatures will be added if dwelling, spells learned if mage guild, and so)
* transitive dependencies are handled (A makes B build, and B makes C and D)

Michał W. Urbańczyk 12 жил өмнө
parent
commit
1c7aad6a50
1 өөрчлөгдсөн 97 нэмэгдсэн , 52 устгасан
  1. 97 52
      server/CGameHandler.cpp

+ 97 - 52
server/CGameHandler.cpp

@@ -2356,18 +2356,27 @@ bool CGameHandler::disbandCreature( ObjectInstanceID id, SlotID pos )
 	return true;
 }
 
-bool CGameHandler::buildStructure( ObjectInstanceID tid, BuildingID bid, bool force /*=false*/ )
+bool CGameHandler::buildStructure( ObjectInstanceID tid, BuildingID requestedID, bool force /*=false*/ )
 {
-	CGTownInstance * t = gs->getTown(tid);
-	CBuilding * b = t->town->buildings[bid];
+	const CGTownInstance * t = getTown(tid);
+	if(!t)
+		COMPLAIN_RETF("No such town (ID=%s)!", tid);
+	if(!t->town->buildings.count(requestedID))
+		COMPLAIN_RETF("Town of faction %s does not have info about building ID=%s!", t->town->faction->name % tid);
+
+	const CBuilding * requestedBuilding = t->town->buildings[requestedID];
+
+	//Vector with future list of built building and buildings in auto-mode that are not yet built.
+	std::vector<const CBuilding*> buildingsThatWillBe, remainingAutoBuildings;
 
+	//Check validity of request
 	if(!force)
 	{
-		switch (b->mode)
+		switch (requestedBuilding->mode)
 		{
 		case CBuilding::BUILD_NORMAL :
 		case CBuilding::BUILD_AUTO   :
-			if (gs->canBuildStructure(t,bid) != EBuildingState::ALLOWED)
+			if (gs->canBuildStructure(t, requestedID) != EBuildingState::ALLOWED)
 				COMPLAIN_RET("Cannot build that building!");
 			break;
 
@@ -2376,68 +2385,105 @@ bool CGameHandler::buildStructure( ObjectInstanceID tid, BuildingID bid, bool fo
 			break;
 
 		case CBuilding::BUILD_GRAIL  :
-			if(b->mode == CBuilding::BUILD_GRAIL) //needs grail
+			if(requestedBuilding->mode == CBuilding::BUILD_GRAIL) //needs grail
 			{
-				if(!t->visitingHero || !t->visitingHero->hasArt(2))
+				if(!t->visitingHero || !t->visitingHero->hasArt(ArtifactID::GRAIL))
 					COMPLAIN_RET("Cannot build this without grail!")
 				else
-					removeArtifact(ArtifactLocation(t->visitingHero, t->visitingHero->getArtPos(2, false)));
+					removeArtifact(ArtifactLocation(t->visitingHero, t->visitingHero->getArtPos(ArtifactID::GRAIL, false)));
 			}
 			break;
 		}
 	}
 
-	NewStructures ns;
-	ns.tid = tid;
+	//Performs stuff that has to be done after new building is built
+	auto processBuiltStructure = [t, this](const BuildingID buildingID)
+	{
+		if(buildingID >= BuildingID::DWELL_FIRST) //dwelling
+		{
+			int level = (buildingID - BuildingID::DWELL_FIRST) % GameConstants::CREATURES_PER_TOWN;
+			int upgradeNumber = (buildingID - BuildingID::DWELL_FIRST) / GameConstants::CREATURES_PER_TOWN;
+
+			if (upgradeNumber >= t->town->creatures[level].size())
+			{
+				complain(boost::str(boost::format("Error ecountered when building dwelling (bid=%s):"
+													"no creature found (upgrade number %d, level %d!")
+												% buildingID % upgradeNumber % level));
+				return;
+			}
+
+			CCreature * crea = VLC->creh->creatures[t->town->creatures[level][upgradeNumber]];
 
-	if(bid >= BuildingID::DWELL_FIRST) //dwelling
+			SetAvailableCreatures ssi;
+			ssi.tid = t->id;
+			ssi.creatures = t->creatures;
+			if (buildingID <= BuildingID::DWELL_LAST)
+				ssi.creatures[level].first = crea->growth;
+			ssi.creatures[level].second.push_back(crea->idNumber);
+			sendAndApply(&ssi);
+		}
+		else if ( t->subID == ETownType::DUNGEON && buildingID == BuildingID::PORTAL_OF_SUMMON )
+		{
+			setPortalDwelling(t);
+		}
+
+		if(buildingID <= BuildingID::MAGES_GUILD_5) //it's mage guild
+		{
+			if(t->visitingHero)
+				giveSpells(t,t->visitingHero);
+			if(t->garrisonHero)
+				giveSpells(t,t->garrisonHero);
+		}
+	};
+
+	//Checks if all requirements will be met with expected building list "buildingsThatWillBe"
+	auto allRequirementsFullfilled = [&buildingsThatWillBe, t](const CBuilding *b)
 	{
-		int level = (bid - BuildingID::DWELL_FIRST) % GameConstants::CREATURES_PER_TOWN;
-		int upgradeNumber = (bid - BuildingID::DWELL_FIRST) / GameConstants::CREATURES_PER_TOWN;
+		BOOST_FOREACH(auto requirementID, b->requirements)
+			if(!vstd::contains(buildingsThatWillBe, t->town->buildings[requirementID]))
+				return false;
 
-		if (upgradeNumber >= t->town->creatures[level].size())
-			COMPLAIN_RET("Cannot build dwelling: no creature found!");
+		return true;
+	};
 
-		CCreature * crea = VLC->creh->creatures[t->town->creatures[level][upgradeNumber]];
 
-		SetAvailableCreatures ssi;
-		ssi.tid = tid;
-		ssi.creatures = t->creatures;
-		if (bid <= BuildingID::DWELL_LAST)
-			ssi.creatures[level].first = crea->growth;
-		ssi.creatures[level].second.push_back(crea->idNumber);
-		sendAndApply(&ssi);
-	}
-	else if ( t->subID == ETownType::DUNGEON && bid == BuildingID::PORTAL_OF_SUMMON )
+
+	//Init the vectors
+	BOOST_FOREACH(auto & build, t->town->buildings)
 	{
-		setPortalDwelling(t);
+		if(t->hasBuilt(build.first))
+			buildingsThatWillBe.push_back(build.second);
+		else if(build.second->mode == CBuilding::BUILD_AUTO) //not built auto building
+			remainingAutoBuildings.push_back(build.second);
 	}
 
-	ns.bid.insert(bid);
-	ns.builded = force?t->builded:(t->builded+1);
+	//Prepare structure (list of building ids will be filled later)
+	NewStructures ns;
+	ns.tid = tid;
+	ns.builded = force ? t->builded : (t->builded+1);
 
-	BOOST_FOREACH(auto & build, t->town->buildings)
+	std::queue<const CBuilding*> buildingsToAdd;
+	buildingsToAdd.push(requestedBuilding);
+
+	while(buildingsToAdd.size())
 	{
-		if (build.second->mode == CBuilding::BUILD_AUTO
-		    && !vstd::contains(t->builtBuildings, build.second->bid))
+		auto b = buildingsToAdd.front();
+		buildingsToAdd.pop();
+
+		ns.bid.insert(b->bid);
+		buildingsThatWillBe.push_back(b);
+		remainingAutoBuildings -= b;
+
+		BOOST_FOREACH(auto autoBuilding, remainingAutoBuildings)
 		{
-			bool canBuild = true;
-			BOOST_FOREACH(int requires, build.second->requirements)
-			{
-				if (!vstd::contains(t->builtBuildings, requires)
-				 && !vstd::contains(ns.bid, requires))
-				{
-					canBuild = false;
-					break;
-				}
-			}
-			if (canBuild)
-				ns.bid.insert(build.second->bid);
+			if(allRequirementsFullfilled(autoBuilding))
+				buildingsToAdd.push(autoBuilding);
 		}
 	}
 
+	//We know what has been built, appluy changes
 	sendAndApply(&ns);
-
+	
 	//reveal ground for lookout tower
 	FoWChange fw;
 	fw.player = t->tempOwner;
@@ -2445,21 +2491,20 @@ bool CGameHandler::buildStructure( ObjectInstanceID tid, BuildingID bid, bool fo
 	t->getSightTiles(fw.tiles);
 	sendAndApply(&fw);
 
+	//Other post-built events
+	BOOST_FOREACH(auto builtID, ns.bid)
+		processBuiltStructure(builtID);
+
+	//Take cost
 	if (!force)
 	{
 		SetResources sr;
 		sr.player = t->tempOwner;
-		sr.res = gs->getPlayer(t->tempOwner)->resources - b->resources;
+		sr.res = gs->getPlayer(t->tempOwner)->resources - requestedBuilding->resources;
 		sendAndApply(&sr);
 	}
 
-	if(bid<5) //it's mage guild
-	{
-		if(t->visitingHero)
-			giveSpells(t,t->visitingHero);
-		if(t->garrisonHero)
-			giveSpells(t,t->garrisonHero);
-	}
+
 	if(t->visitingHero)
 		vistiCastleObjects (t, t->visitingHero);
 	if(t->garrisonHero)