فهرست منبع

check amount of artifacts on hero when checking requirements of a quest that requires artifact(s)

fixes case when 2 or more identical artifacts are required
Andrey Filipenkov 3 سال پیش
والد
کامیت
aa217236b3
6فایلهای تغییر یافته به همراه52 افزوده شده و 16 حذف شده
  1. 26 6
      lib/CArtHandler.cpp
  2. 2 0
      lib/CArtHandler.h
  3. 15 4
      lib/mapObjects/CQuest.cpp
  4. 4 1
      lib/mapObjects/CQuest.h
  5. 2 2
      lib/mapping/MapFormatH3M.cpp
  6. 3 3
      lib/rmg/TreasurePlacer.cpp

+ 26 - 6
lib/CArtHandler.cpp

@@ -1191,18 +1191,27 @@ CArtifactInstance* CArtifactSet::getArt(ArtifactPosition pos, bool excludeLocked
 
 ArtifactPosition CArtifactSet::getArtPos(int aid, bool onlyWorn, bool allowLocked) const
 {
+	const auto result = getAllArtPositions(aid, onlyWorn, allowLocked, false);
+	return result.empty() ? ArtifactPosition{ArtifactPosition::PRE_FIRST} : result[0];
+}
+
+std::vector<ArtifactPosition> CArtifactSet::getAllArtPositions(int aid, bool onlyWorn, bool allowLocked, bool getAll) const
+{
+	std::vector<ArtifactPosition> result;
 	for(auto i = artifactsWorn.cbegin(); i != artifactsWorn.cend(); i++)
 		if(i->second.artifact->artType->id == aid && (allowLocked || !i->second.locked))
-			return i->first;
+			result.push_back(i->first);
 
 	if(onlyWorn)
-		return ArtifactPosition::PRE_FIRST;
+		return result;
+	if(!getAll && !result.empty())
+		return result;
 
 	for(int i = 0; i < artifactsInBackpack.size(); i++)
 		if(artifactsInBackpack[i].artifact->artType->id == aid)
-			return ArtifactPosition(GameConstants::BACKPACK_START + i);
+			result.push_back(ArtifactPosition(GameConstants::BACKPACK_START + i));
 
-	return ArtifactPosition::PRE_FIRST;
+	return result;
 }
 
 ArtifactPosition CArtifactSet::getArtPos(const CArtifactInstance *art) const
@@ -1237,8 +1246,19 @@ bool CArtifactSet::hasArt(
     bool searchBackpackAssemblies,
 	bool allowLocked) const
 {
-	return getArtPos(aid, onlyWorn, allowLocked) != ArtifactPosition::PRE_FIRST ||
-	       (searchBackpackAssemblies && getHiddenArt(aid));
+	return getArtPosCount(aid, onlyWorn, searchBackpackAssemblies, allowLocked) > 0;
+}
+
+unsigned CArtifactSet::getArtPosCount(int aid, bool onlyWorn, bool searchBackpackAssemblies, bool allowLocked) const
+{
+	const auto allPositions = getAllArtPositions(aid, onlyWorn, allowLocked, true);
+	if(!allPositions.empty())
+		return allPositions.size();
+
+	if(searchBackpackAssemblies && getHiddenArt(aid))
+		return 1;
+
+	return 0;
 }
 
 std::pair<const CCombinedArtifactInstance *, const CArtifactInstance *>

+ 2 - 0
lib/CArtHandler.h

@@ -328,6 +328,7 @@ public:
 	/// (if more than one such artifact lower ID is returned)
 	ArtifactPosition getArtPos(int aid, bool onlyWorn = true, bool allowLocked = true) const;
 	ArtifactPosition getArtPos(const CArtifactInstance *art) const;
+	std::vector<ArtifactPosition> getAllArtPositions(int aid, bool onlyWorn, bool allowLocked, bool getAll) const;
 	const CArtifactInstance *getArtByInstanceId(ArtifactInstanceID artInstId) const;
 	/// Search for constituents of assemblies in backpack which do not have an ArtifactPosition
 	const CArtifactInstance *getHiddenArt(int aid) const;
@@ -335,6 +336,7 @@ public:
 	/// Checks if hero possess artifact of given id (either in backack or worn)
 	bool hasArt(ui32 aid, bool onlyWorn = false, bool searchBackpackAssemblies = false, bool allowLocked = true) const;
 	bool isPositionFree(ArtifactPosition pos, bool onlyLockCheck = false) const;
+	unsigned getArtPosCount(int aid, bool onlyWorn = true, bool searchBackpackAssemblies = true, bool allowLocked = true) const;
 
 	virtual ArtBearer::ArtBearer bearerType() const = 0;
 	virtual void putArtifact(ArtifactPosition pos, CArtifactInstance * art) = 0;

+ 15 - 4
lib/mapObjects/CQuest.cpp

@@ -112,11 +112,16 @@ bool CQuest::checkQuest(const CGHeroInstance * h) const
 				return true;
 			return false;
 		case MISSION_ART:
-			for(auto & elem : m5arts)
+			// if the object was deserialized
+			if(artifactsRequirements.empty())
+				for(auto id : m5arts)
+					++artifactsRequirements[id];
+
+			for(const auto & elem : artifactsRequirements)
 			{
-				if(h->hasArt(elem, false, true))
-					continue;
-				return false; //if the artifact was not found
+				// check required amount of artifacts
+				if(h->getArtPosCount(elem.first, false, true, true) < elem.second)
+					return false;
 			}
 			return true;
 		case MISSION_ARMY:
@@ -417,6 +422,12 @@ void CQuest::getCompletionText(MetaString &iwText, std::vector<Component> &compo
 	}
 }
 
