1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-20 20:23:03 +02:00

Unified income handling, added IOwnableObject interface

This commit is contained in:
Ivan Savenko 2024-08-24 20:42:19 +00:00
parent 189cb1c762
commit 0fd9dbf240
20 changed files with 116 additions and 47 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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;
};

View File

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

View File

@ -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;

View File

@ -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)
{

View File

@ -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;
}