浏览代码

Unified income handling, added IOwnableObject interface

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

+ 1 - 3
AI/Nullkiller/Analyzers/BuildAnalyzer.cpp

@@ -314,9 +314,7 @@ void BuildAnalyzer::updateDailyIncome()
 		const CGMine* mine = dynamic_cast<const CGMine*>(obj);
 
 		if(mine)
-		{
-			dailyIncome[mine->producedResource.getNum()] += mine->getProducedQuantity();
-		}
+			dailyIncome += mine->dailyIncome();
 	}
 
 	for(const CGTownInstance* town : towns)

+ 2 - 4
client/windows/CKingdomInterface.cpp

@@ -585,9 +585,7 @@ void CKingdomInterface::generateMinesList(const std::vector<const CGObjectInstan
 			const CGMine * mine = dynamic_cast<const CGMine *>(object);
 			assert(mine);
 			minesCount[mine->producedResource]++;
-
-			if (mine->producedResource == EGameResID::GOLD)
-				totalIncome += mine->getProducedQuantity();
+			totalIncome += mine->dailyIncome()[EGameResID::GOLD];
 		}
 	}
 
@@ -596,7 +594,7 @@ void CKingdomInterface::generateMinesList(const std::vector<const CGObjectInstan
 	auto * playerSettings = LOCPLINT->cb->getPlayerSettings(LOCPLINT->playerID);
 	for(auto & hero : heroes)
 	{
-		totalIncome += hero->valOfBonuses(Selector::typeSubtype(BonusType::GENERATE_RESOURCE, BonusSubtypeID(GameResID(EGameResID::GOLD)))) * playerSettings->handicap.percentIncome / 100;
+		totalIncome += hero->dailyIncome()[EGameResID::GOLD];
 	}
 
 	//Add town income of all towns

+ 1 - 0
lib/CMakeLists.txt

@@ -504,6 +504,7 @@ set(lib_MAIN_HEADERS
 	mapObjects/CRewardableObject.h
 	mapObjects/IMarket.h
 	mapObjects/IObjectInterface.h
+	mapObjects/IOwnableObject.h
 	mapObjects/MapObjects.h
 	mapObjects/MiscObjects.h
 	mapObjects/ObjectTemplate.h

+ 6 - 0
lib/ResourceSet.cpp

@@ -64,6 +64,12 @@ void ResourceSet::positive()
 		vstd::amax(elem, 0);
 }
 
+void ResourceSet::applyHandicap(int percentage)
+{
+	for(auto & elem : *this)
+		elem = vstd::divideAndCeil(elem * percentage, 100);
+}
+
 static bool canAfford(const ResourceSet &res, const ResourceSet &price)
 {
 	assert(res.size() == price.size() && price.size() == GameConstants::RESOURCE_QUANTITY);

+ 1 - 0
lib/ResourceSet.h

@@ -190,6 +190,7 @@ public:
 	DLL_LINKAGE void amax(const TResourceCap &val); //performs vstd::amax on each element
 	DLL_LINKAGE void amin(const TResourceCap &val); //performs vstd::amin on each element
 	DLL_LINKAGE void positive(); //values below 0 are set to 0 - upgrade cost can't be negative, for example
+	DLL_LINKAGE void applyHandicap(int percentage);
 	DLL_LINKAGE bool nonZero() const; //returns true if at least one value is non-zero;
 	DLL_LINKAGE bool canAfford(const ResourceSet &price) const;
 	DLL_LINKAGE bool canBeAfforded(const ResourceSet &res) const;

+ 2 - 4
lib/gameState/GameStatistics.cpp

@@ -255,20 +255,18 @@ si64 Statistic::getTotalExperience(const PlayerState * ps)
 // get total gold income
 int Statistic::getIncome(const CGameState * gs, const PlayerState * ps)
 {
-	int percentIncome = gs->getStartInfo()->getIthPlayersSettings(ps->color).handicap.percentIncome;
 	int totalIncome = 0;
 
 	//Heroes can produce gold as well - skill, specialty or arts
 	for(const auto & h : ps->heroes)
-		totalIncome += h->valOfBonuses(Selector::typeSubtype(BonusType::GENERATE_RESOURCE, BonusSubtypeID(GameResID(GameResID::GOLD)))) * percentIncome / 100;
+		totalIncome += h->dailyIncome()[EGameResID::GOLD];
 
 	//Add town income of all towns
 	for(const auto & t : ps->towns)
 		totalIncome += t->dailyIncome()[EGameResID::GOLD];
 
 	for(const CGMine * mine : getMines(gs, ps))
-		if(mine->producedResource == EGameResID::GOLD)
-			totalIncome += mine->getProducedQuantity();
+			totalIncome += mine->dailyIncome()[EGameResID::GOLD];
 
 	return totalIncome;
 }

+ 18 - 0
lib/mapObjects/CGHeroInstance.cpp

@@ -1822,4 +1822,22 @@ bool CGHeroInstance::isCampaignGem() const
 	return true;
 }
 
+ResourceSet CGHeroInstance::dailyIncome() const
+{
+	ResourceSet income;
+
+	for (GameResID k : GameResID::ALL_RESOURCES())
+		income[k] += valOfBonuses(BonusType::GENERATE_RESOURCE, BonusSubtypeID(k));
+
+	const auto & playerSettings = cb->getPlayerSettings(getOwner());
+	income.applyHandicap(playerSettings->handicap.percentIncome);
+	return income;
+}
+
+const IOwnableObject * CGHeroInstance::asOwnable() const
+{
+	return this;
+}
+
+
 VCMI_LIB_NAMESPACE_END

+ 5 - 1
lib/mapObjects/CGHeroInstance.h

@@ -12,6 +12,7 @@
 #include <vcmi/spells/Caster.h>
 
 #include "CArmedInstance.h"
+#include "IOwnableObject.h"
 
 #include "../CArtHandler.h" // For CArtifactSet
 
@@ -48,7 +49,7 @@ protected:
 };
 
 