+void CQuest::addArtifactID(ui16 id)
+{
+	m5arts.push_back(id);
+	++artifactsRequirements[id];
+}
+
 void CQuest::serializeJson(JsonSerializeFormat & handler, const std::string & fieldName)
 {
 	auto q = handler.enterStruct(fieldName);

+ 4 - 1
lib/mapObjects/CQuest.h

@@ -21,6 +21,8 @@ class CGCreature;
 
 class DLL_LINKAGE CQuest final
 {
+	mutable std::unordered_map<ui16, unsigned> artifactsRequirements; // artifact ID -> required count
+
 public:
 	enum Emission {MISSION_NONE = 0, MISSION_LEVEL = 1, MISSION_PRIMARY_STAT = 2, MISSION_KILL_HERO = 3, MISSION_KILL_CREATURE = 4,
 		MISSION_ART = 5, MISSION_ARMY = 6, MISSION_RESOURCES = 7, MISSION_HERO = 8, MISSION_PLAYER = 9, MISSION_KEYMASTER = 10};
@@ -34,7 +36,7 @@ public:
 
 	ui32 m13489val;
 	std::vector<ui32> m2stats;
-	std::vector<ui16> m5arts; //artifacts id
+	std::vector<ui16> m5arts; // artifact IDs. Add IDs through addArtifactID(), not directly to the field.
 	std::vector<CStackBasicDescriptor> m6creatures; //pair[cre id, cre count], CreatureSet info irrelevant
 	std::vector<ui32> m7resources; //TODO: use resourceset?
 
@@ -60,6 +62,7 @@ public:
 	virtual void getRolloverText (MetaString &text, bool onHover) const; //hover or quest log entry
 	virtual void completeQuest (const CGHeroInstance * h) const {};
 	virtual void addReplacements(MetaString &out, const std::string &base) const;
+	void addArtifactID(ui16 id);
 
 	bool operator== (const CQuest & quest) const
 	{

+ 2 - 2
lib/mapping/MapFormatH3M.cpp

@@ -1758,7 +1758,7 @@ CGSeerHut * CMapLoaderH3M::readSeerHut()
 		if (artID != 255)
 		{
 			//not none quest
-			hut->quest->m5arts.push_back (artID);
+			hut->quest->addArtifactID(artID);
 			hut->quest->missionType = CQuest::MISSION_ART;
 		}
 		else
@@ -1887,7 +1887,7 @@ void CMapLoaderH3M::readQuest(IQuestObject * guard)
 			for(int yy = 0; yy < artNumber; ++yy)
 			{
 				int artid = reader.readUInt16();
-				guard->quest->m5arts.push_back(artid);
+				guard->quest->addArtifactID(artid);
 				map->allowedArtifact[artid] = false; //these are unavailable for random generation
 			}
 			break;

+ 3 - 3
lib/rmg/TreasurePlacer.cpp

@@ -430,7 +430,7 @@ void TreasurePlacer::addAllPossibleObjects()
 				
 				obj->quest->missionType = CQuest::MISSION_ART;
 				ArtifactID artid = *RandomGeneratorUtil::nextItem(generator.getQuestArtsRemaning(), generator.rand);
-				obj->quest->m5arts.push_back(artid);
+				obj->quest->addArtifactID(artid);
 				obj->quest->lastDay = -1;
 				obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false;
 				
@@ -467,7 +467,7 @@ void TreasurePlacer::addAllPossibleObjects()
 				
 				obj->quest->missionType = CQuest::MISSION_ART;
 				ArtifactID artid = *RandomGeneratorUtil::nextItem(generator.getQuestArtsRemaning(), generator.rand);
-				obj->quest->m5arts.push_back(artid);
+				obj->quest->addArtifactID(artid);
 				obj->quest->lastDay = -1;
 				obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false;
 				
@@ -490,7 +490,7 @@ void TreasurePlacer::addAllPossibleObjects()
 				
 				obj->quest->missionType = CQuest::MISSION_ART;
 				ArtifactID artid = *RandomGeneratorUtil::nextItem(generator.getQuestArtsRemaning(), generator.rand);
-				obj->quest->m5arts.push_back(artid);
+				obj->quest->addArtifactID(artid);
 				obj->quest->lastDay = -1;
 				obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false;