Преглед на файлове

- creatures availability tests no longer check for built buildings.
Fixes #1650
- do not crash if town has 0 creatures on some dwelling level
- do not crash if dwelling for some level is not present in town at all

Ivan Savenko преди 11 години
родител
ревизия
fb5c9fc972
променени са 6 файла, в които са добавени 96 реда и са изтрити 67 реда
  1. 59 32
      client/CCastleInterface.cpp
  2. 3 1
      client/CCastleInterface.h
  3. 4 1
      client/CPreGame.cpp
  4. 28 30
      lib/CObjectHandler.cpp
  5. 0 1
      lib/CObjectHandler.h
  6. 2 2
      server/CGameHandler.cpp

+ 59 - 32
client/CCastleInterface.cpp

@@ -1467,7 +1467,7 @@ CFortScreen::CFortScreen(const CGTownInstance * town):
 		}
 		else
 			buildingID = BuildingID::SPECIAL_3;
-		recAreas.push_back(new RecruitArea(positions[i].x, positions[i].y, town, buildingID, i));
+		recAreas.push_back(new RecruitArea(positions[i].x, positions[i].y, town, i));
 	}
 
 	resdatabar = new CMinorResDataBar;
@@ -1529,7 +1529,36 @@ void LabeledValue::hover(bool on)
 	}
 }
 
-CFortScreen::RecruitArea::RecruitArea(int posX, int posY, const CGTownInstance *Town, BuildingID buildingID, int Level):
+const CCreature * CFortScreen::RecruitArea::getMyCreature()
+{
+	if (!town->creatures.at(level).second.empty()) // built
+		return VLC->creh->creatures[town->creatures.at(level).second.back()];
+	if (!town->town->creatures.at(level).empty()) // there are creatures on this level
+		return VLC->creh->creatures[town->town->creatures.at(level).front()];
+	return nullptr;
+}
+
+const CBuilding * CFortScreen::RecruitArea::getMyBuilding()
+{
+	BuildingID myID = BuildingID(BuildingID::DWELL_FIRST).advance(level);
+
+	if (level == GameConstants::CREATURES_PER_TOWN)
+		return town->town->buildings.at(BuildingID::PORTAL_OF_SUMMON);
+
+	if (!town->town->buildings.count(myID))
+		return nullptr;
+
+	const CBuilding * build = town->town->buildings.at(myID);
+	while (town->town->buildings.count(myID))
+	{
+		if (town->hasBuilt(myID))
+			build = town->town->buildings.at(myID);
+		myID.advance(GameConstants::CREATURES_PER_TOWN);
+	}
+	return build;
+}
+
+CFortScreen::RecruitArea::RecruitArea(int posX, int posY, const CGTownInstance *Town, int Level):
 	town(Town),
 	level(Level),
 	availableCount(nullptr)