-class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet, public spells::Caster, public AFactionMember, public ICreatureUpgrader
+class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet, public spells::Caster, public AFactionMember, public ICreatureUpgrader, public IOwnableObject
 {
 	// We serialize heroes into JSON for crossover
 	friend class CampaignState;
@@ -165,6 +166,9 @@ public:
 	EAlignment getAlignment() const;
 	bool needsLastStack()const override;
 
+	ResourceSet dailyIncome() const override;
+	const IOwnableObject * asOwnable() const final;
+
 	//INativeTerrainProvider
 	FactionID getFaction() const override;
 	TerrainId getNativeTerrain() const override;

+ 5 - 0
lib/mapObjects/CGObjectInstance.cpp

@@ -395,4 +395,9 @@ BattleField CGObjectInstance::getBattlefield() const
 	return VLC->objtypeh->getHandlerFor(ID, subID)->getBattlefield();
 }
 
+const IOwnableObject * CGObjectInstance::asOwnable() const
+{
+	return nullptr;
+}
+
 VCMI_LIB_NAMESPACE_END

+ 2 - 0
lib/mapObjects/CGObjectInstance.h

@@ -126,6 +126,8 @@ public:
 	virtual std::vector<Component> getPopupComponents(PlayerColor player) const;
 	virtual std::vector<Component> getPopupComponents(const CGHeroInstance * hero) const;
 
+	const IOwnableObject * asOwnable() const override;
+
 	/** OVERRIDES OF IObjectInterface **/
 
 	void initObj(vstd::RNG & rand) override;

+ 7 - 4
lib/mapObjects/CGTownInstance.cpp

@@ -225,13 +225,16 @@ TResources CGTownInstance::dailyIncome() const
 		}
 	}
 
-	auto playerSettings = cb->gameState()->scenarioOps->getIthPlayersSettings(getOwner());
-	for(TResources::nziterator it(ret); it.valid(); it++)
-		// always round up income - we don't want to always produce zero if handicap in use
-		ret[it->resType] = vstd::divideAndCeil(ret[it->resType] * playerSettings.handicap.percentIncome, 100);
+	const auto & playerSettings = cb->getPlayerSettings(getOwner());
+	ret.applyHandicap(playerSettings->handicap.percentIncome);
 	return ret;
 }
 
+const IOwnableObject * CGTownInstance::asOwnable() const
+{
+	return this;
+}
+
 bool CGTownInstance::hasFort() const
 {
 	return hasBuilt(BuildingID::FORT);

+ 5 - 2
lib/mapObjects/CGTownInstance.h

@@ -11,6 +11,7 @@
 
 #include "IMarket.h"
 #include "CGDwelling.h"
+#include "IOwnableObject.h"
 #include "../entities/faction/CFaction.h" // TODO: remove
 #include "../entities/faction/CTown.h" // TODO: remove
 
@@ -47,7 +48,7 @@ struct DLL_LINKAGE GrowthInfo
 	int handicapPercentage;
 };
 
