Pārlūkot izejas kodu

Check if building's dependencies are allowed

Piotr Wójcik 9 gadi atpakaļ
vecāks
revīzija
40003460ca

+ 22 - 0
lib/CGameInfoCallback.cpp

@@ -520,6 +520,28 @@ EBuildingState::EBuildingState CGameInfoCallback::canBuildStructure( const CGTow
 	if(vstd::contains(t->forbiddenBuildings, ID))
 		return EBuildingState::FORBIDDEN; //forbidden
 
+	std::function<bool(BuildingID id)> allowedTest;
+	std::function<bool(BuildingID id)> possiblyNotBuiltTest;
+
+	allowedTest = [&](BuildingID id) -> bool
+	{
+		if (vstd::contains(t->forbiddenBuildings, id))
+		{
+			return false;
+		}
+
+		return t->genBuildingRequirements(id, true).satisfiable(allowedTest, possiblyNotBuiltTest);
+	};
+
+	possiblyNotBuiltTest = [&](BuildingID id) -> bool
+	{
+		//TODO consider destroing
+		return !t->hasBuilt(id);
+	};
+
+	if (!t->genBuildingRequirements(ID, true).satisfiable(allowedTest, possiblyNotBuiltTest))
+		return EBuildingState::FORBIDDEN;
+
 	if(ID == BuildingID::CAPITOL)
 	{
 		const PlayerState *ps = getPlayer(t->tempOwner, false);

+ 141 - 0
lib/LogicalExpression.h

@@ -99,6 +99,123 @@ namespace LogicalExpressionDetail
 		}
 	};
 
+	template <typename ContainedClass>
+	class FalsifiabilityVisitor;
+
+	/// Visitor to test whether expression's value can be true
+	template <typename ContainedClass>
+	class SatisfiabilityVisitor : public boost::static_visitor<bool>
+	{
+		typedef ExpressionBase<ContainedClass> Base;
+
+		std::function<bool(const typename Base::Value &)> satisfiabilityTest;
+		FalsifiabilityVisitor<ContainedClass> *falsifiabilityVisitor;
+
+		size_t countSatisfiable(const std::vector<typename Base::Variant> & element) const
+		{
+			return boost::range::count_if(element, [&](const typename Base::Variant & expr)
+			{
+				return boost::apply_visitor(*this, expr);
+			});
+		}
+
+		size_t countFalsifiable(const std::vector<typename Base::Variant> & element) const
+		{
+			return boost::range::count_if(element, [&](const typename Base::Variant & expr)
+			{
+				return boost::apply_visitor(*falsifiabilityVisitor, expr);
+			});
+		}
+
+	public:
+		SatisfiabilityVisitor(std::function<bool (const typename Base::Value &)> satisfiabilityTest):
+			satisfiabilityTest(satisfiabilityTest),
+			falsifiabilityVisitor(nullptr)
+		{}
+
+		void setFalsifiabilityVisitor(FalsifiabilityVisitor<ContainedClass> *falsifiabilityVisitor)
+		{
+			this->falsifiabilityVisitor = falsifiabilityVisitor;
+		}
+
+		bool operator()(const typename Base::OperatorAny & element) const
+		{
+			return countSatisfiable(element.expressions) != 0;
+		}
+
+		bool operator()(const typename Base::OperatorAll & element) const
+		{
+			return countSatisfiable(element.expressions) == element.expressions.size();
+		}
+
+		bool operator()(const typename Base::OperatorNone & element) const
+		{
+			return countFalsifiable(element.expressions) == element.expressions.size();
+		}
+
+		bool operator()(const typename Base::Value & element) const
+		{
+			return satisfiabilityTest(element);
+		}
+	};
+
+	/// Visitor to test whether expression's value can be false
+	template <typename ContainedClass>
+	class FalsifiabilityVisitor : public boost::static_visitor<bool>
+	{
+		typedef ExpressionBase<ContainedClass> Base;
+
+		std::function<bool(const typename Base::Value &)> falsifiabilityTest;
+		SatisfiabilityVisitor<ContainedClass> *satisfiabilityVisitor;
+
+		size_t countSatisfiable(const std::vector<typename Base::Variant> & element) const
+		{
+			return boost::range::count_if(element, [&](const typename Base::Variant & expr)
+			{
+				return boost::apply_visitor(*satisfiabilityVisitor, expr);
+			});
+		}
+
+		size_t countFalsifiable(const std::vector<typename Base::Variant> & element) const
+		{
+			return boost::range::count_if(element, [&](const typename Base::Variant & expr)
+			{
+				return boost::apply_visitor(*this, expr);
+			});
+		}
+
+	public:
+		FalsifiabilityVisitor(std::function<bool (const typename Base::Value &)> falsifiabilityTest):
+			falsifiabilityTest(falsifiabilityTest),
+			satisfiabilityVisitor(nullptr)
+		{}
+
+		void setFalsifiabilityVisitor(SatisfiabilityVisitor<ContainedClass> *satisfiabilityVisitor)
+		{
+			this->satisfiabilityVisitor = satisfiabilityVisitor;
+		}
+
+		bool operator()(const typename Base::OperatorAny & element) const
+		{
+			return countFalsifiable(element.expressions) == element.expressions.size();
+		}
+
+		bool operator()(const typename Base::OperatorAll & element) const
+		{
+			return countFalsifiable(element.expressions) != 0;
+		}
+
+		bool operator()(const typename Base::OperatorNone & element) const
+		{
+			return countSatisfiable(element.expressions) != 0;
+		}
+
+		bool operator()(const typename Base::Value & element) const
+		{
+			return falsifiabilityTest(element);
+		}
+	};
+
 	/// visitor that is trying to generates candidates that must be fulfilled
 	/// to complete this expression
 	template <typename ContainedClass>