@@ -1544,39 +1573,37 @@ CFortScreen::RecruitArea::RecruitArea(int posX, int posY, const CGTownInstance *
 		addUsedEvents(LCLICK | RCLICK | HOVER);//Activate only if dwelling is present
 	
 	icons = new CPicture("TPCAINFO", 261, 3);
-	buildingPic = new CAnimImage(town->town->clientInfo.buildingsIcons, buildingID, 0, 4, 21);
+	if (getMyBuilding() != nullptr)
+	{
+		buildingPic = new CAnimImage(town->town->clientInfo.buildingsIcons, getMyBuilding()->bid, 0, 4, 21);
+		dwellingName = new CLabel(78, 101, FONT_SMALL, CENTER, Colors::WHITE, getMyBuilding()->Name());
 
-	const CCreature* creature = nullptr;
+		if (vstd::contains(town->builtBuildings, getMyBuilding()->bid))
+		{
+			ui32 available = town->creatures[level].first;
+			std::string availableText = CGI->generaltexth->allTexts[217]+ boost::lexical_cast<std::string>(available);
+			availableCount = new CLabel(78, 119, FONT_SMALL, CENTER, Colors::WHITE, availableText);
+		}
+	}
 
-	if (!town->creatures[level].second.empty())
-		creature = CGI->creh->creatures[town->creatures[level].second.back()];
-	else
-		creature = CGI->creh->creatures[town->town->creatures[level][0]];
-
-	hoverText = boost::str(boost::format(CGI->generaltexth->tcommands[21]) % creature->namePl);
-	creatureAnim = new CCreaturePic(159, 4, creature, false);
-
-	Rect sizes(287, 4, 96, 18);
-	values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[190], CGI->generaltexth->fcommands[0], creature->Attack()));
-	sizes.y+=20;
-	values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[191], CGI->generaltexth->fcommands[1], creature->Defense()));
-	sizes.y+=21;
-	values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[199], CGI->generaltexth->fcommands[2], creature->getMinDamage(), creature->getMaxDamage()));
-	sizes.y+=20;
-	values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[388], CGI->generaltexth->fcommands[3], creature->MaxHealth()));
-	sizes.y+=21;
-	values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[193], CGI->generaltexth->fcommands[4], creature->valOfBonuses(Bonus::STACKS_SPEED)));
-	sizes.y+=20;
-	values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[194], CGI->generaltexth->fcommands[5], town->creatureGrowth(level)));
-
-	creatureName = new CLabel(78,  11, FONT_SMALL, CENTER, Colors::WHITE, creature->namePl);
-	dwellingName = new CLabel(78, 101, FONT_SMALL, CENTER, Colors::WHITE, town->town->buildings.at(buildingID)->Name());
-
-	if (vstd::contains(town->builtBuildings, buildingID))
+	if (getMyCreature() != nullptr)
 	{
-		ui32 available = town->creatures[level].first;
-		std::string availableText = CGI->generaltexth->allTexts[217]+ boost::lexical_cast<std::string>(available);
-		availableCount = new CLabel(78, 119, FONT_SMALL, CENTER, Colors::WHITE, availableText);
+		hoverText = boost::str(boost::format(CGI->generaltexth->tcommands[21]) % getMyCreature()->namePl);
+		creatureAnim = new CCreaturePic(159, 4, getMyCreature(), false);
+		creatureName = new CLabel(78,  11, FONT_SMALL, CENTER, Colors::WHITE, getMyCreature()->namePl);
+
+		Rect sizes(287, 4, 96, 18);
+		values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[190], CGI->generaltexth->fcommands[0], getMyCreature()->Attack()));
+		sizes.y+=20;
+		values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[191], CGI->generaltexth->fcommands[1], getMyCreature()->Defense()));
+		sizes.y+=21;
+		values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[199], CGI->generaltexth->fcommands[2], getMyCreature()->getMinDamage(), getMyCreature()->getMaxDamage()));
+		sizes.y+=20;
+		values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[388], CGI->generaltexth->fcommands[3], getMyCreature()->MaxHealth()));
+		sizes.y+=21;
+		values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[193], CGI->generaltexth->fcommands[4], getMyCreature()->valOfBonuses(Bonus::STACKS_SPEED)));
+		sizes.y+=20;
+		values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[194], CGI->generaltexth->fcommands[5], town->creatureGrowth(level)));
 	}
 }
 

+ 3 - 1
client/CCastleInterface.h

@@ -315,8 +315,10 @@ class CFortScreen : public CWindowObject
 		CAnimImage * buildingPic;
 		CCreaturePic *creatureAnim;
 
+		const CCreature * getMyCreature();
+		const CBuilding * getMyBuilding();
 	public:
-		RecruitArea(int posX, int posY, const CGTownInstance *town, BuildingID buildingID, int level);
+		RecruitArea(int posX, int posY, const CGTownInstance *town, int level);
 		
 		void creaturesChanged();
 		void hover(bool on);

+ 4 - 1
client/CPreGame.cpp

@@ -2862,7 +2862,10 @@ void OptionsTab::CPregameTooltipBox::genTownWindow()
 	const CTown * town = CGI->townh->factions[settings.castle]->town;
 
 	for (auto & elem : town->creatures)
-		components.push_back(new CComponent(CComponent::creature, elem.front(), 0, CComponent::tiny));
+	{
+		if (!elem.empty())
+			components.push_back(new CComponent(CComponent::creature, elem.front(), 0, CComponent::tiny));
+	}
 
 	new CComponentBox(components, Rect(10, 140, pos.w - 20, 140));
 }

+ 28 - 30
lib/CObjectHandler.cpp

@@ -344,7 +344,7 @@ std::set<int3> CGObjectInstance::getBlockedPos() const
 	{
 		for(int h=0; h<getHeight(); ++h)
 		{
-			if (appearance.isBlockedAt(-w, -h))
+			if (appearance.isBlockedAt(w, h))
 				ret.insert(int3(pos.x - w, pos.y - h, pos.z));
 		}
 	}
@@ -2050,20 +2050,11 @@ int CGTownInstance::mageGuildLevel() const
 	return 0;
 }
 
-int CGTownInstance::creatureDwellingLevel(int dwelling) const
-{
-	if ( dwelling<0 || dwelling >= GameConstants::CREATURES_PER_TOWN )
-		return -1;
-	for (int i=0; ; i++)
-	{
-		if (!hasBuilt(BuildingID(BuildingID::DWELL_FIRST+dwelling+i*GameConstants::CREATURES_PER_TOWN)))
-			return i-1;
-	}
-}
 int CGTownInstance::getHordeLevel(const int & HID)  const//HID - 0 or 1; returns creature level or -1 if that horde structure is not present
 {
 	return town->hordeLvl.at(HID);
 }
