Pārlūkot izejas kodu

Merge pull request #5499 from IvanSavenko/lib_ptr

Remove most of usages of ConstTransitivePtr
Ivan Savenko 7 mēneši atpakaļ
vecāks
revīzija
1594be94ef
55 mainītis faili ar 420 papildinājumiem un 408 dzēšanām
  1. 2 2
      AI/Nullkiller/Analyzers/BuildAnalyzer.cpp
  2. 1 1
      AI/Nullkiller/Goals/BuildThis.cpp
  3. 1 1
      AI/Nullkiller/Pathfinding/AINodeStorage.cpp
  4. 3 3
      AI/VCAI/BuildingManager.cpp
  5. 1 1
      AI/VCAI/Pathfinding/AINodeStorage.cpp
  6. 3 3
      CCallback.cpp
  7. 0 1
      client/CServerHandler.cpp
  8. 3 3
      client/Client.cpp
  9. 1 1
      client/NetPacksClient.cpp
  10. 1 1
      client/widgets/CComponent.cpp
  11. 13 13
      client/windows/CCastleInterface.cpp
  12. 2 1
      client/windows/CMarketWindow.cpp
  13. 0 1
      lib/CArtHandler.h
  14. 0 1
      lib/CArtifactInstance.h
  15. 0 1
      lib/CCreatureHandler.h
  16. 41 42
      lib/CGameInfoCallback.cpp
  17. 9 8
      lib/CGameInfoCallback.h
  18. 16 16
      lib/IGameCallback.cpp
  19. 3 3
      lib/entities/building/CBuilding.cpp
  20. 2 9
      lib/entities/faction/CTown.cpp
  21. 5 7
      lib/entities/faction/CTown.h
  22. 4 4
      lib/entities/faction/CTownHandler.cpp
  23. 7 10
      lib/gameState/CGameState.cpp
  24. 27 4
      lib/gameState/CGameState.h
  25. 4 5
      lib/gameState/CGameStateCampaign.cpp
  26. 10 10
      lib/gameState/GameStatistics.cpp
  27. 6 6
      lib/mapObjects/CGCreature.cpp
  28. 5 5
      lib/mapObjects/CGDwelling.cpp
  29. 2 2
      lib/mapObjects/CGHeroInstance.cpp
  30. 1 0
      lib/mapObjects/CGHeroInstance.h
  31. 3 3
      lib/mapObjects/CGObjectInstance.cpp
  32. 12 12
      lib/mapObjects/CGTownInstance.cpp
  33. 1 0
      lib/mapObjects/CGTownInstance.h
  34. 18 18
      lib/mapObjects/MiscObjects.cpp
  35. 2 2
      lib/mapObjects/TownBuildingInstance.cpp
  36. 82 92
      lib/networkPacks/NetPacksLib.cpp
  37. 7 11
      lib/networkPacks/StackLocation.h
  38. 3 3
      lib/pathfinder/CPathfinder.cpp
  39. 1 1
      lib/pathfinder/NodeStorage.cpp
  40. 1 1
      lib/rewardable/Interface.cpp
  41. 4 4
      lib/serializer/CSerializer.cpp
  42. 0 1
      lib/spells/CSpellHandler.h
  43. 2 2
      mapeditor/inspector/townbuildingswidget.cpp
  44. 2 2
      mapeditor/inspector/towneventdialog.cpp
  45. 82 66
      server/CGameHandler.cpp
  46. 1 1
      server/CVCMIServer.cpp
  47. 1 1
      server/NetPacksLobbyServer.cpp
  48. 1 1
      server/ServerSpellCastEnvironment.cpp
  49. 1 1
      server/battles/BattleProcessor.cpp
  50. 4 4
      server/battles/BattleResultProcessor.cpp
  51. 1 0
      server/battles/BattleResultProcessor.h
  52. 12 12
      server/processors/NewTurnProcessor.cpp
  53. 3 3
      server/processors/PlayerMessageProcessor.cpp
  54. 1 1
      server/processors/TurnOrderProcessor.cpp
  55. 2 1
      test/mock/mock_IGameInfoCallback.h

+ 2 - 2
AI/Nullkiller/Analyzers/BuildAnalyzer.cpp