@@ -436,6 +553,30 @@ public:
 		return boost::apply_visitor(testVisitor, data);
 	}
 
+	/// calculates if expression can evaluate to "true".
+	bool satisfiable (std::function<bool(const Value &)> satisfiabilityTest, std::function<bool(const Value &)> falsifiabilityTest) const
+	{
+		LogicalExpressionDetail::SatisfiabilityVisitor<Value> satisfiabilityVisitor(satisfiabilityTest);
+		LogicalExpressionDetail::FalsifiabilityVisitor<Value> falsifiabilityVisitor(falsifiabilityTest);
+
+		satisfiabilityVisitor.setFalsifiabilityVisitor(&falsifiabilityVisitor);
+		falsifiabilityVisitor.setFalsifiabilityVisitor(&satisfiabilityVisitor);
+
+		return boost::apply_visitor(satisfiabilityVisitor, data);
+	}
+
+	/// calculates if expression can evaluate to "false".
+	bool falsifiable(std::function<bool(const Value &)> satisfiabilityTest, std::function<bool(const Value &)> falsifiabilityTest) const
+	{
+		LogicalExpressionDetail::SatisfiabilityVisitor<Value> satisfiabilityVisitor(satisfiabilityTest);
+		LogicalExpressionDetail::FalsifiabilityVisitor<Value> falsifiabilityVisitor(falsifiabilityTest);
+
+		satisfiabilityVisitor.setFalsifiabilityVisitor(&falsifiabilityVisitor);
+		falsifiabilityVisitor.setFalsifiabilityVisitor(&satisfiabilityVisitor);
+
+		return boost::apply_visitor(falsifiabilityVisitor, data);
+	}
+
 	/// generates list of candidates that can be fulfilled by caller (like AI)
 	std::vector<Value> getFulfillmentCandidates(std::function<bool(const Value &)> toBool) const
 	{

+ 9 - 4
lib/mapObjects/CGTownInstance.cpp

@@ -1126,7 +1126,7 @@ bool CGTownInstance::hasBuilt(BuildingID buildingID) const
 	return vstd::contains(builtBuildings, buildingID);
 }
 
-CBuilding::TRequired CGTownInstance::genBuildingRequirements(BuildingID buildID) const
+CBuilding::TRequired CGTownInstance::genBuildingRequirements(BuildingID buildID, bool deep) const
 {
 	const CBuilding * building = town->buildings.at(buildID);
 
@@ -1134,17 +1134,22 @@ CBuilding::TRequired CGTownInstance::genBuildingRequirements(BuildingID buildID)
 	[&](const BuildingID & id) -> CBuilding::TRequired::Variant
 	{
 		const CBuilding * build = town->buildings.at(id);
+		CBuilding::TRequired::OperatorAll requirements;
 
 		if (!hasBuilt(id))
-			return id;
+		{
+			requirements.expressions.push_back(id);
 
-		CBuilding::TRequired::OperatorAll requirements;
+			if (!deep)
+			{
+				return requirements;
+			}
+		}
 
 		if (build->upgrade != BuildingID::NONE)
 			requirements.expressions.push_back(dependTest(build->upgrade));
 
 		requirements.expressions.push_back(build->requirements.morph(dependTest));
-
 		return requirements;
 	};
 

+ 1 - 1
lib/mapObjects/CGTownInstance.h

@@ -238,7 +238,7 @@ public:
 	bool armedGarrison() const; //true if town has creatures in garrison or garrisoned hero
 	int getTownLevel() const;
 
-	CBuilding::TRequired genBuildingRequirements(BuildingID build) const;
+	CBuilding::TRequired genBuildingRequirements(BuildingID build, bool deep = false) const;
 
 	void mergeGarrisonOnSiege() const; // merge garrison into army of visiting hero
 	void removeCapitols (PlayerColor owner) const;