-class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public IMarket, public INativeTerrainProvider, public ICreatureUpgrader
+class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public IMarket, public INativeTerrainProvider, public ICreatureUpgrader, public IOwnableObject
 {
 	std::string nameTextId; // name of town
 
@@ -181,7 +182,9 @@ public:
 	std::set<BuildingID> getBuildings() const;
 
 	TResources getBuildingCost(const BuildingID & buildingID) const;
-	TResources dailyIncome() const; //calculates daily income of this town
+	ResourceSet dailyIncome() const override;
+	const IOwnableObject * asOwnable() const final;
+
 	int spellsAtLevel(int level, bool checkGuild) const; //levels are counted from 1 (1 - 5)
 	bool armedGarrison() const; //true if town has creatures in garrison or garrisoned hero
 	int getTownLevel() const;

+ 3 - 0
lib/mapObjects/IObjectInterface.h

@@ -33,6 +33,7 @@ class ResourceSet;
 class int3;
 class MetaString;
 class PlayerColor;
+class IOwnableObject;
 
 class DLL_LINKAGE IObjectInterface : public GameCallbackHolder, public virtual Serializeable
 {
@@ -68,6 +69,8 @@ public:
 	//unified helper to show info dialog for object owner
 	virtual void showInfoDialog(const ui32 txtID, const ui16 soundID = 0, EInfoWindowMode mode = EInfoWindowMode::AUTO) const;
 
+	virtual const IOwnableObject * asOwnable() const = 0;
+
 	//unified interface, AI helpers
 	virtual bool wasVisited (PlayerColor player) const;
 	virtual bool wasVisited (const CGHeroInstance * h) const;

+ 23 - 0
lib/mapObjects/IOwnableObject.h

@@ -0,0 +1,23 @@
+/*
+* IOwnableObject.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
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class ResourceSet;
+
+class DLL_LINKAGE IOwnableObject
+{
+public:
+	virtual ResourceSet dailyIncome() const = 0;
+	virtual ~IOwnableObject() = default;
+};
+
+VCMI_LIB_NAMESPACE_END

+ 8 - 12
lib/mapObjects/MiscObjects.cpp

@@ -93,18 +93,6 @@ void CGMine::onHeroVisit( const CGHeroInstance * h ) const
 	}
 
 	flagMine(h->tempOwner);
-
-}
-
-void CGMine::newTurn(vstd::RNG & rand) const
-{
-	if(cb->getDate() == 1)
-		return;
-
-	if (tempOwner == PlayerColor::NEUTRAL)
-		return;
-
-	cb->giveResource(tempOwner, producedResource, getProducedQuantity());
 }
 
 void CGMine::initObj(vstd::RNG & rand)
@@ -139,11 +127,19 @@ bool CGMine::isAbandoned() const
 	return subID.getNum() >= 7;
 }
 
+const IOwnableObject * CGMine::asOwnable() const
+{
+	return this;
+}
+
 ResourceSet CGMine::dailyIncome() const
 {
 	ResourceSet result;
 	result[producedResource] += defaultResProduction();
 
+	const auto & playerSettings = cb->getPlayerSettings(getOwner());
+	result.applyHandicap(playerSettings->handicap.percentIncome);
+
 	return result;
 }
 

+ 5 - 5
lib/mapObjects/MiscObjects.h

@@ -10,6 +10,7 @@
 #pragma once
 
 #include "CArmedInstance.h"
+#include "IOwnableObject.h"
 #include "../texts/MetaString.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
@@ -148,16 +149,13 @@ protected:
 	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 };
 
-class DLL_LINKAGE CGMine : public CArmedInstance
+class DLL_LINKAGE CGMine : public CArmedInstance, public IOwnableObject
 {
 public:
 	GameResID producedResource;
 	ui32 producedQuantity;
 	std::set<GameResID> abandonedMineResources;
-	
 	bool isAbandoned() const;
-	ResourceSet dailyIncome() const;
-
 private:
 	using CArmedInstance::CArmedInstance;
 
@@ -166,7 +164,6 @@ private:
 	void blockingDialogAnswered(const CGHeroInstance *hero, int32_t answer) const override;
 
 	void flagMine(const PlayerColor & player) const;
-	void newTurn(vstd::RNG & rand) const override;
 	void initObj(vstd::RNG & rand) override;
 
 	std::string getObjectName() const override;
@@ -183,6 +180,9 @@ public:
 	ui32 defaultResProduction() const;
 	ui32 getProducedQuantity() const;
 
+	ResourceSet dailyIncome() const override;
+	const IOwnableObject * asOwnable() const final;
+
 protected:
 	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 };

+ 5 - 0
lib/mapObjects/TownBuildingInstance.cpp

@@ -50,6 +50,11 @@ MapObjectSubID TownBuildingInstance::getObjTypeIndex() const
 	return 0;
 }
 
+const IOwnableObject * TownBuildingInstance::asOwnable() const
+{
+	return nullptr;
+}
+
 int3 TownBuildingInstance::visitablePos() const
 {
 	return town->visitablePos();

+ 1 - 0
lib/mapObjects/TownBuildingInstance.h

@@ -35,6 +35,7 @@ public:
 	PlayerColor getOwner() const override;
 	MapObjectID getObjGroupIndex() const override;
 	MapObjectSubID getObjTypeIndex() const override;
+	const IOwnableObject * asOwnable() const override;
 
 	int3 visitablePos() const override;
 	int3 getPosition() const override;

+ 5 - 2
server/CGameHandler.cpp

@@ -643,8 +643,11 @@ void CGameHandler::onNewTurn()
 			throw std::runtime_error("Invalid player in player state! Player " + std::to_string(player.first.getNum()) + ", map name: " + gs->map->name.toString() + ", map description: " + gs->map->description.toString());
 	}
 
-	for (const auto & player : gs->players)
-		n.playerIncome[player.first] = newTurnProcessor->generatePlayerIncome(player.first, newWeek && !firstTurn);
+	if (!firstTurn)
+	{
+		for (const auto & player : gs->players)
+			n.playerIncome[player.first] = newTurnProcessor->generatePlayerIncome(player.first, newWeek);
+	}
 
 	if (newWeek && !firstTurn)
 	{

+ 11 - 10
server/processors/NewTurnProcessor.cpp

@@ -23,6 +23,7 @@
 #include "../../lib/gameState/CGameState.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapObjects/CGTownInstance.h"
+#include "../../lib/mapObjects/IOwnableObject.h"
 #include "../../lib/mapping/CMap.h"
 #include "../../lib/networkPacks/PacksForClient.h"
 #include "../../lib/pathfinder/TurnInfo.h"
@@ -87,20 +88,12 @@ void NewTurnProcessor::onPlayerTurnEnded(PlayerColor which)
 
 ResourceSet NewTurnProcessor::generatePlayerIncome(PlayerColor playerID, bool newWeek)
 {
-	const auto & playerSettings = gameHandler->gameState()->scenarioOps->getIthPlayersSettings(playerID);
+	const auto & playerSettings = gameHandler->getPlayerSettings(playerID);
 	const PlayerState & state = gameHandler->gameState()->players.at(playerID);
 	ResourceSet income;
 
-	for (const auto & hero : state.heroes)
-	{
-		for (GameResID k : GameResID::ALL_RESOURCES())
-			income[k] += hero->valOfBonuses(BonusType::GENERATE_RESOURCE, BonusSubtypeID(k));
-	}
-
 	for (const auto & town : state.towns)
 	{
-		income += town->dailyIncome();
-
 		if (newWeek && town->hasBuilt(BuildingSubID::TREASURY))
 		{
 			//give 10% of starting gold
@@ -150,7 +143,15 @@ ResourceSet NewTurnProcessor::generatePlayerIncome(PlayerColor playerID, bool ne
 			income[EGameResID::CRYSTAL] += 3;
 	}
 
-	TResources incomeHandicapped = income * playerSettings.handicap.percentIncome / 100;
+	TResources incomeHandicapped = income;
+	incomeHandicapped.applyHandicap(playerSettings->handicap.percentIncome);
+
+	// FIXME: store pre-filtered, all owned objects in PlayerState
+	for (auto obj : gameHandler->gameState()->map->objects)
+	{
+		if (obj && obj->asOwnable() && obj->getOwner() == playerID)
+			incomeHandicapped += obj->asOwnable()->dailyIncome();
+	}
 
 	return incomeHandicapped;
 }