@@ -202,7 +202,7 @@ BuildingInfo BuildAnalyzer::getBuildingOrPrerequisite(
 	BuildingID building = toBuild;
 	auto townInfo = town->getTown();
 
-	const CBuilding * buildPtr = townInfo->buildings.at(building);
+	const auto & buildPtr = townInfo->buildings.at(building);
 	const CCreature * creature = nullptr;
 	CreatureID baseCreatureID;
 
@@ -234,7 +234,7 @@ BuildingInfo BuildAnalyzer::getBuildingOrPrerequisite(
 		creature = creatureID.toCreature();
 	}
 
-	auto info = BuildingInfo(buildPtr, creature, baseCreatureID, town, ai);
+	auto info = BuildingInfo(buildPtr.get(), creature, baseCreatureID, town, ai);
 
 	//logAi->trace("checking %s buildInfo %s", info.name, info.toString());
 

+ 1 - 1
AI/Nullkiller/Goals/BuildThis.cpp

@@ -23,7 +23,7 @@ BuildThis::BuildThis(BuildingID Bid, const CGTownInstance * tid)
 	: ElementarGoal(Goals::BUILD_STRUCTURE)
 {
 	buildingInfo = BuildingInfo(
-		tid->getTown()->buildings.at(Bid),
+		tid->getTown()->buildings.at(Bid).get(),
 		nullptr,
 		CreatureID::NONE,
 		tid,

+ 1 - 1
AI/Nullkiller/Pathfinding/AINodeStorage.cpp

@@ -140,7 +140,7 @@ void AINodeStorage::initialize(const PathfinderOptions & options, const CGameSta
 			{
 				for(pos.y = 0; pos.y < sizes.y; ++pos.y)
 				{
-					const TerrainTile & tile = gs->map->getTile(pos);
+					const TerrainTile & tile = gs->getMap().getTile(pos);
 					if (!tile.getTerrain()->isPassable())
 						continue;
 

+ 3 - 3
AI/VCAI/BuildingManager.cpp

@@ -29,7 +29,7 @@ bool BuildingManager::tryBuildThisStructure(const CGTownInstance * t, BuildingID
 	if (t->hasBuilt(building)) //Already built? Shouldn't happen in general
 		return true;
 
-	const CBuilding * buildPtr = t->getTown()->buildings.at(building);
+	const auto & buildPtr = t->getTown()->buildings.at(building);
 
 	auto toBuild = buildPtr->requirements.getFulfillmentCandidates([&](const BuildingID & buildID)
 	{
@@ -51,7 +51,7 @@ bool BuildingManager::tryBuildThisStructure(const CGTownInstance * t, BuildingID
 
 	for (const auto & buildID : toBuild)
 	{
-		const CBuilding * b = t->getTown()->buildings.at(buildID);
+		const auto & b = t->getTown()->buildings.at(buildID);
 
 		EBuildingState canBuild = cb->canBuildStructure(t, buildID);
 		if (canBuild == EBuildingState::ALLOWED)
@@ -220,7 +220,7 @@ bool BuildingManager::getBuildingOptions(const CGTownInstance * t)
 
 	//at the end, try to get and build any extra buildings with nonstandard slots (for example HotA 3rd level dwelling)
 	std::vector<BuildingID> extraBuildings;
-	for (auto buildingInfo : t->getTown()->buildings)
+	for (const auto & buildingInfo : t->getTown()->buildings)
 	{
 		if (buildingInfo.first.isDwelling() && BuildingID::getUpgradedFromDwelling(buildingInfo.first) > 1)
 			extraBuildings.push_back(buildingInfo.first);

+ 1 - 1
AI/VCAI/Pathfinding/AINodeStorage.cpp

@@ -46,7 +46,7 @@ void AINodeStorage::initialize(const PathfinderOptions & options, const CGameSta
 		{
 			for(pos.y=0; pos.y < sizes.y; ++pos.y)
 			{
-				const TerrainTile & tile = gs->map->getTile(pos);
+				const TerrainTile & tile = gs->getMap().getTile(pos);
 				if(!tile.getTerrain()->isPassable())
 					continue;
 				

+ 3 - 3
CCallback.cpp

@@ -379,7 +379,7 @@ CCallback::~CCallback() = default;
 bool CCallback::canMoveBetween(const int3 &a, const int3 &b)
 {
 	//bidirectional
-	return gs->map->canMoveBetween(a, b);
+	return gs->getMap().canMoveBetween(a, b);
 }
 
 std::optional<PlayerColor> CCallback::getPlayerID() const
@@ -389,10 +389,10 @@ std::optional<PlayerColor> CCallback::getPlayerID() const
 
 int3 CCallback::getGuardingCreaturePosition(int3 tile)
 {
-	if (!gs->map->isInTheMap(tile))
+	if (!gs->getMap().isInTheMap(tile))
 		return int3(-1,-1,-1);
 
-	return gs->map->guardingCreaturePositions[tile.z][tile.x][tile.y];
+	return gs->getMap().guardingCreaturePositions[tile.z][tile.x][tile.y];
 }
 
 void CCallback::dig( const CGObjectInstance *hero )

+ 0 - 1
client/CServerHandler.cpp

@@ -49,7 +49,6 @@
 #include "../lib/rmg/CMapGenOptions.h"
 #include "../lib/serializer/Connection.h"
 #include "../lib/filesystem/Filesystem.h"
-#include "../lib/serializer/CMemorySerializer.h"
 #include "../lib/UnlockGuard.h"
 
 #include <boost/uuid/uuid.hpp>

+ 3 - 3
client/Client.cpp

@@ -218,7 +218,7 @@ void CClient::initMapHandler()
 	// During loading CPlayerInterface from serialized state it's depend on MH
 	if(!settings["session"]["headless"].Bool())
 	{
-		GAME->setMapInstance(std::make_unique<CMapHandler>(gs->map));
+		GAME->setMapInstance(std::make_unique<CMapHandler>(&gs->getMap()));
 		logNetwork->trace("Creating mapHandler: %d ms", GAME->server().th->getDiff());
 	}
 }
@@ -254,7 +254,7 @@ void CClient::initPlayerEnvironments()
 
 void CClient::initPlayerInterfaces()
 {
-	for(auto & playerInfo : gs->scenarioOps->playerInfos)
+	for(auto & playerInfo : gs->getStartInfo()->playerInfos)
 	{
 		PlayerColor color = playerInfo.first;
 		if(!vstd::contains(GAME->server().getAllClientPlayers(GAME->server().logicConnection->connectionID), color))
@@ -266,7 +266,7 @@ void CClient::initPlayerInterfaces()
 			if(playerInfo.second.isControlledByAI() || settings["session"]["onlyai"].Bool())
 			{
 				bool alliedToHuman = false;
-				for(auto & allyInfo : gs->scenarioOps->playerInfos)
+				for(auto & allyInfo : gs->getStartInfo()->playerInfos)
 					if (gs->getPlayerTeam(allyInfo.first) == gs->getPlayerTeam(playerInfo.first) && allyInfo.second.isControlledByHuman())
 						alliedToHuman = true;
 

+ 1 - 1
client/NetPacksClient.cpp

@@ -624,7 +624,7 @@ void ApplyClientNetPackVisitor::visitSetHeroesInTown(SetHeroesInTown & pack)
 
 void ApplyClientNetPackVisitor::visitHeroRecruited(HeroRecruited & pack)
 {
-	CGHeroInstance *h = gs.map->heroesOnMap.back();
+	CGHeroInstance *h = gs.getMap().heroesOnMap.back();
 	if(h->getHeroTypeID() != pack.hid)
 	{
 		logNetwork->error("Something wrong with hero recruited!");

+ 1 - 1
client/widgets/CComponent.cpp

@@ -310,7 +310,7 @@ std::string CComponent::getSubtitle() const
 		case ComponentType::BUILDING:
 			{
 				auto index = data.subType.as<BuildingTypeUniqueID>();
-				auto building = (*LIBRARY->townh)[index.getFaction()]->town->buildings[index.getBuilding()];
+				const auto & building = (*LIBRARY->townh)[index.getFaction()]->town->buildings[index.getBuilding()];
 				if(!building)
 				{
 					logGlobal->error("Town of faction %s has no building #%d", (*LIBRARY->townh)[index.getFaction()]->town->faction->getNameTranslated(), index.getBuilding().getNum());

+ 13 - 13
client/windows/CCastleInterface.cpp

@@ -112,7 +112,7 @@ const CBuilding * CBuildingRect::getBuilding()
 		return nullptr;
 
 	if (str->hiddenUpgrade) // hidden upgrades, e.g. hordes - return base (dwelling for hordes)
-		return town->getTown()->buildings.at(str->building->getBase());
+		return town->getTown()->buildings.at(str->building->getBase()).get();
 
 	return str->building;
 }
@@ -161,7 +161,7 @@ void CBuildingRect::showPopupWindow(const Point & cursorPosition)
 		return;
 
 	BuildingID bid = getBuilding()->bid;
-	const CBuilding *bld = town->getTown()->buildings.at(bid);
+	const CBuilding *bld = town->getTown()->buildings.at(bid).get();
 	if (!bid.isDwelling())
 	{
 		CRClickPopup::createAndPush(CInfoWindow::genText(bld->getNameTranslated(), bld->getDescriptionTranslated()),
@@ -685,22 +685,22 @@ void CCastleBuildings::recreate()
 		}
 	}
 
-	for(const CStructure * structure : town->getTown()->clientInfo.structures)
+	for(const auto & structure : town->getTown()->clientInfo.structures)
 	{
 		if(!structure->building)
 		{
-			buildings.push_back(std::make_shared<CBuildingRect>(this, town, structure));
+			buildings.push_back(std::make_shared<CBuildingRect>(this, town, structure.get()));
 			continue;
 		}
 		if(vstd::contains(buildingsCopy, structure->building->bid))
 		{
-			groups[structure->building->getBase()].push_back(structure);
+			groups[structure->building->getBase()].push_back(structure.get());
 		}
 	}
 
 	for(auto & entry : groups)
 	{
-		const CBuilding * build = town->getTown()->buildings.at(entry.first);
+		const CBuilding * build = town->getTown()->buildings.at(entry.first).get();
 
 		const CStructure * toAdd = *boost::max_element(entry.second, [=](const CStructure * a, const CStructure * b)
 		{
@@ -819,7 +819,7 @@ void CCastleBuildings::buildingClicked(BuildingID building)
 
 	for(BuildingID buildingToEnter = building;;)
 	{
-		const CBuilding *b = town->getTown()->buildings.find(buildingToEnter)->second;
+		const auto &b = town->getTown()->buildings.find(buildingToEnter)->second;
 
 		buildingsToTest.push_back(buildingToEnter);
 		if (!b->upgrade.hasValue())
@@ -840,7 +840,7 @@ void CCastleBuildings::buildingClicked(BuildingID building)
 bool CCastleBuildings::buildingTryActivateCustomUI(BuildingID buildingToTest, BuildingID buildingTarget)
 {
 	logGlobal->trace("You've clicked on %d", (int)buildingToTest.toEnum());
-	const CBuilding *b = town->getTown()->buildings.at(buildingToTest);
+	const auto & b = town->getTown()->buildings.at(buildingToTest);
 
 	if (town->getWarMachineInBuilding(buildingToTest).hasValue())
 	{
@@ -1382,7 +1382,7 @@ CTownInfo::CTownInfo(int posX, int posY, const CGTownInstance * Town, bool townH
 			return;//FIXME: suspicious statement, fix or comment
 		picture = std::make_shared<CAnimImage>(AnimationPath::builtin("ITMCL.DEF"), town->fortLevel()-1);
 	}
-	building = town->getTown()->buildings.at(BuildingID(buildID));
+	building = town->getTown()->buildings.at(BuildingID(buildID)).get();
 	pos = picture->pos;
 }
 
@@ -1737,7 +1737,7 @@ CHallInterface::CHallInterface(const CGTownInstance * Town):
 					continue;
 				}
 
-				const CBuilding * current = town->getTown()->buildings.at(buildingID);
+				const CBuilding * current = town->getTown()->buildings.at(buildingID).get();
 				if(town->hasBuilt(buildingID))
 				{
 					building = current;
@@ -1922,7 +1922,7 @@ CFortScreen::CFortScreen(const CGTownInstance * town):
 		fortSize--;
 	fortSize = std::min(fortSize, static_cast<ui32>(GameConstants::CREATURES_PER_TOWN)); // for 8 creatures + portal of summoning
 
-	const CBuilding * fortBuilding = town->getTown()->buildings.at(BuildingID(town->fortLevel()+6));
+	const auto & fortBuilding = town->getTown()->buildings.at(BuildingID(town->fortLevel()+6));
 	title = std::make_shared<CLabel>(400, 12, FONT_BIG, ETextAlignment::CENTER, Colors::WHITE, fortBuilding->getNameTranslated());
 
 	std::string text = boost::str(boost::format(LIBRARY->generaltexth->fcommands[6]) % fortBuilding->getNameTranslated());
@@ -2067,11 +2067,11 @@ const CBuilding * CFortScreen::RecruitArea::getMyBuilding()
 	if (!town->getTown()->buildings.count(myID))
 		return nullptr;
 
-	const CBuilding * build = town->getTown()->buildings.at(myID);
+	const CBuilding * build = town->getTown()->buildings.at(myID).get();
 	while (town->getTown()->buildings.count(myID))
 	{
 		if (town->hasBuilt(myID))
-			build = town->getTown()->buildings.at(myID);
+			build = town->getTown()->buildings.at(myID).get();
 		BuildingID::advanceDwelling(myID);
 	}
 

+ 2 - 1
client/windows/CMarketWindow.cpp

@@ -192,7 +192,8 @@ std::string CMarketWindow::getMarketTitle(const ObjectInstanceID marketId, const
 	{
 		for(const auto & buildingId : town->getBuildings())
 		{
-			if(const auto building = town->getTown()->buildings.at(buildingId); vstd::contains(building->marketModes, mode))
+			const auto & building = town->getTown()->buildings.at(buildingId);
+			if(vstd::contains(building->marketModes, mode))
 				return building->getNameTranslated();
 		}
 	}

+ 0 - 1
lib/CArtHandler.h

@@ -14,7 +14,6 @@
 
 #include "bonuses/Bonus.h"
 #include "bonuses/CBonusSystemNode.h"
-#include "ConstTransitivePtr.h"
 #include "GameConstants.h"
 #include "IHandlerBase.h"
 #include "serializer/Serializeable.h"

+ 0 - 1
lib/CArtifactInstance.h

@@ -11,7 +11,6 @@
 
 #include "bonuses/CBonusSystemNode.h"
 #include "GameConstants.h"
-#include "ConstTransitivePtr.h"
 #include "CArtHandler.h"
 
 VCMI_LIB_NAMESPACE_BEGIN

+ 0 - 1
lib/CCreatureHandler.h

@@ -11,7 +11,6 @@
 
 #include "bonuses/Bonus.h"
 #include "bonuses/CBonusSystemNode.h"
-#include "ConstTransitivePtr.h"
 #include "ResourceSet.h"
 #include "GameConstants.h"
 #include "IHandlerBase.h"

+ 41 - 42
lib/CGameInfoCallback.cpp

@@ -53,22 +53,22 @@ int CGameInfoCallback::getResource(PlayerColor Player, GameResID which) const
 
 const PlayerSettings * CGameInfoCallback::getPlayerSettings(PlayerColor color) const
 {
-	return &gs->scenarioOps->getIthPlayersSettings(color);
+	return &gs->getStartInfo()->getIthPlayersSettings(color);
 }
 
 bool CGameInfoCallback::isAllowed(SpellID id) const
 {
-	return gs->map->allowedSpells.count(id) != 0;
+	return gs->getMap().allowedSpells.count(id) != 0;
 }
 
 bool CGameInfoCallback::isAllowed(ArtifactID id) const
 {
-	return gs->map->allowedArtifact.count(id) != 0;
+	return gs->getMap().allowedArtifact.count(id) != 0;
 }
 
 bool CGameInfoCallback::isAllowed(SecondarySkill id) const
 {
-	return gs->map->allowedAbilities.count(id) != 0;
+	return gs->getMap().allowedAbilities.count(id) != 0;
 }
 
 std::optional<PlayerColor> CGameInfoCallback::getPlayerID() const
@@ -132,14 +132,14 @@ TurnTimerInfo CGameInfoCallback::getPlayerTurnTime(PlayerColor color) const
 const CGObjectInstance* CGameInfoCallback::getObj(ObjectInstanceID objid, bool verbose) const
 {
 	si32 oid = objid.num;
-	if(oid < 0  ||  oid >= gs->map->objects.size())
+	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->map->objects[oid];
+	const CGObjectInstance *ret = gs->getMap().objects[oid];
 	if(!ret)
 	{
 		if(verbose)
@@ -192,13 +192,14 @@ void CGameInfoCallback::fillUpgradeInfo(const CArmedInstance *obj, SlotID stackP
 	//return gs->fillUpgradeInfo(obj->getStack(stackPos));
 }
 
-const StartInfo * CGameInfoCallback::getStartInfo(bool beforeRandomization) const
+const StartInfo * CGameInfoCallback::getStartInfo() const
 {
-	//std::shared_lock<std::shared_mutex> lock(*gs->mx);
-	if(beforeRandomization)
-		return gs->initialOpts;
-	else
-		return gs->scenarioOps;
+	return gs->getStartInfo();
+}
+
+const StartInfo * CGameInfoCallback::getInitialStartInfo() const
+{
+	return gs->getInitialStartInfo();
 }
 
 int32_t CGameInfoCallback::getSpellCost(const spells::Spell * sp, const CGHeroInstance * caster) const
@@ -374,7 +375,7 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero
 			for(auto & elem : info.army)
 				elem.second.count = 0;
 
-			const auto factionIndex = getStartInfo(false)->playerInfos.at(h->tempOwner).castle;
+			const auto factionIndex = getStartInfo()->playerInfos.at(h->tempOwner).castle;
 
 			int maxAIValue = 0;
 			const CCreature * mostStrong = nullptr;
@@ -476,14 +477,13 @@ std::vector <const CGObjectInstance *> CGameInfoCallback::getVisitableObjs(int3
 		if(!getPlayerID().has_value() || obj->ID != Obj::EVENT) //hide events from players
 			ret.push_back(obj);
 	}
-
 	return ret;
 }
 
-std::vector<ConstTransitivePtr<CGObjectInstance>> CGameInfoCallback::getAllVisitableObjs() const
+std::vector<const CGObjectInstance *> CGameInfoCallback::getAllVisitableObjs() const
 {
-	std::vector<ConstTransitivePtr<CGObjectInstance>> ret;
-	for(auto & obj : gs->map->objects)
+	std::vector<const CGObjectInstance *> ret;
+	for(auto & obj : gs->getMap().objects)
 		if(obj && obj->isVisitable() && obj->ID != Obj::EVENT && isVisible(obj))
 			ret.push_back(obj);
 
@@ -508,7 +508,7 @@ std::vector <const CGObjectInstance *> CGameInfoCallback::getFlaggableObjects(in
 
 int3 CGameInfoCallback::getMapSize() const
 {
-	return int3(gs->map->width, gs->map->height, gs->map->twoLevel ? 2 : 1);
+	return int3(gs->getMap().width, gs->getMap().height, gs->getMap().twoLevel ? 2 : 1);
 }
 
 std::vector<const CGHeroInstance *> CGameInfoCallback::getAvailableHeroes(const CGObjectInstance * townOrTavern) const
@@ -528,7 +528,7 @@ std::vector<const CGHeroInstance *> CGameInfoCallback::getAvailableHeroes(const
 const TerrainTile * CGameInfoCallback::getTile(int3 tile, bool verbose) const
 {
 	if(isVisible(tile))
-		return &gs->map->getTile(tile);
+		return &gs->getMap().getTile(tile);
 
 	if(verbose)
 		logGlobal->error("\r\n%s: %s\r\n", BOOST_CURRENT_FUNCTION, tile.toString() + " is not visible!");
@@ -538,7 +538,7 @@ const TerrainTile * CGameInfoCallback::getTile(int3 tile, bool verbose) const
 const TerrainTile * CGameInfoCallback::getTileUnchecked(int3 tile) const
 {
 	if (isInTheMap(tile))
-		return &gs->map->getTile(tile);
+		return &gs->getMap().getTile(tile);
 
 	return nullptr;
 }
@@ -548,7 +548,7 @@ EDiggingStatus CGameInfoCallback::getTileDigStatus(int3 tile, bool verbose) cons
 	if(!isVisible(tile))
 		return EDiggingStatus::UNKNOWN;
 
-	for(const auto & object : gs->map->objects)
+	for(const auto & object : gs->getMap().objects)
 	{
 		if(object && object->ID == Obj::HOLE && object->anchorPos() == tile)
 			return EDiggingStatus::TILE_OCCUPIED;
@@ -562,9 +562,9 @@ std::shared_ptr<const boost::multi_array<TerrainTile*, 3>> CGameInfoCallback::ge
 	assert(getPlayerID().has_value());
 	const auto * team = getPlayerTeam(getPlayerID().value());
 
-	size_t width = gs->map->width;
-	size_t height = gs->map->height;
-	size_t levels = gs->map->levels();
+	size_t width = gs->getMap().width;
+	size_t height = gs->getMap().height;
+	size_t levels = gs->getMap().levels();
 
 	auto * ptr = new boost::multi_array<TerrainTile *, 3>(boost::extents[levels][width][height]);
 
@@ -574,7 +574,7 @@ std::shared_ptr<const boost::multi_array<TerrainTile*, 3>> CGameInfoCallback::ge
 			for(tile.y = 0; tile.y < height; tile.y++)
 			{
 				if (team->fogOfWarMap[tile.z][tile.x][tile.y])
-					(*ptr)[tile.z][tile.x][tile.y] = &gs->map->getTile(tile);
+					(*ptr)[tile.z][tile.x][tile.y] = &gs->getMap().getTile(tile);
 				else
 					(*ptr)[tile.z][tile.x][tile.y] = nullptr;
 			}
@@ -589,8 +589,7 @@ EBuildingState CGameInfoCallback::canBuildStructure( const CGTownInstance *t, Bu
 	if(!t->getTown()->buildings.count(ID))
 		return EBuildingState::BUILDING_ERROR;
 
-	const CBuilding * building = t->getTown()->buildings.at(ID);
-
+	const auto & building = t->getTown()->buildings.at(ID);
 
 	if(t->hasBuilt(ID))	//already built
 		return EBuildingState::ALREADY_PRESENT;
@@ -654,7 +653,7 @@ EBuildingState CGameInfoCallback::canBuildStructure( const CGTownInstance *t, Bu
 
 const CMapHeader * CGameInfoCallback::getMapHeader() const
 {
-	return gs->map;
+	return &gs->getMap();
 }
 
 bool CGameInfoCallback::hasAccess(std::optional<PlayerColor> playerId) const
@@ -691,7 +690,7 @@ std::string CGameInfoCallback::getTavernRumor(const CGObjectInstance * townOrTav
 
 		break;
 	case RumorState::TYPE_MAP:
-		text.replaceRawString(gs->map->rumors[rumor.first].text.toString());
+		text.replaceRawString(gs->getMap().rumors[rumor.first].text.toString());
 		break;
 
 	case RumorState::TYPE_RAND:
@@ -779,7 +778,7 @@ std::vector < const CGHeroInstance *> CPlayerSpecificInfoCallback::getHeroesInfo
 {
 	//std::shared_lock<std::shared_mutex> lock(*gs->mx);
 	std::vector < const CGHeroInstance *> ret;
-	for(auto hero : gs->map->heroesOnMap)
+	for(auto hero : gs->getMap().heroesOnMap)
 	{
 		// !player || // - why would we even get access to hero not owned by any player?
 		if((hero->tempOwner == *getPlayerID()) ||
@@ -812,7 +811,7 @@ int CPlayerSpecificInfoCallback::getHeroSerial(const CGHeroInstance * hero, bool
 
 int3 CPlayerSpecificInfoCallback::getGrailPos( double *outKnownRatio )
 {
-	if (!getPlayerID() || gs->map->obeliskCount == 0)
+	if (!getPlayerID() || gs->getMap().obeliskCount == 0)
 	{
 		*outKnownRatio = 0.0;
 	}
@@ -820,12 +819,12 @@ int3 CPlayerSpecificInfoCallback::getGrailPos( double *outKnownRatio )
 	{
 		TeamID t = gs->getPlayerTeam(*getPlayerID())->id;
 		double visited = 0.0;
-		if(gs->map->obelisksVisited.count(t))
-			visited = static_cast<double>(gs->map->obelisksVisited[t]);
+		if(gs->getMap().obelisksVisited.count(t))
+			visited = static_cast<double>(gs->getMap().obelisksVisited[t]);
 
-		*outKnownRatio = visited / gs->map->obeliskCount;
+		*outKnownRatio = visited / gs->getMap().obeliskCount;
 	}
-	return gs->map->grailPos;
+	return gs->getMap().grailPos;
 }
 
 std::vector < const CGObjectInstance * > CPlayerSpecificInfoCallback::getMyObjects() const
@@ -929,15 +928,15 @@ const CGHeroInstance * CGameInfoCallback::getHeroWithSubid( int subid ) const
 {
 	if(subid<0)
 		return nullptr;
-	if(subid>= gs->map->allHeroes.size())
+	if(subid>= gs->getMap().allHeroes.size())
 		return nullptr;
 
-	return gs->map->allHeroes.at(subid).get();
+	return gs->getMap().allHeroes.at(subid).get();
 }
 
 bool CGameInfoCallback::isInTheMap(const int3 &pos) const
 {
-	return gs->map->isInTheMap(pos);
+	return gs->getMap().isInTheMap(pos);
 }
 
 void CGameInfoCallback::getVisibleTilesInRange(std::unordered_set<int3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula) const
@@ -952,12 +951,12 @@ void CGameInfoCallback::calculatePaths(const std::shared_ptr<PathfinderConfig> &
 
 const CArtifactInstance * CGameInfoCallback::getArtInstance( ArtifactInstanceID aid ) const
 {
-	return gs->map->artInstances.at(aid.num);
+	return gs->getMap().artInstances.at(aid.num);
 }
 
 const CGObjectInstance * CGameInfoCallback::getObjInstance( ObjectInstanceID oid ) const
 {
-	return gs->map->objects.at(oid.num);
+	return gs->getMap().objects.at(oid.num);
 }
 
 const CArtifactSet * CGameInfoCallback::getArtSet(const ArtifactLocation & loc) const
@@ -977,12 +976,12 @@ std::vector<ObjectInstanceID> CGameInfoCallback::getVisibleTeleportObjects(std::
 
 std::vector<ObjectInstanceID> CGameInfoCallback::getTeleportChannelEntrances(TeleportChannelID id, PlayerColor player) const
 {
-	return getVisibleTeleportObjects(gs->map->teleportChannels[id]->entrances, player);
+	return getVisibleTeleportObjects(gs->getMap().teleportChannels[id]->entrances, player);
 }
 
 std::vector<ObjectInstanceID> CGameInfoCallback::getTeleportChannelExits(TeleportChannelID id, PlayerColor player) const
 {
-	return getVisibleTeleportObjects(gs->map->teleportChannels[id]->exits, player);
+	return getVisibleTeleportObjects(gs->getMap().teleportChannels[id]->exits, player);
 }
 
 ETeleportChannelType CGameInfoCallback::getTeleportChannelType(TeleportChannelID id, PlayerColor player) const

+ 9 - 8
lib/CGameInfoCallback.h

@@ -11,7 +11,6 @@
 
 #include "int3.h"
 #include "ResourceSet.h" // for Res
-#include "ConstTransitivePtr.h"
 
 #define ASSERT_IF_CALLED_WITH_PLAYER if(!getPlayerID()) {logGlobal->error(BOOST_CURRENT_FUNCTION); assert(0);}
 
@@ -59,7 +58,8 @@ public:
 
 //	//various
 	virtual int getDate(Date mode=Date::DAY) const = 0; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month
-	virtual const StartInfo * getStartInfo(bool beforeRandomization = false) const = 0;
+	virtual const StartInfo * getStartInfo() const = 0;
+	virtual const StartInfo * getInitialStartInfo() const = 0;
 	virtual bool isAllowed(SpellID id) const = 0;
 	virtual bool isAllowed(ArtifactID id) const = 0;
 	virtual bool isAllowed(SecondarySkill id) const = 0;
@@ -146,7 +146,8 @@ protected:
 public:
 	//various
 	int getDate(Date mode=Date::DAY)const override; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month
-	const StartInfo * getStartInfo(bool beforeRandomization = false) const override;
+	const StartInfo * getStartInfo() const override;
+	const StartInfo * getInitialStartInfo() const override;
 	bool isAllowed(SpellID id) const override;
 	bool isAllowed(ArtifactID id) const override;
 	bool isAllowed(SecondarySkill id) const override;
@@ -188,11 +189,11 @@ public:
 
 	//objects
 	const CGObjectInstance * getObj(ObjectInstanceID objid, bool verbose = true) const override;
-	virtual std::vector <const CGObjectInstance * > getBlockingObjs(int3 pos)const;
-	std::vector <const CGObjectInstance * > getVisitableObjs(int3 pos, bool verbose = true) const override;
-	std::vector<ConstTransitivePtr<CGObjectInstance>> getAllVisitableObjs() const;
-	virtual std::vector <const CGObjectInstance * > getFlaggableObjects(int3 pos) const;
-	virtual const CGObjectInstance * getTopObj (int3 pos) const;
+	virtual std::vector<const CGObjectInstance *> getBlockingObjs(int3 pos) const;
+	std::vector<const CGObjectInstance *> getVisitableObjs(int3 pos, bool verbose = true) const override;
+	std::vector<const CGObjectInstance *> getAllVisitableObjs() const;
+	virtual std::vector<const CGObjectInstance *> getFlaggableObjects(int3 pos) const;
+	virtual const CGObjectInstance * getTopObj(int3 pos) const;
 	virtual PlayerColor getOwner(ObjectInstanceID heroID) const;
 	virtual const IMarket * getMarket(ObjectInstanceID objid) const;
 

+ 16 - 16
lib/IGameCallback.cpp

@@ -58,17 +58,17 @@ VCMI_LIB_NAMESPACE_BEGIN
 void CPrivilegedInfoCallback::getFreeTiles(std::vector<int3> & tiles) const
 {
 	std::vector<int> floors;
-	floors.reserve(gs->map->levels());
-	for(int b = 0; b < gs->map->levels(); ++b)
+	floors.reserve(gs->getMap().levels());
+	for(int b = 0; b < gs->getMap().levels(); ++b)
 	{
 		floors.push_back(b);
 	}
 	const TerrainTile * tinfo = nullptr;
 	for (auto zd : floors)
 	{
-		for (int xd = 0; xd < gs->map->width; xd++)
+		for (int xd = 0; xd < gs->getMap().width; xd++)
 		{
-			for (int yd = 0; yd < gs->map->height; yd++)
+			for (int yd = 0; yd < gs->getMap().height; yd++)
 			{
 				tinfo = getTile(int3 (xd,yd,zd));
 				if (tinfo->isLand() && tinfo->getTerrain()->isPassable() && !tinfo->blocked()) //land and free
@@ -95,9 +95,9 @@ void CPrivilegedInfoCallback::getTilesInRange(std::unordered_set<int3> & tiles,
 	else
 	{
 		const TeamState * team = !player ? nullptr : gs->getPlayerTeam(*player);
-		for (int xd = std::max<int>(pos.x - radious , 0); xd <= std::min<int>(pos.x + radious, gs->map->width - 1); xd++)
+		for (int xd = std::max<int>(pos.x - radious , 0); xd <= std::min<int>(pos.x + radious, gs->getMap().width - 1); xd++)
 		{
-			for (int yd = std::max<int>(pos.y - radious, 0); yd <= std::min<int>(pos.y + radious, gs->map->height - 1); yd++)
+			for (int yd = std::max<int>(pos.y - radious, 0); yd <= std::min<int>(pos.y + radious, gs->getMap().height - 1); yd++)
 			{
 				int3 tilePos(xd,yd,pos.z);
 				int distance = pos.dist(tilePos, distanceFormula);
@@ -126,7 +126,7 @@ void CPrivilegedInfoCallback::getAllTiles(std::unordered_set<int3> & tiles, std:
 	std::vector<int> floors;
 	if(level == -1)
 	{
-		for(int b = 0; b < gs->map->levels(); ++b)
+		for(int b = 0; b < gs->getMap().levels(); ++b)
 		{
 			floors.push_back(b);
 		}
@@ -136,9 +136,9 @@ void CPrivilegedInfoCallback::getAllTiles(std::unordered_set<int3> & tiles, std:
 
 	for(auto zd: floors)
 	{
-		for(int xd = 0; xd < gs->map->width; xd++)
+		for(int xd = 0; xd < gs->getMap().width; xd++)
 		{
-			for(int yd = 0; yd < gs->map->height; yd++)
+			for(int yd = 0; yd < gs->getMap().height; yd++)
 			{
 				int3 coordinates(xd, yd, zd);
 				if (filter(getTile(coordinates)))
@@ -160,7 +160,7 @@ void CPrivilegedInfoCallback::pickAllowedArtsSet(std::vector<ArtifactID> & out,
 
 void CPrivilegedInfoCallback::getAllowedSpells(std::vector<SpellID> & out, std::optional<ui16> level)
 {
-	for (auto const & spellID : gs->map->allowedSpells)
+	for (auto const & spellID : gs->getMap().allowedSpells)
 	{
 		const auto * spell = spellID.toEntity(LIBRARY);
 
@@ -208,9 +208,9 @@ void CPrivilegedInfoCallback::saveCommonState(CSaveFile & out) const
 	logGlobal->info("Saving lib part of game...");
 	out.putMagicBytes(SAVEGAME_MAGIC);
 	logGlobal->info("\tSaving header");
-	out.serializer & static_cast<CMapHeader&>(*gs->map);
+	out.serializer & static_cast<CMapHeader&>(gs->getMap());
 	logGlobal->info("\tSaving options");
-	out.serializer & gs->scenarioOps;
+	out.serializer & gs->getStartInfo();
 	logGlobal->info("\tSaving mod list");
 	out.serializer & activeMods;
 	logGlobal->info("\tSaving gamestate");
@@ -219,9 +219,9 @@ void CPrivilegedInfoCallback::saveCommonState(CSaveFile & out) const
 
 TerrainTile * CNonConstInfoCallback::getTile(const int3 & pos)
 {
-	if(!gs->map->isInTheMap(pos))
+	if(!gs->getMap().isInTheMap(pos))
 		return nullptr;
-	return &gs->map->getTile(pos);
+	return &gs->getMap().getTile(pos);
 }
 
 CGHeroInstance * CNonConstInfoCallback::getHero(const ObjectInstanceID & objid)
@@ -251,12 +251,12 @@ PlayerState * CNonConstInfoCallback::getPlayerState(const PlayerColor & color, b
 
 CArtifactInstance * CNonConstInfoCallback::getArtInstance(const ArtifactInstanceID & aid)
 {
-	return gs->map->artInstances.at(aid.num);
+	return gs->getMap().artInstances.at(aid.num);
 }
 
 CGObjectInstance * CNonConstInfoCallback::getObjInstance(const ObjectInstanceID & oid)
 {
-	return gs->map->objects.at(oid.num);
+	return gs->getMap().objects.at(oid.num);
 }
 
 CArmedInstance * CNonConstInfoCallback::getArmyInstance(const ObjectInstanceID & oid)

+ 3 - 3
lib/entities/building/CBuilding.cpp

@@ -73,7 +73,7 @@ BuildingID CBuilding::getBase() const
 	const CBuilding * build = this;
 	while (build->upgrade != BuildingID::NONE)
 	{
-		build = build->town->buildings.at(build->upgrade);
+		build = build->town->buildings.at(build->upgrade).get();
 	}
 
 	return build->bid;
@@ -81,11 +81,11 @@ BuildingID CBuilding::getBase() const
 
 si32 CBuilding::getDistance(const BuildingID & buildID) const
 {
-	const CBuilding * build = town->buildings.at(buildID);
+	const CBuilding * build = town->buildings.at(buildID).get();
 	int distance = 0;
 	while (build->upgrade != BuildingID::NONE && build != this)
 	{
-		build = build->town->buildings.at(build->upgrade);
+		build = build->town->buildings.at(build->upgrade).get();
 		distance++;
 	}
 	if (build == this)

+ 2 - 9
lib/entities/faction/CTown.cpp

@@ -22,14 +22,7 @@ CTown::CTown()
 {
 }
 
-CTown::~CTown()
-{
-	for(auto & build : buildings)
-		build.second.dellNull();
-
-	for(auto & str : clientInfo.structures)
-		str.dellNull();
-}
+CTown::~CTown() = default;
 
 std::string CTown::getRandomNameTextID(size_t index) const
 {
@@ -67,7 +60,7 @@ const CBuilding * CTown::getSpecialBuilding(BuildingSubID::EBuildingSubID subID)
 	for(const auto & kvp : buildings)
 	{
 		if(kvp.second->subId == subID)
-			return buildings.at(kvp.first);
+			return buildings.at(kvp.first).get();
 	}
 	return nullptr;
 }

+ 5 - 7
lib/entities/faction/CTown.h

@@ -10,7 +10,6 @@
 #pragma once
 
 #include "../building/TownFortifications.h"
-#include "../../ConstTransitivePtr.h"
 #include "../../Point.h"
 #include "../../constants/EntityIdentifiers.h"
 #include "../../constants/Enumerations.h"
@@ -26,8 +25,8 @@ class CBuilding;
 /// Should be moved from lib to client
 struct DLL_LINKAGE CStructure
 {
-	CBuilding * building;  // base building. If null - this structure will be always present on screen
-	CBuilding * buildable; // building that will be used to determine built building and visible cost. Usually same as "building"
+	const CBuilding * building;  // base building. If null - this structure will be always present on screen
+	const CBuilding * buildable; // building that will be used to determine built building and visible cost. Usually same as "building"
 
 	int3 pos;
 	AnimationPath defName;
@@ -38,7 +37,7 @@ struct DLL_LINKAGE CStructure
 	bool hiddenUpgrade; // used only if "building" is upgrade, if true - structure on town screen will behave exactly like parent (mouse clicks, hover texts, etc)
 };
 
-class DLL_LINKAGE CTown
+class DLL_LINKAGE CTown : boost::noncopyable
 {
 	friend class CTownHandler;
 	size_t namesCount = 0;
@@ -58,10 +57,9 @@ public:
 	CFaction * faction;
 
 	/// level -> list of creatures on this tier
-	// TODO: replace with pointers to CCreature
 	std::vector<std::vector<CreatureID> > creatures;
 
-	std::map<BuildingID, ConstTransitivePtr<CBuilding> > buildings;
+	std::map<BuildingID, std::unique_ptr<const CBuilding>> buildings;
 
 	std::vector<std::string> dwellings; //defs for adventure map dwellings for new towns, [0] means tier 1 creatures etc.
 	std::vector<std::string> dwellingNames;
@@ -99,7 +97,7 @@ public:
 
 		/// list of town screen structures.
 		/// NOTE: index in vector is meaningless. Vector used instead of list for a bit faster access
-		std::vector<ConstTransitivePtr<CStructure> > structures;
+		std::vector<std::unique_ptr<const CStructure>> structures;
 
 		std::string siegePrefix;
 		std::vector<Point> siegePositions;

+ 4 - 4
lib/entities/faction/CTownHandler.cpp

@@ -385,7 +385,7 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
 	else
 		ret->upgrade = BuildingID::NONE;
 
-	ret->town->buildings[ret->bid] = ret;
+	ret->town->buildings[ret->bid].reset(ret);
 	for(const auto & element : source["marketModes"].Vector())
 	{
 		if(MappedKeys::MARKET_NAMES_TO_TYPES.count(element.String()))
@@ -413,21 +413,21 @@ void CTownHandler::loadStructure(CTown &town, const std::string & stringID, cons
 
 	LIBRARY->identifiers()->tryRequestIdentifier( source.getModScope(), "building." + town.faction->getJsonKey(), stringID, [=, &town](si32 identifier) mutable
 	{
-		ret->building = town.buildings[BuildingID(identifier)];
+		ret->building = town.buildings[BuildingID(identifier)].get();
 	});
 
 	if (source["builds"].isNull())
 	{
 		LIBRARY->identifiers()->tryRequestIdentifier( source.getModScope(), "building." + town.faction->getJsonKey(), stringID, [=, &town](si32 identifier) mutable
 		{
-			ret->building = town.buildings[BuildingID(identifier)];
+			ret->building = town.buildings[BuildingID(identifier)].get();
 		});
 	}
 	else
 	{
 		LIBRARY->identifiers()->requestIdentifier("building." + town.faction->getJsonKey(), source["builds"], [=, &town](si32 identifier) mutable
 		{
-			ret->buildable = town.buildings[BuildingID(identifier)];
+			ret->buildable = town.buildings[BuildingID(identifier)].get();
 		});
 	}
 

+ 7 - 10
lib/gameState/CGameState.cpp

@@ -150,9 +150,6 @@ CGameState::~CGameState()
 {
 	// explicitly delete all ongoing battles first - BattleInfo destructor requires valid CGameState
 	currentBattles.clear();
-	map.dellNull();
-	scenarioOps.dellNull();
-	initialOpts.dellNull();
 }
 
 const IGameSettings & CGameState::getSettings() const
@@ -170,8 +167,8 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, Load::Prog
 {
 	assert(services);
 	assert(callback);
-	scenarioOps = CMemorySerializer::deepCopy(*si).release();
-	initialOpts = CMemorySerializer::deepCopy(*si).release();
+	scenarioOps = CMemorySerializer::deepCopy(*si);
+	initialOpts = CMemorySerializer::deepCopy(*si);
 	si = nullptr;
 
 	switch(scenarioOps->mode)
@@ -336,7 +333,7 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan
 			}
 		}
 
-		map = randomMap.release();
+		map = std::move(randomMap);
 
 		logGlobal->info("Generated random map in %i ms.", sw.getDiff());
 	}
@@ -344,14 +341,14 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan
 	{
 		logGlobal->info("Open map file: %s", scenarioOps->mapname);
 		const ResourcePath mapURI(scenarioOps->mapname, EResType::MAP);
-		map = mapService->loadMap(mapURI, callback).release();
+		map = mapService->loadMap(mapURI, callback);
 	}
 }
 
 void CGameState::initCampaign()
 {
 	campaign = std::make_unique<CGameStateCampaign>(this);
-	map = campaign->getCurrentMap().release();
+	map = campaign->getCurrentMap();
 }
 
 void CGameState::generateOwnedObjectsAfterDeserialize()
@@ -617,7 +614,7 @@ void CGameState::initHeroes()
 
 			boat->setAnchorPos(hero->anchorPos());
 			boat->appearance = handler->getTemplates().front();
-			boat->id = ObjectInstanceID(static_cast<si32>(gs->map->objects.size()));
+			boat->id = ObjectInstanceID(static_cast<si32>(gs->getMap().objects.size()));
 
 			map->objects.emplace_back(boat);
 
@@ -1207,7 +1204,7 @@ std::vector<CGObjectInstance*> CGameState::guardingCreatures (int3 pos) const
 
 int3 CGameState::guardingCreaturePosition (int3 pos) const
 {
-	return gs->map->guardingCreaturePositions[pos.z][pos.x][pos.y];
+	return gs->getMap().guardingCreaturePositions[pos.z][pos.x][pos.y];
 }
 
 bool CGameState::isVisible(int3 pos, const std::optional<PlayerColor> & player) const

+ 27 - 4
lib/gameState/CGameState.h

@@ -12,7 +12,6 @@
 #include "../bonuses/CBonusSystemNode.h"
 #include "../IGameCallback.h"
 #include "../LoadProgress.h"
-#include "../ConstTransitivePtr.h"
 
 #include "RumorState.h"
 #include "GameStatistics.h"
@@ -46,6 +45,10 @@ DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EVictoryLossCheck
 class DLL_LINKAGE CGameState : public CNonConstInfoCallback, public Serializeable
 {
 	friend class CGameStateCampaign;
+
+	std::unique_ptr<StartInfo> initialOpts; //copy of settings received from pregame (not randomized)
+	std::unique_ptr<StartInfo> scenarioOps;
+	std::unique_ptr<CMap> map;
 public:
 	/// Stores number of times each artifact was placed on map via randomization
 	std::map<ArtifactID, int> allocatedArtifacts;
@@ -71,10 +74,7 @@ public:
 	void init(const IMapService * mapService, StartInfo * si, Load::ProgressAccumulator &, bool allowSavingRandomMap = true);
 	void updateOnLoad(StartInfo * si);
 
-	ConstTransitivePtr<StartInfo> scenarioOps;
-	ConstTransitivePtr<StartInfo> initialOpts; //copy of settings received from pregame (not randomized)
 	ui32 day; //total number of days in game
-	ConstTransitivePtr<CMap> map;
 	std::map<PlayerColor, PlayerState> players;
 	std::map<TeamID, TeamState> teams;
 	CBonusSystemNode globalEffects;
@@ -82,6 +82,7 @@ public:
 
 	StatisticDataSet statistic;
 
+	// NOTE: effectively AI mutex, only used by adventure map AI
 	static std::shared_mutex mutex;
 
 	void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) override;
@@ -123,6 +124,28 @@ public:
 	void obtainPlayersStats(SThievesGuildInfo & tgi, int level); //fills tgi with info about other players that is available at given level of thieves' guild
 	const IGameSettings & getSettings() const;
 
+	StartInfo * getStartInfo()
+	{
+		return scenarioOps.get();
+	}
+	const StartInfo * getStartInfo() const final
+	{
+		return scenarioOps.get();
+	}
+	const StartInfo * getInitialStartInfo() const final
+	{
+		return initialOpts.get();
+	}
+
+	CMap & getMap()
+	{
+		return *map;
+	}
+	const CMap & getMap() const
+	{
+		return *map;
+	}
+
 	bool isVisible(int3 pos, const std::optional<PlayerColor> & player) const override;
 	bool isVisible(const CGObjectInstance * obj, const std::optional<PlayerColor> & player) const override;
 

+ 4 - 5
lib/gameState/CGameStateCampaign.cpp

@@ -28,7 +28,6 @@
 #include "../mapping/CMap.h"
 #include "../ArtifactUtils.h"
 #include "../CPlayerState.h"
-#include "../serializer/CMemorySerializer.h"
 
 #include <vstd/RNG.h>
 #include <vcmi/HeroTypeService.h>
@@ -485,7 +484,7 @@ void CGameStateCampaign::generateCampaignHeroesToReplace()
 			continue;
 		}
 
-		CGHeroInstance * hero = campaignState->crossoverDeserialize(node, gameState->map);
+		CGHeroInstance * hero = campaignState->crossoverDeserialize(node, gameState->map.get());
 
 		logGlobal->info("Hero crossover: Loading placeholder for %d (%s)", hero->getHeroType(), hero->getNameTranslated());
 
@@ -510,7 +509,7 @@ void CGameStateCampaign::generateCampaignHeroesToReplace()
 			if (nodeListIter == nodeList.end())
 				break;
 
-			CGHeroInstance * hero = campaignState->crossoverDeserialize(*nodeListIter, gameState->map);
+			CGHeroInstance * hero = campaignState->crossoverDeserialize(*nodeListIter, gameState->map.get());
 			nodeListIter++;
 
 			logGlobal->info("Hero crossover: Loading placeholder as %d (%s)", hero->getHeroType(), hero->getNameTranslated());
@@ -521,7 +520,7 @@ void CGameStateCampaign::generateCampaignHeroesToReplace()
 		// Add remaining heroes without placeholders - to transfer their artifacts to placed heroes
 		for (;nodeListIter != nodeList.end(); ++nodeListIter)
 		{
-			CGHeroInstance * hero = campaignState->crossoverDeserialize(*nodeListIter, gameState->map);
+			CGHeroInstance * hero = campaignState->crossoverDeserialize(*nodeListIter, gameState->map.get());
 			campaignHeroReplacements.emplace_back(hero, ObjectInstanceID::NONE);
 		}
 	}
@@ -677,7 +676,7 @@ void CGameStateCampaign::initTowns()
 
 			town->addBuilding(newBuilding);
 
-			auto building = town->getTown()->buildings.at(newBuilding);
+			const auto & building = town->getTown()->buildings.at(newBuilding);
 			newBuilding = building->upgrade;
 		}
 		break;

+ 10 - 10
lib/gameState/GameStatistics.cpp

@@ -40,7 +40,7 @@ StatisticDataSetEntry StatisticDataSet::createEntry(const PlayerState * ps, cons
 	scenarioHighScores.parameters.push_back(param);
 	scenarioHighScores.isCampaign = false;
 
-	data.map = gs->map->name.toString();
+	data.map = gs->getMap().name.toString();
 	data.timestamp = std::time(nullptr);
 	data.day = gs->getDate(Date::DAY);
 	data.player = ps->color;
@@ -193,7 +193,7 @@ std::vector<const CGMine *> Statistic::getMines(const CGameState * gs, const Pla
 	std::vector<const CGMine *> tmp;
 
 	std::vector<const CGObjectInstance *> ownedObjects;
-	for(const CGObjectInstance * obj : gs->map->objects)
+	for(const CGObjectInstance * obj : gs->getMap().objects)
 	{
 		if(obj && obj->tempOwner == ps->color)
 			ownedObjects.push_back(obj);
@@ -285,11 +285,11 @@ float Statistic::getMapExploredRatio(const CGameState * gs, PlayerColor player)
 	float visible = 0.0;
 	float numTiles = 0.0;
 
-	for(int layer = 0; layer < (gs->map->twoLevel ? 2 : 1); layer++)
-		for(int y = 0; y < gs->map->height; ++y)
-			for(int x = 0; x < gs->map->width; ++x)
+	for(int layer = 0; layer < (gs->getMap().twoLevel ? 2 : 1); layer++)
+		for(int y = 0; y < gs->getMap().height; ++y)
+			for(int x = 0; x < gs->getMap().width; ++x)
 			{
-				TerrainTile tile = gs->map->getTile(int3(x, y, layer));
+				TerrainTile tile = gs->getMap().getTile(int3(x, y, layer));
 
 				if(tile.blocked() && !tile.visitable())
 					continue;
@@ -346,17 +346,17 @@ std::vector<std::vector<PlayerColor>> Statistic::getRank(std::vector<std::pair<P
 
 int Statistic::getObeliskVisited(const CGameState * gs, const TeamID & t)
 {
-	if(gs->map->obelisksVisited.count(t))
-		return gs->map->obelisksVisited.at(t);
+	if(gs->getMap().obelisksVisited.count(t))
+		return gs->getMap().obelisksVisited.at(t);
 	else
 		return 0;
 }
 
 float Statistic::getObeliskVisitedRatio(const CGameState * gs, const TeamID & t)
 {
-	if(!gs->map->obeliskCount)
+	if(!gs->getMap().obeliskCount)
 		return 0;
-	return static_cast<float>(getObeliskVisited(gs, t)) / gs->map->obeliskCount;
+	return static_cast<float>(getObeliskVisited(gs, t)) / gs->getMap().obeliskCount;
 }
 
 std::map<EGameResID, int> Statistic::getNumMines(const CGameState * gs, const PlayerState * ps)

+ 6 - 6
lib/mapObjects/CGCreature.cpp

@@ -476,13 +476,13 @@ void CGCreature::fight( const CGHeroInstance *h ) const
 	for (int slotID = 1; slotID < a; ++slotID)
 	{
 		int stackSize = m + 1;
-		cb->moveStack(StackLocation(this, sourceSlot), StackLocation(this, SlotID(slotID)), stackSize);
+		cb->moveStack(StackLocation(id, sourceSlot), StackLocation(id, SlotID(slotID)), stackSize);
 	}
 	for (int slotID = a; slotID < stacksCount; ++slotID)
 	{
 		int stackSize = m;
 		if (slotID) //don't do this when a = 0 -> stack is single
-			cb->moveStack(StackLocation(this, sourceSlot), StackLocation(this, SlotID(slotID)), stackSize);
+			cb->moveStack(StackLocation(id, sourceSlot), StackLocation(id, SlotID(slotID)), stackSize);
 	}
 	if (stacksCount > 1)
 	{
@@ -493,7 +493,7 @@ void CGCreature::fight( const CGHeroInstance *h ) const
 			if(!upgrades.empty())
 			{
 				auto it = RandomGeneratorUtil::nextItem(upgrades, cb->gameState()->getRandomGenerator());
-				cb->changeStackType(StackLocation(this, slotID), it->toCreature());
+				cb->changeStackType(StackLocation(id, slotID), it->toCreature());
 			}
 		}
 	}
@@ -532,13 +532,13 @@ void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &
 		{
 			if(cre->isMyUpgrade(i->second->getCreature()))
 			{
-				cb->changeStackType(StackLocation(this, i->first), cre); //un-upgrade creatures
+				cb->changeStackType(StackLocation(id, i->first), cre); //un-upgrade creatures
 			}
 		}
 
 		//first stack has to be at slot 0 -> if original one got killed, move there first remaining stack
 		if(!hasStackAtSlot(SlotID(0)))
-			cb->moveStack(StackLocation(this, stacks.begin()->first), StackLocation(this, SlotID(0)), stacks.begin()->second->count);
+			cb->moveStack(StackLocation(id, stacks.begin()->first), StackLocation(id, SlotID(0)), stacks.begin()->second->count);
 
 		while(stacks.size() > 1) //hopefully that's enough
 		{
@@ -549,7 +549,7 @@ void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &
 			if(slot == i->first) //no reason to move stack to its own slot
 				break;
 			else
-				cb->moveStack(StackLocation(this, i->first), StackLocation(this, slot), i->second->count);
+				cb->moveStack(StackLocation(id, i->first), StackLocation(id, slot), i->second->count);
 		}
 
 		cb->setObjPropertyValue(id, ObjProperty::MONSTER_POWER, stacks.begin()->second->count * 1000); //remember casualties

+ 5 - 5
lib/mapObjects/CGDwelling.cpp

@@ -66,16 +66,16 @@ FactionID CGDwelling::randomizeFaction(vstd::RNG & rand)
 
 	if (!randomizationInfo->instanceId.empty())
 	{
-		auto iter = cb->gameState()->map->instanceNames.find(randomizationInfo->instanceId);
+		auto iter = cb->gameState()->getMap().instanceNames.find(randomizationInfo->instanceId);
 
-		if(iter == cb->gameState()->map->instanceNames.end())
+		if(iter == cb->gameState()->getMap().instanceNames.end())
 			logGlobal->error("Map object not found: %s", randomizationInfo->instanceId);
 		linkedTown = dynamic_cast<CGTownInstance *>(iter->second.get());
 	}
 
 	if (randomizationInfo->identifier != 0)
 	{
-		for(auto & elem : cb->gameState()->map->objects)
+		for(auto & elem : cb->gameState()->getMap().objects)
 		{
 			auto town = dynamic_cast<CGTownInstance*>(elem.get());
 			if(town && town->identifier == randomizationInfo->identifier)
@@ -420,7 +420,7 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const
 					std::pair<SlotID, SlotID> toMerge;
 					if (h->mergeableStacks(toMerge))
 					{
-						cb->moveStack(StackLocation(h, toMerge.first), StackLocation(h, toMerge.second), -1); //merge toMerge.first into toMerge.second
+						cb->moveStack(StackLocation(h->id, toMerge.first), StackLocation(h->id, toMerge.second), -1); //merge toMerge.first into toMerge.second
 						assert(!h->hasStackAtSlot(toMerge.first)); //we have now a new free slot
 					}
 				}
@@ -453,7 +453,7 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const
 
 				cb->showInfoDialog(&iw);
 				cb->sendAndApply(sac);
-				cb->addToSlot(StackLocation(h, slot), crs, count);
+				cb->addToSlot(StackLocation(h->id, slot), crs, count);
 			}
 		}
 		else //there no creatures

+ 2 - 2
lib/mapObjects/CGHeroInstance.cpp

@@ -540,7 +540,7 @@ void CGHeroInstance::onHeroVisit(const CGHeroInstance * h) const
 			
 			ObjectInstanceID boatId;
 			const auto boatPos = visitablePos();
-			if (cb->gameState()->map->getTile(boatPos).isWater())
+			if (cb->gameState()->getMap().getTile(boatPos).isWater())
 			{
 				smp.val = movementPointsLimit(false);
 				if (!boat)
@@ -1258,7 +1258,7 @@ void CGHeroInstance::removeSpellbook()
 
 	if(hasSpellbook())
 	{
-		cb->gameState()->map->removeArtifactInstance(*this, ArtifactPosition::SPELLBOOK);
+		cb->gameState()->getMap().removeArtifactInstance(*this, ArtifactPosition::SPELLBOOK);
 	}
 }
 

+ 1 - 0
lib/mapObjects/CGHeroInstance.h

@@ -17,6 +17,7 @@
 #include "../bonuses/BonusCache.h"
 #include "../entities/hero/EHeroGender.h"
 #include "../CArtHandler.h" // For CArtifactSet
+#include "../ConstTransitivePtr.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 3 - 3
lib/mapObjects/CGObjectInstance.cpp

@@ -122,10 +122,10 @@ void CGObjectInstance::setType(MapObjectID newID, MapObjectSubID newSubID)
 {
 	auto position = visitablePos();
 	auto oldOffset = getVisitableOffset();
-	auto &tile = cb->gameState()->map->getTile(position);
+	auto &tile = cb->gameState()->getMap().getTile(position);
 
 	//recalculate blockvis tiles - new appearance might have different blockmap than before
-	cb->gameState()->map->removeBlockVisTiles(this, true);
+	cb->gameState()->getMap().removeBlockVisTiles(this, true);
 	auto handler = LIBRARY->objtypeh->getHandlerFor(newID, newSubID);
 
 	if(!handler->getTemplates(tile.getTerrainID()).empty())
@@ -155,7 +155,7 @@ void CGObjectInstance::setType(MapObjectID newID, MapObjectSubID newSubID)
 	this->ID = Obj(newID);
 	this->subID = newSubID;
 
-	cb->gameState()->map->addBlockVisTiles(this);
+	cb->gameState()->getMap().addBlockVisTiles(this);
 }
 
 void CGObjectInstance::pickRandomObject(vstd::RNG & rand)

+ 12 - 12
lib/mapObjects/CGTownInstance.cpp

@@ -444,10 +444,10 @@ DamageRange CGTownInstance::getKeepDamageRange() const
 FactionID CGTownInstance::randomizeFaction(vstd::RNG & rand)
 {
 	if(getOwner().isValidPlayer())
-		return cb->gameState()->scenarioOps->getIthPlayersSettings(getOwner()).castle;
+		return cb->getStartInfo()->getIthPlayersSettings(getOwner()).castle;
 
 	if(alignmentToPlayer.isValidPlayer())
-		return cb->gameState()->scenarioOps->getIthPlayersSettings(alignmentToPlayer).castle;
+		return cb->getStartInfo()->getIthPlayersSettings(alignmentToPlayer).castle;
 
 	std::vector<FactionID> potentialPicks;
 
@@ -620,12 +620,12 @@ void CGTownInstance::mergeGarrisonOnSiege() const
 		});
 		auto dst = visitingHero->getSlotFor(pair.second->getCreatureID());
 		if(dst.validSlot())
-			cb->moveStack(StackLocation(this, pair.first), StackLocation(visitingHero, dst), -1);
+			cb->moveStack(StackLocation(id, pair.first), StackLocation(visitingHero->id, dst), -1);
 		else
 		{
 			dst = getWeakestStackSlot(static_cast<int>(pair.second->getPower()));
 			if(dst.validSlot())
-				cb->swapStacks(StackLocation(this, pair.first), StackLocation(visitingHero, dst));
+				cb->swapStacks(StackLocation(id, pair.first), StackLocation(visitingHero->id, dst));
 		}
 	}
 }
@@ -654,7 +654,7 @@ void CGTownInstance::clearArmy() const
 {
 	while(!stacks.empty())
 	{
-		cb->eraseStack(StackLocation(this, stacks.begin()->first));
+		cb->eraseStack(StackLocation(id, stacks.begin()->first));
 	}
 }
 
@@ -684,14 +684,14 @@ std::vector<TradeItemBuy> CGTownInstance::availableItemsIds(EMarketMode mode) co
 	if(mode == EMarketMode::RESOURCE_ARTIFACT)
 	{
 		std::vector<TradeItemBuy> ret;
-		for(const ArtifactID a : cb->gameState()->map->townMerchantArtifacts)
+		for(const ArtifactID a : cb->gameState()->getMap().townMerchantArtifacts)
 			ret.push_back(a);
 
 		return ret;
 	}
 	else if ( mode == EMarketMode::RESOURCE_SKILL )
 	{
-		return cb->gameState()->map->townUniversitySkills;
+		return cb->gameState()->getMap().townUniversitySkills;
 	}
 	else
 		return IMarket::availableItemsIds(mode);
@@ -769,7 +769,7 @@ void CGTownInstance::recreateBuildingsBonuses()
 		if (bonusesReplacedByUpgrade)
 			continue;
 
-		auto building = getTown()->buildings.at(bid);
+		const auto & building = getTown()->buildings.at(bid);
 
 		if(building->buildingBonuses.empty())
 			continue;
@@ -960,7 +960,7 @@ TResources CGTownInstance::getBuildingCost(const BuildingID & buildingID) const
 
 CBuilding::TRequired CGTownInstance::genBuildingRequirements(const BuildingID & buildID, bool deep) const
 {
-	const CBuilding * building = getTown()->buildings.at(buildID);
+	const auto & building = getTown()->buildings.at(buildID);
 
 	//TODO: find better solution to prevent infinite loops
 	std::set<BuildingID> processed;
@@ -974,7 +974,7 @@ CBuilding::TRequired CGTownInstance::genBuildingRequirements(const BuildingID &
 			return CBuilding::TRequired::OperatorAll();
 		}
 
-		const CBuilding * build = getTown()->buildings.at(id);
+		const auto & build = getTown()->buildings.at(id);
 		CBuilding::TRequired::OperatorAll requirements;
 
 		if (!hasBuilt(id))
@@ -999,7 +999,7 @@ CBuilding::TRequired CGTownInstance::genBuildingRequirements(const BuildingID &
 	CBuilding::TRequired::OperatorAll requirements;
 	if (building->upgrade != BuildingID::NONE)
 	{
-		const CBuilding * upgr = getTown()->buildings.at(building->upgrade);
+		const auto & upgr = getTown()->buildings.at(building->upgrade);
 
 		requirements.expressions.push_back(dependTest(upgr->bid));
 		processed.clear();
@@ -1095,7 +1095,7 @@ void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler)
 				if(id == BuildingID::DEFAULT)
 					continue;
 
-				const CBuilding * building = getTown()->buildings.at(id);
+				const auto & building = getTown()->buildings.at(id);
 
 				if(building->mode == CBuilding::BUILD_AUTO)
 					continue;

+ 1 - 0
lib/mapObjects/CGTownInstance.h

@@ -11,6 +11,7 @@
 
 #include "IMarket.h"
 #include "CGDwelling.h"
+#include "../ConstTransitivePtr.h"
 #include "../entities/faction/CFaction.h" // TODO: remove
 #include "../entities/faction/CTown.h" // TODO: remove
 

+ 18 - 18
lib/mapObjects/MiscObjects.cpp

@@ -322,7 +322,7 @@ bool CGTeleport::isConnected(const CGObjectInstance * src, const CGObjectInstanc
 
 bool CGTeleport::isExitPassable(CGameState * gs, const CGHeroInstance * h, const CGObjectInstance * obj)
 {
-	auto * objTopVisObj = gs->map->getTile(obj->visitablePos()).topVisitableObj();
+	auto * objTopVisObj = gs->getMap().getTile(obj->visitablePos()).topVisitableObj();
 	if(objTopVisObj->ID == Obj::HERO)
 	{
 		if(h->id == objTopVisObj->id) // Just to be sure it's won't happen.
@@ -374,7 +374,7 @@ 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()->map->objects)
+	for(auto obj : cb->gameState()->getMap().objects)
 	{
 		if(!obj)
 			continue;
@@ -455,9 +455,9 @@ void CGMonolith::initObj(vstd::RNG & rand)
 
 	channel = findMeChannel(IDs, subID);
 	if(channel == TeleportChannelID())
-		channel = TeleportChannelID(static_cast<si32>(cb->gameState()->map->teleportChannels.size()));
+		channel = TeleportChannelID(static_cast<si32>(cb->gameState()->getMap().teleportChannels.size()));
 
-	addToChannel(cb->gameState()->map->teleportChannels, this);
+	addToChannel(cb->gameState()->getMap().teleportChannels, this);
 }
 
 void CGSubterraneanGate::onHeroVisit( const CGHeroInstance * h ) const
@@ -487,7 +487,7 @@ 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()->map->objects)
+	for(auto & obj : cb->gameState()->getMap().objects)
 	{
 		if(!obj)
 			continue;
@@ -507,8 +507,8 @@ void CGSubterraneanGate::postInit(IGameCallback * cb) //matches subterranean gat
 	{
 		if(obj->channel == TeleportChannelID())
 		{ // if object not linked to channel then create new channel
-			obj->channel = TeleportChannelID(static_cast<si32>(cb->gameState()->map->teleportChannels.size()));
-			addToChannel(cb->gameState()->map->teleportChannels, obj);
+			obj->channel = TeleportChannelID(static_cast<si32>(cb->gameState()->getMap().teleportChannels.size()));
+			addToChannel(cb->gameState()->getMap().teleportChannels, obj);
 		}
 	};
 
@@ -535,7 +535,7 @@ void CGSubterraneanGate::postInit(IGameCallback * cb) //matches subterranean gat
 		if(best.first >= 0) //found pair
 		{
 			gatesSplit[1][best.first]->channel = objCurrent->channel;
-			addToChannel(cb->gameState()->map->teleportChannels, gatesSplit[1][best.first]);
+			addToChannel(cb->gameState()->getMap().teleportChannels, gatesSplit[1][best.first]);
 		}
 	}
 
@@ -573,7 +573,7 @@ void CGWhirlpool::onHeroVisit( const CGHeroInstance * h ) const
 		iw.text.appendLocalString(EMetaText::ADVOB_TXT, 168);
 		iw.components.emplace_back(ComponentType::CREATURE, h->getCreature(targetstack)->getId(), -countToTake);
 		cb->showInfoDialog(&iw);
-		cb->changeStackCount(StackLocation(h, targetstack), -countToTake);
+		cb->changeStackCount(StackLocation(h->id, targetstack), -countToTake);
 	}
 	else
 	{
@@ -665,7 +665,7 @@ void CGArtifact::initObj(vstd::RNG & rand)
 		if (!storedArtifact)
 		{
 			storedArtifact = ArtifactUtils::createArtifact(ArtifactID());
-			cb->gameState()->map->addNewArtifactInstance(storedArtifact);
+			cb->gameState()->getMap().addNewArtifactInstance(storedArtifact);
 		}
 		if(!storedArtifact->getType())
 			storedArtifact->setType(getArtifact().toArtifact());
@@ -959,7 +959,7 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const
 
 		std::vector<const CGObjectInstance *> eyes;
 
-		for (auto object : cb->gameState()->map->objects)
+		for (auto object : cb->gameState()->getMap().objects)
 		{
 			if (object && object->ID == Obj::EYE_OF_MAGI && object->subID == this->subID)
 				eyes.push_back(object);
@@ -1043,7 +1043,7 @@ void CGSirens::onHeroVisit( const CGHeroInstance * h ) const
 
 			if(drown)
 			{
-				cb->changeStackCount(StackLocation(h, i->first), -drown);
+				cb->changeStackCount(StackLocation(h->id, i->first), -drown);
 				xp += drown * i->second->getType()->getMaxHealth();
 			}
 		}
@@ -1172,7 +1172,7 @@ void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const
 
 void CGObelisk::initObj(vstd::RNG & rand)
 {
-	cb->gameState()->map->obeliskCount++;
+	cb->gameState()->getMap().obeliskCount++;
 }
 
 std::string CGObelisk::getHoverText(PlayerColor player) const
@@ -1191,13 +1191,13 @@ void CGObelisk::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
 	{
 		case ObjProperty::OBELISK_VISITED:
 			{
-				auto progress = ++cb->gameState()->map->obelisksVisited[identifier.as<TeamID>()];
-				logGlobal->debug("Player %d: obelisk progress %d / %d", identifier.getNum(), static_cast<int>(progress) , static_cast<int>(cb->gameState()->map->obeliskCount));
+				auto progress = ++cb->gameState()->getMap().obelisksVisited[identifier.as<TeamID>()];
+				logGlobal->debug("Player %d: obelisk progress %d / %d", identifier.getNum(), static_cast<int>(progress) , static_cast<int>(cb->gameState()->getMap().obeliskCount));
 
-				if(progress > cb->gameState()->map->obeliskCount)
+				if(progress > cb->gameState()->getMap().obeliskCount)
 				{
-					logGlobal->error("Visited %d of %d", static_cast<int>(progress), cb->gameState()->map->obeliskCount);
-					throw std::runtime_error("Player visited " + std::to_string(progress) + " obelisks out of " + std::to_string(cb->gameState()->map->obeliskCount) + " present on map!");
+					logGlobal->error("Visited %d of %d", static_cast<int>(progress), cb->gameState()->getMap().obeliskCount);
+					throw std::runtime_error("Player visited " + std::to_string(progress) + " obelisks out of " + std::to_string(cb->gameState()->getMap().obeliskCount) + " present on map!");
 				}
 
 				break;

+ 2 - 2
lib/mapObjects/TownBuildingInstance.cpp

@@ -75,7 +75,7 @@ TownRewardableBuildingInstance::TownRewardableBuildingInstance(CGTownInstance *
 Rewardable::Configuration TownRewardableBuildingInstance::generateConfiguration(vstd::RNG & rand) const
 {
 	Rewardable::Configuration result;
-	auto building = town->getTown()->buildings.at(getBuildingType());
+	const auto & building = town->getTown()->buildings.at(getBuildingType());
 
 	building->rewardableObjectInfo.configureObject(result, rand, cb);
 	for(auto & rewardInfo : result.info)
@@ -165,7 +165,7 @@ bool TownRewardableBuildingInstance::wasVisitedBefore(const CGHeroInstance * con
 			return false; //not supported
 		case Rewardable::VISIT_BONUS:
 		{
-			const auto building = town->getTown()->buildings.at(getBuildingType());
+			const auto & building = town->getTown()->buildings.at(getBuildingType());
 			if (building->mapObjectLikeBonuses.hasValue())
 				return contextHero->hasBonusFrom(BonusSource::OBJECT_TYPE, BonusSourceID(building->mapObjectLikeBonuses));
 			else

+ 82 - 92
lib/networkPacks/NetPacksLib.cpp

@@ -998,7 +998,7 @@ void FoWChange::applyGs(CGameState *gs)
 	if (mode == ETileVisibility::HIDDEN) //do not hide too much
 	{
 		std::unordered_set<int3> tilesRevealed;
-		for (auto & elem : gs->map->objects)
+		for (auto & elem : gs->getMap().objects)
 		{
 			const CGObjectInstance *o = elem;
 			if (o)
@@ -1059,9 +1059,9 @@ void ChangeObjPos::applyGs(CGameState *gs)
 		logNetwork->error("Wrong ChangeObjPos: object %d doesn't exist!", objid.getNum());
 		return;
 	}
-	gs->map->removeBlockVisTiles(obj);
+	gs->getMap().removeBlockVisTiles(obj);
 	obj->setAnchorPos(nPos + obj->getVisitableOffset());
-	gs->map->addBlockVisTiles(obj);
+	gs->getMap().addBlockVisTiles(obj);
 }
 
 void ChangeObjectVisitors::applyGs(CGameState *gs)
@@ -1080,7 +1080,7 @@ void ChangeObjectVisitors::applyGs(CGameState *gs)
 			break;
 		case VISITOR_CLEAR:
 			// remove visit info from all heroes, including those that are not present on map
-			for (CGHeroInstance * hero : gs->map->allHeroes)
+			for (CGHeroInstance * hero : gs->getMap().allHeroes)
 				if (hero)
 					hero->visitedObjects.erase(object);
 
@@ -1122,14 +1122,14 @@ void PlayerEndsGame::applyGs(CGameState *gs)
 
 		// TODO: Campaign-specific code might as well go somewhere else
 		// keep all heroes from the winning player
-		if(p->human && gs->scenarioOps->campState)
+		if(p->human && gs->getStartInfo()->campState)
 		{
 			std::vector<CGHeroInstance *> crossoverHeroes;
-			for (CGHeroInstance * hero : gs->map->heroesOnMap)
+			for (CGHeroInstance * hero : gs->getMap().heroesOnMap)
 				if (hero->tempOwner == player)
 					crossoverHeroes.push_back(hero);
 
-			gs->scenarioOps->campState->setCurrentMapAsConquered(crossoverHeroes);
+			gs->getStartInfo()->campState->setCurrentMapAsConquered(crossoverHeroes);
 		}
 	}
 	else
@@ -1143,14 +1143,14 @@ void PlayerEndsGame::applyGs(CGameState *gs)
 
 void PlayerReinitInterface::applyGs(CGameState *gs)
 {
-	if(!gs || !gs->scenarioOps)
+	if(!gs || !gs->getStartInfo())
 		return;
 	
 	//TODO: what does mean if more that one player connected?
 	if(playerConnectionId == PlayerSettings::PLAYER_AI)
 	{
 		for(const auto & player : players)
-			gs->scenarioOps->getIthPlayersSettings(player).connectedPlayerIDs.clear();
+			gs->getStartInfo()->getIthPlayersSettings(player).connectedPlayerIDs.clear();
 	}
 }
 
@@ -1189,7 +1189,7 @@ void RemoveObject::applyGs(CGameState *gs)
 	CGObjectInstance *obj = gs->getObjInstance(objectID);
 	logGlobal->debug("removing object id=%d; address=%x; name=%s", objectID, (intptr_t)obj, obj->getObjectName());
 	//unblock tiles
-	gs->map->removeBlockVisTiles(obj);
+	gs->getMap().removeBlockVisTiles(obj);
 
 	if (initiator.isValidPlayer())
 		gs->getPlayerState(initiator)->destroyedObjects.insert(objectID);
@@ -1209,7 +1209,7 @@ void RemoveObject::applyGs(CGameState *gs)
 	{
 		auto * beatenHero = dynamic_cast<CGHeroInstance *>(obj);
 		assert(beatenHero);
-		gs->map->heroesOnMap -= beatenHero;
+		gs->getMap().heroesOnMap -= beatenHero;
 
 		auto * siegeNode = beatenHero->whereShouldBeAttachedOnSiege(gs);
 
@@ -1238,14 +1238,14 @@ void RemoveObject::applyGs(CGameState *gs)
 		//return hero to the pool, so he may reappear in tavern
 
 		gs->heroesPool->addHeroToPool(beatenHero);
-		gs->map->objects[objectID.getNum()] = nullptr;
+		gs->getMap().objects[objectID.getNum()] = nullptr;
 
 		//If hero on Boat is removed, the Boat disappears
 		if(beatenHero->boat)
 		{
 			beatenHero->detachFrom(const_cast<CGBoat&>(*beatenHero->boat));
-			gs->map->instanceNames.erase(beatenHero->boat->instanceName);
-			gs->map->objects[beatenHero->boat->id.getNum()].dellNull();
+			gs->getMap().instanceNames.erase(beatenHero->boat->instanceName);
+			gs->getMap().objects[beatenHero->boat->id.getNum()].dellNull();
 			beatenHero->boat = nullptr;
 		}
 		return;
@@ -1254,7 +1254,7 @@ void RemoveObject::applyGs(CGameState *gs)
 	const auto * quest = dynamic_cast<const IQuestObject *>(obj);
 	if (quest)
 	{
-		gs->map->quests[quest->quest->qid] = nullptr;
+		gs->getMap().quests[quest->quest->qid] = nullptr;
 		for (auto &player : gs->players)
 		{
 			vstd::erase_if(player.second.quests, [obj](const QuestInfo & q){
@@ -1263,9 +1263,9 @@ void RemoveObject::applyGs(CGameState *gs)
 		}
 	}
 
-	gs->map->instanceNames.erase(obj->instanceName);
-	gs->map->objects[objectID.getNum()].dellNull();
-	gs->map->calculateGuardingGreaturePositions();//FIXME: excessive, update only affected tiles
+	gs->getMap().instanceNames.erase(obj->instanceName);
+	gs->getMap().objects[objectID.getNum()].dellNull();
+	gs->getMap().calculateGuardingGreaturePositions();//FIXME: excessive, update only affected tiles
 }
 
 static int getDir(const int3 & src, const int3 & dst)
@@ -1327,12 +1327,12 @@ void TryMoveHero::applyGs(CGameState *gs)
 
 	if(result == EMBARK) //hero enters boat at destination tile
 	{
-		const TerrainTile &tt = gs->map->getTile(h->convertToVisitablePos(end));
+		const TerrainTile &tt = gs->getMap().getTile(h->convertToVisitablePos(end));
 		assert(tt.visitableObjects.size() >= 1  &&  tt.visitableObjects.back()->ID == Obj::BOAT); //the only visitable object at destination is Boat
 		auto * boat = dynamic_cast<CGBoat *>(tt.visitableObjects.back());
 		assert(boat);
 
-		gs->map->removeBlockVisTiles(boat); //hero blockvis mask will be used, we don't need to duplicate it with boat
+		gs->getMap().removeBlockVisTiles(boat); //hero blockvis mask will be used, we don't need to duplicate it with boat
 		h->boat = boat;
 		h->attachTo(*boat);
 		boat->hero = h;
@@ -1343,18 +1343,18 @@ void TryMoveHero::applyGs(CGameState *gs)
 		b->direction = h->moveDir;
 		b->pos = start;
 		b->hero = nullptr;
-		gs->map->addBlockVisTiles(b);
+		gs->getMap().addBlockVisTiles(b);
 		h->detachFrom(*b);
 		h->boat = nullptr;
 	}
 
 	if(start!=end && (result == SUCCESS || result == TELEPORTATION || result == EMBARK || result == DISEMBARK))
 	{
-		gs->map->removeBlockVisTiles(h);
+		gs->getMap().removeBlockVisTiles(h);
 		h->pos = end;
 		if(auto * b = const_cast<CGBoat *>(h->boat))
 			b->pos = end;
-		gs->map->addBlockVisTiles(h);
+		gs->getMap().addBlockVisTiles(h);
 	}
 
 	auto & fogOfWarMap = gs->getPlayerTeam(h->getOwner())->fogOfWarMap;
@@ -1417,11 +1417,11 @@ void SetHeroesInTown::applyGs(CGameState *gs)
 
 	if(v)
 	{
-		gs->map->addBlockVisTiles(v);
+		gs->getMap().addBlockVisTiles(v);
 	}
 	if(g)
 	{
-		gs->map->removeBlockVisTiles(g);
+		gs->getMap().removeBlockVisTiles(g);
 	}
 }
 
@@ -1437,7 +1437,7 @@ void HeroRecruited::applyGs(CGameState *gs)
 		auto * boat = dynamic_cast<CGBoat *>(obj);
 		if (boat)
 		{
-			gs->map->removeBlockVisTiles(boat);
+			gs->getMap().removeBlockVisTiles(boat);
 			h->attachToBoat(boat);
 		}
 	}
@@ -1448,16 +1448,16 @@ void HeroRecruited::applyGs(CGameState *gs)
 
 	if(h->id == ObjectInstanceID())
 	{
-		h->id = ObjectInstanceID(static_cast<si32>(gs->map->objects.size()));
-		gs->map->objects.emplace_back(h);
+		h->id = ObjectInstanceID(static_cast<si32>(gs->getMap().objects.size()));
+		gs->getMap().objects.emplace_back(h);
 	}
 	else
-		gs->map->objects[h->id.getNum()] = h;
+		gs->getMap().objects[h->id.getNum()] = h;
 
-	gs->map->heroesOnMap.emplace_back(h);
+	gs->getMap().heroesOnMap.emplace_back(h);
 	p->addOwnedObject(h);
 	h->attachTo(*p);
-	gs->map->addBlockVisTiles(h);
+	gs->getMap().addBlockVisTiles(h);
 
 	if(t)
 		t->setVisitingHero(h);
@@ -1473,7 +1473,7 @@ void GiveHero::applyGs(CGameState *gs)
 		auto * boat = dynamic_cast<CGBoat *>(obj);
 		if (boat)
 		{
-			gs->map->removeBlockVisTiles(boat);
+			gs->getMap().removeBlockVisTiles(boat);
 			h->attachToBoat(boat);
 		}
 	}
@@ -1483,26 +1483,26 @@ void GiveHero::applyGs(CGameState *gs)
 	h->attachTo(*gs->getPlayerState(player));
 
 	auto oldVisitablePos = h->visitablePos();
-	gs->map->removeBlockVisTiles(h,true);
+	gs->getMap().removeBlockVisTiles(h,true);
 	h->updateAppearance();
 
 	h->setOwner(player);
 	h->setMovementPoints(h->movementPointsLimit(true));
 	h->setAnchorPos(h->convertFromVisitablePos(oldVisitablePos));
-	gs->map->heroesOnMap.emplace_back(h);
+	gs->getMap().heroesOnMap.emplace_back(h);
 	gs->getPlayerState(h->getOwner())->addOwnedObject(h);
 
-	gs->map->addBlockVisTiles(h);
+	gs->getMap().addBlockVisTiles(h);
 	h->inTownGarrison = false;
 }
 
 void NewObject::applyGs(CGameState *gs)
 {
-	newObject->id = ObjectInstanceID(static_cast<si32>(gs->map->objects.size()));
+	newObject->id = ObjectInstanceID(static_cast<si32>(gs->getMap().objects.size()));
 
-	gs->map->objects.emplace_back(newObject);
-	gs->map->addBlockVisTiles(newObject);
-	gs->map->calculateGuardingGreaturePositions();
+	gs->getMap().objects.emplace_back(newObject);
+	gs->getMap().addBlockVisTiles(newObject);
+	gs->getMap().calculateGuardingGreaturePositions();
 
 	// attach newly spawned wandering monster to global bonus system node
 	auto newArmy = dynamic_cast<CArmedInstance*>(newObject);
@@ -1515,21 +1515,11 @@ void NewObject::applyGs(CGameState *gs)
 void NewArtifact::applyGs(CGameState *gs)
 {
 	auto art = ArtifactUtils::createArtifact(artId, spellId);
-	gs->map->addNewArtifactInstance(art);
+	gs->getMap().addNewArtifactInstance(art);
 	PutArtifact pa(art->getId(), ArtifactLocation(artHolder, pos), false);
 	pa.applyGs(gs);
 }
 
-const CStackInstance * StackLocation::getStack()
-{
-	if(!army->hasStackAtSlot(slot))
-	{
-		logNetwork->warn("%s don't have a stack at slot %d", army->nodeName(), slot.getNum());
-		return nullptr;
-	}
-	return &army->getStack(slot);
-}
-
 struct ObjectRetriever
 {
 	const CArmedInstance * operator()(const ConstTransitivePtr<CGHeroInstance> &h) const
@@ -1616,25 +1606,25 @@ void RebalanceStacks::applyGs(CGameState *gs)
 	if(!dstObj)
 		throw std::runtime_error("RebalanceStacks: invalid army object " + std::to_string(dstArmy.getNum()) + ", possible game state corruption.");
 
-	StackLocation src(srcObj, srcSlot);
-	StackLocation dst(dstObj, dstSlot);
+	StackLocation src(srcObj->id, srcSlot);
+	StackLocation dst(dstObj->id, dstSlot);
 
-	const CCreature * srcType = src.army->getCreature(src.slot);
-	TQuantity srcCount = src.army->getStackCount(src.slot);
+	const CCreature * srcType = srcObj->getCreature(src.slot);
+	TQuantity srcCount = srcObj->getStackCount(src.slot);
 	bool stackExp = gs->getSettings().getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE);
 
 	if(srcCount == count) //moving whole stack
 	{
-		const auto c = dst.army->getCreature(dst.slot);
+		const auto c = dstObj->getCreature(dst.slot);
 
 		if(c) //stack at dest -> merge
 		{
 			assert(c == srcType);
 			
-			const auto srcHero = dynamic_cast<CGHeroInstance*>(src.army.get());
-			const auto dstHero = dynamic_cast<CGHeroInstance*>(dst.army.get());
-			auto srcStack = const_cast<CStackInstance*>(src.getStack());
-			auto dstStack = const_cast<CStackInstance*>(dst.getStack());
+			const auto srcHero = dynamic_cast<CGHeroInstance*>(srcObj);
+			const auto dstHero = dynamic_cast<CGHeroInstance*>(dstObj);
+			auto srcStack = const_cast<CStackInstance*>(srcObj->getStackPtr(src.slot));
+			auto dstStack = const_cast<CStackInstance*>(dstObj->getStackPtr(dst.slot));
 			if(srcStack->getArt(ArtifactPosition::CREATURE_SLOT))
 			{
 				if(auto dstArt = dstStack->getArt(ArtifactPosition::CREATURE_SLOT))
@@ -1642,7 +1632,7 @@ void RebalanceStacks::applyGs(CGameState *gs)
 					auto dstSlot = ArtifactUtils::getArtBackpackPosition(srcHero, dstArt->getTypeId());
 					if(srcHero && dstSlot != ArtifactPosition::PRE_FIRST)
 					{
-						gs->map->moveArtifactInstance(*dstStack, ArtifactPosition::CREATURE_SLOT, *srcHero, dstSlot);
+						gs->getMap().moveArtifactInstance(*dstStack, ArtifactPosition::CREATURE_SLOT, *srcHero, dstSlot);
 					}
 					//else - artifact can be lost :/
 					else
@@ -1654,58 +1644,58 @@ void RebalanceStacks::applyGs(CGameState *gs)
 						ea.applyGs(gs);
 						logNetwork->warn("Cannot move artifact! No free slots");
 					}
-					gs->map->moveArtifactInstance(*srcStack, ArtifactPosition::CREATURE_SLOT, *dstStack, ArtifactPosition::CREATURE_SLOT);
+					gs->getMap().moveArtifactInstance(*srcStack, ArtifactPosition::CREATURE_SLOT, *dstStack, ArtifactPosition::CREATURE_SLOT);
 					//TODO: choose from dialog
 				}
 				else //just move to the other slot before stack gets erased
 				{
-					gs->map->moveArtifactInstance(*srcStack, ArtifactPosition::CREATURE_SLOT, *dstStack, ArtifactPosition::CREATURE_SLOT);
+					gs->getMap().moveArtifactInstance(*srcStack, ArtifactPosition::CREATURE_SLOT, *dstStack, ArtifactPosition::CREATURE_SLOT);
 				}
 			}
 			if (stackExp)
 			{
-				ui64 totalExp = srcCount * src.army->getStackExperience(src.slot) + dst.army->getStackCount(dst.slot) * dst.army->getStackExperience(dst.slot);
-				src.army->eraseStack(src.slot);
-				dst.army->changeStackCount(dst.slot, count);
-				dst.army->setStackExp(dst.slot, totalExp /(dst.army->getStackCount(dst.slot))); //mean
+				ui64 totalExp = srcCount * srcObj->getStackExperience(src.slot) + dstObj->getStackCount(dst.slot) * dstObj->getStackExperience(dst.slot);
+				srcObj->eraseStack(src.slot);
+				dstObj->changeStackCount(dst.slot, count);
+				dstObj->setStackExp(dst.slot, totalExp /(dstObj->getStackCount(dst.slot))); //mean
 			}
 			else
 			{
-				src.army->eraseStack(src.slot);
-				dst.army->changeStackCount(dst.slot, count);
+				srcObj->eraseStack(src.slot);
+				dstObj->changeStackCount(dst.slot, count);
 			}
 		}
 		else //move stack to an empty slot, no exp change needed
 		{
-			CStackInstance *stackDetached = src.army->detachStack(src.slot);
-			dst.army->putStack(dst.slot, stackDetached);
+			CStackInstance *stackDetached = srcObj->detachStack(src.slot);
+			dstObj->putStack(dst.slot, stackDetached);
 		}
 	}
 	else
 	{
-		[[maybe_unused]] const CCreature *c = dst.army->getCreature(dst.slot);
+		[[maybe_unused]] const CCreature *c = dstObj->getCreature(dst.slot);
 		if(c) //stack at dest -> rebalance
 		{
 			assert(c == srcType);
 			if (stackExp)
 			{
-				ui64 totalExp = srcCount * src.army->getStackExperience(src.slot) + dst.army->getStackCount(dst.slot) * dst.army->getStackExperience(dst.slot);
-				src.army->changeStackCount(src.slot, -count);
-				dst.army->changeStackCount(dst.slot, count);
-				dst.army->setStackExp(dst.slot, totalExp /(src.army->getStackCount(src.slot) + dst.army->getStackCount(dst.slot))); //mean
+				ui64 totalExp = srcCount * srcObj->getStackExperience(src.slot) + dstObj->getStackCount(dst.slot) * dstObj->getStackExperience(dst.slot);
+				srcObj->changeStackCount(src.slot, -count);
+				dstObj->changeStackCount(dst.slot, count);
+				dstObj->setStackExp(dst.slot, totalExp /(srcObj->getStackCount(src.slot) + dstObj->getStackCount(dst.slot))); //mean
 			}
 			else
 			{
-				src.army->changeStackCount(src.slot, -count);
-				dst.army->changeStackCount(dst.slot, count);
+				srcObj->changeStackCount(src.slot, -count);
+				dstObj->changeStackCount(dst.slot, count);
 			}
 		}
 		else //split stack to an empty slot
 		{
-			src.army->changeStackCount(src.slot, -count);
-			dst.army->addToSlot(dst.slot, srcType->getId(), count, false);
+			srcObj->changeStackCount(src.slot, -count);
+			dstObj->addToSlot(dst.slot, srcType->getId(), count, false);
 			if (stackExp)
-				dst.army->setStackExp(dst.slot, src.army->getStackExperience(src.slot));
+				dstObj->setStackExp(dst.slot, srcObj->getStackExperience(src.slot));
 		}
 	}
 
@@ -1737,7 +1727,7 @@ void PutArtifact::applyGs(CGameState *gs)
 	assert(hero);
 	assert(art && art->canBePutAt(hero, al.slot));
 	assert(ArtifactUtils::checkIfSlotValid(*hero, al.slot));
-	gs->map->putArtifactInstance(*hero, art, al.slot);
+	gs->getMap().putArtifactInstance(*hero, art, al.slot);
 }
 
 void BulkEraseArtifacts::applyGs(CGameState *gs)
@@ -1776,7 +1766,7 @@ void BulkEraseArtifacts::applyGs(CGameState *gs)
 		{
 			logGlobal->debug("Erasing artifact %s", slotInfo->artifact->getType()->getNameTranslated());
 		}
-		gs->map->removeArtifactInstance(*artSet, slot);
+		gs->getMap().removeArtifactInstance(*artSet, slot);
 	}
 }
 
@@ -1793,7 +1783,7 @@ void BulkMoveArtifacts::applyGs(CGameState *gs)
 			});
 
 		for(const auto & slot : packToRemove)
-			gs->map->removeArtifactInstance(artSet, slot);
+			gs->getMap().removeArtifactInstance(artSet, slot);
 	};
 
 	const auto bulkArtsPut = [gs](std::vector<LinkedSlots> & artsPack, CArtifactSet & initArtSet, CArtifactSet & dstArtSet)
@@ -1802,7 +1792,7 @@ void BulkMoveArtifacts::applyGs(CGameState *gs)
 		{
 			auto * art = initArtSet.getArt(slotsPair.srcPos);
 			assert(art);
-			gs->map->putArtifactInstance(dstArtSet, art, slotsPair.dstPos);
+			gs->getMap().putArtifactInstance(dstArtSet, art, slotsPair.dstPos);
 		}
 	};
 	
@@ -1834,7 +1824,7 @@ void AssembledArtifact::applyGs(CGameState *gs)
 		}));
 
 	auto * combinedArt = new CArtifactInstance(builtArt);
-	gs->map->addNewArtifactInstance(combinedArt);
+	gs->getMap().addNewArtifactInstance(combinedArt);
 
 	// Find slots for all involved artifacts
 	std::set<ArtifactPosition, std::greater<>> slotsInvolved = { al.slot };
@@ -1875,7 +1865,7 @@ void AssembledArtifact::applyGs(CGameState *gs)
 	for(const auto & slot : slotsInvolved)
 	{
 		const auto constituentInstance = artSet->getArt(slot);
-		gs->map->removeArtifactInstance(*artSet, slot);
+		gs->getMap().removeArtifactInstance(*artSet, slot);
 
 		if(!combinedArt->getType()->isFused())
 		{
@@ -1887,7 +1877,7 @@ void AssembledArtifact::applyGs(CGameState *gs)
 	}
 
 	// Put new combined artifacts
-	gs->map->putArtifactInstance(*artSet, combinedArt, al.slot);
+	gs->getMap().putArtifactInstance(*artSet, combinedArt, al.slot);
 }
 
 void DisassembledArtifact::applyGs(CGameState *gs)
@@ -1898,15 +1888,15 @@ void DisassembledArtifact::applyGs(CGameState *gs)
 	assert(disassembledArt);
 
 	const auto parts = disassembledArt->getPartsInfo();
-	gs->map->removeArtifactInstance(*hero, al.slot);
+	gs->getMap().removeArtifactInstance(*hero, al.slot);
 	for(auto & part : parts)
 	{
 		// ArtifactPosition::PRE_FIRST is value of main part slot -> it'll replace combined artifact in its pos
 		auto slot = (ArtifactUtils::isSlotEquipment(part.slot) ? part.slot : al.slot);
 		disassembledArt->detachFrom(*part.art);
-		gs->map->putArtifactInstance(*hero, part.art, slot);
+		gs->getMap().putArtifactInstance(*hero, part.art, slot);
 	}
-	gs->map->eraseArtifactInstance(disassembledArt);
+	gs->getMap().eraseArtifactInstance(disassembledArt);
 }
 
 void HeroVisit::applyGs(CGameState *gs)
@@ -1928,7 +1918,7 @@ void SetAvailableArtifacts::applyGs(CGameState *gs)
 	}
 	else
 	{
-		gs->map->townMerchantArtifacts = arts;
+		gs->getMap().townMerchantArtifacts = arts;
 	}
 }
 
@@ -1959,7 +1949,7 @@ void NewTurn::applyGs(CGameState *gs)
 	for(auto & creatureSet : availableCreatures) //set available creatures in towns
 		creatureSet.applyGs(gs);
 
-	for(CGTownInstance* t : gs->map->towns)
+	for(CGTownInstance* t : gs->getMap().towns)
 	{
 		t->built = 0;
 		t->spellResearchCounterDay = 0;

+ 7 - 11
lib/networkPacks/StackLocation.h

@@ -9,28 +9,24 @@
  */
 #pragma once
 
-#include "../ConstTransitivePtr.h"
-#include "../GameConstants.h"
+#include "../constants/EntityIdentifiers.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-class CArmedInstance;
-class CStackInstance;
-
 struct StackLocation
 {
-	ConstTransitivePtr<CArmedInstance> army;
+	ObjectInstanceID army;
 	SlotID slot;
 
 	StackLocation() = default;
-	StackLocation(const CArmedInstance * Army, const SlotID & Slot)
-		: army(const_cast<CArmedInstance *>(Army))  //we are allowed here to const cast -> change will go through one of our packages... do not abuse!
-		, slot(Slot)
+	StackLocation(const ObjectInstanceID & army, const SlotID & slot)
+		: army(army)
+		, slot(slot)
 	{
 	}
 
-	DLL_LINKAGE const CStackInstance * getStack();
-	template <typename Handler> void serialize(Handler & h)
+	template<typename Handler>
+	void serialize(Handler & h)
 	{
 		h & army;
 		h & slot;

+ 3 - 3
lib/pathfinder/CPathfinder.cpp

@@ -111,7 +111,7 @@ void CPathfinder::calculatePaths()
 
 	for(auto * initialNode : initialNodes)
 	{
-		if(!gamestate->isInTheMap(initialNode->coord)/* || !gs->map->isInTheMap(dest)*/) //check input
+		if(!gamestate->isInTheMap(initialNode->coord)/* || !gs->getMap().isInTheMap(dest)*/) //check input
 		{
 			logGlobal->error("CGameState::calculatePaths: Hero outside the gs->map? How dare you...");
 			throw std::runtime_error("Wrong checksum");
@@ -251,7 +251,7 @@ TeleporterTilesVector CPathfinderHelper::getAllowedTeleportChannelExits(const Te
 			auto pos = obj->getBlockedPos();
 			for(const auto & p : pos)
 			{
-				if(gs->map->getTile(p).topVisitableId() == obj->ID)
+				if(gs->getMap().getTile(p).topVisitableId() == obj->ID)
 					allowedExits.push_back(p);
 			}
 		}
@@ -571,7 +571,7 @@ void CPathfinderHelper::getNeighbours(
 	const boost::logic::tribool & onLand,
 	const bool limitCoastSailing) const
 {
-	CMap * map = gs->map;
+	const CMap * map = &gs->getMap();
 	const TerrainType * sourceTerrain = sourceTile.getTerrain();
 
 	static constexpr std::array dirs = {

+ 1 - 1
lib/pathfinder/NodeStorage.cpp

@@ -40,7 +40,7 @@ void NodeStorage::initialize(const PathfinderOptions & options, const CGameState
 		{
 			for(pos.y=0; pos.y < sizes.y; ++pos.y)
 			{
-				const TerrainTile & tile = gs->map->getTile(pos);
+				const TerrainTile & tile = gs->getMap().getTile(pos);
 				if(tile.isWater())
 				{
 					resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, tile, fow, player, gs));

+ 1 - 1
lib/rewardable/Interface.cpp

@@ -185,7 +185,7 @@ void Rewardable::Interface::grantRewardAfterLevelup(const Rewardable::VisitInfo
 			{
 				if (heroStack->getId() == change.first)
 				{
-					StackLocation location(hero, slot.first);
+					StackLocation location(hero->id, slot.first);
 					cb->changeStackType(location, change.second.toCreature());
 					break;
 				}

+ 4 - 4
lib/serializer/CSerializer.cpp

@@ -23,13 +23,13 @@ CSerializer::~CSerializer() = default;
 
 void CSerializer::addStdVecItems(CGameState *gs, GameLibrary *lib)
 {
-	registerVectoredType<CGObjectInstance, ObjectInstanceID>(&gs->map->objects,
+	registerVectoredType<CGObjectInstance, ObjectInstanceID>(&gs->getMap().objects,
 		[](const CGObjectInstance &obj){ return obj.id; });
-	registerVectoredType<CGHeroInstance, HeroTypeID>(&gs->map->allHeroes,
+	registerVectoredType<CGHeroInstance, HeroTypeID>(&gs->getMap().allHeroes,
 		[](const CGHeroInstance &h){ return h.getHeroType()->getId(); });
-	registerVectoredType<CArtifactInstance, ArtifactInstanceID>(&gs->map->artInstances,
+	registerVectoredType<CArtifactInstance, ArtifactInstanceID>(&gs->getMap().artInstances,
 		[](const CArtifactInstance &artInst){ return artInst.getId(); });
-	registerVectoredType<CQuest, si32>(&gs->map->quests,
+	registerVectoredType<CQuest, si32>(&gs->getMap().quests,
 		[](const CQuest &q){ return q.qid; });
 
 	smartVectorMembersSerialization = true;

+ 0 - 1
lib/spells/CSpellHandler.h

@@ -14,7 +14,6 @@
 #include <vcmi/spells/Service.h>
 #include <vcmi/spells/Magic.h>
 #include "../IHandlerBase.h"
-#include "../ConstTransitivePtr.h"
 #include "../int3.h"
 #include "../bonuses/Bonus.h"
 #include "../filesystem/ResourcePath.h"

+ 2 - 2
mapeditor/inspector/townbuildingswidget.cpp

@@ -142,7 +142,7 @@ TownBuildingsWidget::~TownBuildingsWidget()
 QStandardItem * TownBuildingsWidget::addBuilding(const CTown & ctown, int bId, std::set<si32> & remaining)
 {
 	BuildingID buildingId(bId);
-	const CBuilding * building = ctown.buildings.at(buildingId);
+	const auto & building = ctown.buildings.at(buildingId);
 	if(!building)
 	{
 		remaining.erase(bId);
@@ -175,7 +175,7 @@ QStandardItem * TownBuildingsWidget::addBuilding(const CTown & ctown, int bId, s
 	}
 	else
 	{
-		QStandardItem * parent = getBuildingParentFromTreeModel(building, model);
+		QStandardItem * parent = getBuildingParentFromTreeModel(building.get(), model);
 		
 		if(!parent)
 			parent = addBuilding(ctown, building->upgrade.getNum(), remaining);

+ 2 - 2
mapeditor/inspector/towneventdialog.cpp

@@ -118,7 +118,7 @@ void TownEventDialog::initBuildings()
 QStandardItem * TownEventDialog::addBuilding(const CTown& ctown, BuildingID buildingId, std::set<si32>& remaining)
 {
 	auto bId = buildingId.num;
-	const CBuilding * building = ctown.buildings.at(buildingId);
+	const auto & building = ctown.buildings.at(buildingId);
 
 	QString name = QString::fromStdString(building->getNameTranslated());
 
@@ -141,7 +141,7 @@ QStandardItem * TownEventDialog::addBuilding(const CTown& ctown, BuildingID buil
 	}
 	else
 	{
-		QStandardItem * parent = getBuildingParentFromTreeModel(building, buildingsModel);
+		QStandardItem * parent = getBuildingParentFromTreeModel(building.get(), buildingsModel);
 
 		if (!parent)
 			parent = addBuilding(ctown, building->upgrade.getNum(), remaining);

+ 82 - 66
server/CGameHandler.cpp

@@ -346,8 +346,8 @@ void CGameHandler::giveExperience(const CGHeroInstance * hero, TExpType amountTo
 	TExpType maxExp = LIBRARY->heroh->reqExp(LIBRARY->heroh->maxSupportedLevel());
 	TExpType currExp = hero->exp;
 
-	if (gs->map->levelLimit != 0)
-		maxExp = LIBRARY->heroh->reqExp(gs->map->levelLimit);
+	if (gs->getMap().levelLimit != 0)
+		maxExp = LIBRARY->heroh->reqExp(gs->getMap().levelLimit);
 
 	TExpType canGainExp = 0;
 	if (maxExp > currExp)
@@ -421,7 +421,7 @@ void CGameHandler::changeSecSkill(const CGHeroInstance * hero, SecondarySkill wh
 
 void CGameHandler::handleClientDisconnection(std::shared_ptr<CConnection> c)
 {
-	if(gameLobby().getState() == EServerState::SHUTDOWN || !gs || !gs->scenarioOps)
+	if(gameLobby().getState() == EServerState::SHUTDOWN || !gs || !gs->getStartInfo())
 	{
 		assert(0); // game should have shut down before reaching this point!
 		return;
@@ -430,7 +430,7 @@ void CGameHandler::handleClientDisconnection(std::shared_ptr<CConnection> c)
 	for(auto & playerConnections : connections)
 	{
 		PlayerColor playerId = playerConnections.first;
-		auto * playerSettings = gs->scenarioOps->getPlayersSettings(playerId.getNum());
+		auto * playerSettings = gs->getStartInfo()->getPlayersSettings(playerId.getNum());
 		if(!playerSettings)
 			continue;
 		
@@ -553,7 +553,7 @@ void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTrack
 	for (auto & elem : gs->players)
 		turnOrder->addPlayer(elem.first);
 
-	for (auto & elem : gs->map->allHeroes)
+	for (auto & elem : gs->getMap().allHeroes)
 	{
 		if(elem)
 			heroPool->getHeroSkillsRandomGenerator(elem->getHeroTypeID()); // init RMG seed
@@ -638,7 +638,7 @@ void CGameHandler::onNewTurn()
 
 	if (firstTurn)
 	{
-		for (auto obj : gs->map->objects)
+		for (auto obj : gs->getMap().objects)
 		{
 			if (obj && obj->ID == Obj::PRISON) //give imprisoned hero 0 exp to level him up. easiest to do at this point
 			{
@@ -655,7 +655,7 @@ void CGameHandler::onNewTurn()
 		addStatistics(gameState()->statistic); // write at end of turn
 	}
 
-	for (CGTownInstance *t : gs->map->towns)
+	for (CGTownInstance *t : gs->getMap().towns)
 	{
 		PlayerColor player = t->tempOwner;
 
@@ -669,7 +669,7 @@ void CGameHandler::onNewTurn()
 		}
 	}
 
-	for (CGTownInstance *t : gs->map->towns)
+	for (CGTownInstance *t : gs->getMap().towns)
 	{
 		if (t->hasBonusOfType (BonusType::DARKNESS))
 		{
@@ -696,7 +696,7 @@ void CGameHandler::onNewTurn()
 		checkVictoryLossConditionsForAll(); // check for map turn limit
 
 	//call objects
-	for (auto & elem : gs->map->objects)
+	for (auto & elem : gs->getMap().objects)
 	{
 		if (elem)
 			elem->newTurn(getRandomGenerator());
@@ -810,7 +810,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 	logGlobal->trace("Player %d (%s) wants to move hero %d from %s to %s", asker, asker.toString(), hid.getNum(), h->anchorPos().toString(), dst.toString());
 	const int3 hmpos = h->convertToVisitablePos(dst);
 
-	if (!gs->map->isInTheMap(hmpos))
+	if (!gs->getMap().isInTheMap(hmpos))
 	{
 		logGlobal->error("Destination tile is outside the map!");
 		return false;
@@ -905,7 +905,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 	// should be called if hero changes tile but before applying TryMoveHero package
 	auto leaveTile = [&]()
 	{
-		for (CGObjectInstance *obj : gs->map->getTile(h->visitablePos()).visitableObjects)
+		for (CGObjectInstance *obj : gs->getMap().getTile(h->visitablePos()).visitableObjects)
 		{
 			obj->onHeroLeave(h);
 		}
@@ -1142,7 +1142,7 @@ void CGameHandler::giveCreatures(const CArmedInstance *obj, const CGHeroInstance
 	//first we move creatures to give to make them army of object-source
 	for (auto & elem : creatures.Slots())
 	{
-		addToSlot(StackLocation(obj, obj->getSlotFor(elem.second->getCreature())), elem.second->getCreature(), elem.second->count);
+		addToSlot(StackLocation(obj->id, obj->getSlotFor(elem.second->getCreature())), elem.second->getCreature(), elem.second->count);
 	}
 
 	tryJoiningArmy(obj, h, remove, true);
@@ -1166,7 +1166,7 @@ void CGameHandler::takeCreatures(ObjectInstanceID objid, const std::vector<CStac
 				if (i->second->getType() == sbd.getType())
 				{
 					TQuantity take = std::min(sbd.count - collected, i->second->count); //collect as much cres as we can
-					changeStackCount(StackLocation(obj, i->first), -take, false);
+					changeStackCount(StackLocation(obj->id, i->first), -take, false);
 					collected += take;
 					foundSth = true;
 					break;
@@ -1879,8 +1879,8 @@ bool CGameHandler::arrangeStacks(ObjectInstanceID id1, ObjectInstanceID id2, ui8
 
 	const CCreatureSet & S1 = *s1;
 	const CCreatureSet & S2 = *s2;
-	StackLocation sl1(s1, p1);
-	StackLocation sl2(s2, p2);
+	StackLocation sl1(s1->id, p1);
+	StackLocation sl2(s2->id, p2);
 
 	if (!sl1.slot.validSlot()  ||  !sl2.slot.validSlot())
 	{
@@ -1926,12 +1926,12 @@ bool CGameHandler::arrangeStacks(ObjectInstanceID id1, ObjectInstanceID id2, ui8
 
 		if (!s1->slotEmpty(p1) && !s2->slotEmpty(p2))
 		{
-			if (notRemovable(sl1.army) || notRemovable(sl2.army))
+			if (notRemovable(s1) || notRemovable(s2))
 				return false;
 		}
-		if (s1->slotEmpty(p1) && notRemovable(sl2.army))
+		if (s1->slotEmpty(p1) && notRemovable(s2))
 			return false;
-		else if (s2->slotEmpty(p2) && notRemovable(sl1.army))
+		else if (s2->slotEmpty(p2) && notRemovable(s1))
 			return false;
 
 		swapStacks(sl1, sl2);
@@ -1947,7 +1947,7 @@ bool CGameHandler::arrangeStacks(ObjectInstanceID id1, ObjectInstanceID id2, ui8
 			complain("Cannot merge empty stack!");
 			return false;
 		}
-		else if (notRemovable(sl1.army))
+		else if (notRemovable(s1))
 			return false;
 
 		moveStack(sl1, sl2);
@@ -1982,12 +1982,12 @@ bool CGameHandler::arrangeStacks(ObjectInstanceID id1, ObjectInstanceID id2, ui8
 				return false;
 			}
 
-			if (notRemovable(sl1.army))
+			if (notRemovable(s1))
 			{
 				if (s1->getStackCount(p1) > countLeftOnSrc)
 					return false;
 			}
-			else if (notRemovable(sl2.army))
+			else if (notRemovable(s2))
 			{
 				if (s2->getStackCount(p1) < countLeftOnSrc)
 					return false;
@@ -2005,7 +2005,7 @@ bool CGameHandler::arrangeStacks(ObjectInstanceID id1, ObjectInstanceID id2, ui8
 				return false;
 			}
 
-			if (notRemovable(sl1.army))
+			if (notRemovable(s1))
 				return false;
 
 			moveStack(sl1, sl2, val);
@@ -2034,7 +2034,7 @@ bool CGameHandler::disbandCreature(ObjectInstanceID id, SlotID pos)
 		return false;
 	}
 
-	eraseStack(StackLocation(s1, pos));
+	eraseStack(StackLocation(s1->id, pos));
 	return true;
 }
 
@@ -2048,7 +2048,7 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
 	if(t->hasBuilt(requestedID))
 		COMPLAIN_RETF("Building %s is already built in %s", t->getTown()->buildings.at(requestedID)->getNameTranslated() % t->getNameTranslated());
 
-	const CBuilding * requestedBuilding = t->getTown()->buildings.at(requestedID);
+	const auto & requestedBuilding = t->getTown()->buildings.at(requestedID);
 
 	//Vector with future list of built building and buildings in auto-mode that are not yet built.
 	std::vector<const CBuilding*> remainingAutoBuildings;
@@ -2144,7 +2144,7 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
 		else
 		{
 			if(build.second->mode == CBuilding::BUILD_AUTO) //not built auto building
-				remainingAutoBuildings.push_back(build.second);
+				remainingAutoBuildings.push_back(build.second.get());
 		}
 	}
 
@@ -2154,7 +2154,7 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
 	ns.built = force ? t->built : (t->built+1);
 
 	std::queue<const CBuilding*> buildingsToAdd;
-	buildingsToAdd.push(requestedBuilding);
+	buildingsToAdd.push(requestedBuilding.get());
 
 	while(!buildingsToAdd.empty())
 	{
@@ -2403,7 +2403,7 @@ bool CGameHandler::recruitCreatures(ObjectInstanceID objid, ObjectInstanceID dst
 	}
 	else
 	{
-		addToSlot(StackLocation(army, slot), c, cram);
+		addToSlot(StackLocation(army->id, slot), c, cram);
 	}
 	return true;
 }
@@ -2437,17 +2437,19 @@ bool CGameHandler::upgradeCreature(ObjectInstanceID objid, SlotID pos, CreatureI
 	gs->statistic.accumulatedValues[player].spentResourcesForArmy += totalCost;
 
 	//upgrade creature
-	changeStackType(StackLocation(obj, pos), upgID.toCreature());
+	changeStackType(StackLocation(obj->id, pos), upgID.toCreature());
 	return true;
 }
 
 bool CGameHandler::changeStackType(const StackLocation &sl, const CCreature *c)
 {
-	if (!sl.army->hasStackAtSlot(sl.slot))
+	const CArmedInstance * obj = static_cast<const CArmedInstance *>(getObjInstance(sl.army));
+
+	if (!obj->hasStackAtSlot(sl.slot))
 		COMPLAIN_RET("Cannot find a stack to change type");
 
 	SetStackType sst;
-	sst.army = sl.army->id;
+	sst.army = obj->id;
 	sst.slot = sl.slot;
 	sst.type = c->getId();
 	sendAndApply(sst);
@@ -2460,7 +2462,7 @@ void CGameHandler::moveArmy(const CArmedInstance *src, const CArmedInstance *dst
 	while(src->stacksCount())//while there are unmoved creatures
 	{
 		auto i = src->Slots().begin(); //iterator to stack to move
-		StackLocation sl(src, i->first); //location of stack to move
+		StackLocation sl(src->id, i->first); //location of stack to move
 
 		SlotID pos = dst->getSlotFor(i->second->getCreature());
 		if (!pos.validSlot())
@@ -2469,9 +2471,9 @@ void CGameHandler::moveArmy(const CArmedInstance *src, const CArmedInstance *dst
 			std::pair<SlotID, SlotID> toMerge;
 			if (dst->mergeableStacks(toMerge, i->first) && allowMerging)
 			{
-				moveStack(StackLocation(dst, toMerge.first), StackLocation(dst, toMerge.second)); //merge toMerge.first into toMerge.second
+				moveStack(StackLocation(dst->id, toMerge.first), StackLocation(dst->id, toMerge.second)); //merge toMerge.first into toMerge.second
 				assert(!dst->hasStackAtSlot(toMerge.first)); //we have now a new free slot
-				moveStack(sl, StackLocation(dst, toMerge.first)); //move stack to freed slot
+				moveStack(sl, StackLocation(dst->id, toMerge.first)); //move stack to freed slot
 			}
 			else
 			{
@@ -2481,7 +2483,7 @@ void CGameHandler::moveArmy(const CArmedInstance *src, const CArmedInstance *dst
 		}
 		else
 		{
-			moveStack(sl, StackLocation(dst, pos));
+			moveStack(sl, StackLocation(dst->id, pos));
 		}
 	}
 }
@@ -3048,7 +3050,7 @@ bool CGameHandler::buyArtifact(const IMarket *m, const CGHeroInstance *h, GameRe
 	if(dynamic_cast<const CGTownInstance *>(m))
 	{
 		saa.id = ObjectInstanceID::NONE;
-		saa.arts = gs->map->townMerchantArtifacts;
+		saa.arts = gs->getMap().townMerchantArtifacts;
 	}
 	else if(const CGBlackMarket *bm = dynamic_cast<const CGBlackMarket *>(m)) //black market
 	{
@@ -3170,7 +3172,7 @@ bool CGameHandler::sellCreatures(ui32 count, const IMarket *market, const CGHero
 		assert(0);
 	}
 
-	changeStackCount(StackLocation(hero, slot), -(TQuantity)count);
+	changeStackCount(StackLocation(hero->id, slot), -(TQuantity)count);
 
 	giveResource(hero->tempOwner, resourceID, b2 * units);
 
@@ -3201,7 +3203,7 @@ bool CGameHandler::transformInUndead(const IMarket *market, const CGHeroInstance
 			|| (s.getCreatureID() == CreatureID::HYDRA)
 			|| (s.getCreatureID() == CreatureID::CHAOS_HYDRA))
 		resCreature = CreatureID::BONE_DRAGON;
-	changeStackType(StackLocation(army, slot), resCreature.toCreature());
+	changeStackType(StackLocation(army->id, slot), resCreature.toCreature());
 	return true;
 }
 
@@ -3467,7 +3469,7 @@ bool CGameHandler::buildBoat(ObjectInstanceID objid, PlayerColor playerID)
 	}
 
 	int3 tile = obj->bestLocation();
-	if (!gs->map->isInTheMap(tile))
+	if (!gs->getMap().isInTheMap(tile))
 	{
 		complain("Cannot find appropriate tile for a boat!");
 		return false;
@@ -3557,7 +3559,7 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
 			}
 
 			//player lost -> all his objects become unflagged (neutral)
-			for (auto obj : gs->map->objects) //unflag objs
+			for (auto obj : gs->getMap().objects) //unflag objs
 			{
 				if (obj.get() && obj->tempOwner == player)
 					setOwner(obj, PlayerColor::NEUTRAL);
@@ -3613,7 +3615,7 @@ bool CGameHandler::dig(const CGHeroInstance *h)
 	InfoWindow iw;
 	iw.type = EInfoWindowMode::AUTO;
 	iw.player = h->tempOwner;
-	if (gs->map->grailPos == h->visitablePos())
+	if (gs->getMap().grailPos == h->visitablePos())
 	{
 		ArtifactID grail = ArtifactID::GRAIL;
 
@@ -3679,7 +3681,7 @@ bool CGameHandler::sacrificeCreatures(const IMarket * market, const CGHeroInstan
 
 		int crid = hero->getStack(slot[i]).getId();
 
-		changeStackCount(StackLocation(hero, slot[i]), -(TQuantity)count[i]);
+		changeStackCount(StackLocation(hero->id, slot[i]), -(TQuantity)count[i]);
 
 		int dump;
 		int exp;
@@ -3742,14 +3744,16 @@ bool CGameHandler::sacrificeArtifact(const IMarket * market, const CGHeroInstanc
 
 bool CGameHandler::insertNewStack(const StackLocation &sl, const CCreature *c, TQuantity count)
 {
-	if (sl.army->hasStackAtSlot(sl.slot))
+	auto army = dynamic_cast<const CArmedInstance*>(getObj(sl.army));
+
+	if (army->hasStackAtSlot(sl.slot))
 		COMPLAIN_RET("Slot is already taken!");
 
 	if (!sl.slot.validSlot())
 		COMPLAIN_RET("Cannot insert stack to that slot!");
 
 	InsertNewStack ins;
-	ins.army = sl.army->id;
+	ins.army = army->id;
 	ins.slot = sl.slot;
 	ins.type = c->getId();
 	ins.count = count;
@@ -3759,18 +3763,20 @@ bool CGameHandler::insertNewStack(const StackLocation &sl, const CCreature *c, T
 
 bool CGameHandler::eraseStack(const StackLocation &sl, bool forceRemoval)
 {
-	if (!sl.army->hasStackAtSlot(sl.slot))
+	auto army = dynamic_cast<const CArmedInstance*>(getObj(sl.army));
+
+	if (!army->hasStackAtSlot(sl.slot))
 		COMPLAIN_RET("Cannot find a stack to erase");
 
-	if (sl.army->stacksCount() == 1 //from the last stack
-		&& sl.army->needsLastStack() //that must be left
+	if (army->stacksCount() == 1 //from the last stack
+		&& army->needsLastStack() //that must be left
 		&& !forceRemoval) //ignore above conditions if we are forcing removal
 	{
 		COMPLAIN_RET("Cannot erase the last stack!");
 	}
 
 	EraseStack es;
-	es.army = sl.army->id;
+	es.army = army->id;
 	es.slot = sl.slot;
 	sendAndApply(es);
 	return true;
@@ -3778,7 +3784,9 @@ bool CGameHandler::eraseStack(const StackLocation &sl, bool forceRemoval)
 
 bool CGameHandler::changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue)
 {
-	TQuantity currentCount = sl.army->getStackCount(sl.slot);
+	auto army = dynamic_cast<const CArmedInstance*>(getObj(sl.army));
+
+	TQuantity currentCount = army->getStackCount(sl.slot);
 	if ((absoluteValue && count < 0)
 		|| (!absoluteValue && -count > currentCount))
 	{
@@ -3793,7 +3801,7 @@ bool CGameHandler::changeStackCount(const StackLocation &sl, TQuantity count, bo
 	else
 	{
 		ChangeStackCount csc;
-		csc.army = sl.army->id;
+		csc.army = army->id;
 		csc.slot = sl.slot;
 		csc.count = count;
 		csc.absoluteValue = absoluteValue;
@@ -3804,7 +3812,9 @@ bool CGameHandler::changeStackCount(const StackLocation &sl, TQuantity count, bo
 
 bool CGameHandler::addToSlot(const StackLocation &sl, const CCreature *c, TQuantity count)
 {
-	const CCreature *slotC = sl.army->getCreature(sl.slot);
+	auto army = dynamic_cast<const CArmedInstance*>(getObj(sl.army));
+
+	const CCreature *slotC = army->getCreature(sl.slot);
 	if (!slotC) //slot is empty
 		insertNewStack(sl, c, count);
 	else if (c == slotC)
@@ -3833,7 +3843,7 @@ void CGameHandler::tryJoiningArmy(const CArmedInstance *src, const CArmedInstanc
 					SlotID pos = dst->getSlotFor(i->second->getCreature());
 					if (pos.validSlot())
 					{
-						moveStack(StackLocation(src, i->first), StackLocation(dst, pos));
+						moveStack(StackLocation(src->id, i->first), StackLocation(dst->id, pos));
 						cont = true;
 						break; //or iterator crashes
 					}
@@ -3851,10 +3861,13 @@ void CGameHandler::tryJoiningArmy(const CArmedInstance *src, const CArmedInstanc
 
 bool CGameHandler::moveStack(const StackLocation &src, const StackLocation &dst, TQuantity count)
 {
-	if (!src.army->hasStackAtSlot(src.slot))
+	auto srcArmy = dynamic_cast<const CArmedInstance*>(getObj(src.army));
+	auto dstArmy = dynamic_cast<const CArmedInstance*>(getObj(dst.army));
+
+	if (!srcArmy->hasStackAtSlot(src.slot))
 		COMPLAIN_RET("No stack to move!");
 
-	if (dst.army->hasStackAtSlot(dst.slot) && dst.army->getCreature(dst.slot) != src.army->getCreature(src.slot))
+	if (dstArmy->hasStackAtSlot(dst.slot) && dstArmy->getCreature(dst.slot) != srcArmy->getCreature(src.slot))
 		COMPLAIN_RET("Cannot move: stack of different type at destination pos!");
 
 	if (!dst.slot.validSlot())
@@ -3862,20 +3875,20 @@ bool CGameHandler::moveStack(const StackLocation &src, const StackLocation &dst,
 
 	if (count == -1)
 	{
-		count = src.army->getStackCount(src.slot);
+		count = srcArmy->getStackCount(src.slot);
 	}
 
-	if (src.army != dst.army  //moving away
-		&&  count == src.army->getStackCount(src.slot) //all creatures
-		&& src.army->stacksCount() == 1 //from the last stack
-		&& src.army->needsLastStack()) //that must be left
+	if (srcArmy != dstArmy  //moving away
+		&&  count == srcArmy->getStackCount(src.slot) //all creatures
+		&& srcArmy->stacksCount() == 1 //from the last stack
+		&& srcArmy->needsLastStack()) //that must be left
 	{
 		COMPLAIN_RET("Cannot move away the last creature!");
 	}
 
 	RebalanceStacks rs;
-	rs.srcArmy = src.army->id;
-	rs.dstArmy = dst.army->id;
+	rs.srcArmy = srcArmy->id;
+	rs.dstArmy = dstArmy->id;
 	rs.srcSlot = src.slot;
 	rs.dstSlot = dst.slot;
 	rs.count = count;
@@ -3898,19 +3911,22 @@ void CGameHandler::castSpell(const spells::Caster * caster, SpellID spellID, con
 
 bool CGameHandler::swapStacks(const StackLocation & sl1, const StackLocation & sl2)
 {
-	if(!sl1.army->hasStackAtSlot(sl1.slot))
+	auto army1 = dynamic_cast<const CArmedInstance*>(getObj(sl1.army));
+	auto army2 = dynamic_cast<const CArmedInstance*>(getObj(sl2.army));
+
+	if(!army1->hasStackAtSlot(sl1.slot))
 	{
 		return moveStack(sl2, sl1);
 	}
-	else if(!sl2.army->hasStackAtSlot(sl2.slot))
+	else if(!army2->hasStackAtSlot(sl2.slot))
 	{
 		return moveStack(sl1, sl2);
 	}
 	else
 	{
 		SwapStacks ss;
-		ss.srcArmy = sl1.army->id;
-		ss.dstArmy = sl2.army->id;
+		ss.srcArmy = army1->id;
+		ss.dstArmy = army2->id;
 		ss.srcSlot = sl1.slot;
 		ss.dstSlot = sl2.slot;
 		sendAndApply(ss);
@@ -4036,7 +4052,7 @@ void CGameHandler::synchronizeArtifactHandlerLists()
 
 bool CGameHandler::isValidObject(const CGObjectInstance *obj) const
 {
-	return vstd::contains(gs->map->objects, obj);
+	return vstd::contains(gs->getMap().objects, obj);
 }
 
 bool CGameHandler::isBlockedByQueries(const CPackForServer *pack, PlayerColor player)
@@ -4237,7 +4253,7 @@ CGObjectInstance * CGameHandler::createNewObject(const int3 & visitablePosition,
 	if (!gs->isInTheMap(visitablePosition))
 		throw std::runtime_error("Attempt to create object outside map at " + visitablePosition.toString());
 
-	const TerrainTile & t = gs->map->getTile(visitablePosition);
+	const TerrainTile & t = gs->getMap().getTile(visitablePosition);
 	terrainType = t.getTerrainID();
 
 	auto handler = LIBRARY->objtypeh->getHandlerFor(objectID, subID);

+ 1 - 1
server/CVCMIServer.cpp

@@ -208,7 +208,7 @@ void CVCMIServer::prepareToRestart()
 		return;
 	}
 
-	* si = * gh->gs->initialOpts;
+	* si = * gh->gs->getInitialStartInfo();
 	setState(EServerState::LOBBY);
 	if (si->campState)
 	{

+ 1 - 1
server/NetPacksLobbyServer.cpp

@@ -225,7 +225,7 @@ void ApplyOnServerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack)
 		return;
 	}
 	
-	pack.initializedStartInfo = std::make_shared<StartInfo>(*srv.gh->getStartInfo(true));
+	pack.initializedStartInfo = std::make_shared<StartInfo>(*srv.gh->getInitialStartInfo());
 	pack.initializedGameState = srv.gh->gameState();
 	result = true;
 }

+ 1 - 1
server/ServerSpellCastEnvironment.cpp

@@ -86,7 +86,7 @@ const CGameInfoCallback * ServerSpellCastEnvironment::getCb() const
 
 const CMap * ServerSpellCastEnvironment::getMap() const
 {
-	return gh->gameState()->map;
+	return &gh->gameState()->getMap();
 }
 
 bool ServerSpellCastEnvironment::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode mode)

+ 1 - 1
server/battles/BattleProcessor.cpp

@@ -160,7 +160,7 @@ BattleID BattleProcessor::setupBattle(int3 tile, BattleSideArray<const CArmedIns
 	TerrainId terrain = t.getTerrainID();
 	if (town)
 		terrain = town->getNativeTerrain();
-	else if (gameHandler->gameState()->map->isCoastalTile(tile)) //coastal tile is always ground
+	else if (gameHandler->gameState()->getMap().isCoastalTile(tile)) //coastal tile is always ground
 		terrain = ETerrainId::SAND;
 
 	BattleField battlefieldType = gameHandler->gameState()->battleGetBattlefieldType(tile, gameHandler->getRandomGenerator());

+ 4 - 4
server/battles/BattleResultProcessor.cpp

@@ -124,13 +124,13 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CBattleInfoCallback & battle,
 			if(st->getCount() == 0 || !st->alive())
 			{
 				logGlobal->debug("Stack has been destroyed.");
-				StackLocation sl(army, st->unitSlot());
+				StackLocation sl(army->id, st->unitSlot());
 				newStackCounts.push_back(TStackAndItsNewCount(sl, 0));
 			}
 			else if(st->getCount() != army->getStackCount(st->unitSlot()))
 			{
 				logGlobal->debug("Stack size changed: %d -> %d units.", army->getStackCount(st->unitSlot()), st->getCount());
-				StackLocation sl(army, st->unitSlot());
+				StackLocation sl(army->id, st->unitSlot());
 				newStackCounts.push_back(TStackAndItsNewCount(sl, st->getCount()));
 			}
 		}
@@ -158,7 +158,7 @@ void CasualtiesAfterBattle::updateArmy(CGameHandler *gh)
 		SlotID slot = army->getSlotFor(summoned_iter.first);
 		if (slot.validSlot())
 		{
-			StackLocation location(army, slot);
+			StackLocation location(army->id, slot);
 			gh->addToSlot(location, summoned_iter.first.toCreature(), summoned_iter.second);
 		}
 		else
@@ -561,7 +561,7 @@ void BattleResultProcessor::battleAfterLevelUp(const BattleID & battleID, const
 	if (necroSlot != SlotID() && !finishingBattle->isDraw())
 	{
 		finishingBattle->winnerHero->showNecromancyDialog(raisedStack, gameHandler->getRandomGenerator());
-		gameHandler->addToSlot(StackLocation(finishingBattle->winnerHero, necroSlot), raisedStack.getCreature(), raisedStack.count);
+		gameHandler->addToSlot(StackLocation(finishingBattle->winnerHero->id, necroSlot), raisedStack.getCreature(), raisedStack.count);
 	}
 
 	BattleResultsApplied resultsApplied;

+ 1 - 0
server/battles/BattleResultProcessor.h

@@ -19,6 +19,7 @@ struct SideInBattle;
 struct BattleResult;
 class CBattleInfoCallback;
 class CGHeroInstance;
+class CArmedInstance;
 VCMI_LIB_NAMESPACE_END
 
 class CBattleQuery;

+ 12 - 12
server/processors/NewTurnProcessor.cpp

@@ -41,7 +41,7 @@ NewTurnProcessor::NewTurnProcessor(CGameHandler * gameHandler)
 
 void NewTurnProcessor::handleTimeEvents(PlayerColor color)
 {
-	for (auto const & event : gameHandler->gameState()->map->events)
+	for (auto const & event : gameHandler->gameState()->getMap().events)
 	{
 		if (!event.occursToday(gameHandler->gameState()->day))
 			continue;
@@ -385,7 +385,7 @@ void NewTurnProcessor::updateNeutralTownGarrison(const CGTownInstance * t, int c
 		if (creature->getLevel() != tierToGrow)
 			continue;
 
-		StackLocation stackLocation(t, slot.first);
+		StackLocation stackLocation(t->id, slot.first);
 		gameHandler->changeStackCount(stackLocation, creature->getGrowth(), false);
 		takeFromAvailable(creature->getGrowth());
 
@@ -410,7 +410,7 @@ void NewTurnProcessor::updateNeutralTownGarrison(const CGTownInstance * t, int c
 			if (baseCreature.toEntity(LIBRARY)->getLevel() != tierToGrow)
 				continue;
 
-			StackLocation stackLocation(t, freeSlotID);
+			StackLocation stackLocation(t->id, freeSlotID);
 
 			if (upgradeUnit && !baseCreature.toCreature()->upgrades.empty())
 			{
@@ -436,7 +436,7 @@ RumorState NewTurnProcessor::pickNewRumor()
 	static const std::vector<RumorState::ERumorType> rumorTypes = {RumorState::TYPE_MAP, RumorState::TYPE_SPECIAL, RumorState::TYPE_RAND, RumorState::TYPE_RAND};
 	std::vector<RumorState::ERumorTypeSpecial> sRumorTypes = {
 															  RumorState::RUMOR_OBELISKS, RumorState::RUMOR_ARTIFACTS, RumorState::RUMOR_ARMY, RumorState::RUMOR_INCOME};
-	if(gameHandler->gameState()->map->grailPos.isValid()) // Grail should always be on map, but I had related crash I didn't manage to reproduce
+	if(gameHandler->gameState()->getMap().grailPos.isValid()) // Grail should always be on map, but I had related crash I didn't manage to reproduce
 		sRumorTypes.push_back(RumorState::RUMOR_GRAIL);
 
 	int rumorId = -1;
@@ -455,7 +455,7 @@ RumorState NewTurnProcessor::pickNewRumor()
 				rumorId = *RandomGeneratorUtil::nextItem(sRumorTypes, rand);
 				if(rumorId == RumorState::RUMOR_GRAIL)
 				{
-					rumorExtra = gameHandler->gameState()->getTile(gameHandler->gameState()->map->grailPos)->getTerrainID().getNum();
+					rumorExtra = gameHandler->gameState()->getTile(gameHandler->gameState()->getMap().grailPos)->getTerrainID().getNum();
 					break;
 				}
 
@@ -484,9 +484,9 @@ RumorState NewTurnProcessor::pickNewRumor()
 			}
 			case RumorState::TYPE_MAP:
 				// Makes sure that map rumors only used if there enough rumors too choose from
-				if(!gameHandler->gameState()->map->rumors.empty() && (gameHandler->gameState()->map->rumors.size() > 1 || !gameHandler->gameState()->currentRumor.last.count(RumorState::TYPE_MAP)))
+				if(!gameHandler->gameState()->getMap().rumors.empty() && (gameHandler->gameState()->getMap().rumors.size() > 1 || !gameHandler->gameState()->currentRumor.last.count(RumorState::TYPE_MAP)))
 				{
-					rumorId = rand.nextInt(gameHandler->gameState()->map->rumors.size() - 1);
+					rumorId = rand.nextInt(gameHandler->gameState()->getMap().rumors.size() - 1);
 					break;
 				}
 				else
@@ -507,7 +507,7 @@ RumorState NewTurnProcessor::pickNewRumor()
 
 std::tuple<EWeekType, CreatureID> NewTurnProcessor::pickWeekType(bool newMonth)
 {
-	for (const CGTownInstance *t : gameHandler->gameState()->map->towns)
+	for (const CGTownInstance *t : gameHandler->gameState()->getMap().towns)
 	{
 		if (t->hasBuilt(BuildingID::GRAIL, ETownType::INFERNO))
 			return { EWeekType::DEITYOFFIRE, CreatureID::IMP };
@@ -587,7 +587,7 @@ std::vector<SetMovePoints> NewTurnProcessor::updateHeroesMovementPoints()
 		{
 			auto ti = h->getTurnInfo(1);
 			// NOTE: this code executed when bonuses of previous day not yet updated (this happen in NewTurn::applyGs). See issue 2356
-			int32_t newMovementPoints = h->movementPointsLimitCached(gameHandler->gameState()->map->getTile(h->visitablePos()).isLand(), ti.get());
+			int32_t newMovementPoints = h->movementPointsLimitCached(gameHandler->gameState()->getMap().getTile(h->visitablePos()).isLand(), ti.get());
 
 			if (newMovementPoints != h->movementPointsRemaining())
 				result.emplace_back(h->id, newMovementPoints, true);
@@ -666,7 +666,7 @@ NewTurn NewTurnProcessor::generateNewTurnPack()
 
 	if (newWeek)
 	{
-		for (CGTownInstance *t : gameHandler->gameState()->map->towns)
+		for (CGTownInstance *t : gameHandler->gameState()->getMap().towns)
 			n.availableCreatures.push_back(generateTownGrowth(t, n.specialWeek, n.creatureid, firstTurn));
 	}
 
@@ -695,14 +695,14 @@ void NewTurnProcessor::onNewTurn()
 
 	if (newWeek)
 	{
-		for (CGTownInstance *t : gameHandler->gameState()->map->towns)
+		for (CGTownInstance *t : gameHandler->gameState()->getMap().towns)
 			if (t->hasBuilt(BuildingSubID::PORTAL_OF_SUMMONING))
 				gameHandler->setPortalDwelling(t, true, (n.specialWeek == EWeekType::PLAGUE ? true : false)); //set creatures for Portal of Summoning
 	}
 
 	if (newWeek && !firstTurn)
 	{
-		for (CGTownInstance *t : gameHandler->gameState()->map->towns)
+		for (CGTownInstance *t : gameHandler->gameState()->getMap().towns)
 		{
 			if (!t->getOwner().isValidPlayer())
 				updateNeutralTownGarrison(t, 1 + gameHandler->getDate(Date::DAY) / 7);

+ 3 - 3
server/processors/PlayerMessageProcessor.cpp

@@ -432,9 +432,9 @@ void PlayerMessageProcessor::cheatGiveArmy(PlayerColor player, const CGHeroInsta
 			if (!hero->hasStackAtSlot(SlotID(i)))
 			{
 				if (amountPerSlot.has_value())
-					gameHandler->insertNewStack(StackLocation(hero, SlotID(i)), creature, *amountPerSlot);
+					gameHandler->insertNewStack(StackLocation(hero->id, SlotID(i)), creature, *amountPerSlot);
 				else
-					gameHandler->insertNewStack(StackLocation(hero, SlotID(i)), creature, 5 * std::pow(10, i));
+					gameHandler->insertNewStack(StackLocation(hero->id, SlotID(i)), creature, 5 * std::pow(10, i));
 			}
 		}
 	}
@@ -627,7 +627,7 @@ void PlayerMessageProcessor::cheatPuzzleReveal(PlayerColor player)
 {
 	TeamState *t = gameHandler->gameState()->getPlayerTeam(player);
 
-	for(auto & obj : gameHandler->gameState()->map->objects)
+	for(auto & obj : gameHandler->gameState()->getMap().objects)
 	{
 		if(obj && obj->ID == Obj::OBELISK && !obj->wasVisited(player))
 		{

+ 1 - 1
server/processors/TurnOrderProcessor.cpp

@@ -109,7 +109,7 @@ 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()->map->objects)
+	for (auto obj : gameHandler->gameState()->getMap().objects)
 	{
 		if (obj && obj->isVisitable())
 		{

+ 2 - 1
test/mock/mock_IGameInfoCallback.h

@@ -17,7 +17,8 @@ class IGameInfoCallbackMock : public IGameInfoCallback
 public:
 	//various
 	MOCK_CONST_METHOD1(getDate, int(Date));
-	MOCK_CONST_METHOD1(getStartInfo, const StartInfo *(bool));
+	MOCK_CONST_METHOD0(getStartInfo, const StartInfo *());
+	MOCK_CONST_METHOD0(getInitialStartInfo, const StartInfo *());
 
 	MOCK_CONST_METHOD1(isAllowed, bool(SpellID));
 	MOCK_CONST_METHOD1(isAllowed, bool(ArtifactID));