diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index 3a6b002bd..5ed6a1283 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -822,34 +822,12 @@ int3 CPlayerSpecificInfoCallback::getGrailPos( double *outKnownRatio ) std::vector < const CGObjectInstance * > CPlayerSpecificInfoCallback::getMyObjects() const { - std::vector < const CGObjectInstance * > ret; - for(const CGObjectInstance * obj : gs->map->objects) - { - if(obj && obj->tempOwner == getPlayerID()) - ret.push_back(obj); - } - return ret; -} - -std::vector < const CGDwelling * > CPlayerSpecificInfoCallback::getMyDwellings() const -{ - ASSERT_IF_CALLED_WITH_PLAYER - std::vector < const CGDwelling * > ret; - for(const CGDwelling * dw : gs->getPlayerState(*getPlayerID())->getDwellings()) - { - ret.push_back(dw); - } - return ret; + return gs->getPlayerState(*getPlayerID())->getOwnedObjects(); } std::vector CPlayerSpecificInfoCallback::getMyQuests() const { - std::vector ret; - for(const auto & quest : gs->getPlayerState(*getPlayerID())->quests) - { - ret.push_back (quest); - } - return ret; + return gs->getPlayerState(*getPlayerID())->quests; } int CPlayerSpecificInfoCallback::howManyHeroes(bool includeGarrisoned) const diff --git a/lib/CGameInfoCallback.h b/lib/CGameInfoCallback.h index c4d0799fa..597282a04 100644 --- a/lib/CGameInfoCallback.h +++ b/lib/CGameInfoCallback.h @@ -247,7 +247,6 @@ public: virtual const CGTownInstance* getTownBySerial(int serialId) const; // serial id is [0, number of towns) virtual const CGHeroInstance* getHeroBySerial(int serialId, bool includeGarrisoned=true) const; // serial id is [0, number of heroes) virtual std::vector getHeroesInfo(bool onlyOur = true) const; //true -> only owned; false -> all visible - virtual std::vector getMyDwellings() const; //returns all dwellings that belong to player virtual std::vector getMyObjects() const; //returns all objects flagged by belonging player virtual std::vector getMyQuests() const; diff --git a/lib/CPlayerState.cpp b/lib/CPlayerState.cpp index 3480ce332..2242d5381 100644 --- a/lib/CPlayerState.cpp +++ b/lib/CPlayerState.cpp @@ -93,19 +93,6 @@ int PlayerState::getResourceAmount(int type) const return vstd::atOrDefault(resources, static_cast(type), 0); } -std::vector PlayerState::getDwellings() const -{ - std::vector result; - for (auto const & object : ownedObjects) - { - auto dwelling = dynamic_cast(object); - auto town = dynamic_cast(object); - if (dwelling && !town) - result.push_back(dwelling); - } - return result; -} - template std::vector PlayerState::getObjectsOfType() const { @@ -146,6 +133,7 @@ std::vector PlayerState::getOwnedObjects() const void PlayerState::addOwnedObject(CGObjectInstance * object) { + assert(object->asOwnable() != nullptr); ownedObjects.push_back(object); } diff --git a/lib/CPlayerState.h b/lib/CPlayerState.h index ef602a69f..cc8dcb4c5 100644 --- a/lib/CPlayerState.h +++ b/lib/CPlayerState.h @@ -97,7 +97,6 @@ public: std::vector getHeroes(); std::vector getTowns(); - std::vector getDwellings() const; std::vector getOwnedObjects() const; void addOwnedObject(CGObjectInstance * object); diff --git a/lib/gameState/GameStatistics.cpp b/lib/gameState/GameStatistics.cpp index d458f8aa4..237da8f54 100644 --- a/lib/gameState/GameStatistics.cpp +++ b/lib/gameState/GameStatistics.cpp @@ -53,7 +53,7 @@ StatisticDataSetEntry StatisticDataSet::createEntry(const PlayerState * ps, cons data.numberHeroes = ps->getHeroes().size(); data.numberTowns = gs->howManyTowns(ps->color); data.numberArtifacts = Statistic::getNumberOfArts(ps); - data.numberDwellings = gs->getPlayerState(ps->color)->getDwellings().size(); + data.numberDwellings = Statistic::getNumberOfDwellings(ps); data.armyStrength = Statistic::getArmyStrength(ps, true); data.totalExperience = Statistic::getTotalExperience(ps); data.income = Statistic::getIncome(gs, ps); @@ -228,6 +228,16 @@ int Statistic::getNumberOfArts(const PlayerState * ps) return ret; } +int Statistic::getNumberOfDwellings(const PlayerState * ps) +{ + int ret = 0; + for(const auto * obj : ps->getOwnedObjects()) + if (!obj->asOwnable()->providedCreatures().empty()) + ret += 1; + + return ret; +} + // get total strength of player army si64 Statistic::getArmyStrength(const PlayerState * ps, bool withTownGarrison) { diff --git a/lib/gameState/GameStatistics.h b/lib/gameState/GameStatistics.h index 450230e63..851f6417c 100644 --- a/lib/gameState/GameStatistics.h +++ b/lib/gameState/GameStatistics.h @@ -158,6 +158,7 @@ class DLL_LINKAGE Statistic static std::vector getMines(const CGameState * gs, const PlayerState * ps); public: static int getNumberOfArts(const PlayerState * ps); + static int getNumberOfDwellings(const PlayerState * ps); static si64 getArmyStrength(const PlayerState * ps, bool withTownGarrison = false); static si64 getTotalExperience(const PlayerState * ps); static int getIncome(const CGameState * gs, const PlayerState * ps); diff --git a/lib/mapObjects/CGDwelling.cpp b/lib/mapObjects/CGDwelling.cpp index 8d1619b02..4c8e473fb 100644 --- a/lib/mapObjects/CGDwelling.cpp +++ b/lib/mapObjects/CGDwelling.cpp @@ -534,4 +534,26 @@ void CGDwelling::serializeJsonOptions(JsonSerializeFormat & handler) } } +const IOwnableObject * CGDwelling::asOwnable() const +{ + return this; +} + +ResourceSet CGDwelling::dailyIncome() const +{ + return {}; +} + +std::vector CGDwelling::providedCreatures() const +{ + if (ID == Obj::WAR_MACHINE_FACTORY || ID == Obj::REFUGEE_CAMP) + return {}; + + std::vector result; + for (const auto & level : creatures) + result.insert(result.end(), level.second.begin(), level.second.end()); + + return result; +} + VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/CGDwelling.h b/lib/mapObjects/CGDwelling.h index feb4b9bc5..250693285 100644 --- a/lib/mapObjects/CGDwelling.h +++ b/lib/mapObjects/CGDwelling.h @@ -11,6 +11,7 @@ #pragma once #include "CArmedInstance.h" +#include "IOwnableObject.h" VCMI_LIB_NAMESPACE_BEGIN @@ -30,7 +31,7 @@ public: void serializeJson(JsonSerializeFormat & handler); }; -class DLL_LINKAGE CGDwelling : public CArmedInstance +class DLL_LINKAGE CGDwelling : public CArmedInstance, public IOwnableObject { public: using TCreaturesSet = std::vector > >; @@ -41,6 +42,10 @@ public: CGDwelling(IGameCallback *cb); ~CGDwelling() override; + const IOwnableObject * asOwnable() const final; + ResourceSet dailyIncome() const override; + std::vector providedCreatures() const override; + protected: void serializeJsonOptions(JsonSerializeFormat & handler) override; diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 1571c969f..1954a622c 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -1834,6 +1834,11 @@ ResourceSet CGHeroInstance::dailyIncome() const return income; } +std::vector CGHeroInstance::providedCreatures() const +{ + return {}; +} + const IOwnableObject * CGHeroInstance::asOwnable() const { return this; diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 7ba43a9fb..2ad2811a9 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -167,6 +167,7 @@ public: bool needsLastStack()const override; ResourceSet dailyIncome() const override; + std::vector providedCreatures() const override; const IOwnableObject * asOwnable() const final; //INativeTerrainProvider diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 96142e591..7d18c9337 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -181,7 +181,7 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const int dwellingBonus = 0; if(const PlayerState *p = cb->getPlayerState(tempOwner, false)) { - dwellingBonus = getDwellingBonus(creatures[level].second, p->getDwellings()); + dwellingBonus = getDwellingBonus(creatures[level].second, p->getOwnedObjects()); } if(dwellingBonus) ret.entries.emplace_back(VLC->generaltexth->allTexts[591], dwellingBonus); // \nExternal dwellings %+d @@ -192,15 +192,18 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const return ret; } -int CGTownInstance::getDwellingBonus(const std::vector& creatureIds, const std::vector& dwellings) const +int CGTownInstance::getDwellingBonus(const std::vector& creatureIds, const std::vector& dwellings) const { int totalBonus = 0; for (const auto& dwelling : dwellings) { - for (const auto& creature : dwelling->creatures) - { - totalBonus += vstd::contains(creatureIds, creature.second[0]) ? 1 : 0; - } + const auto & dwellingCreatures = dwelling->asOwnable()->providedCreatures(); + bool hasMatch = false; + for (const auto& creature : dwellingCreatures) + hasMatch = vstd::contains(creatureIds, creature); + + if (hasMatch) + totalBonus += 1; } return totalBonus; } @@ -230,9 +233,9 @@ TResources CGTownInstance::dailyIncome() const return ret; } -const IOwnableObject * CGTownInstance::asOwnable() const +std::vector CGTownInstance::providedCreatures() const { - return this; + return {}; } bool CGTownInstance::hasFort() const diff --git a/lib/mapObjects/CGTownInstance.h b/lib/mapObjects/CGTownInstance.h index 1c25c5856..34b4d11b3 100644 --- a/lib/mapObjects/CGTownInstance.h +++ b/lib/mapObjects/CGTownInstance.h @@ -11,7 +11,6 @@ #include "IMarket.h" #include "CGDwelling.h" -#include "IOwnableObject.h" #include "../entities/faction/CFaction.h" // TODO: remove #include "../entities/faction/CTown.h" // TODO: remove @@ -48,7 +47,7 @@ struct DLL_LINKAGE GrowthInfo int handicapPercentage; }; -class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public IMarket, public INativeTerrainProvider, public ICreatureUpgrader, public IOwnableObject +class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public IMarket, public INativeTerrainProvider, public ICreatureUpgrader { std::string nameTextId; // name of town @@ -183,7 +182,7 @@ public: TResources getBuildingCost(const BuildingID & buildingID) const; ResourceSet dailyIncome() const override; - const IOwnableObject * asOwnable() const final; + std::vector providedCreatures() const override; 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 @@ -239,7 +238,7 @@ private: FactionID randomizeFaction(vstd::RNG & rand); void setOwner(const PlayerColor & owner) const; void onTownCaptured(const PlayerColor & winner) const; - int getDwellingBonus(const std::vector& creatureIds, const std::vector& dwellings) const; + int getDwellingBonus(const std::vector& creatureIds, const std::vector& dwellings) const; bool townEnvisagesBuilding(BuildingSubID::EBuildingSubID bid) const; void initializeConfigurableBuildings(vstd::RNG & rand); }; diff --git a/lib/mapObjects/IOwnableObject.h b/lib/mapObjects/IOwnableObject.h index c6736c1fe..1e8b850f1 100644 --- a/lib/mapObjects/IOwnableObject.h +++ b/lib/mapObjects/IOwnableObject.h @@ -12,11 +12,19 @@ VCMI_LIB_NAMESPACE_BEGIN class ResourceSet; +class CreatureID; class DLL_LINKAGE IOwnableObject { public: + /// Fixed daily income of this object + /// May not include random or periodical (e.g. weekly) income sources virtual ResourceSet dailyIncome() const = 0; + + /// List of creatures that are provided by this building + /// For use in town dwellings growth bonus and for portal of summoning + virtual std::vector providedCreatures() const = 0; + virtual ~IOwnableObject() = default; }; diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 6c1b81a2c..a9f18cb55 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -132,6 +132,11 @@ const IOwnableObject * CGMine::asOwnable() const return this; } +std::vector CGMine::providedCreatures() const +{ + return {}; +} + ResourceSet CGMine::dailyIncome() const { ResourceSet result; @@ -974,6 +979,21 @@ void CGSignBottle::serializeJsonOptions(JsonSerializeFormat& handler) handler.serializeStruct("text", message); } +const IOwnableObject * CGGarrison::asOwnable() const +{ + return this; +} + +ResourceSet CGGarrison::dailyIncome() const +{ + return {}; +} + +std::vector CGGarrison::providedCreatures() const +{ + return {}; +} + void CGGarrison::onHeroVisit (const CGHeroInstance *h) const { auto relations = cb->gameState()->getPlayerRelations(h->tempOwner, tempOwner); @@ -1277,6 +1297,21 @@ void CGObelisk::setPropertyDer(ObjProperty what, ObjPropertyID identifier) } } +const IOwnableObject * CGLighthouse::asOwnable() const +{ + return this; +} + +ResourceSet CGLighthouse::dailyIncome() const +{ + return {}; +} + +std::vector CGLighthouse::providedCreatures() const +{ + return {}; +} + void CGLighthouse::onHeroVisit( const CGHeroInstance * h ) const { if(h->tempOwner != tempOwner) diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index a4110571c..58366c167 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -60,7 +60,7 @@ protected: void serializeJsonOptions(JsonSerializeFormat & handler) override; }; -class DLL_LINKAGE CGGarrison : public CArmedInstance +class DLL_LINKAGE CGGarrison : public CArmedInstance, public IOwnableObject { public: using CArmedInstance::CArmedInstance; @@ -72,6 +72,10 @@ public: void onHeroVisit(const CGHeroInstance * h) const override; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; + const IOwnableObject * asOwnable() const final; + ResourceSet dailyIncome() const override; + std::vector providedCreatures() const override; + template void serialize(Handler &h) { h & static_cast(*this); @@ -80,6 +84,7 @@ public: protected: void serializeJsonOptions(JsonSerializeFormat & handler) override; void addAntimagicGarrisonBonus(); + }; class DLL_LINKAGE CGArtifact : public CArmedInstance @@ -180,8 +185,9 @@ public: ui32 defaultResProduction() const; ui32 getProducedQuantity() const; - ResourceSet dailyIncome() const override; const IOwnableObject * asOwnable() const final; + ResourceSet dailyIncome() const override; + std::vector providedCreatures() const override; protected: void serializeJsonOptions(JsonSerializeFormat & handler) override; @@ -403,7 +409,7 @@ protected: void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override; }; -class DLL_LINKAGE CGLighthouse : public CGObjectInstance +class DLL_LINKAGE CGLighthouse : public CGObjectInstance, public IOwnableObject { public: using CGObjectInstance::CGObjectInstance; @@ -411,6 +417,10 @@ public: void onHeroVisit(const CGHeroInstance * h) const override; void initObj(vstd::RNG & rand) override; + const IOwnableObject * asOwnable() const final; + ResourceSet dailyIncome() const override; + std::vector providedCreatures() const override; + template void serialize(Handler &h) { h & static_cast(*this); diff --git a/lib/networkPacks/NetPacksLib.cpp b/lib/networkPacks/NetPacksLib.cpp index fb70d0a9b..8e2a2352c 100644 --- a/lib/networkPacks/NetPacksLib.cpp +++ b/lib/networkPacks/NetPacksLib.cpp @@ -1943,7 +1943,7 @@ void SetObjectProperty::applyGs(CGameState *gs) auto * cai = dynamic_cast(obj); - if(what == ObjProperty::OWNER) + if(what == ObjProperty::OWNER && obj->asOwnable()) { PlayerColor oldOwner = obj->getOwner(); PlayerColor newOwner = identifier.as(); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index b95c23def..ec03cdbd3 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -555,25 +555,25 @@ void CGameHandler::setPortalDwelling(const CGTownInstance * town, bool forced=fa ssi.creatures = town->creatures; ssi.creatures[town->town->creatures.size()].second.clear();//remove old one - const auto &dwellings = p->getDwellings(); - if (dwellings.empty())//no dwellings - just remove + std::set availableCreatures; + for (const auto & dwelling : p->getOwnedObjects()) { - sendAndApply(&ssi); - return; + const auto & dwellingCreatures = dwelling->asOwnable()->providedCreatures(); + availableCreatures.insert(dwellingCreatures.begin(), dwellingCreatures.end()); } - auto dwelling = *RandomGeneratorUtil::nextItem(dwellings, getRandomGenerator()); + if (availableCreatures.empty()) + return; - // for multi-creature dwellings like Golem Factory - auto creatureId = RandomGeneratorUtil::nextItem(dwelling->creatures, getRandomGenerator())->second[0]; + CreatureID creatureId = *RandomGeneratorUtil::nextItem(availableCreatures, getRandomGenerator()); if (clear) { - ssi.creatures[town->town->creatures.size()].first = std::max(1, (VLC->creh->objects.at(creatureId)->getGrowth())/2); + ssi.creatures[town->town->creatures.size()].first = std::max(1, (creatureId.toEntity(VLC)->getGrowth())/2); } else { - ssi.creatures[town->town->creatures.size()].first = VLC->creh->objects.at(creatureId)->getGrowth(); + ssi.creatures[town->town->creatures.size()].first = creatureId.toEntity(VLC)->getGrowth(); } ssi.creatures[town->town->creatures.size()].second.push_back(creatureId); sendAndApply(&ssi); @@ -1284,9 +1284,7 @@ void CGameHandler::setOwner(const CGObjectInstance * obj, const PlayerColor owne } } - const PlayerState * p = getPlayerState(owner); - - if ((obj->ID == Obj::CREATURE_GENERATOR1 || obj->ID == Obj::CREATURE_GENERATOR4) && p && p->getDwellings().size()==1)//first dwelling captured + if ((obj->ID == Obj::CREATURE_GENERATOR1 || obj->ID == Obj::CREATURE_GENERATOR4)) { for (const CGTownInstance * t : getPlayerState(owner)->getTowns()) { diff --git a/server/processors/NewTurnProcessor.cpp b/server/processors/NewTurnProcessor.cpp index 8629bd4b6..d08aff45a 100644 --- a/server/processors/NewTurnProcessor.cpp +++ b/server/processors/NewTurnProcessor.cpp @@ -147,10 +147,7 @@ ResourceSet NewTurnProcessor::generatePlayerIncome(PlayerColor playerID, bool ne incomeHandicapped.applyHandicap(playerSettings->handicap.percentIncome); for (auto obj : state.getOwnedObjects()) - { - if (obj->asOwnable()) - incomeHandicapped += obj->asOwnable()->dailyIncome(); - } + incomeHandicapped += obj->asOwnable()->dailyIncome(); return incomeHandicapped; }