浏览代码

Rewardable object randomization is now always server-sided

Ivan Savenko 1 年之前
父节点
当前提交
d2839c8e52

+ 2 - 0
client/Client.h

@@ -214,6 +214,8 @@ public:
 
 	void setObjPropertyValue(ObjectInstanceID objid, ObjProperty prop, int32_t value) override {};
 	void setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier) override {};
+	void setRewardableObjectConfiguration(ObjectInstanceID objid, const Rewardable::Configuration & configuration) override {};
+	void setRewardableObjectConfiguration(ObjectInstanceID townInstanceID, BuildingID buildingID, const Rewardable::Configuration & configuration) override{};
 
 	void showInfoDialog(InfoWindow * iw) override {};
 	void removeGUI() const;

+ 1 - 0
lib/CMakeLists.txt

@@ -529,6 +529,7 @@ set(lib_MAIN_HEADERS
 	networkPacks/PacksForClientBattle.h
 	networkPacks/PacksForLobby.h
 	networkPacks/PacksForServer.h
+	networkPacks/SetRewardableConfiguration.h
 	networkPacks/SetStackEffect.h
 	networkPacks/StackLocation.h
 	networkPacks/TradeItem.h

+ 7 - 0
lib/IGameCallback.h

@@ -37,6 +37,11 @@ namespace spells
 	class Caster;
 }
 
