Explorar el Código

Objects list is now private member of CMap

Ivan Savenko hace 7 meses
padre
commit
bdae7285ae

+ 3 - 4
client/adventureMap/MapAudioPlayer.cpp

@@ -132,7 +132,7 @@ std::vector<AudioPath> MapAudioPlayer::getAmbientSounds(const int3 & tile)
 
 	for(auto & objectID : objects[tile.z][tile.x][tile.y])
 	{
-		const auto & object = GAME->map().getMap()->objects[objectID.getNum()];
+		const auto & object = GAME->map().getMap()->getObject(objectID);
 
 		assert(object);
 		if (!object)
@@ -206,10 +206,9 @@ MapAudioPlayer::MapAudioPlayer()
 
 	objects.resize(boost::extents[mapSize.z][mapSize.x][mapSize.y]);
 
-	for(const auto & obj : GAME->map().getMap()->objects)
+	for(const auto & obj : GAME->map().getMap()->getObjects())
 	{
-		if (obj)
-			addObject(obj.get());
+		addObject(obj);
 	}
 }
 

+ 1 - 1
client/mapView/MapRendererContext.cpp

@@ -103,7 +103,7 @@ const MapRendererBaseContext::MapObjectsList & MapRendererBaseContext::getObject
 
 const CGObjectInstance * MapRendererBaseContext::getObject(ObjectInstanceID objectID) const
 {
-	return GAME->map().getMap()->objects.at(objectID.getNum()).get();
+	return GAME->map().getMap()->getObject(objectID);
 }
 
 const CGPath * MapRendererBaseContext::currentPath() const

+ 3 - 3
client/mapView/MapRendererContextState.cpp

@@ -25,7 +25,7 @@
 static bool compareObjectBlitOrder(ObjectInstanceID left, ObjectInstanceID right)
 {
 	//FIXME: remove mh access
-	return GAME->map().compareObjectBlitOrder(GAME->map().getMap()->objects.at(left.getNum()).get(), GAME->map().getMap()->objects.at(right.getNum()).get());
+	return GAME->map().compareObjectBlitOrder(GAME->map().getMap()->getObject(left), GAME->map().getMap()->getObject(right));
 }
 
 MapRendererContextState::MapRendererContextState()
@@ -35,8 +35,8 @@ MapRendererContextState::MapRendererContextState()
 	objects.resize(boost::extents[mapSize.z][mapSize.x][mapSize.y]);
 
 	logGlobal->debug("Loading map objects");
-	for(const auto & obj : GAME->map().getMap()->objects)
-		addObject(obj.get());
+	for(const auto & obj : GAME->map().getMap()->getObjects())
+		addObject(obj);
 	logGlobal->debug("Done loading map objects");
 }
 

+ 2 - 2
client/mapView/mapHandler.cpp

@@ -57,9 +57,9 @@ std::string CMapHandler::getTerrainDescr(const int3 & pos, bool rightClick) cons
 
 	std::string result = t.getTerrain()->getNameTranslated();
 
