소스 검색

PlayerState now stores all objects owned by player

Ivan Savenko 1 년 전
부모
커밋
a481f07daf

+ 14 - 14
lib/CGameInfoCallback.cpp

@@ -237,7 +237,7 @@ void CGameInfoCallback::getThievesGuildInfo(SThievesGuildInfo & thi, const CGObj
 	if(obj->ID == Obj::TOWN || obj->ID == Obj::TAVERN)
 	{
 		int taverns = 0;
-		for(auto town : gs->players[*getPlayerID()].towns)
+		for(auto town : gs->players[*getPlayerID()].getTowns())
 		{
 			if(town->hasBuilt(BuildingID::TAVERN))
 				taverns++;
@@ -256,7 +256,7 @@ void CGameInfoCallback::getThievesGuildInfo(SThievesGuildInfo & thi, const CGObj
 int CGameInfoCallback::howManyTowns(PlayerColor Player) const
 {
 	ERROR_RET_VAL_IF(!hasAccess(Player), "Access forbidden!", -1);
-	return static_cast<int>(gs->players[Player].towns.size());
+	return static_cast<int>(gs->players[Player].getTowns().size());
 }
 
 bool CGameInfoCallback::getTownInfo(const CGObjectInstance * town, InfoAboutTown & dest, const CGObjectInstance * selectedObject) const
@@ -609,7 +609,7 @@ EBuildingState CGameInfoCallback::canBuildStructure( const CGTownInstance *t, Bu
 		const PlayerState *ps = getPlayerState(t->tempOwner, false);
 		if(ps)
 		{
-			for(const CGTownInstance *town : ps->towns)
+			for(const CGTownInstance *town : ps->getTowns())
 			{
 				if(town->hasBuilt(BuildingID::CAPITOL))
 				{
@@ -711,9 +711,9 @@ int CGameInfoCallback::getHeroCount( PlayerColor player, bool includeGarrisoned
 	ERROR_RET_VAL_IF(!p, "No such player!", -1);
 
 	if(includeGarrisoned)
-		return static_cast<int>(p->heroes.size());
+		return static_cast<int>(p->getHeroes().size());
 	else
-		for(const auto & elem : p->heroes)
+		for(const auto & elem : p->getHeroes())
 			if(!elem->inTownGarrison)
 				ret++;
 	return ret;
@@ -757,7 +757,7 @@ std::vector < const CGTownInstance *> CPlayerSpecificInfoCallback::getTownsInfo(
 	auto ret = std::vector < const CGTownInstance *>();
 	for(const auto & i : gs->players)
 	{
-		for(const auto & town : i.second.towns)
+		for(const auto & town : i.second.getTowns())
 		{
 			if(i.first == getPlayerID() || (!onlyOur && isVisible(town, getPlayerID())))
 			{
@@ -789,7 +789,7 @@ int CPlayerSpecificInfoCallback::getHeroSerial(const CGHeroInstance * hero, bool
 		return -1;
 
 	size_t index = 0;
-	auto & heroes = gs->players[*getPlayerID()].heroes;
+	const auto & heroes = gs->players[*getPlayerID()].getHeroes();
 
 	for (auto & possibleHero : heroes)
 	{
@@ -835,7 +835,7 @@ std::vector < const CGDwelling * > CPlayerSpecificInfoCallback::getMyDwellings()
 {
 	ASSERT_IF_CALLED_WITH_PLAYER
 	std::vector < const CGDwelling * > ret;
-	for(CGDwelling * dw : gs->getPlayerState(*getPlayerID())->dwellings)
+	for(const CGDwelling * dw : gs->getPlayerState(*getPlayerID())->getDwellings())
 	{
 		ret.push_back(dw);
 	}
@@ -867,12 +867,12 @@ const CGHeroInstance* CPlayerSpecificInfoCallback::getHeroBySerial(int serialId,
 
 	if (!includeGarrisoned)
 	{
-		for(ui32 i = 0; i < p->heroes.size() && static_cast<int>(i) <= serialId; i++)
-			if(p->heroes[i]->inTownGarrison)
+		for(ui32 i = 0; i < p->getHeroes().size() && static_cast<int>(i) <= serialId; i++)
+			if(p->getHeroes()[i]->inTownGarrison)
 				serialId++;
 	}
-	ERROR_RET_VAL_IF(serialId < 0 || serialId >= p->heroes.size(), "No player info", nullptr);
-	return p->heroes[serialId];
+	ERROR_RET_VAL_IF(serialId < 0 || serialId >= p->getHeroes().size(), "No player info", nullptr);
+	return p->getHeroes()[serialId];
 }
 
 const CGTownInstance* CPlayerSpecificInfoCallback::getTownBySerial(int serialId) const
@@ -880,8 +880,8 @@ const CGTownInstance* CPlayerSpecificInfoCallback::getTownBySerial(int serialId)
 	ASSERT_IF_CALLED_WITH_PLAYER
 	const PlayerState *p = getPlayerState(*getPlayerID());
 	ERROR_RET_VAL_IF(!p, "No player info", nullptr);
-	ERROR_RET_VAL_IF(serialId < 0 || serialId >= p->towns.size(), "No player info", nullptr);
-	return p->towns[serialId];
+	ERROR_RET_VAL_IF(serialId < 0 || serialId >= p->getTowns().size(), "No player info", nullptr);
+	return p->getTowns()[serialId];
 }
 
 int CPlayerSpecificInfoCallback::getResourceAmount(GameResID type) const

+ 1 - 1
lib/CGameInfoCallback.h

@@ -23,7 +23,7 @@ struct InfoWindow;
 struct PlayerSettings;
 struct CPackForClient;
 struct TerrainTile;
-struct PlayerState;
+class PlayerState;
 class CTown;
 struct StartInfo;
 struct CPathsInfo;

+ 65 - 0
lib/CPlayerState.cpp

@@ -10,6 +10,9 @@
 #include "StdInc.h"
 
 #include "CPlayerState.h"
+#include "mapObjects/CGDwelling.h"
+#include "mapObjects/CGTownInstance.h"
+#include "mapObjects/CGHeroInstance.h"
 #include "gameState/QuestInfo.h"
 #include "texts/CGeneralTextHandler.h"
 #include "VCMI_Lib.h"
@@ -90,4 +93,66 @@ int PlayerState::getResourceAmount(int type) const
 	return vstd::atOrDefault(resources, static_cast<size_t>(type), 0);
 }
 
+std::vector<const CGDwelling* > PlayerState::getDwellings() const
+{
+	std::vector<const CGDwelling* > result;
+	for (auto const & object : ownedObjects)
+	{
+		auto dwelling = dynamic_cast<const CGDwelling *>(object);
+		auto town = dynamic_cast<const CGTownInstance *>(object);
+		if (dwelling && !town)
+			result.push_back(dwelling);
+	}
+	return result;
+}
+
+template<typename T>
+std::vector<T> PlayerState::getObjectsOfType() const
+{
+	std::vector<T> result;
+	for (auto const & object : ownedObjects)
+	{
+		auto casted = dynamic_cast<T>(object);
+		if (casted)
+			result.push_back(casted);
+	}
+	return result;
+}
+
+std::vector<const CGHeroInstance *> PlayerState::getHeroes() const
+{
+	return getObjectsOfType<const CGHeroInstance *>();
+}
+
+std::vector<const CGTownInstance *> PlayerState::getTowns() const
+{
+	return getObjectsOfType<const CGTownInstance *>();
+}
+
+std::vector<CGHeroInstance *> PlayerState::getHeroes()
+{
+	return getObjectsOfType<CGHeroInstance *>();
+}
+
+std::vector<CGTownInstance *> PlayerState::getTowns()
+{
+	return getObjectsOfType<CGTownInstance *>();
+}
+
+std::vector<const CGObjectInstance *> PlayerState::getOwnedObjects() const
+{
+	return {ownedObjects.begin(), ownedObjects.end()};
+}
+
+void PlayerState::addOwnedObject(CGObjectInstance * object)
+{
+	ownedObjects.push_back(object);
+}
+
+void PlayerState::removeOwnedObject(CGObjectInstance * object)
+{
+	vstd::erase(ownedObjects, object);
+}
+
+
 VCMI_LIB_NAMESPACE_END

+ 23 - 9
lib/CPlayerState.h

@@ -20,12 +20,13 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
+class CGObjectInstance;
 class CGHeroInstance;
 class CGTownInstance;
 class CGDwelling;
 struct QuestInfo;
 
-struct DLL_LINKAGE PlayerState : public CBonusSystemNode, public Player
+class DLL_LINKAGE PlayerState : public CBonusSystemNode, public Player
 {
 	struct VisitedObjectGlobal
 	{
@@ -47,6 +48,11 @@ struct DLL_LINKAGE PlayerState : public CBonusSystemNode, public Player
 		}
 	};
 
+	std::vector<CGObjectInstance*> ownedObjects;
+
+	template<typename T>
+	std::vector<T> getObjectsOfType() const;
+
 public:
 	PlayerColor color;
 	bool human; //true if human controlled player, false for AI
@@ -55,12 +61,8 @@ public:
 
 	/// list of objects that were "destroyed" by player, either via simple pick-up (e.g. resources) or defeated heroes or wandering monsters
 	std::set<ObjectInstanceID> destroyedObjects;
-
 	std::set<ObjectInstanceID> visitedObjects; // as a std::set, since most accesses here will be from visited status checks
 	std::set<VisitedObjectGlobal> visitedObjectsGlobal;
-	std::vector<ConstTransitivePtr<CGHeroInstance> > heroes;
-	std::vector<ConstTransitivePtr<CGTownInstance> > towns;
-	std::vector<ConstTransitivePtr<CGDwelling> > dwellings; //used for town growth
 	std::vector<QuestInfo> quests; //store info about all received quests
 	std::vector<Bonus> battleBonuses; //additional bonuses to be added during battle with neutrals
 	std::map<uint32_t, std::map<ArtifactPosition, ArtifactID>> costumesArtifacts;
@@ -90,9 +92,20 @@ public:
 	std::string getNameTextID() const override;
 	void registerIcons(const IconRegistar & cb) const override;
 
+	std::vector<const CGHeroInstance* > getHeroes() const;
+	std::vector<const CGTownInstance* > getTowns() const;
+	std::vector<CGHeroInstance* > getHeroes();
+	std::vector<CGTownInstance* > getTowns();
+
+	std::vector<const CGDwelling* > getDwellings() const;
+	std::vector<const CGObjectInstance* > getOwnedObjects() const;
+
+	void addOwnedObject(CGObjectInstance * object);
+	void removeOwnedObject(CGObjectInstance * object);
+
 	bool checkVanquished() const
 	{
-		return heroes.empty() && towns.empty();
+		return ownedObjects.empty();
 	}
 
 	template <typename Handler> void serialize(Handler &h)
@@ -103,9 +116,10 @@ public:
 		h & resources;
 		h & status;
 		h & turnTimer;
-		h & heroes;
-		h & towns;
-		h & dwellings;
+		h & ownedObjects;
+		//h & heroes;
+		//h & towns;
+		//h & dwellings;
 		h & quests;
 		h & visitedObjects;
 		h & visitedObjectsGlobal;

+ 17 - 14
lib/gameState/CGameState.cpp

@@ -574,7 +574,6 @@ void CGameState::initHeroes()
 		}
 
 		hero->initHero(getRandomGenerator());
-		getPlayerState(hero->getOwner())->heroes.push_back(hero);
 		map->allHeroes[hero->getHeroType().getNum()] = hero;
 	}
 
@@ -699,14 +698,14 @@ void CGameState::initStartingBonus()
 			}
 		case PlayerStartingBonus::ARTIFACT:
 			{
-				if(elem.second.heroes.empty())
+				if(elem.second.getHeroes().empty())
 				{
 					logGlobal->error("Cannot give starting artifact - no heroes!");
 					break;
 				}
 				const Artifact * toGive = pickRandomArtifact(getRandomGenerator(), CArtifact::ART_TREASURE).toEntity(VLC);
 
-				CGHeroInstance *hero = elem.second.heroes[0];
+				CGHeroInstance *hero = elem.second.getHeroes()[0];
 				if(!giveHeroArtifact(hero, toGive->getId()))
 					logGlobal->error("Cannot give starting artifact - no free slots!");
 			}
@@ -893,8 +892,6 @@ void CGameState::initTowns()
 			vti->possibleSpells -= s->id;
 		}
 		vti->possibleSpells.clear();
-		if(vti->getOwner() != PlayerColor::NEUTRAL)
-			getPlayerState(vti->getOwner())->towns.emplace_back(vti);
 	}
 }
 
@@ -902,6 +899,12 @@ void CGameState::initMapObjects()
 {
 	logGlobal->debug("\tObject initialization");
 
+	for(CGObjectInstance *obj : map->objects)
+	{
+		if (obj && obj->getOwner().isValidPlayer())
+			getPlayerState(obj->getOwner())->addOwnedObject(obj);
+	}
+
 //	objCaller->preInit();
 	for(CGObjectInstance *obj : map->objects)
 	{
@@ -937,9 +940,9 @@ void CGameState::placeHeroesInTowns()
 		if(player.first == PlayerColor::NEUTRAL)
 			continue;
 
-		for(CGHeroInstance * h : player.second.heroes)
+		for(CGHeroInstance * h : player.second.getHeroes())
 		{
-			for(CGTownInstance * t : player.second.towns)
+			for(CGTownInstance * t : player.second.getTowns())
 			{
 				if(h->visitablePos().z != t->visitablePos().z)
 					continue;
@@ -971,9 +974,9 @@ void CGameState::initVisitingAndGarrisonedHeroes()
 			continue;
 
 		//init visiting and garrisoned heroes
-		for(CGHeroInstance * h : player.second.heroes)
+		for(CGHeroInstance * h : player.second.getHeroes())
 		{
-			for(CGTownInstance * t : player.second.towns)
+			for(CGTownInstance * t : player.second.getTowns())
 			{
 				if(h->visitablePos().z != t->visitablePos().z)
 					continue;
@@ -1371,7 +1374,7 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
 		}
 		case EventCondition::HAVE_ARTIFACT: //check if any hero has winning artifact
 		{
-			for(const auto & elem : p->heroes)
+			for(const auto & elem : p->getHeroes())
 				if(elem->hasArt(condition.objectType.as<ArtifactID>()))
 					return true;
 			return false;
@@ -1405,7 +1408,7 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
 			}
 			else // any town
 			{
-				for (const CGTownInstance * t : p->towns)
+				for (const CGTownInstance * t : p->getTowns())
 				{
 					if (t->hasBuilt(condition.objectType.as<BuildingID>()))
 						return true;
@@ -1550,9 +1553,9 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
 	if(level >= 0) //num of towns & num of heroes
 	{
 		//num of towns
-		FILL_FIELD(numOfTowns, g->second.towns.size())
+		FILL_FIELD(numOfTowns, g->second.getTowns().size())
 		//num of heroes
-		FILL_FIELD(numOfHeroes, g->second.heroes.size())
+		FILL_FIELD(numOfHeroes, g->second.getHeroes().size())
 	}
 	if(level >= 1) //best hero's portrait
 	{
@@ -1624,7 +1627,7 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
 			if(playerInactive(player.second.color)) //do nothing for neutral player
 				continue;
 			CreatureID bestCre; //best creature's ID
-			for(const auto & elem : player.second.heroes)
+			for(const auto & elem : player.second.getHeroes())
 			{
 				for(const auto & it : elem->Slots())
 				{

+ 1 - 1
lib/gameState/CGameStateCampaign.cpp

@@ -536,7 +536,7 @@ void CGameStateCampaign::initHeroes()
 		}
 		assert(humanPlayer != PlayerColor::NEUTRAL);
 
-		std::vector<ConstTransitivePtr<CGHeroInstance> > & heroes = gameState->players[humanPlayer].heroes;
+		const auto & heroes = gameState->players[humanPlayer].getHeroes();
 
 		if (chosenBonus->info1 == 0xFFFD) //most powerful
 		{

+ 9 - 9
lib/gameState/GameStatistics.cpp

@@ -50,10 +50,10 @@ StatisticDataSetEntry StatisticDataSet::createEntry(const PlayerState * ps, cons
 	data.isHuman = ps->isHuman();
 	data.status = ps->status;
 	data.resources = ps->resources;
-	data.numberHeroes = ps->heroes.size();
+	data.numberHeroes = ps->getHeroes().size();
 	data.numberTowns = gs->howManyTowns(ps->color);
 	data.numberArtifacts = Statistic::getNumberOfArts(ps);
-	data.numberDwellings = gs->getPlayerState(ps->color)->dwellings.size();
+	data.numberDwellings = gs->getPlayerState(ps->color)->getDwellings().size();
 	data.armyStrength = Statistic::getArmyStrength(ps, true);
 	data.totalExperience = Statistic::getTotalExperience(ps);
 	data.income = Statistic::getIncome(gs, ps);
@@ -221,7 +221,7 @@ std::vector<const CGMine *> Statistic::getMines(const CGameState * gs, const Pla
 int Statistic::getNumberOfArts(const PlayerState * ps)
 {
 	int ret = 0;
-	for(auto h : ps->heroes)
+	for(auto h : ps->getHeroes())
 	{
 		ret += h->artifactsInBackpack.size() + h->artifactsWorn.size();
 	}
@@ -233,7 +233,7 @@ si64 Statistic::getArmyStrength(const PlayerState * ps, bool withTownGarrison)
 {
 	si64 str = 0;
 
-	for(auto h : ps->heroes)
+	for(auto h : ps->getHeroes())
 	{
 		if(!h->inTownGarrison || withTownGarrison)		//original h3 behavior
 			str += h->getArmyStrength();
@@ -246,7 +246,7 @@ si64 Statistic::getTotalExperience(const PlayerState * ps)
 {
 	si64 tmp = 0;
 
-	for(auto h : ps->heroes)
+	for(auto h : ps->getHeroes())
 		tmp += h->exp;
 	
 	return tmp;
@@ -258,11 +258,11 @@ int Statistic::getIncome(const CGameState * gs, const PlayerState * ps)
 	int totalIncome = 0;
 
 	//Heroes can produce gold as well - skill, specialty or arts
-	for(const auto & h : ps->heroes)
+	for(const auto & h : ps->getHeroes())
 		totalIncome += h->dailyIncome()[EGameResID::GOLD];
 
 	//Add town income of all towns
-	for(const auto & t : ps->towns)
+	for(const auto & t : ps->getTowns())
 		totalIncome += t->dailyIncome()[EGameResID::GOLD];
 
 	for(const CGMine * mine : getMines(gs, ps))
@@ -295,7 +295,7 @@ float Statistic::getMapExploredRatio(const CGameState * gs, PlayerColor player)
 
 const CGHeroInstance * Statistic::findBestHero(const CGameState * gs, const PlayerColor & color)
 {
-	auto &h = gs->players.at(color).heroes;
+	const auto &h = gs->players.at(color).getHeroes();
 	if(h.empty())
 		return nullptr;
 	//best hero will be that with highest exp
@@ -368,7 +368,7 @@ float Statistic::getTownBuiltRatio(const PlayerState * ps)
 	float built = 0.0;
 	float total = 0.0;
 
-	for(const auto & t : ps->towns)
+	for(const auto & t : ps->getTowns())
 	{
 		built += t->getBuildings().size();
 		for(const auto & b : t->town->buildings)

+ 1 - 1
lib/gameState/GameStatistics.h

@@ -14,7 +14,7 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-struct PlayerState;
+class PlayerState;
 class CGameState;
 class CGHeroInstance;
 class CGMine;

+ 2 - 2
lib/gameState/HighScore.cpp

@@ -29,10 +29,10 @@ HighScoreParameter HighScore::prepareHighScores(const CGameState * gs, PlayerCol
 	param.townAmount = gs->howManyTowns(player);
 	param.usedCheat = gs->getPlayerState(player)->cheated;
 	param.hasGrail = false;
-	for(const CGHeroInstance * h : playerState->heroes)
+	for(const CGHeroInstance * h : playerState->getHeroes())
 		if(h->hasArt(ArtifactID::GRAIL))
 			param.hasGrail = true;
-	for(const CGTownInstance * t : playerState->towns)
+	for(const CGTownInstance * t : playerState->getTowns())
 		if(t->hasBuilt(BuildingID::GRAIL))
 			param.hasGrail = true;
 	param.allEnemiesDefeated = true;

+ 0 - 17
lib/mapObjects/CGDwelling.cpp

@@ -182,10 +182,6 @@ void CGDwelling::initObj(vstd::RNG & rand)
 	case Obj::CREATURE_GENERATOR4:
 		{
 			getObjectHandler()->configureObject(this, rand);
-
-			if (getOwner() != PlayerColor::NEUTRAL)
-				cb->gameState()->players[getOwner()].dwellings.emplace_back(this);
-
 			assert(!creatures.empty());
 			assert(!creatures[0].second.empty());
 			break;
@@ -211,19 +207,6 @@ void CGDwelling::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
 {
 	switch (what)
 	{
-		case ObjProperty::OWNER: //change owner
-			if (ID == Obj::CREATURE_GENERATOR1 || ID == Obj::CREATURE_GENERATOR2
-				|| ID == Obj::CREATURE_GENERATOR3 || ID == Obj::CREATURE_GENERATOR4)
-			{
-				if (tempOwner != PlayerColor::NEUTRAL)
-				{
-					std::vector<ConstTransitivePtr<CGDwelling> >* dwellings = &cb->gameState()->players[tempOwner].dwellings;
-					dwellings->erase (std::find(dwellings->begin(), dwellings->end(), this));
-				}
-				if (identifier.as<PlayerColor>().isValidPlayer())
-					cb->gameState()->players[identifier.as<PlayerColor>()].dwellings.emplace_back(this);
-			}
-			break;
 		case ObjProperty::AVAILABLE_CREATURE:
 			creatures.resize(1);
 			creatures[0].second.resize(1);

+ 5 - 5
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->dwellings);
+		dwellingBonus = getDwellingBonus(creatures[level].second, p->getDwellings());
 	}
 	if(dwellingBonus)
 		ret.entries.emplace_back(VLC->generaltexth->allTexts[591], dwellingBonus); // \nExternal dwellings %+d
@@ -192,7 +192,7 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const
 	return ret;
 }
 
-int CGTownInstance::getDwellingBonus(const std::vector<CreatureID>& creatureIds, const std::vector<ConstTransitivePtr<CGDwelling> >& dwellings) const
+int CGTownInstance::getDwellingBonus(const std::vector<CreatureID>& creatureIds, const std::vector<const CGDwelling * >& dwellings) const
 {
 	int totalBonus = 0;
 	for (const auto& dwelling : dwellings)
@@ -635,9 +635,9 @@ void CGTownInstance::removeCapitols(const PlayerColor & owner) const
 	if (hasCapitol()) // search if there's an older capitol
 	{
 		PlayerState* state = cb->gameState()->getPlayerState(owner); //get all towns owned by player
-		for (auto i = state->towns.cbegin(); i < state->towns.cend(); ++i)
+		for (const auto & town : state->getTowns())
 		{
-			if (*i != this && (*i)->hasCapitol())
+			if (town != this && town->hasCapitol())
 			{
 				RazeStructures rs;
 				rs.tid = id;
@@ -672,7 +672,7 @@ int CGTownInstance::getMarketEfficiency() const
 	assert(p);
 
 	int marketCount = 0;
-	for(const CGTownInstance *t : p->towns)
+	for(const CGTownInstance *t : p->getTowns())
 		if(t->hasBuiltSomeTradeBuilding())
 			marketCount++;
 

+ 1 - 1
lib/mapObjects/CGTownInstance.h

@@ -239,7 +239,7 @@ private:
 	FactionID randomizeFaction(vstd::RNG & rand);
 	void setOwner(const PlayerColor & owner) const;
 	void onTownCaptured(const PlayerColor & winner) const;
-	int getDwellingBonus(const std::vector<CreatureID>& creatureIds, const std::vector<ConstTransitivePtr<CGDwelling> >& dwellings) const;
+	int getDwellingBonus(const std::vector<CreatureID>& creatureIds, const std::vector<const CGDwelling* >& dwellings) const;
 	bool townEnvisagesBuilding(BuildingSubID::EBuildingSubID bid) const;
 	void initializeConfigurableBuildings(vstd::RNG & rand);
 };

+ 17 - 9
lib/networkPacks/NetPacksLib.cpp

@@ -1171,7 +1171,7 @@ void RemoveObject::applyGs(CGameState *gs)
 		assert(beatenHero);
 		PlayerState * p = gs->getPlayerState(beatenHero->tempOwner);
 		gs->map->heroesOnMap -= beatenHero;
-		p->heroes -= beatenHero;
+		p->removeOwnedObject(beatenHero);
 
 
 		auto * siegeNode = beatenHero->whereShouldBeAttachedOnSiege(gs);
@@ -1417,7 +1417,7 @@ void HeroRecruited::applyGs(CGameState *gs)
 		gs->map->objects[h->id.getNum()] = h;
 
 	gs->map->heroesOnMap.emplace_back(h);
-	p->heroes.emplace_back(h);
+	p->addOwnedObject(h);
 	h->attachTo(*p);
 	gs->map->addBlockVisTiles(h);
 
@@ -1452,7 +1452,7 @@ void GiveHero::applyGs(CGameState *gs)
 	h->setMovementPoints(h->movementPointsLimit(true));
 	h->pos = h->convertFromVisitablePos(oldVisitablePos);
 	gs->map->heroesOnMap.emplace_back(h);
-	gs->getPlayerState(h->getOwner())->heroes.emplace_back(h);
+	gs->getPlayerState(h->getOwner())->addOwnedObject(h);
 
 	gs->map->addBlockVisTiles(h);
 	h->inTownGarrison = false;
@@ -1942,6 +1942,18 @@ void SetObjectProperty::applyGs(CGameState *gs)
 	}
 
 	auto * cai = dynamic_cast<CArmedInstance *>(obj);
+
+	if(what == ObjProperty::OWNER)
+	{
+		PlayerColor oldOwner = obj->getOwner();
+		PlayerColor newOwner = identifier.as<PlayerColor>();
+		if(oldOwner.isValidPlayer())
+			gs->getPlayerState(oldOwner)->removeOwnedObject(obj);;
+
+		if(newOwner.isValidPlayer())
+			gs->getPlayerState(newOwner)->addOwnedObject(obj);;
+	}
+
 	if(what == ObjProperty::OWNER && cai)
 	{
 		if(obj->ID == Obj::TOWN)
@@ -1953,17 +1965,13 @@ void SetObjectProperty::applyGs(CGameState *gs)
 			if(oldOwner.isValidPlayer())
 			{
 				auto * state = gs->getPlayerState(oldOwner);
-				state->towns -= t;
-
-				if(state->towns.empty())
+				if(state->getTowns().empty())
 					state->daysWithoutCastle = 0;
 			}
 			if(identifier.as<PlayerColor>().isValidPlayer())
 			{
-				PlayerState * p = gs->getPlayerState(identifier.as<PlayerColor>());
-				p->towns.emplace_back(t);
-
 				//reset counter before NewTurn to avoid no town message if game loaded at turn when one already captured
+				PlayerState * p = gs->getPlayerState(identifier.as<PlayerColor>());
 				if(p->daysWithoutCastle)
 					p->daysWithoutCastle = std::nullopt;
 			}

+ 1 - 2
lib/pathfinder/CPathfinder.cpp

@@ -264,8 +264,7 @@ TeleporterTilesVector CPathfinderHelper::getCastleGates(const PathNodeInfo & sou
 {
 	TeleporterTilesVector allowedExits;
 
-	auto towns = getPlayerState(hero->tempOwner)->towns;
-	for(const auto & town : towns)
+	for(const auto & town : getPlayerState(hero->tempOwner)->getTowns())
 	{
 		if(town->id != source.nodeObject->id && town->visitingHero == nullptr
 			&& town->hasBuilt(BuildingID::CASTLE_GATE, ETownType::INFERNO))

+ 2 - 2
lib/spells/AdventureSpellMechanics.cpp

@@ -692,9 +692,9 @@ std::vector <const CGTownInstance*> TownPortalMechanics::getPossibleTowns(SpellC
 
 	for(const auto & color : team->players)
 	{
-		for(auto currTown : env->getCb()->getPlayerState(color)->towns)
+		for(auto currTown : env->getCb()->getPlayerState(color)->getTowns())
 		{
-			ret.push_back(currTown.get());
+			ret.push_back(currTown);
 		}
 	}
 	return ret;

+ 10 - 10
server/CGameHandler.cpp

@@ -555,7 +555,7 @@ 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 std::vector<ConstTransitivePtr<CGDwelling> > &dwellings = p->dwellings;
+			const auto &dwellings = p->getDwellings();
 			if (dwellings.empty())//no dwellings - just remove
 			{
 				sendAndApply(&ssi);
@@ -639,7 +639,7 @@ void CGameHandler::onNewTurn()
 		if (player.second.status != EPlayerStatus::INGAME)
 			continue;
 
-		if (player.second.heroes.empty() && player.second.towns.empty())
+		if (player.second.getHeroes().empty() && player.second.getTowns().empty())
 			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());
 	}
 
@@ -724,7 +724,7 @@ void CGameHandler::onNewTurn()
 		if (firstTurn)
 			heroPool->onNewWeek(elem.first);
 
-		for (CGHeroInstance *h : (elem).second.heroes)
+		for (CGHeroInstance *h : (elem).second.getHeroes())
 		{
 			if (h->visitedTown)
 				giveSpells(h->visitedTown, h);
@@ -1286,9 +1286,9 @@ 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->dwellings.size()==1)//first dwelling captured
+	if ((obj->ID == Obj::CREATURE_GENERATOR1 || obj->ID == Obj::CREATURE_GENERATOR4) && p && p->getDwellings().size()==1)//first dwelling captured
 	{
-		for (const CGTownInstance * t : getPlayerState(owner)->towns)
+		for (const CGTownInstance * t : getPlayerState(owner)->getTowns())
 		{
 			if (t->hasBuilt(BuildingSubID::PORTAL_OF_SUMMONING))
 				setPortalDwelling(t);//set initial creatures for all portals of summoning
@@ -3297,7 +3297,7 @@ void CGameHandler::handleTimeEvents(PlayerColor color)
 	}
 }
 
-void CGameHandler::handleTownEvents(CGTownInstance * town)
+void CGameHandler::handleTownEvents(const CGTownInstance * town)
 {
 	for (auto const & event : town->events)
 	{
@@ -3631,10 +3631,10 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
 		else
 		{
 			//copy heroes vector to avoid iterator invalidation as removal change PlayerState
-			auto hlp = p->heroes;
+			auto hlp = p->getHeroes();
 			for (auto h : hlp) //eliminate heroes
 			{
-				if (h.get())
+				if (h)
 					removeObject(h, player);
 			}
 
@@ -4159,11 +4159,11 @@ void CGameHandler::changeFogOfWar(int3 center, ui32 radius, PlayerColor player,
 
 		std::unordered_set<int3> observedTiles; //do not hide tiles observed by heroes. May lead to disastrous AI problems
 		auto p = getPlayerState(player);
-		for (auto h : p->heroes)
+		for (auto h : p->getHeroes())
 		{
 			getTilesInRange(observedTiles, h->getSightCenter(), h->getSightRadius(), ETileVisibility::REVEALED, h->tempOwner);
 		}
-		for (auto t : p->towns)
+		for (auto t : p->getTowns())
 		{
 			getTilesInRange(observedTiles, t->getSightCenter(), t->getSightRadius(), ETileVisibility::REVEALED, t->tempOwner);
 		}

+ 1 - 1
server/CGameHandler.h

@@ -230,7 +230,7 @@ public:
 	void addStatistics(StatisticDataSet &stat) const;
 
 	void handleTimeEvents(PlayerColor player);
-	void handleTownEvents(CGTownInstance *town);
+	void handleTownEvents(const CGTownInstance *town);
 	bool complain(const std::string &problem); //sends message to all clients, prints on the logs and return true
 	void objectVisited( const CGObjectInstance * obj, const CGHeroInstance * h );
 	void objectVisitEnded(const CObjectVisitQuery &query);

+ 1 - 1
server/battles/BattleResultProcessor.cpp

@@ -484,7 +484,7 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle)
 		if(!finishingBattle->isDraw())
 		{
 			ConstTransitivePtr<CGHeroInstance> strongestHero = nullptr;
-			for(auto & hero : gameHandler->gameState()->getPlayerState(finishingBattle->loser)->heroes)
+			for(auto & hero : gameHandler->gameState()->getPlayerState(finishingBattle->loser)->getHeroes())
 				if(!strongestHero || hero->exp > strongestHero->exp)
 					strongestHero = hero;
 			if(strongestHero->id == finishingBattle->loserHero->id && strongestHero->level > 5)

+ 9 - 10
server/processors/NewTurnProcessor.cpp

@@ -40,10 +40,10 @@ void NewTurnProcessor::onPlayerTurnStarted(PlayerColor which)
 	const auto * playerState = gameHandler->gameState()->getPlayerState(which);
 
 	gameHandler->handleTimeEvents(which);
-	for (auto t : playerState->towns)
+	for (const auto * t : playerState->getTowns())
 		gameHandler->handleTownEvents(t);
 
-	for (auto t : playerState->towns)
+	for (const auto * t : playerState->getTowns())
 	{
 		//garrison hero first - consistent with original H3 Mana Vortex and Battle Scholar Academy levelup windows order
 		if (t->garrisonHero != nullptr)
@@ -59,7 +59,7 @@ void NewTurnProcessor::onPlayerTurnEnded(PlayerColor which)
 	const auto * playerState = gameHandler->gameState()->getPlayerState(which);
 	assert(playerState->status == EPlayerStatus::INGAME);
 
-	if (playerState->towns.empty())
+	if (playerState->getTowns().empty())
 	{
 		DaysWithoutTown pack;
 		pack.player = which;
@@ -92,7 +92,7 @@ ResourceSet NewTurnProcessor::generatePlayerIncome(PlayerColor playerID, bool ne
 	const PlayerState & state = gameHandler->gameState()->players.at(playerID);
 	ResourceSet income;
 
-	for (const auto & town : state.towns)
+	for (const auto & town : state.getTowns())
 	{
 		if (newWeek && town->hasBuilt(BuildingSubID::TREASURY))
 		{
@@ -123,18 +123,18 @@ ResourceSet NewTurnProcessor::generatePlayerIncome(PlayerColor playerID, bool ne
 	for (GameResID k = GameResID::WOOD; k < GameResID::COUNT; k++)
 	{
 		income += state.valOfBonuses(BonusType::RESOURCES_CONSTANT_BOOST, BonusSubtypeID(k));
-		income += state.valOfBonuses(BonusType::RESOURCES_TOWN_MULTIPLYING_BOOST, BonusSubtypeID(k)) * state.towns.size();
+		income += state.valOfBonuses(BonusType::RESOURCES_TOWN_MULTIPLYING_BOOST, BonusSubtypeID(k)) * state.getTowns().size();
 	}
 
 	if(newWeek) //weekly crystal generation if 1 or more crystal dragons in any hero army or town garrison
 	{
 		bool hasCrystalGenCreature = false;
-		for (const auto & hero : state.heroes)
+		for (const auto & hero : state.getHeroes())
 			for(auto stack : hero->stacks)
 				if(stack.second->hasBonusOfType(BonusType::SPECIAL_CRYSTAL_GENERATION))
 					hasCrystalGenCreature = true;
 
-		for(const auto & town : state.towns)
+		for(const auto & town : state.getTowns())
 			for(auto stack : town->stacks)
 				if(stack.second->hasBonusOfType(BonusType::SPECIAL_CRYSTAL_GENERATION))
 					hasCrystalGenCreature = true;
@@ -146,10 +146,9 @@ ResourceSet NewTurnProcessor::generatePlayerIncome(PlayerColor playerID, bool ne
 	TResources incomeHandicapped = income;
 	incomeHandicapped.applyHandicap(playerSettings->handicap.percentIncome);
 
-	// FIXME: store pre-filtered, all owned objects in PlayerState
-	for (auto obj : gameHandler->gameState()->map->objects)
+	for (auto obj :	state.getOwnedObjects())
 	{
-		if (obj && obj->asOwnable() && obj->getOwner() == playerID)
+		if (obj->asOwnable())
 			incomeHandicapped += obj->asOwnable()->dailyIncome();
 	}
 

+ 2 - 2
server/processors/PlayerMessageProcessor.cpp

@@ -710,11 +710,11 @@ bool PlayerMessageProcessor::handleCheatCode(const std::string & cheat, PlayerCo
 			executeCheatCode(cheatName, i.first, ObjectInstanceID::NONE, parameters);
 
 		if (vstd::contains(townTargetedCheats, cheatName))
-			for (const auto & t : i.second.towns)
+			for (const auto & t : i.second.getTowns())
 				executeCheatCode(cheatName, i.first, t->id, parameters);
 
 		if (vstd::contains(heroTargetedCheats, cheatName))
-			for (const auto & h : i.second.heroes)
+			for (const auto & h : i.second.getHeroes())
 				executeCheatCode(cheatName, i.first, h->id, parameters);
 	}
 

+ 2 - 2
server/processors/TurnOrderProcessor.cpp

@@ -121,7 +121,7 @@ bool TurnOrderProcessor::playersInContact(PlayerColor left, PlayerColor right) c
 		}
 	}
 
-	for(const auto & hero : leftInfo->heroes)
+	for(const auto & hero : leftInfo->getHeroes())
 	{
 		CPathsInfo out(mapSize, hero);
 		auto config = std::make_shared<SingleHeroPathfinderConfig>(out, gameHandler->gameState(), hero);
@@ -137,7 +137,7 @@ bool TurnOrderProcessor::playersInContact(PlayerColor left, PlayerColor right) c
 						leftReachability[z][x][y] = true;
 	}
 
-	for(const auto & hero : rightInfo->heroes)
+	for(const auto & hero : rightInfo->getHeroes())
 	{
 		CPathsInfo out(mapSize, hero);
 		auto config = std::make_shared<SingleHeroPathfinderConfig>(out, gameHandler->gameState(), hero);