+namespace Rewardable
+{
+	struct Configuration;
+}
+
 #if SCRIPTING_ENABLED
 namespace scripting
 {
@@ -79,6 +84,8 @@ class DLL_LINKAGE IGameEventCallback
 {
 public:
 	virtual void setObjPropertyValue(ObjectInstanceID objid, ObjProperty prop, int32_t value = 0) = 0;
+	virtual void setRewardableObjectConfiguration(ObjectInstanceID mapObjectID, const Rewardable::Configuration & configuration) = 0;
+	virtual void setRewardableObjectConfiguration(ObjectInstanceID townInstanceID, BuildingID buildingID, const Rewardable::Configuration & configuration) = 0;
 	virtual void setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier) = 0;
 
 	virtual void showInfoDialog(InfoWindow * iw) = 0;

+ 19 - 9
lib/mapObjectConstructors/CRewardableConstructor.cpp

@@ -40,19 +40,29 @@ CGObjectInstance * CRewardableConstructor::create(IGameCallback * cb, std::share
 	return ret;
 }
 
-void CRewardableConstructor::configureObject(CGObjectInstance * object, vstd::RNG & rng) const
+Rewardable::Configuration CRewardableConstructor::generateConfiguration(IGameCallback * cb, vstd::RNG & rand, MapObjectID objectID) const
 {
-	if(auto * rewardableObject = dynamic_cast<CRewardableObject*>(object))
+	Rewardable::Configuration result;
+	objectInfo.configureObject(result, rand, cb);
+
+	for(auto & rewardInfo : result.info)
 	{
-		objectInfo.configureObject(rewardableObject->configuration, rng, object->cb);
-		for(auto & rewardInfo : rewardableObject->configuration.info)
+		for (auto & bonus : rewardInfo.reward.bonuses)
 		{
-			for (auto & bonus : rewardInfo.reward.bonuses)
-			{
-				bonus.source = BonusSource::OBJECT_TYPE;
-				bonus.sid = BonusSourceID(rewardableObject->ID);
-			}
+			bonus.source = BonusSource::OBJECT_TYPE;
+			bonus.sid = BonusSourceID(objectID);
 		}
+	}
+
+	return result;
+}
+
+void CRewardableConstructor::configureObject(CGObjectInstance * object, vstd::RNG & rng) const
+{
+	if(auto * rewardableObject = dynamic_cast<CRewardableObject*>(object))
+	{
+		rewardableObject->configuration = generateConfiguration(object->cb, rng, object->ID);
+
 		if (rewardableObject->configuration.info.empty())
 		{
 			if (objectInfo.getParameters()["rewards"].isNull())

+ 2 - 0
lib/mapObjectConstructors/CRewardableConstructor.h

@@ -30,6 +30,8 @@ public:
 	void configureObject(CGObjectInstance * object, vstd::RNG & rng) const override;
 
 	std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const override;
+
+	Rewardable::Configuration generateConfiguration(IGameCallback * cb, vstd::RNG & rand, MapObjectID objectID) const;
 };
 
 VCMI_LIB_NAMESPACE_END

+ 11 - 9
lib/mapObjects/CGTownBuilding.cpp

@@ -328,11 +328,16 @@ CTownRewardableBuilding::CTownRewardableBuilding(const BuildingID & index, Build
 void CTownRewardableBuilding::initObj(vstd::RNG & rand)
 {
 	assert(town && town->town);
+	configuration = generateConfiguration(rand);
+}
 
+Rewardable::Configuration CTownRewardableBuilding::generateConfiguration(vstd::RNG & rand) const
+{
+	Rewardable::Configuration result;
 	auto building = town->town->buildings.at(bID);
 
-	building->rewardableObjectInfo.configureObject(configuration, rand, cb);
-	for(auto & rewardInfo : configuration.info)
+	building->rewardableObjectInfo.configureObject(result, rand, cb);
+	for(auto & rewardInfo : result.info)
 	{
 		for (auto & bonus : rewardInfo.reward.bonuses)
 		{
@@ -340,16 +345,16 @@ void CTownRewardableBuilding::initObj(vstd::RNG & rand)
 			bonus.sid = BonusSourceID(building->getUniqueTypeID());
 		}
 	}
+	return result;
 }
 
 void CTownRewardableBuilding::newTurn(vstd::RNG & rand) const
 {
 	if (configuration.resetParameters.period != 0 && cb->getDate(Date::DAY) > 1 && ((cb->getDate(Date::DAY)-1) % configuration.resetParameters.period) == 0)
 	{
-		if(configuration.resetParameters.rewards)
-		{
-			cb->setObjPropertyValue(town->id, ObjProperty::REWARD_RANDOMIZE, indexOnTV);
-		}
+		auto newConfiguration = generateConfiguration(rand);
+		cb->setRewardableObjectConfiguration(town->id, bID, newConfiguration);
+
 		if(configuration.resetParameters.visitors)
 		{
 			cb->setObjPropertyValue(town->id, ObjProperty::STRUCTURE_CLEAR_VISITORS, indexOnTV);
@@ -367,9 +372,6 @@ void CTownRewardableBuilding::setProperty(ObjProperty what, ObjPropertyID identi
 		case ObjProperty::STRUCTURE_CLEAR_VISITORS:
 			visitors.clear();
 			break;
-		case ObjProperty::REWARD_RANDOMIZE:
-			initObj(cb->gameState()->getRandomGenerator());
-			break;
 		case ObjProperty::REWARD_SELECT:
 			selectedReward = identifier.getNum();
 			break;

+ 2 - 0
lib/mapObjects/CGTownBuilding.h

@@ -118,6 +118,8 @@ class DLL_LINKAGE CTownRewardableBuilding : public CGTownBuilding, public Reward
 	bool wasVisitedBefore(const CGHeroInstance * contextHero) const;
 	
 	void grantReward(ui32 rewardID, const CGHeroInstance * hero) const;
+
+	Rewardable::Configuration generateConfiguration(vstd::RNG & rand) const;
 	
 public:
 	void setProperty(ObjProperty what, ObjPropertyID identifier) override;

+ 0 - 3
lib/mapObjects/CGTownInstance.cpp

@@ -71,9 +71,6 @@ void CGTownInstance::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
 		case ObjProperty::BONUS_VALUE_SECOND:
 			bonusValue.second = identifier.getNum();
 			break;
-		case ObjProperty::REWARD_RANDOMIZE:
-			bonusingBuildings[identifier.getNum()]->setProperty(ObjProperty::REWARD_RANDOMIZE, NumericID(0));
-			break;
 	}
 }
 CGTownInstance::EFortLevel CGTownInstance::fortLevel() const //0 - none, 1 - fort, 2 - citadel, 3 - castle

+ 4 - 4
lib/mapObjects/CRewardableObject.cpp

@@ -16,6 +16,7 @@
 #include "../IGameCallback.h"
 #include "../mapObjectConstructors/AObjectTypeHandler.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
+#include "../mapObjectConstructors/CRewardableConstructor.h"
 #include "../mapObjects/CGHeroInstance.h"
 #include "../networkPacks/PacksForClient.h"
 #include "../serializer/JsonSerializeFormat.h"
@@ -377,9 +378,6 @@ void CRewardableObject::setPropertyDer(ObjProperty what, ObjPropertyID identifie
 {
 	switch (what)
 	{
-		case ObjProperty::REWARD_RANDOMIZE:
-			initObj(cb->gameState()->getRandomGenerator());
-			break;
 		case ObjProperty::REWARD_SELECT:
 			selectedReward = identifier.getNum();
 			break;
@@ -395,7 +393,9 @@ void CRewardableObject::newTurn(vstd::RNG & rand) const
 	{
 		if (configuration.resetParameters.rewards)
 		{
-			cb->setObjPropertyValue(id, ObjProperty::REWARD_RANDOMIZE, 0);
+			auto handler = std::dynamic_pointer_cast<const CRewardableConstructor>(getObjectHandler());
+			auto newConfiguration = handler->generateConfiguration(cb, rand, ID);
+			cb->setRewardableObjectConfiguration(id, newConfiguration);
 		}
 		if (configuration.resetParameters.visitors)
 		{

+ 3 - 0
lib/mapObjects/IObjectInterface.h

@@ -50,6 +50,9 @@ public:
 
 	virtual void onHeroVisit(const CGHeroInstance * h) const;
 	virtual void onHeroLeave(const CGHeroInstance * h) const;
+
+	/// Called on new turn by server. This method can not modify object state on its own
+	/// Instead all changes must be propagated via netpacks
 	virtual void newTurn(vstd::RNG & rand) const;
 	virtual void initObj(vstd::RNG & rand); //synchr
 	virtual void pickRandomObject(vstd::RNG & rand);

+ 2 - 0
lib/networkPacks/NetPackVisitor.h

@@ -13,6 +13,7 @@
 #include "PacksForClientBattle.h"
 #include "PacksForServer.h"
 #include "PacksForLobby.h"
+#include "SetRewardableConfiguration.h"
 #include "SetStackEffect.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
@@ -34,6 +35,7 @@ public:
 	virtual void visitTurnTimeUpdate(TurnTimeUpdate & pack) {}
 	virtual void visitGamePause(GamePause & pack) {}
 	virtual void visitEntitiesChanged(EntitiesChanged & pack) {}
+	virtual void visitSetRewardableConfiguration(SetRewardableConfiguration & pack) {}
 	virtual void visitSetResources(SetResources & pack) {}
 	virtual void visitSetPrimSkill(SetPrimSkill & pack) {}
 	virtual void visitSetSecSkill(SetSecSkill & pack) {}

+ 16 - 0
lib/networkPacks/NetPacksLib.cpp

@@ -12,6 +12,7 @@
 #include "PacksForClient.h"
 #include "PacksForClientBattle.h"
 #include "PacksForServer.h"
+#include "SetRewardableConfiguration.h"
 #include "StackLocation.h"
 #include "PacksForLobby.h"
 #include "SetStackEffect.h"
@@ -123,6 +124,11 @@ void EntitiesChanged::visitTyped(ICPackVisitor & visitor)
 	visitor.visitEntitiesChanged(*this);
 }
 
+void SetRewardableConfiguration::visitTyped(ICPackVisitor & visitor)
+{
+	visitor.visitSetRewardableConfiguration(*this);
+}
+
 void SetResources::visitTyped(ICPackVisitor & visitor)
 {
 	visitor.visitSetResources(*this);
@@ -2479,6 +2485,16 @@ void EntitiesChanged::applyGs(CGameState * gs)
 		gs->updateEntity(change.metatype, change.entityIndex, change.data);
 }
 
+void SetRewardableConfiguration::applyGs(CGameState * gs)
+{
+	auto * objectPtr = gs->getObjInstance(objectID);
+	auto * rewardablePtr = dynamic_cast<CRewardableObject *>(objectPtr);
+
+	assert(rewardablePtr);
+
+	rewardablePtr->configuration = configuration;
+}
+
 const CArtifactInstance * ArtSlotInfo::getArt() const
 {
 	if(locked)

+ 0 - 1
lib/networkPacks/ObjProperty.h

@@ -47,7 +47,6 @@ enum class ObjProperty : int8_t
 	BANK_CLEAR,
 
 	//object with reward
-	REWARD_RANDOMIZE,
 	REWARD_SELECT,
 	REWARD_CLEARED
 };

+ 35 - 0
lib/networkPacks/SetRewardableConfiguration.h

@@ -0,0 +1,35 @@
+/*
+ * SetRewardableConfiguration.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "NetPacksBase.h"
+
+#include "../rewardable/Configuration.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+struct DLL_LINKAGE SetRewardableConfiguration : public CPackForClient
+{
+	void applyGs(CGameState * gs);
+	void visitTyped(ICPackVisitor & visitor) override;
+
+	ObjectInstanceID objectID;
+	BuildingID buildingID;
+	Rewardable::Configuration configuration;
+
+	template <typename Handler> void serialize(Handler & h)
+	{
+		h & objectID;
+		h & buildingID;
+		h & configuration;
+	}
+};
+
+VCMI_LIB_NAMESPACE_END

+ 3 - 0
lib/registerTypes/RegisterTypesClientPacks.h

@@ -12,6 +12,7 @@
 #include "../networkPacks/PacksForClient.h"
 #include "../networkPacks/PacksForClientBattle.h"
 #include "../networkPacks/SetStackEffect.h"
+#include "../networkPacks/SetRewardableConfiguration.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -119,6 +120,8 @@ void registerTypesClientPacks(Serializer &s)
 	s.template registerType<CPackForClient, PlayerMessageClient>();
 	s.template registerType<CGarrisonOperationPack, BulkRebalanceStacks>();
 	s.template registerType<CGarrisonOperationPack, BulkSmartRebalanceStacks>();
+
+	s.template registerType<SetRewardableConfiguration, CPackForClient>();
 }
 
 VCMI_LIB_NAMESPACE_END

+ 17 - 0
server/CGameHandler.cpp

@@ -4374,6 +4374,23 @@ void CGameHandler::setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, Ob
 	sendAndApply(&sob);
 }
 
+void CGameHandler::setRewardableObjectConfiguration(ObjectInstanceID objid, const Rewardable::Configuration & configuration)
+{
+	SetRewardableConfiguration srb;
+	srb.objectID = objid;
+	srb.configuration = configuration;
+	sendAndApply(&srb);
+}
+
+void CGameHandler::setRewardableObjectConfiguration(ObjectInstanceID townInstanceID, BuildingID buildingID, const Rewardable::Configuration & configuration)
+{
+	SetRewardableConfiguration srb;
+	srb.objectID = townInstanceID;
+	srb.buildingID = buildingID;
+	srb.configuration = configuration;
+	sendAndApply(&srb);
+}
+
 void CGameHandler::showInfoDialog(InfoWindow * iw)
 {
 	sendAndApply(iw);

+ 2 - 0
server/CGameHandler.h

@@ -162,6 +162,8 @@ public:
 	bool isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, const CGHeroInstance *hero) override;
 	void setObjPropertyValue(ObjectInstanceID objid, ObjProperty prop, int32_t value) override;
 	void setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier) override;
+	void setRewardableObjectConfiguration(ObjectInstanceID objid, const Rewardable::Configuration & configuration) override;
+	void setRewardableObjectConfiguration(ObjectInstanceID townInstanceID, BuildingID buildingID, const Rewardable::Configuration & configuration) override;
 	void showInfoDialog(InfoWindow * iw) override;
 
 	//////////////////////////////////////////////////////////////////////////