-	for(const auto & object : map->objects)
+	for(const auto & object : map->getObjects())
 	{
-		if(object && object->coveringAt(pos) && object->isTile2Terrain())
+		if(object->coveringAt(pos) && object->isTile2Terrain())
 		{
 			result = object->getObjectName();
 			break;

+ 9 - 17
lib/CGameInfoCallback.cpp

@@ -131,26 +131,18 @@ TurnTimerInfo CGameInfoCallback::getPlayerTurnTime(PlayerColor color) const
 
 const CGObjectInstance* CGameInfoCallback::getObj(ObjectInstanceID objid, bool verbose) const
 {
-	si32 oid = objid.num;
-	if(oid < 0  ||  oid >= gs->getMap().objects.size())
-	{
-		if(verbose)
-			logGlobal->error("Cannot get object with id %d", oid);
-		return nullptr;
-	}
-
-	const CGObjectInstance *ret = gs->getMap().objects[oid].get();
+	const CGObjectInstance *ret = gs->getMap().getObject(objid);
 	if(!ret)
 	{
 		if(verbose)
-			logGlobal->error("Cannot get object with id %d. Object was removed", oid);
+			logGlobal->error("Cannot get object with id %d. Object was removed", objid.getNum());
 		return nullptr;
 	}
 
 	if(!isVisible(ret, getPlayerID()) && ret->tempOwner != getPlayerID())
 	{
 		if(verbose)
-			logGlobal->error("Cannot get object with id %d. Object is not visible.", oid);
+			logGlobal->error("Cannot get object with id %d. Object is not visible.", objid.getNum());
 		return nullptr;
 	}
 
@@ -483,9 +475,9 @@ std::vector <const CGObjectInstance *> CGameInfoCallback::getVisitableObjs(int3
 std::vector<const CGObjectInstance *> CGameInfoCallback::getAllVisitableObjs() const
 {
 	std::vector<const CGObjectInstance *> ret;
-	for(auto & obj : gs->getMap().objects)
-		if(obj && obj->isVisitable() && obj->ID != Obj::EVENT && isVisible(obj.get()))
-			ret.push_back(obj.get());
+	for(auto & obj : gs->getMap().getObjects())
+		if(obj->isVisitable() && obj->ID != Obj::EVENT && isVisible(obj))
+			ret.push_back(obj);
 
 	return ret;
 }
@@ -548,9 +540,9 @@ EDiggingStatus CGameInfoCallback::getTileDigStatus(int3 tile, bool verbose) cons
 	if(!isVisible(tile))
 		return EDiggingStatus::UNKNOWN;
 
-	for(const auto & object : gs->getMap().objects)
+	for(const auto & object : gs->getMap().getObjects())
 	{
-		if(object && object->ID == Obj::HOLE && object->anchorPos() == tile)
+		if(object->ID == Obj::HOLE && object->anchorPos() == tile)
 			return EDiggingStatus::TILE_OCCUPIED;
 	}
 	return getTile(tile)->getDiggingStatus();
@@ -936,7 +928,7 @@ const CArtifactInstance * CGameInfoCallback::getArtInstance( ArtifactInstanceID
 
 const CGObjectInstance * CGameInfoCallback::getObjInstance( ObjectInstanceID oid ) const
 {
-	return gs->getMap().objects.at(oid.num).get();
+	return gs->getMap().getObject((oid));
 }
 
 const CArtifactSet * CGameInfoCallback::getArtSet(const ArtifactLocation & loc) const

+ 1 - 1
lib/IGameCallback.cpp

@@ -256,7 +256,7 @@ CArtifactInstance * CNonConstInfoCallback::getArtInstance(const ArtifactInstance
 
 CGObjectInstance * CNonConstInfoCallback::getObjInstance(const ObjectInstanceID & oid)
 {
-	return gs->getMap().objects.at(oid.num).get();
+	return gs->getMap().getObject(oid);
 }
 
 CArmedInstance * CNonConstInfoCallback::getArmyInstance(const ObjectInstanceID & oid)

+ 35 - 73
lib/gameState/CGameState.cpp

@@ -251,16 +251,18 @@ void CGameState::updateEntity(Metatype metatype, int32_t index, const JsonNode &
 		break;
 	}
 	case Metatype::MAP_OBJECT_INSTANCE:
-		if(index >= 0 && index < map->objects.size())
+	{
+		CGObjectInstance * obj = getObjInstance(ObjectInstanceID(index));
+		if(obj)
 		{
-			CGObjectInstance * obj = getObjInstance(ObjectInstanceID(index));
 			obj->updateFrom(data);
 		}
 		else
 		{
-			logGlobal->error("Update entity: object index %s is out of range [%d,%d]", index, 0,  map->objects.size());
+			logGlobal->error("Update entity: object index %s is out of range", index);
 		}
 		break;
+	}
 	default:
 		logGlobal->error("This metatype update is not implemented");
 		break;
@@ -446,7 +448,7 @@ void CGameState::initGrailPosition()
 		}
 
 		//remove tiles with holes
-		for(auto & elem : map->objects)
+		for(auto & elem : map->getObjects())
 			if(elem && elem->ID == Obj::HOLE)
 				allowedPos -= elem->anchorPos();
 
@@ -480,11 +482,8 @@ void CGameState::initRandomFactionsForPlayers()
 void CGameState::randomizeMapObjects()
 {
 	logGlobal->debug("\tRandomizing objects");
-	for(const auto & object : map->objects)
+	for(const auto & object : map->getObjects())
 	{
-		if(!object)
-			continue;
-
 		object->pickRandomObject(getRandomGenerator());
 
 		//handle Favouring Winds - mark tiles under it
@@ -504,10 +503,10 @@ void CGameState::randomizeMapObjects()
 
 void CGameState::initOwnedObjects()
 {
-	for(const auto & object : map->objects)
+	for(const auto & object : map->getObjects())
 	{
 		if (object && object->getOwner().isValidPlayer())
-			getPlayerState(object->getOwner())->addOwnedObject(object.get());
+			getPlayerState(object->getOwner())->addOwnedObject(object);
 	}
 }
 
@@ -575,16 +574,11 @@ void CGameState::placeStartingHeroes()
 void CGameState::removeHeroPlaceholders()
 {
 	// remove any hero placeholders that remain on map after (potential) campaign heroes placement
-	for(auto obj : map->objects)
+	for(auto obj : map->getObjects<CGHeroPlaceholder>())
 	{
-		if(obj && obj->ID == Obj::HERO_PLACEHOLDER)
-		{
-			auto heroPlaceholder = dynamic_cast<CGHeroPlaceholder *>(obj.get());
-			map->removeBlockVisTiles(heroPlaceholder, true);
-			map->instanceNames.erase(obj->instanceName);
-			map->objects[heroPlaceholder->id.getNum()] = nullptr;
-			delete heroPlaceholder;
-		}
+		map->removeBlockVisTiles(obj, true);
+		map->instanceNames.erase(obj->instanceName);
+		map->eraseObject(obj->id);
 	}
 }
 
@@ -616,21 +610,15 @@ void CGameState::initHeroes()
 
 			boat->setAnchorPos(hero->anchorPos());
 			boat->appearance = handler->getTemplates().front();
-			boat->id = ObjectInstanceID(static_cast<si32>(gs->getMap().objects.size()));
-
-			map->objects.emplace_back(boat);
-
+			map->addNewObject(boat);
 			hero->attachToBoat(boat.get());
 		}
 	}
 
-	for(auto obj : map->objects) //prisons
+	for(auto hero : map->getObjects<CGHeroInstance>()) //prisons
 	{
-		if(obj && obj->ID == Obj::PRISON)
-		{
-			auto * hero = dynamic_cast<CGHeroInstance*>(obj.get());
+		if(hero->ID == Obj::PRISON)
 			hero->initHero(getRandomGenerator());
-		}
 	}
 
 	std::set<HeroTypeID> heroesToCreate = getUnusedAllowedHeroes(); //ids of heroes to be created and put into the pool
@@ -671,9 +659,10 @@ void CGameState::initFogOfWar()
 		fow.resize(boost::extents[layers][map->width][map->height]);
 		std::fill(fow.data(), fow.data() + fow.num_elements(), 0);
 
-		for(const auto & obj : map->objects)
+		for(const auto & obj : map->getObjects())
 		{
-			if(!obj || !vstd::contains(elem.second.players, obj->tempOwner)) continue; //not a flagged object
+			if(!vstd::contains(elem.second.players, obj->getOwner()))
+				continue; //not a flagged object
 
 			std::unordered_set<int3> tiles;
 			getTilesInRange(tiles, obj->getSightCenter(), obj->getSightRadius(), ETileVisibility::HIDDEN, obj->tempOwner);
@@ -927,27 +916,14 @@ void CGameState::initMapObjects()
 {
 	logGlobal->debug("\tObject initialization");
 
-	for(auto & obj : map->objects)
-	{
-		if(obj)
-			obj->initObj(getRandomGenerator());
-	}
+	for(auto & obj : map->getObjects())
+		obj->initObj(getRandomGenerator());
+
 	logGlobal->debug("\tObject initialization done");
-	for(auto & obj : map->objects)
+	for(auto & q : map->getObjects<CGSeerHut>())
 	{
-		if(!obj)
-			continue;
-
-		switch(obj->ID.toEnum())
-		{
-			case Obj::QUEST_GUARD:
-			case Obj::SEER_HUT:
-			{
-				auto * q = dynamic_cast<CGSeerHut *>(obj.get());
-				assert (q);
-				q->setObjToKill();
-			}
-		}
+		if (q->ID ==Obj::QUEST_GUARD || q->ID ==Obj::SEER_HUT)
+			q->setObjToKill();
 	}
 	CGSubterraneanGate::postInit(callback); //pairing subterranean gates
 
@@ -1060,10 +1036,10 @@ BattleField CGameState::battleGetBattlefieldType(int3 tile, vstd::RNG & rand)
 		return topObject->getBattlefield();
 	}
 
-	for(auto &obj : map->objects)
+	for(auto & obj : map->getObjects())
 	{
 		//look only for objects covering given tile
-		if( !obj || !obj->coveringAt(tile))
+		if( !obj->coveringAt(tile))
 			continue;
 
 		auto customBattlefield = obj->getBattlefield();
@@ -1313,10 +1289,9 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
 		{
 			//check if in players armies there is enough creatures
 			int total = 0; //creature counter
-			for(auto object : map->objects)
+			for(auto ai : map->getObjects<CArmedInstance>())
 			{
-				const auto * ai = dynamic_cast<const CArmedInstance *>(object.get());
-				if(ai && ai->getOwner() == player)
+				if(ai->getOwner() == player)
 				{
 					for(const auto & elem : ai->Slots()) //iterate through army
 						if(elem.second->getId() == condition.objectType.as<CreatureID>()) //it's searched creature
@@ -1354,7 +1329,7 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
 			}
 			else
 			{
-				for(const auto & elem : map->objects) // mode B - destroy all objects of this type
+				for(const auto & elem : map->getObjects()) // mode B - destroy all objects of this type
 				{
 					if(elem && elem->ID == condition.objectType.as<MapObjectID>())
 						return false;
@@ -1378,7 +1353,7 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
 			}
 			else
 			{
-				for(const auto & elem : map->objects) // mode B - flag all objects of this type
+				for(const auto & elem : map->getObjects()) // mode B - flag all objects of this type
 				{
 					 //check not flagged objs
 					if ( elem && elem->ID == condition.objectType.as<MapObjectID>() && team->players.count(elem->getOwner()) == 0 )
@@ -1616,12 +1591,9 @@ void CGameState::buildGlobalTeamPlayerTree()
 
 void CGameState::attachArmedObjects()
 {
-	for(auto & obj : map->objects)
+	for(auto & armed : map->getObjects<CArmedInstance>())
 	{
-		if(auto * armed = dynamic_cast<CArmedInstance *>(obj.get()))
-		{
-			armed->whatShouldBeAttached().attachTo(armed->whereShouldBeAttached(this));
-		}
+		armed->whatShouldBeAttached().attachTo(armed->whereShouldBeAttached(this));
 	}
 }
 
@@ -1653,9 +1625,8 @@ std::set<HeroTypeID> CGameState::getUnusedAllowedHeroes(bool alsoIncludeNotAllow
 	for (auto heroID : map->getHeroesOnMap())
 		ret -= getHero(heroID)->getHeroTypeID();
 
-	for(auto obj : map->objects) //prisons
+	for(auto hero : map->getObjects<CGHeroInstance>()) //prisons
 	{
-		auto * hero = dynamic_cast<const CGHeroInstance *>(obj.get());
 		if(hero && hero->ID == Obj::PRISON)
 			ret -= hero->getHeroTypeID();
 	}
@@ -1670,17 +1641,8 @@ bool CGameState::isUsedHero(const HeroTypeID & hid) const
 
 CGHeroInstance * CGameState::getUsedHero(const HeroTypeID & hid) const
 {
-	for(auto obj : map->objects) //prisons
+	for(auto hero : map->getObjects<CGHeroInstance>()) //prisons
 	{
-		if (!obj)
-			continue;
-
-		if ( obj->ID !=Obj::PRISON && obj->ID != Obj::HERO)
-			continue;
-
-		auto * hero = dynamic_cast<CGHeroInstance *>(obj.get());
-		assert(hero);
-
 		if (hero->getHeroTypeID() == hid)
 			return hero;
 	}

+ 3 - 19
lib/gameState/CGameStateCampaign.cpp

@@ -361,7 +361,7 @@ void CGameStateCampaign::replaceHeroesPlaceholders()
 		if (!campaignHeroReplacement.heroPlaceholderId.hasValue())
 			continue;
 
-		auto heroPlaceholder = gameState->map->objects.at(campaignHeroReplacement.heroPlaceholderId.getNum());
+		auto heroPlaceholder = gameState->map->getObject(campaignHeroReplacement.heroPlaceholderId);
 		auto heroToPlace = campaignHeroReplacement.hero;
 
 		if(heroPlaceholder->tempOwner.isValidPlayer())
@@ -380,16 +380,8 @@ void CGameStateCampaign::transferMissingArtifacts(const CampaignTravel & travelO
 {
 	CGHeroInstance * receiver = nullptr;
 
-	for(auto obj : gameState->map->objects)
+	for(auto hero : gameState->map->getObjects<CGHeroInstance>())
 	{
-		if (!obj)
-			continue;
-
-		if (obj->ID != Obj::HERO)
-			continue;
-
-		auto * hero = dynamic_cast<CGHeroInstance *>(obj.get());
-
 		if (gameState->getPlayerState(hero->getOwner())->isHuman())
 		{
 			receiver = hero;
@@ -445,16 +437,8 @@ void CGameStateCampaign::generateCampaignHeroesToReplace()
 	campaignHeroReplacements.clear();
 
 	// find all placeholders on map
-	for(auto obj : gameState->map->objects)
+	for(auto heroPlaceholder : gameState->map->getObjects<CGHeroPlaceholder>())
 	{
-		if(!obj)
-			continue;
-
-		if (obj->ID != Obj::HERO_PLACEHOLDER)
-			continue;
-
-		auto * heroPlaceholder = dynamic_cast<CGHeroPlaceholder *>(obj.get());
-
 		// only 1 field must be set
 		assert(heroPlaceholder->powerRank.has_value() != heroPlaceholder->heroType.has_value());
 

+ 2 - 2
lib/mapObjects/CGDwelling.cpp

@@ -75,9 +75,9 @@ FactionID CGDwelling::randomizeFaction(vstd::RNG & rand)
 
 	if (randomizationInfo->identifier != 0)
 	{
-		for(auto & elem : cb->gameState()->getMap().objects)
+		for(auto & townID : cb->gameState()->getMap().getAllTowns())
 		{
-			auto town = dynamic_cast<CGTownInstance*>(elem.get());
+			auto town = cb->gameState()->getTown(townID);
 			if(town && town->identifier == randomizationInfo->identifier)
 			{
 				linkedTown = town;

+ 7 - 16
lib/mapObjects/MiscObjects.cpp

@@ -374,13 +374,9 @@ void CGTeleport::addToChannel(std::map<TeleportChannelID, std::shared_ptr<Telepo
 
 TeleportChannelID CGMonolith::findMeChannel(const std::vector<Obj> & IDs, MapObjectSubID SubID) const
 {
-	for(auto obj : cb->gameState()->getMap().objects)
+	for(auto teleportObj : cb->gameState()->getMap().getObjects<CGMonolith>())
 	{
-		if(!obj)
-			continue;
-
-		const auto * teleportObj = dynamic_cast<const CGMonolith *>(cb->getObj(obj->id));
-		if(teleportObj && vstd::contains(IDs, teleportObj->ID) && teleportObj->subID == SubID)
+		if(vstd::contains(IDs, teleportObj->ID) && teleportObj->subID == SubID)
 			return teleportObj->channel;
 	}
 	return TeleportChannelID();
@@ -487,14 +483,9 @@ void CGSubterraneanGate::postInit(IGameCallback * cb) //matches subterranean gat
 {
 	//split on underground and surface gates
 	std::vector<CGSubterraneanGate *> gatesSplit[2]; //surface and underground gates
-	for(auto & obj : cb->gameState()->getMap().objects)
+	for(auto gate : cb->gameState()->getMap().getObjects<CGSubterraneanGate>())
 	{
-		if(!obj)
-			continue;
-
-		auto * hlp = dynamic_cast<CGSubterraneanGate *>(cb->gameState()->getObjInstance(obj->id));
-		if(hlp)
-			gatesSplit[hlp->visitablePos().z].push_back(hlp);
+		gatesSplit[gate->visitablePos().z].push_back(gate);
 	}
 
 	//sort by position
@@ -948,10 +939,10 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const
 
 		std::vector<const CGObjectInstance *> eyes;
 
-		for (const auto & object : cb->gameState()->getMap().objects)
+		for (const auto & object : cb->gameState()->getMap().getObjects<CGMagi>())
 		{
-			if (object && object->ID == Obj::EYE_OF_MAGI && object->subID == this->subID)
-				eyes.push_back(object.get());
+			if (object->ID == Obj::EYE_OF_MAGI && object->subID == this->subID)
+				eyes.push_back(object);
 		}
 
 		if (!eyes.empty())

+ 10 - 0
lib/mapping/CMap.cpp

@@ -888,4 +888,14 @@ std::vector<HeroTypeID> CMap::getHeroesInPool() const
 	return result;
 }
 
+CGObjectInstance * CMap::getObject(ObjectInstanceID obj)
+{
+	return objects.at(obj).get();
+}
+
+const CGObjectInstance * CMap::getObject(ObjectInstanceID obj) const
+{
+	return objects.at(obj).get();
+}
+
 VCMI_LIB_NAMESPACE_END

+ 31 - 3
lib/mapping/CMap.h

@@ -62,6 +62,8 @@ class DLL_LINKAGE CMap : public CMapHeader, public GameCallbackHolder
 
 	std::unique_ptr<GameSettings> gameSettings;
 
+	/// Central lists of items in game. Position of item in the vectors below is their (instance) id.
+	std::vector< std::shared_ptr<CGObjectInstance> > objects;
 	/// All quests that are currently present on map
 	std::vector<std::shared_ptr<CQuest>> quests;
 	/// All artifacts that exists on map, whether on map, in hero inventory, or stored in some object
@@ -138,6 +140,35 @@ public:
 	CGHeroInstance * tryGetFromHeroPool(HeroTypeID hero);
 	std::vector<HeroTypeID> getHeroesInPool() const;
 
+	CGObjectInstance * getObject(ObjectInstanceID obj);
+	const CGObjectInstance * getObject(ObjectInstanceID obj) const;
+
+	template<typename ObjectType = CGObjectInstance>
+	std::vector<const ObjectType *> getObjects() const
+	{
+		std::vector<const ObjectType *> result;
+		for (const auto & object : objects)
+		{
+			auto casted = dynamic_cast<const ObjectType*>(object.get());
+			if (casted)
+				result.push_back(casted);
+		}
+		return result;
+	}
+
+	template<typename ObjectType = CGObjectInstance>
+	std::vector<ObjectType *> getObjects()
+	{
+		std::vector<ObjectType *> result;
+		for (const auto & object : objects)
+		{
+			auto casted = dynamic_cast<ObjectType*>(object.get());
+			if (casted)
+				result.push_back(casted);
+		}
+		return result;
+	}
+
 	bool isWaterMap() const;
 	bool calculateWaterContent();
 	void banWaterArtifacts();
@@ -177,9 +208,6 @@ public:
 	int3 grailPos;
 	int grailRadius;
 
-	//Central lists of items in game. Position of item in the vectors below is their (instance) id.
-	std::vector< std::shared_ptr<CGObjectInstance> > objects;
-
 	//Helper lists
 	std::map<TeleportChannelID, std::shared_ptr<TeleportChannel> > teleportChannels;
 

+ 0 - 4
lib/mapping/CMapOperation.cpp

@@ -587,8 +587,6 @@ CInsertObjectOperation::CInsertObjectOperation(CMap* map, std::shared_ptr<CGObje
 
 void CInsertObjectOperation::execute()
 {
-	obj->id = ObjectInstanceID(map->objects.size());
-
 	map->generateUniqueInstanceName(obj.get());
 	map->addNewObject(obj);
 }
@@ -650,8 +648,6 @@ void CRemoveObjectOperation::execute()
 void CRemoveObjectOperation::undo()
 {
 	assert(removedObject != nullptr);
-	//set new id, but do not rename object
-	removedObject->id = ObjectInstanceID(static_cast<si32>(map->objects.size()));
 	map->addNewObject(removedObject);
 }
 

+ 4 - 4
lib/mapping/MapFormatH3M.cpp

@@ -1920,6 +1920,7 @@ std::shared_ptr<CGObjectInstance> CMapLoaderH3M::readObject(MapObjectID id, MapO
 void CMapLoaderH3M::readObjects()
 {
 	uint32_t objectsCount = reader->readUInt32();
+	ObjectInstanceID nextObjectID(0);
 
 	for(uint32_t i = 0; i < objectsCount; ++i)
 	{
@@ -1927,7 +1928,6 @@ void CMapLoaderH3M::readObjects()
 		assert(map->isInTheMap(mapPosition) || map->isInTheMap(mapPosition - int3(0,8,0)) || map->isInTheMap(mapPosition - int3(8,0,0)) || map->isInTheMap(mapPosition - int3(8,8,0)));
 
 		uint32_t defIndex = reader->readUInt32();
-		ObjectInstanceID objectInstanceID = ObjectInstanceID(static_cast<si32>(map->objects.size()));
 
 		std::shared_ptr<ObjectTemplate> originalTemplate = originalTemplates.at(defIndex);
 		std::shared_ptr<ObjectTemplate> remappedTemplate = remappedTemplates.at(defIndex);
@@ -1935,20 +1935,19 @@ void CMapLoaderH3M::readObjects()
 		auto originalSubID = originalTemplate->subid;
 		reader->skipZero(5);
 
-		auto newObject = readObject(originalID, originalSubID, remappedTemplate, mapPosition, objectInstanceID);
+		auto newObject = readObject(originalID, originalSubID, remappedTemplate, mapPosition, nextObjectID);
 
 		if(!newObject)
 			continue;
 
 		newObject->setAnchorPos(mapPosition);
 		newObject->ID = remappedTemplate->id;
-		newObject->id = objectInstanceID;
+		newObject->id = nextObjectID;
 		if(newObject->ID != Obj::HERO && newObject->ID != Obj::HERO_PLACEHOLDER && newObject->ID != Obj::PRISON)
 		{
 			newObject->subID = remappedTemplate->subid;
 		}
 		newObject->appearance = remappedTemplate;
-		assert(objectInstanceID == ObjectInstanceID((si32)map->objects.size()));
 
 		if (newObject->isVisitable() && !map->isInTheMap(newObject->visitablePos()))
 			logGlobal->error("Map '%s': Object at %s - outside of map borders!", mapName, mapPosition.toString());
@@ -1962,6 +1961,7 @@ void CMapLoaderH3M::readObjects()
 			newObject->instanceName = fmt.str();
 		}
 		map->addNewObject(newObject);
+		nextObjectID.num += 1;
 	}
 
 	map->postInitialize();

+ 11 - 18
lib/mapping/MapFormatJson.cpp

@@ -89,14 +89,14 @@ std::string MapObjectResolver::encode(si32 identifier) const
 		id = owner->map->questIdentifierToId[identifier];
 	}
 
-	si32 oid = id.getNum();
-	if(oid < 0  ||  oid >= owner->map->objects.size())
+	auto object = owner->map->getObject(id);
+
+	if(!object)
 	{
-        logGlobal->error("Cannot get object with id %d", oid);
+		logGlobal->error("Cannot get object with id %d", id.getNum());
 		return "";
 	}
-
-	return owner->map->objects[oid]->instanceName;
+	return object->instanceName;
 }
 
 namespace HeaderDetail
@@ -421,12 +421,10 @@ void CMapFormatJson::serializePlayerInfo(JsonSerializeFormat & handler)
 		{
 			//ignoring heroesNames and saving from actual map objects
 			//TODO: optimize
-			for(auto & obj : map->objects)
+			for(auto & hero : map->getObjects<CGHeroInstance>())
 			{
-				if((obj->ID == Obj::HERO || obj->ID == Obj::RANDOM_HERO) && obj->tempOwner == PlayerColor(player))
+				if((hero->getOwner()) == PlayerColor(player))
 				{
-					auto * hero = dynamic_cast<CGHeroInstance *>(obj.get());
-
 					auto heroes = handler.enterStruct("heroes");
 					if(hero)
 					{
@@ -1066,7 +1064,6 @@ void CMapLoaderJson::MapObjectLoader::construct()
 	// Will be destroyed soon and replaced with shared template
 	instance = handler->create(owner->map->cb, appearance);
 
-	instance->id = ObjectInstanceID(static_cast<si32>(owner->map->objects.size()));
 	instance->instanceName = jsonKey;
 	instance->setAnchorPos(pos);
 	owner->map->addNewObject(instance);
@@ -1137,13 +1134,11 @@ void CMapLoaderJson::readObjects()
 	map->postInitialize();
 
 	std::set<HeroTypeID> debugHeroesOnMap;
-	for (auto const & object : map->objects)
+	for (auto const & hero : map->getObjects<CGHeroInstance>())
 	{
-		if(object->ID != Obj::HERO && object->ID != Obj::PRISON)
+		if(hero->ID != Obj::HERO && hero->ID != Obj::PRISON)
 			continue;
 
-		auto * hero = dynamic_cast<const CGHeroInstance *>(object.get());
-
 		if (debugHeroesOnMap.count(hero->getHeroTypeID()))
 			logGlobal->error("Hero is already on the map at %s", hero->visitablePos().toString());
 
@@ -1301,7 +1296,7 @@ void CMapSaverJson::writeObjects()
 
 	JsonSerializer handler(mapObjectResolver.get(), data);
 
-	for(const auto & obj : map->objects)
+	for(const auto & obj : map->getObjects())
 	{
 		//logGlobal->trace("\t%s", obj->instanceName);
 		auto temp = handler.enterStruct(obj->instanceName);
@@ -1320,9 +1315,7 @@ void CMapSaverJson::writeObjects()
 
 		grail["options"]["radius"].Float() = map->grailRadius;
 
-		std::string grailId = boost::str(boost::format("grail_%d") % map->objects.size());
-
-		data[grailId] = grail;
+		data["grail"] = grail;
 	}
 
 	//cleanup empty options

+ 5 - 18
lib/networkPacks/NetPacksLib.cpp

@@ -992,20 +992,12 @@ void FoWChange::applyGs(CGameState *gs)
 	if (mode == ETileVisibility::HIDDEN) //do not hide too much
 	{
 		std::unordered_set<int3> tilesRevealed;
-		for (auto & o : gs->getMap().objects)
+		for (auto & o : gs->getMap().getObjects())
 		{
-			if (o)
+			if (o->asOwnable())
 			{
-				switch(o->ID.toEnum())
-				{
-				case Obj::HERO:
-				case Obj::MINE:
-				case Obj::TOWN:
-				case Obj::ABANDONED_MINE:
-					if(vstd::contains(team->players, o->tempOwner)) //check owned observators
-						gs->getTilesInRange(tilesRevealed, o->getSightCenter(), o->getSightRadius(), ETileVisibility::HIDDEN, o->tempOwner);
-					break;
-				}
+				if(vstd::contains(team->players, o->getOwner())) //check owned observators
+					gs->getTilesInRange(tilesRevealed, o->getSightCenter(), o->getSightRadius(), ETileVisibility::HIDDEN, o->tempOwner);
 			}
 		}
 		for(const int3 & t : tilesRevealed) //probably not the most optimal solution ever
@@ -1232,7 +1224,6 @@ void RemoveObject::applyGs(CGameState *gs)
 		//return hero to the pool, so he may reappear in tavern
 
 		gs->heroesPool->addHeroToPool(beatenHero);
-		gs->getMap().objects[objectID.getNum()] = nullptr;
 
 		//If hero on Boat is removed, the Boat disappears
 		if(beatenHero->boat)
@@ -1448,13 +1439,11 @@ void HeroRecruited::applyGs(CGameState *gs)
 	assert(h->id == ObjectInstanceID());
 	if(h->id == ObjectInstanceID())
 	{
-		h->id = ObjectInstanceID(static_cast<si32>(gs->getMap().objects.size()));
-		gs->getMap().objects.emplace_back(h);
+		gs->getMap().addNewObject(h);
 	}
 	else
 		gs->getMap().replaceObject(h->id, h);
 
-	gs->getMap().addNewObject(h);
 	p->addOwnedObject(h.get());
 	h->attachTo(*p);
 
@@ -1497,8 +1486,6 @@ void GiveHero::applyGs(CGameState *gs)
 
 void NewObject::applyGs(CGameState *gs)
 {
-	newObject->id = ObjectInstanceID(static_cast<si32>(gs->getMap().objects.size()));
-
 	gs->getMap().addNewObject(newObject);
 	gs->getMap().calculateGuardingGreaturePositions();
 

+ 11 - 15
lib/spells/AdventureSpellMechanics.cpp

@@ -201,20 +201,16 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment
 	//try to find unoccupied boat to summon
 	const CGBoat * nearest = nullptr;
 	double dist = 0;
-	for(const auto & obj : env->getMap()->objects)
+	for(const auto & b : env->getMap()->getObjects<CGBoat>())
 	{
-		if(obj && obj->ID == Obj::BOAT)
-		{
-			const auto * b = dynamic_cast<const CGBoat *>(obj.get());
-			if(b->getBoardedHero() || b->layer != EPathfindingLayer::SAIL)
-				continue; //we're looking for unoccupied boat
+		if(b->getBoardedHero() || b->layer != EPathfindingLayer::SAIL)
+			continue; //we're looking for unoccupied boat
 
-			double nDist = b->visitablePos().dist2d(parameters.caster->getHeroCaster()->visitablePos());
-			if(!nearest || nDist < dist) //it's first boat or closer than previous
-			{
-				nearest = b;
-				dist = nDist;
-			}
+		double nDist = b->visitablePos().dist2d(parameters.caster->getHeroCaster()->visitablePos());
+		if(!nearest || nDist < dist) //it's first boat or closer than previous
+		{
+			nearest = b;
+			dist = nDist;
 		}
 	}
 
@@ -726,12 +722,12 @@ ESpellCastResult ViewMechanics::applyAdventureEffects(SpellCastEnvironment * env
 
 	const auto & fowMap = env->getCb()->getPlayerTeam(parameters.caster->getCasterOwner())->fogOfWarMap;
 
-	for(const auto & obj : env->getMap()->objects)
+	for(const auto & obj : env->getMap()->getObjects())
 	{
 		//deleted object remain as empty pointer
-		if(obj && filterObject(obj.get(), spellLevel))
+		if(obj && filterObject(obj, spellLevel))
 		{
-			ObjectPosInfo posInfo(obj.get());
+			ObjectPosInfo posInfo(obj);
 
 			if(fowMap[posInfo.pos.z][posInfo.pos.x][posInfo.pos.y] == 0)
 				pack.objectPositions.push_back(posInfo);

+ 6 - 6
server/CGameHandler.cpp

@@ -640,11 +640,11 @@ void CGameHandler::onNewTurn()
 
 	if (firstTurn)
 	{
-		for (auto obj : gs->getMap().objects)
+		for (auto obj : gs->getMap().getObjects<CGHeroInstance>())
 		{
-			if (obj && obj->ID == Obj::PRISON) //give imprisoned hero 0 exp to level him up. easiest to do at this point
+			if (obj->ID == Obj::PRISON) //give imprisoned hero 0 exp to level him up. easiest to do at this point
 			{
-				giveExperience(getHero(obj->id), 0);
+				giveExperience(obj, 0);
 			}
 		}
 
@@ -700,7 +700,7 @@ void CGameHandler::onNewTurn()
 		checkVictoryLossConditionsForAll(); // check for map turn limit
 
 	//call objects
-	for (auto & elem : gs->getMap().objects)
+	for (auto & elem : gs->getMap().getObjects())
 	{
 		if (elem)
 			elem->newTurn(getRandomGenerator());
@@ -3570,10 +3570,10 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
 			}
 
 			//player lost -> all his objects become unflagged (neutral)
-			for (auto obj : gs->getMap().objects) //unflag objs
+			for (auto obj : gs->getMap().getObjects()) //unflag objs
 			{
 				if (obj && obj->tempOwner == player)
-					setOwner(obj.get(), PlayerColor::NEUTRAL);
+					setOwner(obj, PlayerColor::NEUTRAL);
 			}
 
 			//eliminating one player may cause victory of another:

+ 3 - 2
server/processors/PlayerMessageProcessor.cpp

@@ -23,6 +23,7 @@
 #include "../../lib/gameState/CGameState.h"
 #include "../../lib/mapObjects/CGTownInstance.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
+#include "../../lib/mapObjects/MiscObjects.h"
 #include "../../lib/modding/IdentifierStorage.h"
 #include "../../lib/modding/ModScope.h"
 #include "../../lib/mapping/CMap.h"
@@ -627,9 +628,9 @@ void PlayerMessageProcessor::cheatPuzzleReveal(PlayerColor player)
 {
 	TeamState *t = gameHandler->gameState()->getPlayerTeam(player);
 
-	for(auto & obj : gameHandler->gameState()->getMap().objects)
+	for(auto & obj : gameHandler->gameState()->getMap().getObjects<CGObelisk>())
 	{
-		if(obj && obj->ID == Obj::OBELISK && !obj->wasVisited(player))
+		if(!obj->wasVisited(player))
 		{
 			gameHandler->setObjPropertyID(obj->id, ObjProperty::OBELISK_VISITED, t->id);
 			for(const auto & color : t->players)

+ 4 - 4
server/processors/TurnOrderProcessor.cpp

@@ -109,14 +109,14 @@ bool TurnOrderProcessor::playersInContact(PlayerColor left, PlayerColor right) c
 	const auto * leftInfo = gameHandler->getPlayerState(left, false);
 	const auto * rightInfo = gameHandler->getPlayerState(right, false);
 
-	for (auto obj : gameHandler->gameState()->getMap().objects)
+	for (auto obj : gameHandler->gameState()->getMap().getObjects())
 	{
-		if (obj && obj->isVisitable())
+		if (obj->asOwnable())
 		{
 			int3 pos = obj->visitablePos();
-			if (obj->tempOwner == left)
+			if (obj->getOwner() == left)
 				leftReachability[pos.z][pos.x][pos.y] = true;
-			if (obj->tempOwner == right)
+			if (obj->getOwner() == right)
 				rightReachability[pos.z][pos.x][pos.y] = true;
 		}
 	}

+ 11 - 11
test/game/CGameStateTest.cpp

@@ -227,8 +227,8 @@ TEST_F(CGameStateTest, DISABLED_issue2765)
 	auto attackerID = map->getHeroesOnMap()[0];
 	auto defenderID = map->getHeroesOnMap()[1];
 
-	auto attacker = std::dynamic_pointer_cast<CGHeroInstance>(map->objects.at(attackerID.getNum()));
-	auto defender = std::dynamic_pointer_cast<CGHeroInstance>(map->objects.at(defenderID.getNum()));
+	auto attacker = dynamic_cast<CGHeroInstance *>(map->getObject(attackerID));
+	auto defender = dynamic_cast<CGHeroInstance *>(map->getObject(defenderID));
 
 	ASSERT_NE(attacker->tempOwner, defender->tempOwner);
 
@@ -240,7 +240,7 @@ TEST_F(CGameStateTest, DISABLED_issue2765)
 		gameCallback->sendAndApply(na);
 	}
 
-	startTestBattle(attacker.get(), defender.get());
+	startTestBattle(attacker, defender);
 
 	{
 		battle::UnitInfo info;
@@ -271,11 +271,11 @@ TEST_F(CGameStateTest, DISABLED_issue2765)
 	ASSERT_NE(def, nullptr);
 	ASSERT_NE(att, def);
 
-	EXPECT_NE(att->getMyHero(), defender.get());
-	EXPECT_NE(def->getMyHero(), attacker.get());
+	EXPECT_NE(att->getMyHero(), defender);
+	EXPECT_NE(def->getMyHero(), attacker);
 
-	EXPECT_EQ(att->getMyHero(), attacker.get()) << att->nodeName();
-	EXPECT_EQ(def->getMyHero(), defender.get()) << def->nodeName();
+	EXPECT_EQ(att->getMyHero(), attacker) << att->nodeName();
+	EXPECT_EQ(def->getMyHero(), defender) << def->nodeName();
 
 	{
 		using namespace ::testing;
@@ -312,8 +312,8 @@ TEST_F(CGameStateTest, DISABLED_battleResurrection)
 	auto attackerID = map->getHeroesOnMap()[0];
 	auto defenderID = map->getHeroesOnMap()[1];
 
-	auto attacker = std::dynamic_pointer_cast<CGHeroInstance>(map->objects.at(attackerID.getNum()));
-	auto defender = std::dynamic_pointer_cast<CGHeroInstance>(map->objects.at(defenderID.getNum()));
+	auto attacker = dynamic_cast<CGHeroInstance *>(map->getObject(attackerID));
+	auto defender = dynamic_cast<CGHeroInstance *>(map->getObject(defenderID));
 
 	ASSERT_NE(attacker->tempOwner, defender->tempOwner);
 
@@ -331,7 +331,7 @@ TEST_F(CGameStateTest, DISABLED_battleResurrection)
 		gameCallback->sendAndApply(na);
 	}
 
-	startTestBattle(attacker.get(), defender.get());
+	startTestBattle(attacker, defender);
 
 	uint32_t unitId = gameState->currentBattles.front()->battleNextUnitId();
 
@@ -384,7 +384,7 @@ TEST_F(CGameStateTest, DISABLED_battleResurrection)
 		const CSpell * spell = SpellID(SpellID::RESURRECTION).toSpell();
 		ASSERT_NE(spell, nullptr);
 
-			spells::BattleCast cast(gameState->currentBattles.front().get(), attacker.get(), spells::Mode::HERO, spell);
+			spells::BattleCast cast(gameState->currentBattles.front().get(), attacker, spells::Mode::HERO, spell);
 
 		spells::Target target;
 		target.emplace_back(unit);

+ 3 - 7
test/map/MapComparer.cpp

@@ -211,14 +211,10 @@ void MapComparer::compareObject(const CGObjectInstance * actual, const CGObjectI
 
 void MapComparer::compareObjects()
 {
-	EXPECT_EQ(actual->objects.size(), expected->objects.size());
+	EXPECT_EQ(actual->getObjects().size(), expected->getObjects().size());
 
-	for(size_t idx = 0; idx < expected->objects.size(); idx++)
+	for(const auto & expectedObject : expected->getObjects())
 	{
-		auto expectedObject = expected->objects[idx];
-
-		ASSERT_EQ(idx, expectedObject->id.getNum());
-
 		{
 			auto it = expected->instanceNames.find(expectedObject->instanceName);
 
@@ -232,7 +228,7 @@ void MapComparer::compareObjects()
 
 			auto actualObject = it->second;
 
-			compareObject(actualObject.get(), expectedObject.get());
+			compareObject(actualObject.get(), expectedObject);
 		}
 	}
 }