+
 int CGTownInstance::creatureGrowth(const int & level) const
 {
 	return getGrowthInfo(level).totalGrowth();
@@ -2243,12 +2234,16 @@ void CGTownInstance::initObj()
 		creatures.resize(GameConstants::CREATURES_PER_TOWN+1);//extra dwelling for Dungeon
 	else
 		creatures.resize(GameConstants::CREATURES_PER_TOWN);
-	for (int i = 0; i < GameConstants::CREATURES_PER_TOWN; i++)
+	for (int level = 0; level < GameConstants::CREATURES_PER_TOWN; level++)
 	{
-		int dwellingLevel = creatureDwellingLevel(i);
-		int creaturesTotal = town->creatures[i].size();
-		for (int j=0; j< std::min(dwellingLevel + 1, creaturesTotal); j++)
-			creatures[i].second.push_back(town->creatures[i][j]);
+		BuildingID buildID = BuildingID(BuildingID::DWELL_FIRST).advance(level);
+		int upgradeNum = 0;
+
+		for (; town->buildings.count(buildID); upgradeNum++, buildID.advance(GameConstants::CREATURES_PER_TOWN))
+		{
+			if (hasBuilt(buildID) && town->creatures.at(level).size() > upgradeNum)
+				creatures[level].second.push_back(town->creatures[level][upgradeNum]);
+		}
 	}
 
 	switch (subID)
@@ -2321,23 +2316,26 @@ void CGTownInstance::newTurn() const
 				}
 				if ((stacksCount() < GameConstants::ARMY_SIZE && rand()%100 < 25) || Slots().empty()) //add new stack
 				{
-					int i = rand() % std::min (GameConstants::ARMY_SIZE, cb->getDate(Date::MONTH)<<1);
-					CreatureID c = town->creatures[i][0];
-					SlotID n;
+					int i = rand() % std::min (GameConstants::CREATURES_PER_TOWN, cb->getDate(Date::MONTH)<<1);
+					if (!town->creatures[i].empty())
+					{
+						CreatureID c = town->creatures[i][0];
+						SlotID n;
 
-					TQuantity count = creatureGrowth(i);
-					if (!count) // no dwelling
-						count = VLC->creh->creatures[c]->growth;
+						TQuantity count = creatureGrowth(i);
+						if (!count) // no dwelling
+							count = VLC->creh->creatures[c]->growth;
 
-					{//no lower tiers or above current month
+						{//no lower tiers or above current month
 
-						if ((n = getSlotFor(c)).validSlot())
-						{
-							StackLocation sl(this, n);
-							if (slotEmpty(n))
-								cb->insertNewStack(sl, VLC->creh->creatures[c], count);
-							else //add to existing
-								cb->changeStackCount(sl, count);
+							if ((n = getSlotFor(c)).validSlot())
+							{
+								StackLocation sl(this, n);
+								if (slotEmpty(n))
+									cb->insertNewStack(sl, VLC->creh->creatures[c], count);
+								else //add to existing
+									cb->changeStackCount(sl, count);
+							}
 						}
 					}
 				}

+ 0 - 1
lib/CObjectHandler.h

@@ -694,7 +694,6 @@ public:
 	CGTownInstance::EFortLevel fortLevel() const;
 	int hallLevel() const; // -1 - none, 0 - village, 1 - town, 2 - city, 3 - capitol
 	int mageGuildLevel() const; // -1 - none, 0 - village, 1 - town, 2 - city, 3 - capitol
-	int creatureDwellingLevel(int dwelling) const;
 	int getHordeLevel(const int & HID) const; //HID - 0 or 1; returns creature level or -1 if that horde structure is not present
 	int creatureGrowth(const int & level) const;
 	GrowthInfo getGrowthInfo(int level) const;

+ 2 - 2
server/CGameHandler.cpp

@@ -1290,7 +1290,7 @@ void CGameHandler::newTurn()
 
 			for (int k=0; k < GameConstants::CREATURES_PER_TOWN; k++) //creature growths
 			{
-				if(t->creatureDwellingLevel(k) >= 0)//there is dwelling (k-level)
+				if (!t->creatures.at(k).second.empty()) // there are creatures at this level
 				{
 					ui32 &availableCount = sac.creatures.at(k).first;
 					const CCreature *cre = VLC->creh->creatures.at(t->creatures.at(k).second.back());
@@ -4853,7 +4853,7 @@ void CGameHandler::handleTownEvents(CGTownInstance * town, NewTurn &n)
 
 			for(si32 i=0;i<ev.creatures.size();i++) //creature growths
 			{
-				if(town->creatureDwellingLevel(i) >= 0 && ev.creatures.at(i))//there is dwelling
+				if(!town->creatures.at(i).second.empty() && ev.creatures.at(i) > 0)//there is dwelling
 				{
 					sac.creatures[i].first += ev.creatures.at(i);
 					iw.components.push_back(Component(Component::CREATURE,