Quellcode durchsuchen

Merge pull request #5601 from IvanSavenko/const_trans_ptr_remove

Remove usage of raw, owning pointers from game state
Ivan Savenko vor 5 Monaten
Ursprung
Commit
5433b07e5f
100 geänderte Dateien mit 601 neuen und 587 gelöschten Zeilen
  1. 2 0
      AI/BattleAI/AttackPossibility.cpp
  2. 1 0
      AI/BattleAI/BattleEvaluator.cpp
  3. 3 1
      AI/BattleAI/BattleExchangeVariant.cpp
  4. 30 31
      AI/Nullkiller/AIGateway.cpp
  5. 14 13
      AI/Nullkiller/AIUtility.cpp
  6. 4 4
      AI/Nullkiller/Analyzers/ArmyManager.cpp
  7. 1 1
      AI/Nullkiller/Analyzers/BuildAnalyzer.cpp
  8. 1 1
      AI/Nullkiller/Analyzers/BuildAnalyzer.h
  9. 3 2
      AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp
  10. 1 1
      AI/Nullkiller/Analyzers/HeroManager.cpp
  11. 35 35
      AI/Nullkiller/Behaviors/DefenceBehavior.cpp
  12. 7 7
      AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp
  13. 2 2
      AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp
  14. 17 17
      AI/Nullkiller/Behaviors/StartupBehavior.cpp
  15. 1 1
      AI/Nullkiller/Behaviors/StayAtTownBehavior.cpp
  16. 3 3
      AI/Nullkiller/Engine/AIMemory.cpp
  17. 1 1
      AI/Nullkiller/Engine/FuzzyEngines.cpp
  18. 4 4
      AI/Nullkiller/Engine/FuzzyHelper.cpp
  19. 4 4
      AI/Nullkiller/Engine/Nullkiller.cpp
  20. 8 8
      AI/Nullkiller/Engine/PriorityEvaluator.cpp
  21. 2 2
      AI/Nullkiller/Engine/PriorityEvaluator.h
  22. 5 5
      AI/Nullkiller/Goals/AdventureSpellCast.cpp
  23. 1 1
      AI/Nullkiller/Goals/BuildThis.cpp
  24. 1 1
      AI/Nullkiller/Goals/BuildThis.h
  25. 3 3
      AI/Nullkiller/Goals/BuyArmy.cpp
  26. 1 1
      AI/Nullkiller/Goals/CGoal.h
  27. 32 52
      AI/Nullkiller/Goals/CompleteQuest.cpp
  28. 20 20
      AI/Nullkiller/Goals/ExchangeSwapTownHeroes.cpp
  29. 1 1
      AI/Nullkiller/Goals/ExecuteHeroChain.cpp
  30. 2 2
      AI/Nullkiller/Goals/RecruitHero.cpp
  31. 1 1
      AI/Nullkiller/Goals/StayAtTown.cpp
  32. 6 6
      AI/Nullkiller/Helpers/ArmyFormation.cpp
  33. 1 1
      AI/Nullkiller/Markers/UnlockCluster.h
  34. 11 11
      AI/Nullkiller/Pathfinding/AINodeStorage.cpp
  35. 2 2
      AI/Nullkiller/Pathfinding/AINodeStorage.h
  36. 2 2
      AI/Nullkiller/Pathfinding/AIPathfinderConfig.cpp
  37. 1 1
      AI/Nullkiller/Pathfinding/AIPathfinderConfig.h
  38. 2 2
      AI/Nullkiller/Pathfinding/Actions/BuyArmyAction.cpp
  39. 9 6
      AI/Nullkiller/Pathfinding/Actions/QuestAction.cpp
  40. 4 4
      AI/Nullkiller/Pathfinding/Actors.cpp
  41. 5 4
      AI/Nullkiller/Pathfinding/GraphPaths.cpp
  42. 2 2
      AI/Nullkiller/Pathfinding/ObjectGraphCalculator.cpp
  43. 4 3
      AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp
  44. 8 3
      AI/VCAI/AIUtility.cpp
  45. 1 1
      AI/VCAI/FuzzyEngines.cpp
  46. 1 0
      AI/VCAI/FuzzyHelper.cpp
  47. 2 2
      AI/VCAI/Goals/AdventureSpellCast.cpp
  48. 1 1
      AI/VCAI/Goals/Build.cpp
  49. 1 1
      AI/VCAI/Goals/BuildThis.cpp
  50. 2 2
      AI/VCAI/Goals/BuildThis.h
  51. 4 4
      AI/VCAI/Goals/CollectRes.cpp
  52. 42 30
      AI/VCAI/Goals/CompleteQuest.cpp
  53. 0 1
      AI/VCAI/Goals/CompleteQuest.h
  54. 2 2
      AI/VCAI/Goals/GatherArmy.cpp
  55. 4 4
      AI/VCAI/Goals/GatherTroops.cpp
  56. 1 2
      AI/VCAI/Goals/Goals.h
  57. 4 4
      AI/VCAI/Goals/Win.cpp
  58. 4 4
      AI/VCAI/MapObjectsEvaluator.cpp
  59. 5 5
      AI/VCAI/Pathfinding/AINodeStorage.cpp
  60. 2 2
      AI/VCAI/Pathfinding/AIPathfinderConfig.cpp
  61. 1 1
      AI/VCAI/Pathfinding/AIPathfinderConfig.h
  62. 4 2
      AI/VCAI/Pathfinding/PathfindingManager.cpp
  63. 38 39
      AI/VCAI/VCAI.cpp
  64. 6 9
      CCallback.cpp
  65. 6 1
      CCallback.h
  66. 2 4
      Global.h
  67. 6 5
      client/ArtifactsUIController.cpp
  68. 10 22
      client/CPlayerInterface.cpp
  69. 4 4
      client/CServerHandler.cpp
  70. 1 1
      client/CServerHandler.h
  71. 30 36
      client/Client.cpp
  72. 7 3
      client/Client.h
  73. 2 2
      client/ClientCommandManager.cpp
  74. 28 21
      client/NetPacksClient.cpp
  75. 3 3
      client/PlayerLocalState.cpp
  76. 1 1
      client/adventureMap/AdventureMapInterface.cpp
  77. 2 1
      client/adventureMap/AdventureMapShortcuts.cpp
  78. 1 0
      client/adventureMap/CInfoBar.cpp
  79. 1 0
      client/adventureMap/CList.cpp
  80. 2 1
      client/adventureMap/CMinimap.cpp
  81. 3 4
      client/adventureMap/MapAudioPlayer.cpp
  82. 4 4
      client/adventureMap/TurnTimerWidget.cpp
  83. 1 0
      client/battle/BattleActionsController.cpp
  84. 4 2
      client/battle/BattleEffectsController.cpp
  85. 7 5
      client/battle/BattleInterface.cpp
  86. 4 3
      client/battle/BattleInterfaceClasses.cpp
  87. 7 5
      client/battle/BattleStacksController.cpp
  88. 9 7
      client/battle/BattleWindow.cpp
  89. 10 11
      client/lobby/CBonusSelection.cpp
  90. 1 1
      client/lobby/CSavingScreen.cpp
  91. 2 2
      client/lobby/OptionsTab.cpp
  92. 1 1
      client/lobby/RandomMapTab.cpp
  93. 6 8
      client/mainmenu/CCampaignScreen.cpp
  94. 7 7
      client/mapView/MapRenderer.cpp
  95. 10 8
      client/mapView/MapRendererContext.cpp
  96. 2 2
      client/mapView/MapRendererContextState.cpp
  97. 16 16
      client/mapView/MapViewController.cpp
  98. 2 2
      client/mapView/mapHandler.cpp
  99. 1 1
      client/render/Graphics.cpp
  100. 5 5
      client/widgets/CArtifactsOfHeroBackpack.cpp

+ 2 - 0
AI/BattleAI/AttackPossibility.cpp

@@ -17,6 +17,8 @@
 #include "../../lib/spells/ObstacleCasterProxy.h"
 #include "../../lib/battle/CObstacleInstance.h"
 
+#include "../../lib/GameLibrary.h"
+
 uint64_t averageDmg(const DamageRange & range)
 {
 	return (range.min + range.max) / 2;

+ 1 - 0
AI/BattleAI/BattleEvaluator.cpp

@@ -24,6 +24,7 @@
 #include "../../lib/battle/CObstacleInstance.h"
 #include "../../lib/battle/BattleAction.h"
 #include "../../lib/CRandomGenerator.h"
+#include "../../lib/GameLibrary.h"
 
 
 // TODO: remove

+ 3 - 1
AI/BattleAI/BattleExchangeVariant.cpp

@@ -11,7 +11,9 @@
 #include "BattleExchangeVariant.h"
 #include "BattleEvaluator.h"
 #include "../../lib/CStack.h"
-#include "tbb/parallel_for.h"
+#include "../../lib/GameLibrary.h"
+
+#include <tbb/parallel_for.h>
 
 AttackerValue::AttackerValue()
 	: value(0),

+ 30 - 31
AI/Nullkiller/AIGateway.cpp

@@ -124,7 +124,7 @@ void AIGateway::heroMoved(const TryMoveHero & details, bool verbose)
 	else if(details.result == TryMoveHero::EMBARK && hero)
 	{
 		//make sure AI not attempt to visit used boat
-		validateObject(hero->boat);
+		validateObject(hero->getBoat());
 	}
 	else if(details.result == TryMoveHero::DISEMBARK && o1)
 	{
@@ -325,7 +325,7 @@ void AIGateway::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID her
 
 void AIGateway::heroPrimarySkillChanged(const CGHeroInstance * hero, PrimarySkill which, si64 val)
 {
-	LOG_TRACE_PARAMS(logAi, "which '%i', val '%i'", static_cast<int>(which) % val);
+	LOG_TRACE_PARAMS(logAi, "which '%i', val '%i'", which.getNum() % val);
 	NET_EVENT_HANDLER;
 }
 
@@ -628,7 +628,7 @@ void AIGateway::commanderGotLevel(const CCommanderInstance * commander, std::vec
 {
 	LOG_TRACE_PARAMS(logAi, "queryID '%i'", queryID);
 	NET_EVENT_HANDLER;
-	status.addQuery(queryID, boost::str(boost::format("Commander %s of %s got level %d") % commander->name % commander->armyObj->nodeName() % (int)commander->level));
+	status.addQuery(queryID, boost::str(boost::format("Commander %s of %s got level %d") % commander->name % commander->getArmy()->nodeName() % (int)commander->level));
 	executeActionAsync("commanderGotLevel", [this, queryID](){ answerQuery(queryID, 0); });
 }
 
@@ -904,19 +904,19 @@ void AIGateway::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h
 	switch(obj->ID)
 	{
 	case Obj::TOWN:
-		if(h->visitedTown) //we are inside, not just attacking
+		if(h->getVisitedTown()) //we are inside, not just attacking
 		{
 			makePossibleUpgrades(h.get());
 
 			std::unique_lock lockGuard(nullkiller->aiStateMutex);
 
-			if(!h->visitedTown->garrisonHero || !nullkiller->isHeroLocked(h->visitedTown->garrisonHero))
-				moveCreaturesToHero(h->visitedTown);
+			if(!h->getVisitedTown()->getGarrisonHero() || !nullkiller->isHeroLocked(h->getVisitedTown()->getGarrisonHero()))
+				moveCreaturesToHero(h->getVisitedTown());
 
 			if(nullkiller->heroManager->getHeroRole(h) == HeroRole::MAIN && !h->hasSpellbook()
 				&& nullkiller->getFreeGold() >= GameConstants::SPELLBOOK_GOLD_COST)
 			{
-				if(h->visitedTown->hasBuilt(BuildingID::MAGES_GUILD_1))
+				if(h->getVisitedTown()->hasBuilt(BuildingID::MAGES_GUILD_1))
 					cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK);
 			}
 		}
@@ -929,9 +929,9 @@ void AIGateway::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h
 
 void AIGateway::moveCreaturesToHero(const CGTownInstance * t)
 {
-	if(t->visitingHero && t->armedGarrison() && t->visitingHero->tempOwner == t->tempOwner)
+	if(t->getVisitingHero() && t->armedGarrison() && t->getVisitingHero()->tempOwner == t->tempOwner)
 	{
-		pickBestCreatures(t->visitingHero, t->getUpperArmy());
+		pickBestCreatures(t->getVisitingHero(), t->getUpperArmy());
 	}
 }
 
@@ -1039,7 +1039,7 @@ void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance
 	auto equipBest = [](const CGHeroInstance * h, const CGHeroInstance * otherh, bool giveStuffToFirstHero) -> void
 	{
 		bool changeMade = false;
-		std::set<std::pair<CArtifactInstance *, CArtifactInstance *> > swappedSet;
+		std::set<std::pair<ArtifactInstanceID, ArtifactInstanceID> > swappedSet;
 		do
 		{
 			changeMade = false;
@@ -1050,22 +1050,22 @@ void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance
 			{
 				for(auto p : h->artifactsWorn)
 				{
-					if(p.second.artifact)
+					if(p.second.getArt())
 						allArtifacts.push_back(ArtifactLocation(h->id, p.first));
 				}
 			}
 			for(auto slot : h->artifactsInBackpack)
-				allArtifacts.push_back(ArtifactLocation(h->id, h->getArtPos(slot.artifact)));
+				allArtifacts.push_back(ArtifactLocation(h->id, h->getArtPos(slot.getArt())));
 
 			if(otherh)
 			{
 				for(auto p : otherh->artifactsWorn)
 				{
-					if(p.second.artifact)
+					if(p.second.getArt())
 						allArtifacts.push_back(ArtifactLocation(otherh->id, p.first));
 				}
 				for(auto slot : otherh->artifactsInBackpack)
-					allArtifacts.push_back(ArtifactLocation(otherh->id, otherh->getArtPos(slot.artifact)));
+					allArtifacts.push_back(ArtifactLocation(otherh->id, otherh->getArtPos(slot.getArt())));
 			}
 			//we give stuff to one hero or another, depending on giveStuffToFirstHero
 
@@ -1087,7 +1087,7 @@ void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance
 				auto s = artHolder->getSlot(location.slot);
 				if(!s || s->locked) //we can't move locks
 					continue;
-				auto artifact = s->artifact;
+				auto artifact = s->getArt();
 				if(!artifact)
 					continue;
 				//FIXME: why are the above possible to be null?
@@ -1111,30 +1111,30 @@ void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance
 					for(auto slot : artifact->getType()->getPossibleSlots().at(target->bearerType()))
 					{
 						auto otherSlot = target->getSlot(slot);
-						if(otherSlot && otherSlot->artifact) //we need to exchange artifact for better one
+						if(otherSlot && otherSlot->getArt()) //we need to exchange artifact for better one
 						{
-							int64_t otherArtifactScore = getArtifactScoreForHero(target, otherSlot->artifact);
-							logAi->trace( "Comparing artifacts of %s: %s vs %s. Score: %d vs %d", target->getHeroTypeName(), artifact->getType()->getJsonKey(), otherSlot->artifact->getType()->getJsonKey(), artifactScore, otherArtifactScore);
+							int64_t otherArtifactScore = getArtifactScoreForHero(target, otherSlot->getArt());
+							logAi->trace( "Comparing artifacts of %s: %s vs %s. Score: %d vs %d", target->getHeroTypeName(), artifact->getType()->getJsonKey(), otherSlot->getArt()->getType()->getJsonKey(), artifactScore, otherArtifactScore);
 
 							//if that artifact is better than what we have, pick it
 							//combined artifacts are not always allowed to move
 							if(artifactScore > otherArtifactScore && artifact->canBePutAt(target, slot, true))
 							{
-								auto swapPair = std::minmax(artifact, otherSlot->artifact);
+								std::pair swapPair = std::minmax<ArtifactInstanceID>({artifact->getId(), otherSlot->artifactID});
 								if (swappedSet.find(swapPair) != swappedSet.end())
 								{
 									logAi->warn(
 										"Artifacts % s < -> % s have already swapped before, ignored.",
 										artifact->getType()->getJsonKey(),
-										otherSlot->artifact->getType()->getJsonKey());
+										otherSlot->getArt()->getType()->getJsonKey());
 									continue;
 								}
 								logAi->trace(
 									"Exchange artifacts %s <-> %s",
 									artifact->getType()->getJsonKey(),
-									otherSlot->artifact->getType()->getJsonKey());
+									otherSlot->getArt()->getType()->getJsonKey());
 
-								if(!otherSlot->artifact->canBePutAt(artHolder, location.slot, true))
+								if(!otherSlot->getArt()->canBePutAt(artHolder, location.slot, true))
 								{
 									ArtifactLocation destLocation(target->id, slot);
 									ArtifactLocation backpack(artHolder->id, ArtifactPosition::BACKPACK_START);
@@ -1144,7 +1144,7 @@ void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance
 								}
 								else
 								{
-									cb->swapArtifacts(location, ArtifactLocation(target->id, target->getArtPos(otherSlot->artifact)));
+									cb->swapArtifacts(location, ArtifactLocation(target->id, target->getArtPos(otherSlot->getArt())));
 								}
 
 								changeMade = true;
@@ -1180,7 +1180,7 @@ void AIGateway::recruitCreatures(const CGDwelling * d, const CArmedInstance * re
 
 		if(!recruiter->getSlotFor(creID).validSlot())
 		{
-			for(auto stack : recruiter->Slots())
+			for(const auto & stack : recruiter->Slots())
 			{
 				if(!stack.second->getType())
 					continue;
@@ -1282,10 +1282,10 @@ void AIGateway::addVisitableObj(const CGObjectInstance * obj)
 
 bool AIGateway::moveHeroToTile(int3 dst, HeroPtr h)
 {
-	if(h->inTownGarrison && h->visitedTown)
+	if(h->isGarrisoned() && h->getVisitedTown())
 	{
-		cb->swapGarrisonHero(h->visitedTown);
-		moveCreaturesToHero(h->visitedTown);
+		cb->swapGarrisonHero(h->getVisitedTown());
+		moveCreaturesToHero(h->getVisitedTown());
 	}
 
 	//TODO: consider if blockVisit objects change something in our checks: AIUtility::isBlockVisitObj()
@@ -1332,8 +1332,7 @@ bool AIGateway::moveHeroToTile(int3 dst, HeroPtr h)
 		{
 			auto tile = cb->getTile(coord, false);
 			assert(tile);
-			return tile->topVisitableObj(ignoreHero);
-			//return cb->getTile(coord,false)->topVisitableObj(ignoreHero);
+			return cb->getObj(tile->topVisitableObj(ignoreHero), false);
 		};
 
 		auto isTeleportAction = [&](EPathNodeAction action) -> bool
@@ -1543,7 +1542,7 @@ void AIGateway::tryRealize(Goals::Trade & g) //trade
 
 				int toGive;
 				int toGet;
-				m->getOffer(res, g.resID, toGive, toGet, EMarketMode::RESOURCE_RESOURCE);
+				m->getOffer(res.getNum(), g.resID, toGive, toGet, EMarketMode::RESOURCE_RESOURCE);
 				toGive = static_cast<int>(toGive * (it->resVal / toGive)); //round down
 				//TODO trade only as much as needed
 				if (toGive) //don't try to sell 0 resources
@@ -1596,7 +1595,7 @@ void AIGateway::endTurn()
 
 void AIGateway::buildArmyIn(const CGTownInstance * t)
 {
-	makePossibleUpgrades(t->visitingHero);
+	makePossibleUpgrades(t->getVisitingHero());
 	makePossibleUpgrades(t);
 	recruitCreatures(t, t->getUpperArmy());
 	moveCreaturesToHero(t);

+ 14 - 13
AI/Nullkiller/AIUtility.cpp

@@ -15,6 +15,7 @@
 #include "../../lib/UnlockGuard.h"
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/mapObjects/MapObjects.h"
+#include "../../lib/mapObjects/CQuest.h"
 #include "../../lib/mapping/CMapDefines.h"
 #include "../../lib/gameState/QuestInfo.h"
 #include "../../lib/IGameSettings.h"
@@ -201,7 +202,7 @@ bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater)
 	}
 	else if(!fromWater) // do not try to board when in water sector
 	{
-		if(t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT)
+		if(t->visitableObjects.size() == 1 && cb->getObjInstance(t->topVisitableObj())->ID == Obj::BOAT)
 			return true;
 	}
 	return false;
@@ -374,10 +375,10 @@ double getArtifactBonusRelevance(const CGHeroInstance * hero, const std::shared_
 	switch (bonus->type)
 	{
 		case BonusType::MOVEMENT:
-			if (hero->boat && bonus->subtype == BonusCustomSubtype::heroMovementSea)
+			if (hero->getBoat() && bonus->subtype == BonusCustomSubtype::heroMovementSea)
 				return veryRelevant;
 
-			if (!hero->boat && bonus->subtype == BonusCustomSubtype::heroMovementLand)
+			if (!hero->getBoat() && bonus->subtype == BonusCustomSubtype::heroMovementLand)
 				return relevant;
 			return notRelevant;
 		case BonusType::STACKS_SPEED:
@@ -394,9 +395,9 @@ double getArtifactBonusRelevance(const CGHeroInstance * hero, const std::shared_
 				return relevant; // spellpower / knowledge - always relevant
 		case BonusType::WATER_WALKING:
 		case BonusType::FLYING_MOVEMENT:
-			return hero->boat ? notRelevant : relevant; // boat can't fly
+			return hero->getBoat() ? notRelevant : relevant; // boat can't fly
 		case BonusType::WHIRLPOOL_PROTECTION:
-			return hero->boat ? relevant : notRelevant;
+			return hero->getBoat() ? relevant : notRelevant;
 		case BonusType::UNDEAD_RAISE_PERCENTAGE:
 			return hero->hasBonusOfType(BonusType::IMPROVED_NECROMANCY) ? veryRelevant : notRelevant;
 		case BonusType::SPELL_DAMAGE:
@@ -632,7 +633,7 @@ int getDuplicatingSlots(const CArmedInstance * army)
 {
 	int duplicatingSlots = 0;
 
-	for(auto stack : army->Slots())
+	for(const auto & stack : army->Slots())
 	{
 		if(stack.second->getCreature() && army->getSlotFor(stack.second->getCreature()) != stack.first)
 			duplicatingSlots++;
@@ -656,7 +657,7 @@ bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObject
 	{
 		for(auto q : ai->cb->getMyQuests())
 		{
-			if(q.obj == obj)
+			if(q.obj == obj->id)
 			{
 				return false; // do not visit guards or gates when wandering
 			}
@@ -669,9 +670,9 @@ bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObject
 	{
 		for(auto q : ai->cb->getMyQuests())
 		{
-			if(q.obj == obj)
+			if(q.obj == obj->id)
 			{
-				if(q.quest->checkQuest(h))
+				if(q.getQuest(ai->cb.get())->checkQuest(h))
 					return true; //we completed the quest
 				else
 					return false; //we can't complete this quest
@@ -707,7 +708,7 @@ bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObject
 	}
 	case Obj::HILL_FORT:
 	{
-		for(auto slot : h->Slots())
+		for(const auto & slot : h->Slots())
 		{
 			if(slot.second->getType()->hasUpgrades())
 				return true; //TODO: check price?
@@ -767,7 +768,7 @@ bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObject
 bool townHasFreeTavern(const CGTownInstance * town)
 {
 	if(!town->hasBuilt(BuildingID::TAVERN)) return false;
-	if(!town->visitingHero) return true;
+	if(!town->getVisitingHero()) return true;
 
 	bool canMoveVisitingHeroToGarrison = !town->getUpperArmy()->stacksCount();
 
@@ -778,9 +779,9 @@ uint64_t getHeroArmyStrengthWithCommander(const CGHeroInstance * hero, const CCr
 {
 	auto armyStrength = heroArmy->getArmyStrength(fortLevel);
 
-	if(hero && hero->commander && hero->commander->alive)
+	if(hero && hero->getCommander() && hero->getCommander()->alive)
 	{
-		armyStrength += 100 * hero->commander->level;
+		armyStrength += 100 * hero->getCommander()->level;
 	}
 
 	return armyStrength;

+ 4 - 4
AI/Nullkiller/Analyzers/ArmyManager.cpp

@@ -489,7 +489,7 @@ void ArmyManager::update()
 
 	for(auto army : total)
 	{
-		for(auto slot : army->Slots())
+		for(const auto & slot : army->Slots())
 		{
 			totalArmy[slot.second->getCreatureID()].count += slot.second->count;
 		}
@@ -506,7 +506,7 @@ std::vector<SlotInfo> ArmyManager::convertToSlots(const CCreatureSet * army) con
 {
 	std::vector<SlotInfo> result;
 
-	for(auto slot : army->Slots())
+	for(const auto & slot : army->Slots())
 	{
 		SlotInfo slotInfo;
 
@@ -524,7 +524,7 @@ std::vector<StackUpgradeInfo> ArmyManager::getHillFortUpgrades(const CCreatureSe
 {
 	std::vector<StackUpgradeInfo> upgrades;
 
-	for(auto creature : army->Slots())
+	for(const auto & creature : army->Slots())
 	{
 		CreatureID initial = creature.second->getCreatureID();
 		auto possibleUpgrades = initial.toCreature()->upgrades;
@@ -552,7 +552,7 @@ std::vector<StackUpgradeInfo> ArmyManager::getDwellingUpgrades(const CCreatureSe
 {
 	std::vector<StackUpgradeInfo> upgrades;
 
-	for(auto creature : army->Slots())
+	for(const auto & creature : army->Slots())
 	{
 		CreatureID initial = creature.second->getCreatureID();
 		auto possibleUpgrades = initial.toCreature()->upgrades;

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

@@ -351,7 +351,7 @@ void BuildAnalyzer::updateDailyIncome()
 	}
 }
 
-bool BuildAnalyzer::hasAnyBuilding(int32_t alignment, BuildingID bid) const
+bool BuildAnalyzer::hasAnyBuilding(FactionID alignment, BuildingID bid) const
 {
 	for(auto tdi : developmentInfos)
 	{

+ 1 - 1
AI/Nullkiller/Analyzers/BuildAnalyzer.h

@@ -97,7 +97,7 @@ public:
 	TResources getDailyIncome() const { return dailyIncome; }
 	float getGoldPressure() const { return goldPressure; }
 	bool isGoldPressureHigh() const;
-	bool hasAnyBuilding(int32_t alignment, BuildingID bid) const;
+	bool hasAnyBuilding(FactionID alignment, BuildingID bid) const;
 
 private:
 	BuildingInfo getBuildingOrPrerequisite(

+ 3 - 2
AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp

@@ -93,8 +93,8 @@ void DangerHitMapAnalyzer::updateHitMap()
 		{
 			auto town = dynamic_cast<const CGTownInstance *>(obj);
 
-			if(town->garrisonHero)
-				heroes[town->garrisonHero->tempOwner][town->garrisonHero] = HeroRole::MAIN;
+			if(town->getGarrisonHero())
+				heroes[town->getGarrisonHero()->tempOwner][town->getGarrisonHero()] = HeroRole::MAIN;
 		}
 	}
 
@@ -214,6 +214,7 @@ void DangerHitMapAnalyzer::calculateTileOwners()
 			CRandomGenerator rng;
 			auto visitablePos = town->visitablePos();
 			
+			townHero->id = town->id;
 			townHero->setOwner(ai->playerID); // lets avoid having multiple colors
 			townHero->initHero(rng, static_cast<HeroTypeID>(0));
 			townHero->pos = townHero->convertFromVisitablePos(visitablePos);

+ 1 - 1
AI/Nullkiller/Analyzers/HeroManager.cpp

@@ -293,7 +293,7 @@ const CGHeroInstance * HeroManager::findWeakHeroToDismiss(uint64_t armyLimit, co
 			|| existingHero->getArmyStrength() >armyLimit
 			|| getHeroRole(existingHero) == HeroRole::MAIN
 			|| existingHero->movementPointsRemaining()
-			|| (townToSpare != nullptr && existingHero->visitedTown == townToSpare)
+			|| (townToSpare != nullptr && existingHero->getVisitedTown() == townToSpare)
 			|| existingHero->artifactsWorn.size() > (existingHero->hasSpellbook() ? 2 : 1))
 		{
 			continue;

+ 35 - 35
AI/Nullkiller/Behaviors/DefenceBehavior.cpp

@@ -109,30 +109,30 @@ void handleCounterAttack(
 
 bool handleGarrisonHeroFromPreviousTurn(const CGTownInstance * town, Goals::TGoalVec & tasks, const Nullkiller * ai)
 {
-	if(ai->isHeroLocked(town->garrisonHero.get()))
+	if(ai->isHeroLocked(town->getGarrisonHero()))
 	{
 		logAi->trace(
 			"Hero %s in garrison of town %s is supposed to defend the town",
-			town->garrisonHero->getNameTranslated(),
+			town->getGarrisonHero()->getNameTranslated(),
 			town->getNameTranslated());
 
 		return true;
 	}
 
-	if(!town->visitingHero)
+	if(!town->getVisitingHero())
 	{
 		if(ai->cb->getHeroCount(ai->playerID, false) < GameConstants::MAX_HEROES_PER_PLAYER)
 		{
 			logAi->trace(
 				"Extracting hero %s from garrison of town %s",
-				town->garrisonHero->getNameTranslated(),
+				town->getGarrisonHero()->getNameTranslated(),
 				town->getNameTranslated());
 
 			tasks.push_back(Goals::sptr(Goals::ExchangeSwapTownHeroes(town, nullptr).setpriority(5)));
 
 			return false;
 		}
-		else if(ai->heroManager->getHeroRole(town->garrisonHero.get()) == HeroRole::MAIN)
+		else if(ai->heroManager->getHeroRole(town->getGarrisonHero()) == HeroRole::MAIN)
 		{
 			auto armyDismissLimit = 1000;
 			auto heroToDismiss = ai->heroManager->findWeakHeroToDismiss(armyDismissLimit);
@@ -160,7 +160,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 	
 	threats.push_back(threatNode.fastestDanger); // no guarantee that fastest danger will be there
 
-	if (town->garrisonHero && handleGarrisonHeroFromPreviousTurn(town, tasks, ai))
+	if (town->getGarrisonHero() && handleGarrisonHeroFromPreviousTurn(town, tasks, ai))
 	{
 		return;
 	}
@@ -233,16 +233,16 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 				path.toString());
 #endif
 
-			auto townDefenseStrength = town->garrisonHero
-				? town->garrisonHero->getTotalStrength()
-				: (town->visitingHero ? town->visitingHero->getTotalStrength() : town->getUpperArmy()->getArmyStrength());
+			auto townDefenseStrength = town->getGarrisonHero()
+				? town->getGarrisonHero()->getTotalStrength()
+				: (town->getVisitingHero() ? town->getVisitingHero()->getTotalStrength() : town->getUpperArmy()->getArmyStrength());
 
-			if(town->visitingHero && path.targetHero == town->visitingHero.get())
+			if(town->getVisitingHero() && path.targetHero == town->getVisitingHero())
 			{
 				if(path.getHeroStrength() < townDefenseStrength)
 					continue;
 			}
-			else if(town->garrisonHero && path.targetHero == town->garrisonHero.get())
+			else if(town->getGarrisonHero() && path.targetHero == town->getGarrisonHero())
 			{
 				if(path.getHeroStrength() < townDefenseStrength)
 					continue;
@@ -271,7 +271,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 				continue;
 			}
 
-			if(path.targetHero == town->visitingHero.get() && path.exchangeCount == 1)
+			if(path.targetHero == town->getVisitingHero() && path.exchangeCount == 1)
 			{
 #if NKAI_TRACE_LEVEL >= 1
 				logAi->trace("Put %s to garrison of town %s",
@@ -280,7 +280,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 #endif
 
 				// dismiss creatures we are not able to pick to be able to hide in garrison
-				if(town->garrisonHero
+				if(town->getGarrisonHero()
 					|| town->getUpperArmy()->stacksCount() == 0
 					|| path.targetHero->canBeMergedWith(*town)
 					|| (town->getUpperArmy()->getArmyStrength() < 500 && town->fortLevel() >= CGTownInstance::CITADEL))
@@ -288,25 +288,25 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 					tasks.push_back(
 						Goals::sptr(Composition()
 							.addNext(DefendTown(town, threat, path.targetHero))
-							.addNext(ExchangeSwapTownHeroes(town, town->visitingHero.get(), HeroLockedReason::DEFENCE))));
+							.addNext(ExchangeSwapTownHeroes(town, town->getVisitingHero(), HeroLockedReason::DEFENCE))));
 				}
 
 				continue;
 			}
 
 			// main without army and visiting scout with army, very specific case
-			if(town->visitingHero && town->getUpperArmy()->stacksCount() == 0
-				&& path.targetHero != town->visitingHero.get() && path.exchangeCount == 1 && path.turn() == 0
-				&& ai->heroManager->evaluateHero(path.targetHero) > ai->heroManager->evaluateHero(town->visitingHero.get())
-				&& 10 * path.targetHero->getTotalStrength() < town->visitingHero->getTotalStrength())
+			if(town->getVisitingHero() && town->getUpperArmy()->stacksCount() == 0
+				&& path.targetHero != town->getVisitingHero() && path.exchangeCount == 1 && path.turn() == 0
+				&& ai->heroManager->evaluateHero(path.targetHero) > ai->heroManager->evaluateHero(town->getVisitingHero())
+				&& 10 * path.targetHero->getTotalStrength() < town->getVisitingHero()->getTotalStrength())
 			{
-				path.heroArmy = town->visitingHero.get();
+				path.heroArmy = town->getVisitingHero();
 
 				tasks.push_back(
 					Goals::sptr(Composition()
 						.addNext(DefendTown(town, threat, path))
 						.addNextSequence({
-								sptr(ExchangeSwapTownHeroes(town, town->visitingHero.get())),
+								sptr(ExchangeSwapTownHeroes(town, town->getVisitingHero())),
 								sptr(ExecuteHeroChain(path, town)),
 								sptr(ExchangeSwapTownHeroes(town, path.targetHero, HeroLockedReason::DEFENCE))
 							})));
@@ -350,22 +350,22 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 			composition.addNext(DefendTown(town, threat, path));
 			TGoalVec sequence;
 
-			if(town->garrisonHero && path.targetHero == town->garrisonHero.get() && path.exchangeCount == 1)
+			if(town->getGarrisonHero() && path.targetHero == town->getGarrisonHero() && path.exchangeCount == 1)
 			{
-				composition.addNext(ExchangeSwapTownHeroes(town, town->garrisonHero.get(), HeroLockedReason::DEFENCE));
+				composition.addNext(ExchangeSwapTownHeroes(town, town->getGarrisonHero(), HeroLockedReason::DEFENCE));
 				tasks.push_back(Goals::sptr(composition));
 
 #if NKAI_TRACE_LEVEL >= 1
 				logAi->trace("Locking hero %s in garrison of %s",
-					town->garrisonHero.get()->getObjectName(),
+					town->getGarrisonHero()->getObjectName(),
 					town->getObjectName());
 #endif
 
 				continue;
 			}
-			else if(town->visitingHero && path.targetHero != town->visitingHero && !path.containsHero(town->visitingHero))
+			else if(town->getVisitingHero() && path.targetHero != town->getVisitingHero() && !path.containsHero(town->getVisitingHero()))
 			{
-				if(town->garrisonHero && town->garrisonHero != path.targetHero)
+				if(town->getGarrisonHero() && town->getGarrisonHero() != path.targetHero)
 				{
 #if NKAI_TRACE_LEVEL >= 1
 					logAi->trace("Cancel moving %s to defend town %s as the town has garrison hero",
@@ -376,7 +376,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 				}
 				else if(path.turn() == 0)
 				{
-					sequence.push_back(sptr(ExchangeSwapTownHeroes(town, town->visitingHero.get())));
+					sequence.push_back(sptr(ExchangeSwapTownHeroes(town, town->getVisitingHero())));
 				}
 			}
 
@@ -425,7 +425,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 
 void DefenceBehavior::evaluateRecruitingHero(Goals::TGoalVec & tasks, const HitMapInfo & threat, const CGTownInstance * town, const Nullkiller * ai) const
 {
-	if (threat.turn > 0 || town->garrisonHero || town->visitingHero)
+	if (threat.turn > 0 || town->getGarrisonHero() || town->getVisitingHero())
 		return;
 	
 	if(town->hasBuilt(BuildingID::TAVERN)
@@ -461,25 +461,25 @@ void DefenceBehavior::evaluateRecruitingHero(Goals::TGoalVec & tasks, const HitM
 			bool needSwap = false;
 			const CGHeroInstance * heroToDismiss = nullptr;
 
-			if(town->visitingHero)
+			if(town->getVisitingHero())
 			{
-				if(!town->garrisonHero)
+				if(!town->getGarrisonHero())
 					needSwap = true;
 				else
 				{
-					if(town->visitingHero->getArmyStrength() < town->garrisonHero->getArmyStrength())
+					if(town->getVisitingHero()->getArmyStrength() < town->getGarrisonHero()->getArmyStrength())
 					{
-						if(town->visitingHero->getArmyStrength() >= hero->getArmyStrength())
+						if(town->getVisitingHero()->getArmyStrength() >= hero->getArmyStrength())
 							continue;
 
-						heroToDismiss = town->visitingHero.get();
+						heroToDismiss = town->getVisitingHero();
 					}
-					else if(town->garrisonHero->getArmyStrength() >= hero->getArmyStrength())
+					else if(town->getGarrisonHero()->getArmyStrength() >= hero->getArmyStrength())
 						continue;
 					else
 					{
 						needSwap = true;
-						heroToDismiss = town->garrisonHero.get();
+						heroToDismiss = town->getGarrisonHero();
 					}
 				}
 
@@ -499,7 +499,7 @@ void DefenceBehavior::evaluateRecruitingHero(Goals::TGoalVec & tasks, const HitM
 			Goals::Composition recruitHeroComposition;
 
 			if(needSwap)
-				sequence.push_back(sptr(ExchangeSwapTownHeroes(town, town->visitingHero.get())));
+				sequence.push_back(sptr(ExchangeSwapTownHeroes(town, town->getVisitingHero())));
 
 			if(heroToDismiss)
 				sequence.push_back(sptr(DismissHero(heroToDismiss)));

+ 7 - 7
AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp

@@ -167,21 +167,21 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const Nullkiller * ai, con
 
 			composition.addNext(heroExchange);
 
-			if(hero->inTownGarrison && path.turn() == 0)
+			if(hero->isGarrisoned() && path.turn() == 0)
 			{
 				auto lockReason = ai->getHeroLockedReason(hero);
 
-				if(path.targetHero->visitedTown == hero->visitedTown)
+				if(path.targetHero->getVisitedTown() == hero->getVisitedTown())
 				{
 					composition.addNextSequence({
-						sptr(ExchangeSwapTownHeroes(hero->visitedTown, hero, lockReason))});
+						sptr(ExchangeSwapTownHeroes(hero->getVisitedTown(), hero, lockReason))});
 				}
 				else
 				{
 					composition.addNextSequence({
-						sptr(ExchangeSwapTownHeroes(hero->visitedTown)),
+						sptr(ExchangeSwapTownHeroes(hero->getVisitedTown())),
 						sptr(exchangePath),
-						sptr(ExchangeSwapTownHeroes(hero->visitedTown, hero, lockReason))});
+						sptr(ExchangeSwapTownHeroes(hero->getVisitedTown(), hero, lockReason))});
 				}
 			}
 			else
@@ -262,7 +262,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const Nullkiller * ai, const CGT
 			continue;
 		}
 
-		if(upgrader->visitingHero && (upgrader->visitingHero.get() != path.targetHero || path.exchangeCount == 1))
+		if(upgrader->getVisitingHero() && (upgrader->getVisitingHero() != path.targetHero || path.exchangeCount == 1))
 		{
 #if NKAI_TRACE_LEVEL >= 2
 			logAi->trace("Ignore path. Town has visiting hero.");
@@ -289,7 +289,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const Nullkiller * ai, const CGT
 
 		auto upgrade = ai->armyManager->calculateCreaturesUpgrade(path.heroArmy, upgrader, availableResources);
 
-		if(!upgrader->garrisonHero
+		if(!upgrader->getGarrisonHero()
 			&& (
 				hasMainAround
 				|| ai->heroManager->getHeroRole(path.targetHero) == HeroRole::MAIN))

+ 2 - 2
AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp

@@ -68,7 +68,7 @@ Goals::TGoalVec RecruitHeroBehavior::decompose(const Nullkiller * ai) const
 			closestThreat = std::min(closestThreat, threat.turn);
 		}
 		//Don't hire a hero where there already is one present
-		if (town->visitingHero && town->garrisonHero)
+		if (town->getVisitingHero() && town->getGarrisonHero())
 			continue;
 		float visitability = 0;
 		for (auto checkHero : ourHeroes)
@@ -98,7 +98,7 @@ Goals::TGoalVec RecruitHeroBehavior::decompose(const Nullkiller * ai) const
 
 			for(auto hero : availableHeroes)
 			{
-				if ((town->visitingHero || town->garrisonHero) 
+				if ((town->getVisitingHero() || town->getGarrisonHero()) 
 					&& closestThreat < 1
 					&& hero->getArmyCost() < GameConstants::HERO_GOLD_COST / 3.0)
 					continue;

+ 17 - 17
AI/Nullkiller/Behaviors/StartupBehavior.cpp

@@ -32,7 +32,7 @@ const AIPath getShortestPath(const CGTownInstance * town, const std::vector<AIPa
 {
 	auto shortestPath = *vstd::minElementByFun(paths, [town](const AIPath & path) -> float
 	{
-		if(town->garrisonHero && path.targetHero == town->garrisonHero.get())
+		if(town->getGarrisonHero() && path.targetHero == town->getGarrisonHero())
 			return 1;
 
 		return path.movementCost();
@@ -53,7 +53,7 @@ const CGHeroInstance * getNearestHero(const Nullkiller * ai, const CGTownInstanc
 	if(shortestPath.nodes.size() > 1
 		|| shortestPath.turn() != 0
 		|| shortestPath.targetHero->visitablePos().dist2dSQ(town->visitablePos()) > 4
-		|| (town->garrisonHero && shortestPath.targetHero == town->garrisonHero.get()))
+		|| (town->getGarrisonHero() && shortestPath.targetHero == town->getGarrisonHero()))
 		return nullptr;
 
 	return shortestPath.targetHero;
@@ -64,7 +64,7 @@ bool needToRecruitHero(const Nullkiller * ai, const CGTownInstance * startupTown
 	if(!ai->heroManager->canRecruitHero(startupTown))
 		return false;
 
-	if(!startupTown->garrisonHero && !startupTown->visitingHero)
+	if(!startupTown->getGarrisonHero() && !startupTown->getVisitingHero())
 		return true;
 
 	int treasureSourcesCount = 0;
@@ -122,8 +122,8 @@ Goals::TGoalVec StartupBehavior::decompose(const Nullkiller * ai) const
 	{
 		startupTown = *vstd::maxElementByFun(towns, [ai](const CGTownInstance * town) -> float
 		{
-			if(town->garrisonHero)
-				return ai->heroManager->evaluateHero(town->garrisonHero.get());
+			if(town->getGarrisonHero())
+				return ai->heroManager->evaluateHero(town->getGarrisonHero());
 
 			auto closestHero = getNearestHero(ai, town);
 
@@ -147,7 +147,7 @@ Goals::TGoalVec StartupBehavior::decompose(const Nullkiller * ai) const
 
 	if(closestHero)
 	{
-		if(!startupTown->visitingHero)
+		if(!startupTown->getVisitingHero())
 		{
 			if(ai->armyManager->howManyReinforcementsCanGet(startupTown->getUpperArmy(), startupTown->getUpperArmy(), closestHero, TerrainId::NONE) > 200)
 			{
@@ -163,12 +163,12 @@ Goals::TGoalVec StartupBehavior::decompose(const Nullkiller * ai) const
 		}
 		else
 		{
-			auto visitingHero = startupTown->visitingHero.get();
+			auto visitingHero = startupTown->getVisitingHero();
 			auto visitingHeroScore = ai->heroManager->evaluateHero(visitingHero);
 				
-			if(startupTown->garrisonHero)
+			if(startupTown->getGarrisonHero())
 			{
-				auto garrisonHero = startupTown->garrisonHero.get();
+				auto garrisonHero = startupTown->getGarrisonHero();
 				auto garrisonHeroScore = ai->heroManager->evaluateHero(garrisonHero);
 
 				if(visitingHeroScore > garrisonHeroScore
@@ -187,7 +187,7 @@ Goals::TGoalVec StartupBehavior::decompose(const Nullkiller * ai) const
 			else if(canRecruitHero)
 			{
 				auto canPickTownArmy = startupTown->stacksCount() == 0
-					|| ai->armyManager->howManyReinforcementsCanGet(startupTown->visitingHero, startupTown) > 0;
+					|| ai->armyManager->howManyReinforcementsCanGet(startupTown->getVisitingHero(), startupTown) > 0;
 
 				if(canPickTownArmy)
 				{
@@ -197,16 +197,16 @@ Goals::TGoalVec StartupBehavior::decompose(const Nullkiller * ai) const
 		}
 	}
 
-	if(tasks.empty() && canRecruitHero && !startupTown->visitingHero)
+	if(tasks.empty() && canRecruitHero && !startupTown->getVisitingHero())
 	{
 		tasks.push_back(Goals::sptr(Goals::RecruitHero(startupTown)));
 	}
 
-	if(tasks.empty() && !startupTown->visitingHero)
+	if(tasks.empty() && !startupTown->getVisitingHero())
 	{
 		for(auto town : towns)
 		{
-			if(!town->visitingHero && needToRecruitHero(ai, town))
+			if(!town->getVisitingHero() && needToRecruitHero(ai, town))
 			{
 				tasks.push_back(Goals::sptr(Goals::RecruitHero(town)));
 
@@ -219,10 +219,10 @@ Goals::TGoalVec StartupBehavior::decompose(const Nullkiller * ai) const
 	{
 		for(const CGTownInstance * town : towns)
 		{
-			if(town->garrisonHero
-				&& town->garrisonHero->movementPointsRemaining()
-				&& !town->visitingHero
-				&& ai->getHeroLockedReason(town->garrisonHero) != HeroLockedReason::DEFENCE)
+			if(town->getGarrisonHero()
+				&& town->getGarrisonHero()->movementPointsRemaining()
+				&& !town->getVisitingHero()
+				&& ai->getHeroLockedReason(town->getGarrisonHero()) != HeroLockedReason::DEFENCE)
 			{
 				tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(town, nullptr).setpriority(MIN_PRIORITY)));
 			}

+ 1 - 1
AI/Nullkiller/Behaviors/StayAtTownBehavior.cpp

@@ -43,7 +43,7 @@ Goals::TGoalVec StayAtTownBehavior::decompose(const Nullkiller * ai) const
 
 		for(auto & path : paths)
 		{
-			if(town->visitingHero && town->visitingHero.get() != path.targetHero)
+			if(town->getVisitingHero() && town->getVisitingHero() != path.targetHero)
 				continue;
 
 			if(!path.getFirstBlockedAction() && path.exchangeCount <= 1)

+ 3 - 3
AI/Nullkiller/Engine/AIMemory.cpp

@@ -22,10 +22,10 @@ void AIMemory::removeFromMemory(const CGObjectInstance * obj)
 	//TODO: Find better way to handle hero boat removal
 	if(auto hero = dynamic_cast<const CGHeroInstance *>(obj))
 	{
-		if(hero->boat)
+		if(hero->inBoat())
 		{
-			vstd::erase_if_present(visitableObjs, hero->boat);
-			vstd::erase_if_present(alreadyVisited, hero->boat);
+			vstd::erase_if_present(visitableObjs, hero->getBoat());
+			vstd::erase_if_present(alreadyVisited, hero->getBoat());
 		}
 	}
 }

+ 1 - 1
AI/Nullkiller/Engine/FuzzyEngines.cpp

@@ -61,7 +61,7 @@ armyStructure evaluateArmyStructure(const CArmedInstance * army)
 	static const CSelector selectorSTACKS_SPEED = Selector::type()(BonusType::STACKS_SPEED);
 	static const std::string keySTACKS_SPEED = "type_"+std::to_string((int32_t)BonusType::STACKS_SPEED);
 
-	for(auto s : army->Slots())
+	for(const auto & s : army->Slots())
 	{
 		bool walker = true;
 		auto bearer = s.second->getType()->getBonusBearer();

+ 4 - 4
AI/Nullkiller/Engine/FuzzyHelper.cpp

@@ -47,16 +47,16 @@ ui64 FuzzyHelper::evaluateDanger(const int3 & tile, const CGHeroInstance * visit
 		{
 			auto hero = dynamic_cast<const CGHeroInstance *>(dangerousObject);
 
-			if(hero->visitedTown && !hero->visitedTown->garrisonHero)
+			if(hero->getVisitedTown() && !hero->getVisitedTown()->getGarrisonHero())
 			{
-				objectDanger += evaluateDanger(hero->visitedTown.get());
+				objectDanger += evaluateDanger(hero->getVisitedTown());
 			}
 			objectDanger *= ai->heroManager->getFightingStrengthCached(hero);
 		}
 		if (objWithID<Obj::TOWN>(dangerousObject))
 		{
 			auto town = dynamic_cast<const CGTownInstance*>(dangerousObject);
-			auto hero = town->garrisonHero;
+			auto hero = town->getGarrisonHero();
 
 			if (hero)
 				objectDanger *= ai->heroManager->getFightingStrengthCached(hero);
@@ -121,7 +121,7 @@ ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj)
 		const CGTownInstance * town = dynamic_cast<const CGTownInstance *>(obj);
 		auto danger = town->getUpperArmy()->getArmyStrength();
 
-		if(danger || town->visitingHero)
+		if(danger || town->getVisitingHero())
 		{
 			auto fortLevel = town->fortLevel();
 

+ 4 - 4
AI/Nullkiller/Engine/Nullkiller.cpp

@@ -77,7 +77,7 @@ void Nullkiller::init(std::shared_ptr<CCallback> cb, AIGateway * gateway)
 
 	settings = std::make_unique<Settings>(cb->getStartInfo()->difficulty);
 
-	PathfinderOptions pathfinderOptions(cb.get());
+	PathfinderOptions pathfinderOptions(*cb);
 
 	pathfinderOptions.useTeleportTwoWay = true;
 	pathfinderOptions.useTeleportOneWay = settings->isOneWayMonolithUsageAllowed();
@@ -642,7 +642,7 @@ bool Nullkiller::handleTrading()
 {
 	bool haveTraded = false;
 	bool shouldTryToTrade = true;
-	int marketId = -1;
+	ObjectInstanceID marketId;
 	for (auto town : cb->getTownsInfo())
 	{
 		if (town->hasBuiltSomeTradeBuilding())
@@ -650,9 +650,9 @@ bool Nullkiller::handleTrading()
 			marketId = town->id;
 		}
 	}
-	if (marketId == -1)
+	if (!marketId.hasValue())
 		return false;
-	if (const CGObjectInstance* obj = cb->getObj(ObjectInstanceID(marketId), false))
+	if (const CGObjectInstance* obj = cb->getObj(marketId, false))
 	{
 		if (const auto* m = dynamic_cast<const IMarket*>(obj))
 		{

+ 8 - 8
AI/Nullkiller/Engine/PriorityEvaluator.cpp

@@ -238,7 +238,7 @@ uint64_t RewardEvaluator::getArmyReward(
 	case Obj::SPELL_SCROLL:
 		//FALL_THROUGH
 	case Obj::ARTIFACT:
-		return evaluateArtifactArmyValue(dynamic_cast<const CGArtifact *>(target)->storedArtifact->getType());
+		return evaluateArtifactArmyValue(dynamic_cast<const CGArtifact *>(target)->getArtifactInstance()->getType());
 	case Obj::HERO:
 		return  relations == PlayerRelations::ENEMIES
 			? enemyArmyEliminationRewardRatio * dynamic_cast<const CGHeroInstance *>(target)->getArmyStrength()
@@ -380,7 +380,7 @@ float RewardEvaluator::getEnemyHeroStrategicalValue(const CGHeroInstance * enemy
 	return std::min(1.5f, objectValue * 0.9f + (1.5f - (1.5f / (1 + enemy->level))));
 }
 
-float RewardEvaluator::getResourceRequirementStrength(int resType) const
+float RewardEvaluator::getResourceRequirementStrength(GameResID resType) const
 {
 	TResources requiredResources = ai->buildAnalyzer->getResourcesRequiredNow();
 	TResources dailyIncome = ai->buildAnalyzer->getDailyIncome();
@@ -396,7 +396,7 @@ float RewardEvaluator::getResourceRequirementStrength(int resType) const
 	return std::min(ratio, 1.0f);
 }
 
-float RewardEvaluator::getTotalResourceRequirementStrength(int resType) const
+float RewardEvaluator::getTotalResourceRequirementStrength(GameResID resType) const
 {
 	TResources requiredResources = ai->buildAnalyzer->getTotalResourcesRequired();
 	TResources dailyIncome = ai->buildAnalyzer->getDailyIncome();
@@ -696,7 +696,7 @@ int32_t getArmyCost(const CArmedInstance * army)
 {
 	int32_t value = 0;
 
-	for(auto stack : army->Slots())
+	for(const auto & stack : army->Slots())
 	{
 		value += stack.second->getCreatureID().toCreature()->getFullRecruitCost().marketValue() * stack.second->count;
 	}
@@ -987,12 +987,12 @@ public:
 		float totalPower = 0;
 
 		// Map to store the aggregated power of creatures by CreatureID
-		std::map<int, float> totalPowerByCreatureID;
+		std::map<CreatureID, float> totalPowerByCreatureID;
 
 		// Calculate hero power and total power by CreatureID
-		for (auto slot : hero->Slots())
+		for (const auto & slot : hero->Slots())
 		{
-			int creatureID = slot.second->getCreatureID();
+			CreatureID creatureID = slot.second->getCreatureID();
 			float slotPower = slot.second->getPower();
 
 			// Add the power of this slot to the heroPower
@@ -1220,7 +1220,7 @@ public:
 		}
 		else if(bi.id >= BuildingID::MAGES_GUILD_1 && bi.id <= BuildingID::MAGES_GUILD_5)
 		{
-			evaluationContext.skillReward += 2 * (bi.id - BuildingID::MAGES_GUILD_1);
+			evaluationContext.skillReward += 2 * bi.id.getMagesGuildLevel();
 			if (!alreadyOwn && evaluationContext.evaluator.ai->cb->canBuildStructure(buildThis.town, highestMageGuildPossible) != EBuildingState::FORBIDDEN)
 			{
 				for (auto hero : evaluationContext.evaluator.ai->cb->getHeroesInfo())

+ 2 - 2
AI/Nullkiller/Engine/PriorityEvaluator.h

@@ -38,11 +38,11 @@ public:
 	uint64_t getArmyGrowth(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army) const;
 	int getGoldCost(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army) const;
 	float getEnemyHeroStrategicalValue(const CGHeroInstance * enemy) const;
-	float getResourceRequirementStrength(int resType) const;
+	float getResourceRequirementStrength(GameResID resType) const;
 	float getResourceRequirementStrength(const TResources & res) const;
 	float getStrategicalValue(const CGObjectInstance * target, const CGHeroInstance * hero = nullptr) const;
 	float getConquestValue(const CGObjectInstance* target) const;
-	float getTotalResourceRequirementStrength(int resType) const;
+	float getTotalResourceRequirementStrength(GameResID resType) const;
 	float evaluateWitchHutSkillScore(const CGObjectInstance * hut, const CGHeroInstance * hero, HeroRole role) const;
 	float getSkillReward(const CGObjectInstance * target, const CGHeroInstance * hero, HeroRole role) const;
 	int32_t getGoldReward(const CGObjectInstance * target, const CGHeroInstance * hero) const;

+ 5 - 5
AI/Nullkiller/Goals/AdventureSpellCast.cpp

@@ -44,17 +44,17 @@ void AdventureSpellCast::accept(AIGateway * ai)
 	{
 		ai->selectedObject = town->id;
 
-		if(town->visitingHero && town->tempOwner == ai->playerID && !town->getUpperArmy()->stacksCount())
+		if(town->getVisitingHero() && town->tempOwner == ai->playerID && !town->getUpperArmy()->stacksCount())
 		{
 			ai->myCb->swapGarrisonHero(town);
 		}
 
-		if(town->visitingHero)
-			throw cannotFulfillGoalException("The town is already occupied by " + town->visitingHero->getNameTranslated());
+		if(town->getVisitingHero())
+			throw cannotFulfillGoalException("The town is already occupied by " + town->getVisitingHero()->getNameTranslated());
 	}
 
-	if (hero->inTownGarrison)
-		ai->myCb->swapGarrisonHero(hero->visitedTown);
+	if (hero->isGarrisoned())
+		ai->myCb->swapGarrisonHero(hero->getVisitedTown());
 
 	auto wait = cb->waitTillRealize;
 

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

@@ -29,7 +29,7 @@ BuildThis::BuildThis(BuildingID Bid, const CGTownInstance * tid)
 		tid,
 		nullptr);
 
-	bid = Bid;
+	bid = Bid.getNum();
 	town = tid;
 }
 

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

@@ -34,7 +34,7 @@ namespace Goals
 		BuildThis(const BuildingInfo & buildingInfo, const TownDevelopmentInfo & townInfo) //should be private, but unit test uses it
 			: ElementarGoal(Goals::BUILD_STRUCTURE), buildingInfo(buildingInfo), townInfo(townInfo)
 		{
-			bid = buildingInfo.id;
+			bid = buildingInfo.id.getNum();
 			town = townInfo.town;
 		}
 		BuildThis(BuildingID Bid, const CGTownInstance * tid);

+ 3 - 3
AI/Nullkiller/Goals/BuyArmy.cpp

@@ -62,7 +62,7 @@ void BuyArmy::accept(AIGateway * ai)
 			{
 				SlotID lowestValueSlot;
 				int lowestValue = std::numeric_limits<int>::max();
-				for (auto slot : town->getUpperArmy()->Slots())
+				for (const auto & slot : town->getUpperArmy()->Slots())
 				{
 					if (slot.second->getCreatureID() != CreatureID::NONE)
 					{
@@ -97,9 +97,9 @@ void BuyArmy::accept(AIGateway * ai)
 		throw cannotFulfillGoalException("No creatures to buy.");
 	}
 
-	if(town->visitingHero && !town->garrisonHero)
+	if(town->getVisitingHero() && !town->getGarrisonHero())
 	{
-		ai->moveHeroToTile(town->visitablePos(), town->visitingHero.get());
+		ai->moveHeroToTile(town->visitablePos(), town->getVisitingHero());
 	}
 }
 

+ 1 - 1
AI/Nullkiller/Goals/CGoal.h

@@ -94,7 +94,7 @@ namespace Goals
 		bool isObjectAffected(ObjectInstanceID id) const override
 		{
 			return (AbstractGoal::hero && AbstractGoal::hero->id == id)
-				|| AbstractGoal::objid == id
+				|| AbstractGoal::objid == id.getNum()
 				|| (AbstractGoal::town && AbstractGoal::town->id == id);
 		}
 

+ 32 - 52
AI/Nullkiller/Goals/CompleteQuest.cpp

@@ -12,6 +12,7 @@
 #include "../Behaviors/CaptureObjectsBehavior.h"
 #include "../AIGateway.h"
 #include "../../../lib/GameLibrary.h"
+#include "../../../lib/mapObjects/CQuest.h"
 #include "../../../lib/texts/CGeneralTextHandler.h"
 
 namespace NKAI
@@ -21,7 +22,8 @@ using namespace Goals;
 
 bool isKeyMaster(const QuestInfo & q)
 {
-	return q.obj && (q.obj->ID == Obj::BORDER_GATE || q.obj->ID == Obj::BORDERGUARD);
+	auto object = q.getObject(cb);
+	return object && (object->ID == Obj::BORDER_GATE || object->ID == Obj::BORDERGUARD);
 }
 
 std::string CompleteQuest::toString() const
@@ -37,27 +39,28 @@ TGoalVec CompleteQuest::decompose(const Nullkiller * ai) const
 	}
 
 	logAi->debug("Trying to realize quest: %s", questToString());
-	
-	if(!q.quest->mission.artifacts.empty())
+	auto quest = q.getQuest(cb);
+
+	if(!quest->mission.artifacts.empty())
 		return missionArt(ai);
 
-	if(!q.quest->mission.heroes.empty())
+	if(!quest->mission.heroes.empty())
 		return missionHero(ai);
 
-	if(!q.quest->mission.creatures.empty())
+	if(!quest->mission.creatures.empty())
 		return missionArmy(ai);
 
-	if(q.quest->mission.resources.nonZero())
+	if(quest->mission.resources.nonZero())
 		return missionResources(ai);
 
-	if(q.quest->killTarget != ObjectInstanceID::NONE)
+	if(quest->killTarget != ObjectInstanceID::NONE)
 		return missionDestroyObj(ai);
 
-	for(auto & s : q.quest->mission.primary)
+	for(auto & s : quest->mission.primary)
 		if(s)
 			return missionIncreasePrimaryStat(ai);
 
-	if(q.quest->mission.heroLevel > 0)
+	if(quest->mission.heroLevel > 0)
 		return missionLevel(ai);
 
 	return TGoalVec();
@@ -67,52 +70,52 @@ bool CompleteQuest::operator==(const CompleteQuest & other) const
 {
 	if(isKeyMaster(q))
 	{
-		return isKeyMaster(other.q) && q.obj->subID == other.q.obj->subID;
+		return isKeyMaster(other.q) && q.getObject(cb)->subID == other.q.getObject(cb)->subID;
 	}
 	else if(isKeyMaster(other.q))
 	{
 		return false;
 	}
 
-	return q.quest->qid == other.q.quest->qid;
+	return q.getQuest(cb) == other.q.getQuest(cb);
 }
 
 uint64_t CompleteQuest::getHash() const
 {
 	if(isKeyMaster(q))
 	{
-		return q.obj->subID;
+		return q.getObject(cb)->subID;
 	}
 
-	return q.quest->qid;
+	return q.getObject(cb)->id.getNum();
 }
 
 std::string CompleteQuest::questToString() const
 {
 	if(isKeyMaster(q))
 	{
-		return "find " + LIBRARY->generaltexth->tentColors[q.obj->subID] + " keymaster tent";
+		return "find " + LIBRARY->generaltexth->tentColors[q.getObject(cb)->subID] + " keymaster tent";
 	}
 
-	if(q.quest->questName == CQuest::missionName(EQuestMission::NONE))
+	if(q.getQuest(cb)->questName == CQuest::missionName(EQuestMission::NONE))
 		return "inactive quest";
 
 	MetaString ms;
-	q.quest->getRolloverText(q.obj->cb, ms, false);
+	q.getQuest(cb)->getRolloverText(cb, ms, false);
 
 	return ms.toString();
 }
 
 TGoalVec CompleteQuest::tryCompleteQuest(const Nullkiller * ai) const
 {
-	auto paths = ai->pathfinder->getPathInfo(q.obj->visitablePos());
+	auto paths = ai->pathfinder->getPathInfo(q.getObject(cb)->visitablePos());
 
 	vstd::erase_if(paths, [&](const AIPath & path) -> bool
 	{
-		return !q.quest->checkQuest(path.targetHero);
+		return !q.getQuest(cb)->checkQuest(path.targetHero);
 	});
 	
-	return CaptureObjectsBehavior::getVisitGoals(paths, ai, q.obj);
+	return CaptureObjectsBehavior::getVisitGoals(paths, ai, q.getObject(cb));
 }
 
 TGoalVec CompleteQuest::missionArt(const Nullkiller * ai) const
@@ -124,9 +127,9 @@ TGoalVec CompleteQuest::missionArt(const Nullkiller * ai) const
 
 	CaptureObjectsBehavior findArts;
 
-	for(auto art : q.quest->mission.artifacts)
+	for(auto art : q.getQuest(cb)->mission.artifacts)
 	{
-		solutions.push_back(sptr(CaptureObjectsBehavior().ofType(Obj::ARTIFACT, art)));
+		solutions.push_back(sptr(CaptureObjectsBehavior().ofType(Obj::ARTIFACT, art.getNum())));
 	}
 
 	return solutions;
@@ -147,14 +150,14 @@ TGoalVec CompleteQuest::missionHero(const Nullkiller * ai) const
 
 TGoalVec CompleteQuest::missionArmy(const Nullkiller * ai) const
 {
-	auto paths = ai->pathfinder->getPathInfo(q.obj->visitablePos());
+	auto paths = ai->pathfinder->getPathInfo(q.getObject(cb)->visitablePos());
 
 	vstd::erase_if(paths, [&](const AIPath & path) -> bool
 	{
-		return !CQuest::checkMissionArmy(q.quest, path.heroArmy);
+		return !CQuest::checkMissionArmy(q.getQuest(cb), path.heroArmy);
 	});
 
-	return CaptureObjectsBehavior::getVisitGoals(paths, ai, q.obj);
+	return CaptureObjectsBehavior::getVisitGoals(paths, ai, q.getObject(cb));
 }
 
 TGoalVec CompleteQuest::missionIncreasePrimaryStat(const Nullkiller * ai) const
@@ -169,51 +172,28 @@ TGoalVec CompleteQuest::missionLevel(const Nullkiller * ai) const
 
 TGoalVec CompleteQuest::missionKeymaster(const Nullkiller * ai) const
 {
-	if(isObjectPassable(ai, q.obj))
+	if(isObjectPassable(ai, q.getObject(cb)))
 	{
-		return CaptureObjectsBehavior(q.obj).decompose(ai);
+		return CaptureObjectsBehavior(q.getObject(cb)).decompose(ai);
 	}
 	else
 	{
-		return CaptureObjectsBehavior().ofType(Obj::KEYMASTER, q.obj->subID).decompose(ai);
+		return CaptureObjectsBehavior().ofType(Obj::KEYMASTER, q.getObject(cb)->subID).decompose(ai);
 	}
 }
 
 TGoalVec CompleteQuest::missionResources(const Nullkiller * ai) const
 {
 	TGoalVec solutions = tryCompleteQuest(ai);
-
-	/*auto heroes = cb->getHeroesInfo(); //TODO: choose best / free hero from among many possibilities?
-
-	if(heroes.size())
-	{
-		if(q.quest->checkQuest(heroes.front())) //it doesn't matter which hero it is
-		{
-			return solutions;// ai->ah->howToVisitObj(q.obj);
-		}
-		else
-		{
-			for(int i = 0; i < q.quest->m7resources.size(); ++i)
-			{
-				if(q.quest->m7resources[i])
-					solutions.push_back(sptr(CollectRes(static_cast<EGameResID>(i), q.quest->m7resources[i])));
-			}
-		}
-	}
-	else
-	{
-		solutions.push_back(sptr(Goals::RecruitHero())); //FIXME: checkQuest requires any hero belonging to player :(
-	}*/
-
 	return solutions;
 }
 
 TGoalVec CompleteQuest::missionDestroyObj(const Nullkiller * ai) const
 {
-	auto obj = ai->cb->getObj(q.quest->killTarget);
+	auto obj = ai->cb->getObj(q.getQuest(cb)->killTarget);
 
 	if(!obj)
-		return CaptureObjectsBehavior(q.obj).decompose(ai);
+		return CaptureObjectsBehavior(q.getObject(cb)).decompose(ai);
 
 	auto relations = ai->cb->getPlayerRelations(ai->playerID, obj->tempOwner);
 

+ 20 - 20
AI/Nullkiller/Goals/ExchangeSwapTownHeroes.cpp

@@ -19,7 +19,7 @@ namespace NKAI
 using namespace Goals;
 
 ExchangeSwapTownHeroes::ExchangeSwapTownHeroes(
-	const CGTownInstance * town, 
+	const CGTownInstance * town,
 	const CGHeroInstance * garrisonHero,
 	HeroLockedReason lockingReason)
 	:ElementarGoal(Goals::EXCHANGE_SWAP_TOWN_HEROES), town(town), garrisonHero(garrisonHero), lockingReason(lockingReason)
@@ -30,11 +30,11 @@ std::vector<ObjectInstanceID> ExchangeSwapTownHeroes::getAffectedObjects() const
 {
 	std::vector<ObjectInstanceID> affectedObjects = { town->id };
 
-	if(town->garrisonHero)
-		affectedObjects.push_back(town->garrisonHero->id);
+	if(town->getGarrisonHero())
+		affectedObjects.push_back(town->getGarrisonHero()->id);
 
-	if(town->visitingHero)
-		affectedObjects.push_back(town->visitingHero->id);
+	if(town->getVisitingHero())
+		affectedObjects.push_back(town->getVisitingHero()->id);
 
 	return affectedObjects;
 }
@@ -42,8 +42,8 @@ std::vector<ObjectInstanceID> ExchangeSwapTownHeroes::getAffectedObjects() const
 bool ExchangeSwapTownHeroes::isObjectAffected(ObjectInstanceID id) const
 {
 	return town->id == id
-		|| (town->visitingHero && town->visitingHero->id == id)
-		|| (town->garrisonHero && town->garrisonHero->id == id);
+		|| (town->getVisitingHero() && town->getVisitingHero()->id == id)
+		|| (town->getGarrisonHero() && town->getGarrisonHero()->id == id);
 }
 
 std::string ExchangeSwapTownHeroes::toString() const
@@ -58,39 +58,39 @@ bool ExchangeSwapTownHeroes::operator==(const ExchangeSwapTownHeroes & other) co
 
 void ExchangeSwapTownHeroes::accept(AIGateway * ai)
 {
-	if(!garrisonHero)
+	if(!getGarrisonHero())
 	{
-		auto currentGarrisonHero = town->garrisonHero;
+		auto currentGarrisonHero = town->getGarrisonHero();
 		
 		if(!currentGarrisonHero)
 			throw cannotFulfillGoalException("Invalid configuration. There is no hero in town garrison.");
 		
 		cb->swapGarrisonHero(town);
 
-		if(currentGarrisonHero.get() != town->visitingHero.get())
+		if(currentGarrisonHero != town->getVisitingHero())
 		{
 			logAi->error("VisitingHero is empty, expected %s", currentGarrisonHero->getNameTranslated());
 			return;
 		}
 
 		ai->buildArmyIn(town);
-		ai->nullkiller->unlockHero(currentGarrisonHero.get());
+		ai->nullkiller->unlockHero(currentGarrisonHero);
 		logAi->debug("Extracted hero %s from garrison of %s", currentGarrisonHero->getNameTranslated(), town->getNameTranslated());
 
 		return;
 	}
 
-	if(town->visitingHero && town->visitingHero.get() != garrisonHero)
+	if(town->getVisitingHero() && town->getVisitingHero() != getGarrisonHero())
 		cb->swapGarrisonHero(town);
 
 	ai->makePossibleUpgrades(town);
-	ai->moveHeroToTile(town->visitablePos(), garrisonHero);
+	ai->moveHeroToTile(town->visitablePos(), getGarrisonHero());
 
 	auto upperArmy = town->getUpperArmy();
 	
-	if(!town->garrisonHero)
+	if(!town->getGarrisonHero())
 	{
-		if (!garrisonHero->canBeMergedWith(*town))
+		if (!getGarrisonHero()->canBeMergedWith(*town))
 		{
 			while (upperArmy->stacksCount() != 0)
 			{
@@ -103,16 +103,16 @@ void ExchangeSwapTownHeroes::accept(AIGateway * ai)
 
 	if(lockingReason != HeroLockedReason::NOT_LOCKED)
 	{
-		ai->nullkiller->lockHero(garrisonHero, lockingReason);
+		ai->nullkiller->lockHero(getGarrisonHero(), lockingReason);
 	}
 
-	if(town->visitingHero && town->visitingHero != garrisonHero)
+	if(town->getVisitingHero() && town->getVisitingHero() != getGarrisonHero())
 	{
-		ai->nullkiller->unlockHero(town->visitingHero.get());
-		ai->makePossibleUpgrades(town->visitingHero);
+		ai->nullkiller->unlockHero(town->getVisitingHero());
+		ai->makePossibleUpgrades(town->getVisitingHero());
 	}
 
-	logAi->debug("Put hero %s to garrison of %s", garrisonHero->getNameTranslated(), town->getNameTranslated());
+	logAi->debug("Put hero %s to garrison of %s", getGarrisonHero()->getNameTranslated(), town->getNameTranslated());
 }
 
 }

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

@@ -68,7 +68,7 @@ std::vector<ObjectInstanceID> ExecuteHeroChain::getAffectedObjects() const
 
 bool ExecuteHeroChain::isObjectAffected(ObjectInstanceID id) const
 {
-	if(chainPath.targetHero->id == id || objid == id)
+	if(chainPath.targetHero->id == id || objid == id.getNum())
 		return true;
 
 	for(auto & node : chainPath.nodes)

+ 2 - 2
AI/Nullkiller/Goals/RecruitHero.cpp

@@ -59,12 +59,12 @@ void RecruitHero::accept(AIGateway * ai)
 	if(!heroToHire)
 		throw cannotFulfillGoalException("No hero to hire!");
 
-	if(t->visitingHero)
+	if(t->getVisitingHero())
 	{
 		cb->swapGarrisonHero(t);
 	}
 
-	if(t->visitingHero)
+	if(t->getVisitingHero())
 		throw cannotFulfillGoalException("Town " + t->nodeName() + " is occupied. Cannot recruit hero!");
 
 	cb->recruitHero(t, heroToHire);

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

@@ -23,7 +23,7 @@ StayAtTown::StayAtTown(const CGTownInstance * town, AIPath & path)
 {
 	sethero(path.targetHero);
 	settown(town);
-	movementWasted = static_cast<float>(hero->movementPointsRemaining()) / hero->movementPointsLimit(!hero->boat) - path.movementCost();
+	movementWasted = static_cast<float>(hero->movementPointsRemaining()) / hero->movementPointsLimit(!hero->inBoat()) - path.movementCost();
 	vstd::amax(movementWasted, 0);
 }
 

+ 6 - 6
AI/Nullkiller/Helpers/ArmyFormation.cpp

@@ -25,7 +25,7 @@ void ArmyFormation::addSingleCreatureStacks(const CGHeroInstance * hero)
 
 	while(!freeSlots.empty())
 	{
-		auto weakestCreature = vstd::minElementByFun(hero->Slots(), [](const std::pair<SlotID, CStackInstance *> & slot) -> int
+		TSlots::const_iterator weakestCreature = vstd::minElementByFun(hero->Slots(), [](const auto & slot) -> int
 			{
 				return slot.second->getCount() == 1
 					? std::numeric_limits<int>::max()
@@ -48,14 +48,14 @@ void ArmyFormation::rearrangeArmyForSiege(const CGTownInstance * town, const CGH
 
 	if(town->fortLevel() > CGTownInstance::FORT)
 	{
-		std::vector<CStackInstance *> stacks;
+		std::vector<const CStackInstance *> stacks;
 
-		for(auto slot : attacker->Slots())
-			stacks.push_back(slot.second);
+		for(const auto & slot : attacker->Slots())
+			stacks.push_back(slot.second.get());
 
 		boost::sort(
 			stacks,
-			[](CStackInstance * slot1, CStackInstance * slot2) -> bool
+			[](const CStackInstance * slot1, const CStackInstance * slot2) -> bool
 			{
 				auto cre1 = slot1->getCreatureID().toCreature();
 				auto cre2 = slot2->getCreatureID().toCreature();
@@ -67,7 +67,7 @@ void ArmyFormation::rearrangeArmyForSiege(const CGTownInstance * town, const CGH
 
 		for(int i = 0; i < stacks.size(); i++)
 		{
-			auto pos = vstd::findKey(attacker->Slots(), stacks[i]);
+			auto pos = stacks[i]->getArmy()->findStack(stacks[i]);
 
 			if(pos.getNum() != i)
 				cb->swapCreatures(attacker, attacker, static_cast<SlotID>(i), pos);

+ 1 - 1
AI/Nullkiller/Markers/UnlockCluster.h

@@ -34,7 +34,7 @@ namespace Goals
 		{
 			tile = cluster->blocker->visitablePos();
 			hero = pathToCenter.targetHero;
-			objid = cluster->blocker->id;
+			objid = cluster->blocker->id.getNum();
 		}
 
 		bool operator==(const UnlockCluster & other) const override;

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

@@ -180,7 +180,7 @@ std::optional<AIPathNode *> AINodeStorage::getOrCreateNode(
 	const EPathfindingLayer layer, 
 	const ChainActor * actor)
 {
-	int bucketIndex = ((uintptr_t)actor + static_cast<uint32_t>(layer)) % ai->settings->getPathfinderBucketsCount();
+	int bucketIndex = ((uintptr_t)actor + layer.getNum()) % ai->settings->getPathfinderBucketsCount();
 	int bucketOffset = bucketIndex * ai->settings->getPathfinderBucketSize();
 	auto chains = nodes.get(pos);
 
@@ -284,7 +284,7 @@ void AINodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInf
 					return;
 				}
 
-				auto weakest = vstd::minElementByFun(dstNode->actor->creatureSet->Slots(), [](std::pair<SlotID, const CStackInstance *> pair) -> int
+				const auto & weakest = vstd::minElementByFun(dstNode->actor->creatureSet->Slots(), [](const auto & pair) -> int
 					{
 						return pair.second->getCount() * pair.second->getCreatureID().toCreature()->getAIValue();
 					});
@@ -958,7 +958,7 @@ void AINodeStorage::setHeroes(std::map<const CGHeroInstance *, HeroRole> heroes)
 	{
 		// do not allow our own heroes in garrison to act on map
 		if(hero.first->getOwner() == ai->playerID
-			&& hero.first->inTownGarrison
+			&& hero.first->isGarrisoned()
 			&& (ai->isHeroLocked(hero.first) || ai->heroManager->heroCapReached(false)))
 		{
 			continue;
@@ -969,7 +969,7 @@ void AINodeStorage::setHeroes(std::map<const CGHeroInstance *, HeroRole> heroes)
 
 		if(actor->hero->tempOwner != ai->playerID)
 		{
-			bool onLand = !actor->hero->boat || actor->hero->boat->layer != EPathfindingLayer::SAIL;
+			bool onLand = !actor->hero->inBoat() || actor->hero->getBoat()->layer != EPathfindingLayer::SAIL;
 			actor->initialMovement = actor->hero->movementPointsLimit(onLand);
 		}
 
@@ -987,9 +987,9 @@ void AINodeStorage::setTownsAndDwellings(
 	{
 		uint64_t mask = FirstActorMask << actors.size();
 
-		// TODO: investigate logix of second condition || ai->nullkiller->getHeroLockedReason(town->garrisonHero) != HeroLockedReason::DEFENCE
+		// TODO: investigate logix of second condition || ai->nullkiller->getHeroLockedReason(town->getGarrisonHero()) != HeroLockedReason::DEFENCE
 		// check defence imrove
-		if(!town->garrisonHero)
+		if(!town->getGarrisonHero())
 		{
 			actors.push_back(std::make_shared<TownGarrisonActor>(town, mask));
 		}
@@ -1185,19 +1185,19 @@ void AINodeStorage::calculateTownPortal(
 	{
 		for(const CGTownInstance * targetTown : towns)
 		{
-			if(targetTown->visitingHero
+			if(targetTown->getVisitingHero()
 				&& targetTown->getUpperArmy()->stacksCount()
-				&& maskMap.find(targetTown->visitingHero.get()) != maskMap.end())
+				&& maskMap.find(targetTown->getVisitingHero()) != maskMap.end())
 			{
-				auto basicMask = maskMap.at(targetTown->visitingHero.get());
+				auto basicMask = maskMap.at(targetTown->getVisitingHero());
 				bool sameActorInTown = actor->chainMask == basicMask;
 
 				if(!sameActorInTown)
 					continue;
 			}
 
-			if (targetTown->visitingHero
-				&& (targetTown->visitingHero.get()->getFactionID() != actor->hero->getFactionID()
+			if (targetTown->getVisitingHero()
+				&& (targetTown->getVisitingHero()->getFactionID() != actor->hero->getFactionID()
 					|| targetTown->getUpperArmy()->stacksCount()))
 				continue;
 

+ 2 - 2
AI/Nullkiller/Pathfinding/AINodeStorage.h

@@ -286,12 +286,12 @@ public:
 
 	inline EPathAccessibility getAccessibility(const int3 & tile, EPathfindingLayer layer) const
 	{
-		return (*this->accessibility)[tile.z][tile.x][tile.y][layer];
+		return (*this->accessibility)[tile.z][tile.x][tile.y][layer.getNum()];
 	}
 
 	inline void resetTile(const int3 & tile, EPathfindingLayer layer, EPathAccessibility tileAccessibility)
 	{
-		(*this->accessibility)[tile.z][tile.x][tile.y][layer] = tileAccessibility;
+		(*this->accessibility)[tile.z][tile.x][tile.y][layer.getNum()] = tileAccessibility;
 	}
 
 	inline int getBucket(const ChainActor * actor) const

+ 2 - 2
AI/Nullkiller/Pathfinding/AIPathfinderConfig.cpp

@@ -44,7 +44,7 @@ namespace AIPathfinding
 		Nullkiller * ai,
 		std::shared_ptr<AINodeStorage> nodeStorage,
 		bool allowBypassObjects)
-		:PathfinderConfig(nodeStorage, cb, makeRuleset(cb, ai, nodeStorage, allowBypassObjects)), aiNodeStorage(nodeStorage)
+		:PathfinderConfig(nodeStorage, *cb, makeRuleset(cb, ai, nodeStorage, allowBypassObjects)), aiNodeStorage(nodeStorage)
 	{
 		options.canUseCast = true;
 		options.allowLayerTransitioningAfterBattle = true;
@@ -56,7 +56,7 @@ namespace AIPathfinding
 
 	AIPathfinderConfig::~AIPathfinderConfig() = default;
 
-	CPathfinderHelper * AIPathfinderConfig::getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs)
+	CPathfinderHelper * AIPathfinderConfig::getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState & gs)
 	{
 		auto hero = aiNodeStorage->getHero(source.node);
 		auto & helper = pathfindingHelpers[hero];

+ 1 - 1
AI/Nullkiller/Pathfinding/AIPathfinderConfig.h

@@ -35,7 +35,7 @@ namespace AIPathfinding
 
 		~AIPathfinderConfig();
 
-		CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs) override;
+		CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState & gs) override;
 	};
 }
 

+ 2 - 2
AI/Nullkiller/Pathfinding/Actions/BuyArmyAction.cpp

@@ -20,13 +20,13 @@ namespace AIPathfinding
 {
 	void BuyArmyAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
 	{
-		if(!hero->visitedTown)
+		if(!hero->getVisitedTown())
 		{
 			throw cannotFulfillGoalException(
 				hero->getNameTranslated() + " being at " + hero->visitablePos().toString() + " has no town to recruit creatures.");
 		}
 
-		ai->recruitCreatures(hero->visitedTown, hero);
+		ai->recruitCreatures(hero->getVisitedTown(), hero);
 	}
 
 	std::string BuyArmyAction::toString() const

+ 9 - 6
AI/Nullkiller/Pathfinding/Actions/QuestAction.cpp

@@ -12,6 +12,7 @@
 #include "QuestAction.h"
 #include "../../AIGateway.h"
 #include "../../Goals/CompleteQuest.h"
+#include "../../../../lib/mapObjects/CQuest.h"
 
 namespace NKAI
 {
@@ -30,16 +31,18 @@ namespace AIPathfinding
 
 	bool QuestAction::canAct(const Nullkiller * ai, const CGHeroInstance * hero) const
 	{
-		if(questInfo.obj->ID == Obj::BORDER_GATE || questInfo.obj->ID == Obj::BORDERGUARD)
+		auto object = questInfo.getObject(ai->cb.get());
+		auto quest = questInfo.getQuest(ai->cb.get());
+		if(object->ID == Obj::BORDER_GATE || object->ID == Obj::BORDERGUARD)
 		{
-			return dynamic_cast<const IQuestObject *>(questInfo.obj)->checkQuest(hero);
+			return dynamic_cast<const IQuestObject *>(object)->checkQuest(hero);
 		}
 
-		auto notActivated = !questInfo.obj->wasVisited(ai->playerID)
-			&& !questInfo.quest->activeForPlayers.count(hero->getOwner());
+		auto notActivated = !object->wasVisited(ai->playerID)
+			&& !quest->activeForPlayers.count(hero->getOwner());
 		
 		return notActivated
-			|| questInfo.quest->checkQuest(hero);
+			|| quest->checkQuest(hero);
 	}
 
 	Goals::TSubgoal QuestAction::decompose(const Nullkiller * ai, const CGHeroInstance * hero) const
@@ -49,7 +52,7 @@ namespace AIPathfinding
 
 	void QuestAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
 	{
-		ai->moveHeroToTile(questInfo.obj->visitablePos(), hero);
+		ai->moveHeroToTile(questInfo.getObject(ai->myCb.get())->visitablePos(), hero);
 	}
 
 	std::string QuestAction::toString() const

+ 4 - 4
AI/Nullkiller/Pathfinding/Actors.cpp

@@ -43,7 +43,7 @@ ChainActor::ChainActor(const CGHeroInstance * hero, HeroRole heroRole, uint64_t
 	baseActor(this), carrierParent(nullptr), otherParent(nullptr), actorExchangeCount(1), armyCost(), actorAction()
 {
 	initialPosition = hero->visitablePos();
-	layer = hero->boat ? hero->boat->layer : EPathfindingLayer::LAND;
+	layer = hero->inBoat() ? hero->getBoat()->layer : EPathfindingLayer::LAND;
 	initialMovement = hero->movementPointsRemaining();
 	initialTurn = 0;
 	armyValue = getHeroArmyStrengthWithCommander(hero, hero);
@@ -75,7 +75,7 @@ int ChainActor::maxMovePoints(CGPathNode::ELayer layer)
 		throw std::logic_error("Asking movement points for static actor");
 #endif
 
-	return hero->movementPointsLimit(layer);
+	return hero->movementPointsLimit(layer != EPathfindingLayer::SAIL);
 }
 
 std::string ChainActor::toString() const
@@ -358,9 +358,9 @@ HeroExchangeArmy * HeroExchangeMap::tryUpgrade(
 	}
 	else
 	{
-		for(auto slot : army->Slots())
+		for(const auto & slot : army->Slots())
 		{
-			auto targetSlot = target->getSlotFor(slot.second->getCreatureID());
+			const auto & targetSlot = target->getSlotFor(slot.second->getCreatureID());
 
 			target->addToSlot(targetSlot, slot.second->getCreatureID(), slot.second->count);
 		}

+ 5 - 4
AI/Nullkiller/Pathfinding/GraphPaths.cpp

@@ -12,6 +12,7 @@
 #include "AIPathfinderConfig.h"
 #include "../../../lib/CRandomGenerator.h"
 #include "../../../CCallback.h"
+#include "../../../lib/mapObjects/CQuest.h"
 #include "../../../lib/mapping/CMap.h"
 #include "../Engine/Nullkiller.h"
 #include "../../../lib/logging/VisualLogger.h"
@@ -58,7 +59,7 @@ void GraphPaths::calculatePaths(const CGHeroInstance * targetHero, const Nullkil
 	graph.copyFrom(*ai->baseGraph);
 	graph.connectHeroes(ai);
 
-	visualKey = std::to_string(ai->playerID) + ":" + targetHero->getNameTranslated();
+	visualKey = std::to_string(ai->playerID.getNum()) + ":" + targetHero->getNameTranslated();
 	pathNodes.clear();
 
 	GraphNodeComparer cmp(pathNodes);
@@ -82,11 +83,11 @@ void GraphPaths::calculatePaths(const CGHeroInstance * targetHero, const Nullkil
 				|| node.obj->ID == Obj::BORDER_GATE)
 			{
 				auto questObj = dynamic_cast<const IQuestObject *>(node.obj);
-				auto questInfo = QuestInfo(questObj->quest, node.obj, pos.coord);
+				auto questInfo = QuestInfo(node.obj->id);
 
 				if(node.obj->ID == Obj::QUEST_GUARD
-					&& questObj->quest->mission == Rewardable::Limiter{}
-					&& questObj->quest->killTarget == ObjectInstanceID::NONE)
+					&& questObj->getQuest().mission == Rewardable::Limiter{}
+					&& questObj->getQuest().killTarget == ObjectInstanceID::NONE)
 				{
 					continue;
 				}

+ 2 - 2
AI/Nullkiller/Pathfinding/ObjectGraphCalculator.cpp

@@ -298,7 +298,7 @@ void ObjectGraphCalculator::addObjectActor(const CGObjectInstance * obj)
 
 	if(cb->getTile(visitablePos)->isWater())
 	{
-		objectActor->boat = temporaryBoats.emplace_back(std::make_unique<CGBoat>(objectActor->cb)).get();
+		objectActor->setBoat(temporaryBoats.emplace_back(std::make_unique<CGBoat>(objectActor->cb)).get());
 	}
 
 	assert(objectActor->visitablePos() == visitablePos);
@@ -335,7 +335,7 @@ void ObjectGraphCalculator::addJunctionActor(const int3 & visitablePos, bool isV
 
 	if(isVirtualBoat || ai->cb->getTile(visitablePos)->isWater())
 	{
-		objectActor->boat = temporaryBoats.emplace_back(std::make_unique<CGBoat>(objectActor->cb)).get();
+		objectActor->setBoat(temporaryBoats.emplace_back(std::make_unique<CGBoat>(objectActor->cb)).get());
 	}
 
 	assert(objectActor->visitablePos() == visitablePos);

+ 4 - 3
AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp

@@ -14,6 +14,7 @@
 #include "../Actions/WhirlpoolAction.h"
 #include "../../Goals/Invalid.h"
 #include "AIPreviousNodeRule.h"
+#include "../../../../lib/mapObjects/CQuest.h"
 #include "../../../../lib/pathfinder/PathfinderOptions.h"
 #include "../../../../lib/pathfinder/CPathfinder.h"
 
@@ -165,12 +166,12 @@ namespace AIPathfinding
 	{
 		const AIPathNode * destinationNode = nodeStorage->getAINode(destination.node);
 		auto questObj = dynamic_cast<const IQuestObject *>(destination.nodeObject);
-		auto questInfo = QuestInfo(questObj->quest, destination.nodeObject, destination.coord);
+		auto questInfo = QuestInfo(destination.nodeObject->id);
 		QuestAction questAction(questInfo);
 
 		if(destination.nodeObject->ID == Obj::QUEST_GUARD
-		   && questObj->quest->mission == Rewardable::Limiter{}
-		   && questObj->quest->killTarget == ObjectInstanceID::NONE)
+		   && questObj->getQuest().mission == Rewardable::Limiter{}
+		   && questObj->getQuest().killTarget == ObjectInstanceID::NONE)
 		{
 			return false;
 		}

+ 8 - 3
AI/VCAI/AIUtility.cpp

@@ -192,7 +192,7 @@ bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater)
 	}
 	else if(!fromWater) // do not try to board when in water sector
 	{
-		if(t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT)
+		if(t->visitableObjects.size() == 1 && cb->getObjInstance(t->topVisitableObj())->ID == Obj::BOAT)
 			return true;
 	}
 	return false;
@@ -200,9 +200,14 @@ bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater)
 
 bool isBlockedBorderGate(int3 tileToHit) //TODO: is that function needed? should be handled by pathfinder
 {
-	if(cb->getTile(tileToHit)->topVisitableId() != Obj::BORDER_GATE)
+	const auto * object = cb->getTopObj(tileToHit);
+	if(!object)
 		return false;
-	auto gate = dynamic_cast<const CGKeys *>(cb->getTile(tileToHit)->topVisitableObj());
+
+	if(object->ID != Obj::BORDER_GATE)
+		return false;
+
+	auto gate = dynamic_cast<const CGKeys *>(object);
 	return !gate->passableFor(ai->playerID);
 }
 

+ 1 - 1
AI/VCAI/FuzzyEngines.cpp

@@ -60,7 +60,7 @@ armyStructure evaluateArmyStructure(const CArmedInstance * army)
 	static const CSelector selectorSTACKS_SPEED = Selector::type()(BonusType::STACKS_SPEED);
 	static const std::string keySTACKS_SPEED = "type_"+std::to_string((int32_t)BonusType::STACKS_SPEED);
 
-	for(auto s : army->Slots())
+	for(const auto & s : army->Slots())
 	{
 		bool walker = true;
 		auto bearer = s.second->getType()->getBonusBearer();

+ 1 - 0
AI/VCAI/FuzzyHelper.cpp

@@ -11,6 +11,7 @@
 #include "FuzzyHelper.h"
 
 #include "Goals/Goals.h"
+#include "Goals/CompleteQuest.h"
 #include "VCAI.h"
 
 #include "../../lib/mapObjectConstructors/AObjectTypeHandler.h"

+ 2 - 2
AI/VCAI/Goals/AdventureSpellCast.cpp

@@ -39,8 +39,8 @@ TSubgoal AdventureSpellCast::whatToDoToAchieve()
 	if(hero->mana < hero->getSpellCost(spell))
 		throw cannotFulfillGoalException("Hero has not enough mana to cast " + spell->getNameTranslated());
 
-	if(spellID == SpellID::TOWN_PORTAL && town && town->visitingHero)
-		throw cannotFulfillGoalException("The town is already occupied by " + town->visitingHero->getNameTranslated());
+	if(spellID == SpellID::TOWN_PORTAL && town && town->getVisitingHero())
+		throw cannotFulfillGoalException("The town is already occupied by " + town->getVisitingHero()->getNameTranslated());
 
 	return iAmElementar();
 }

+ 1 - 1
AI/VCAI/Goals/Build.cpp

@@ -36,7 +36,7 @@ TGoalVec Build::getAllPossibleSubgoals()
 		if(!t->hasBuilt(ai->ah->getMaxPossibleGoldBuilding(t)) && expensiveBuilding.has_value())
 		{
 			auto potentialBuilding = expensiveBuilding.value();
-			switch(expensiveBuilding.value().bid)
+			switch(expensiveBuilding.value().bid.toEnum())
 			{
 			case BuildingID::TOWN_HALL:
 			case BuildingID::CITY_HALL:

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

@@ -32,7 +32,7 @@ TSubgoal BuildThis::whatToDoToAchieve()
 
 	// find town if not set
 	if(!town && hero)
-		town = hero->visitedTown;
+		town = hero->getVisitedTown();
 
 	if(!town)
 	{

+ 2 - 2
AI/VCAI/Goals/BuildThis.h

@@ -27,14 +27,14 @@ namespace Goals
 		BuildThis(BuildingID Bid, const CGTownInstance * tid)
 			: CGoal(Goals::BUILD_STRUCTURE)
 		{
-			bid = Bid;
+			bid = Bid.getNum();
 			town = tid;
 			priority = 1;
 		}
 		BuildThis(BuildingID Bid)
 			: CGoal(Goals::BUILD_STRUCTURE)
 		{
-			bid = Bid;
+			bid = Bid.getNum();
 			priority = 1;
 		}
 		TGoalVec getAllPossibleSubgoals() override

+ 4 - 4
AI/VCAI/Goals/CollectRes.cpp

@@ -41,7 +41,7 @@ TGoalVec CollectRes::getAllPossibleSubgoals()
 		switch (obj->ID.num)
 		{
 		case Obj::TREASURE_CHEST:
-			return resID == GameResID(EGameResID::GOLD);
+			return resID == GameResID(EGameResID::GOLD).getNum();
 			break;
 		case Obj::RESOURCE:
 			return dynamic_cast<const CGResource*>(obj)->resourceID() == GameResID(resID);
@@ -62,13 +62,13 @@ TGoalVec CollectRes::getAllPossibleSubgoals()
 			}
 			break;
 		case Obj::MYSTICAL_GARDEN:
-			if ((resID != GameResID(EGameResID::GOLD)) && (resID != GameResID(EGameResID::GEMS)))
+			if (resID != GameResID(EGameResID::GOLD).getNum() && resID != GameResID(EGameResID::GEMS).getNum())
 				return false;
 			break;
 		case Obj::WATER_WHEEL:
 		case Obj::LEAN_TO:
 		case Obj::WAGON:
-			if (resID != GameResID(EGameResID::GOLD))
+			if (resID != GameResID(EGameResID::GOLD).getNum())
 				return false;
 			break;
 		default:
@@ -177,7 +177,7 @@ TSubgoal CollectRes::whatToDoToTrade()
 				continue;
 			int toGive = -1;
 			int toReceive = -1;
-			m->getOffer(i, resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
+			m->getOffer(i.getNum(), resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
 			assert(toGive > 0 && toReceive > 0);
 			howManyCanWeBuy += toReceive * (ai->ah->freeResources()[i] / toGive);
 		}

+ 42 - 30
AI/VCAI/Goals/CompleteQuest.cpp

@@ -8,7 +8,15 @@
 *
 */
 #include "StdInc.h"
-#include "Goals.h"
+#include "CompleteQuest.h"
+
+#include "CollectRes.h"
+#include "FindObj.h"
+#include "GatherArmy.h"
+#include "GatherTroops.h"
+#include "GetArtOfType.h"
+#include "RecruitHero.h"
+
 #include "../VCAI.h"
 #include "../FuzzyHelper.h"
 #include "../AIhelper.h"
@@ -18,45 +26,47 @@ using namespace Goals;
 
 bool CompleteQuest::operator==(const CompleteQuest & other) const
 {
-	return q.quest->qid == other.q.quest->qid;
+	return q.getQuest(cb) == other.q.getQuest(cb);
 }
 
 bool isKeyMaster(const QuestInfo & q)
 {
-	return q.obj && (q.obj->ID == Obj::BORDER_GATE || q.obj->ID == Obj::BORDERGUARD);
+	auto object = q.getObject(cb);
+	return object && (object->ID == Obj::BORDER_GATE || object->ID == Obj::BORDERGUARD);
 }
 
 TGoalVec CompleteQuest::getAllPossibleSubgoals()
 {
 	TGoalVec solutions;
+	auto quest = q.getQuest(cb);
 
-	if(!q.quest->isCompleted)
+	if(!quest->isCompleted)
 	{
 		logAi->debug("Trying to realize quest: %s", questToString());
 		
 		if(isKeyMaster(q))
 			return missionKeymaster();
 
-		if(!q.quest->mission.artifacts.empty())
+		if(!quest->mission.artifacts.empty())
 			return missionArt();
 
-		if(!q.quest->mission.heroes.empty())
+		if(!quest->mission.heroes.empty())
 			return missionHero();
 
-		if(!q.quest->mission.creatures.empty())
+		if(!quest->mission.creatures.empty())
 			return missionArmy();
 
-		if(q.quest->mission.resources.nonZero())
+		if(quest->mission.resources.nonZero())
 			return missionResources();
 
-		if(q.quest->killTarget != ObjectInstanceID::NONE)
+		if(quest->killTarget != ObjectInstanceID::NONE)
 			return missionDestroyObj();
 
-		for(auto & s : q.quest->mission.primary)
+		for(auto & s : quest->mission.primary)
 			if(s)
 				return missionIncreasePrimaryStat();
 
-		if(q.quest->mission.heroLevel > 0)
+		if(quest->mission.heroLevel > 0)
 			return missionLevel();
 	}
 
@@ -65,7 +75,7 @@ TGoalVec CompleteQuest::getAllPossibleSubgoals()
 
 TSubgoal CompleteQuest::whatToDoToAchieve()
 {
-	if(q.quest->mission == Rewardable::Limiter{})
+	if(q.getQuest(cb)->mission == Rewardable::Limiter{})
 	{
 		throw cannotFulfillGoalException("Can not complete inactive quest");
 	}
@@ -99,11 +109,13 @@ std::string CompleteQuest::completeMessage() const
 
 std::string CompleteQuest::questToString() const
 {
-	if(q.quest->questName == CQuest::missionName(EQuestMission::NONE))
+	auto quest = q.getQuest(cb);
+
+	if(quest->questName == CQuest::missionName(EQuestMission::NONE))
 		return "inactive quest";
 
 	MetaString ms;
-	q.quest->getRolloverText(q.obj->cb, ms, false);
+	quest->getRolloverText(cb, ms, false);
 
 	return ms.toString();
 }
@@ -116,9 +128,9 @@ TGoalVec CompleteQuest::tryCompleteQuest() const
 
 	for(auto hero : heroes)
 	{
-		if(q.quest->checkQuest(hero))
+		if(q.getQuest(cb)->checkQuest(hero))
 		{
-			vstd::concatenate(solutions, ai->ah->howToVisitObj(hero, ObjectIdRef(q.obj->id)));
+			vstd::concatenate(solutions, ai->ah->howToVisitObj(hero, ObjectIdRef(q.getObject(cb)->id)));
 		}
 	}
 
@@ -132,9 +144,9 @@ TGoalVec CompleteQuest::missionArt() const
 	if(!solutions.empty())
 		return solutions;
 
-	for(auto art : q.quest->mission.artifacts)
+	for(auto art : q.getQuest(cb)->mission.artifacts)
 	{
-		solutions.push_back(sptr(GetArtOfType(art))); //TODO: transport?
+		solutions.push_back(sptr(GetArtOfType(art.getNum()))); //TODO: transport?
 	}
 
 	return solutions;
@@ -160,9 +172,9 @@ TGoalVec CompleteQuest::missionArmy() const
 	if(!solutions.empty())
 		return solutions;
 
-	for(auto creature : q.quest->mission.creatures)
+	for(auto creature : q.getQuest(cb)->mission.creatures)
 	{
-		solutions.push_back(sptr(GatherTroops(creature.getId(), creature.count)));
+		solutions.push_back(sptr(GatherTroops(creature.getId().getNum(), creature.count)));
 	}
 
 	return solutions;
@@ -174,7 +186,7 @@ TGoalVec CompleteQuest::missionIncreasePrimaryStat() const
 
 	if(solutions.empty())
 	{
-		for(int i = 0; i < q.quest->mission.primary.size(); ++i)
+		for(int i = 0; i < q.getQuest(cb)->mission.primary.size(); ++i)
 		{
 			// TODO: library, school and other boost objects
 			logAi->debug("Don't know how to increase primary stat %d", i);
@@ -190,7 +202,7 @@ TGoalVec CompleteQuest::missionLevel() const
 
 	if(solutions.empty())
 	{
-		logAi->debug("Don't know how to reach hero level %d", q.quest->mission.heroLevel);
+		logAi->debug("Don't know how to reach hero level %d", q.getQuest(cb)->mission.heroLevel);
 	}
 
 	return solutions;
@@ -202,7 +214,7 @@ TGoalVec CompleteQuest::missionKeymaster() const
 
 	if(solutions.empty())
 	{
-		solutions.push_back(sptr(Goals::FindObj(Obj::KEYMASTER, q.obj->subID)));
+		solutions.push_back(sptr(Goals::FindObj(Obj::KEYMASTER, q.getObject(cb)->subID)));
 	}
 
 	return solutions;
@@ -216,16 +228,16 @@ TGoalVec CompleteQuest::missionResources() const
 
 	if(heroes.size())
 	{
-		if(q.quest->checkQuest(heroes.front())) //it doesn't matter which hero it is
+		if(q.getQuest(cb)->checkQuest(heroes.front())) //it doesn't matter which hero it is
 		{
-			return ai->ah->howToVisitObj(q.obj);
+			return ai->ah->howToVisitObj(q.getObject(cb));
 		}
 		else
 		{
-			for(int i = 0; i < q.quest->mission.resources.size(); ++i)
+			for(int i = 0; i < q.getQuest(cb)->mission.resources.size(); ++i)
 			{
-				if(q.quest->mission.resources[i])
-					solutions.push_back(sptr(CollectRes(static_cast<EGameResID>(i), q.quest->mission.resources[i])));
+				if(q.getQuest(cb)->mission.resources[i])
+					solutions.push_back(sptr(CollectRes(static_cast<EGameResID>(i), q.getQuest(cb)->mission.resources[i])));
 			}
 		}
 	}
@@ -241,10 +253,10 @@ TGoalVec CompleteQuest::missionDestroyObj() const
 {
 	TGoalVec solutions;
 
-	auto obj = cb->getObj(q.quest->killTarget);
+	auto obj = cb->getObj(q.getQuest(cb)->killTarget);
 
 	if(!obj)
-		return ai->ah->howToVisitObj(q.obj);
+		return ai->ah->howToVisitObj(q.getObject(cb));
 
 	if(obj->ID == Obj::HERO)
 	{

+ 0 - 1
AI/VCAI/Goals/CompleteQuest.h

@@ -10,7 +10,6 @@
 #pragma once
 
 #include "CGoal.h"
-#include "../../../lib/GameLibrary.h"
 #include "../../../lib/gameState/QuestInfo.h"
 
 namespace Goals

+ 2 - 2
AI/VCAI/Goals/GatherArmy.cpp

@@ -56,14 +56,14 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
 		if(waysToVisit.size())
 		{
 			//grab army from town
-			if(!t->visitingHero && ai->ah->howManyReinforcementsCanGet(hero.get(), t))
+			if(!t->getVisitingHero() && ai->ah->howManyReinforcementsCanGet(hero.get(), t))
 			{
 				if(!vstd::contains(ai->townVisitsThisWeek[hero], t))
 					vstd::concatenate(ret, waysToVisit);
 			}
 
 			//buy army in town
-			if (!t->visitingHero || t->visitingHero == hero.get(true))
+			if (!t->getVisitingHero() || t->getVisitingHero() == hero.get(true))
 			{
 				std::vector<int> values = {
 					value,

+ 4 - 4
AI/VCAI/Goals/GatherTroops.cpp

@@ -29,7 +29,7 @@ int GatherTroops::getCreaturesCount(const CArmedInstance * army)
 {
 	int count = 0;
 
-	for(auto stack : army->Slots())
+	for(const auto & stack : army->Slots())
 	{
 		if(objid == stack.second->getCreatureID().num)
 		{
@@ -44,7 +44,7 @@ TSubgoal GatherTroops::whatToDoToAchieve()
 {
 	logAi->trace("Entering GatherTroops::whatToDoToAchieve");
 
-	auto heroes = cb->getHeroesInfo(true);
+	auto heroes = cb->getHeroesInfo();
 
 	for(auto hero : heroes)
 	{
@@ -75,9 +75,9 @@ TGoalVec GatherTroops::getAllPossibleSubgoals()
 
 		if(count >= this->value)
 		{
-			if(t->visitingHero)
+			if(t->getVisitingHero())
 			{
-				solutions.push_back(sptr(VisitObj(t->id.getNum()).sethero(t->visitingHero.get())));
+				solutions.push_back(sptr(VisitObj(t->id.getNum()).sethero(t->getVisitingHero())));
 			}
 			else
 			{

+ 1 - 2
AI/VCAI/Goals/Goals.h

@@ -30,5 +30,4 @@
 #include "ClearWayTo.h"
 #include "DigAtTile.h"
 #include "FindObj.h"
-#include "CompleteQuest.h"
-#include "AdventureSpellCast.h"
+#include "AdventureSpellCast.h"

+ 4 - 4
AI/VCAI/Goals/Win.cpp

@@ -51,7 +51,7 @@ TSubgoal Win::whatToDoToAchieve()
 		switch(goal.condition)
 		{
 		case EventCondition::HAVE_ARTIFACT:
-			return sptr(GetArtOfType(goal.objectType.as<ArtifactID>()));
+			return sptr(GetArtOfType(goal.objectType.as<ArtifactID>().getNum()));
 		case EventCondition::DESTROY:
 		{
 			if(goal.objectID != ObjectInstanceID::NONE)
@@ -83,9 +83,9 @@ TSubgoal Win::whatToDoToAchieve()
 				if(auto h = ai->getHeroWithGrail())
 				{
 					//hero is in a town that can host Grail
-					if(h->visitedTown && !vstd::contains(h->visitedTown->forbiddenBuildings, BuildingID::GRAIL))
+					if(h->getVisitedTown() && !vstd::contains(h->getVisitedTown()->forbiddenBuildings, BuildingID::GRAIL))
 					{
-						const CGTownInstance * t = h->visitedTown;
+						const CGTownInstance * t = h->getVisitedTown();
 						return sptr(BuildThis(BuildingID::GRAIL, t).setpriority(10));
 					}
 					else
@@ -151,7 +151,7 @@ TSubgoal Win::whatToDoToAchieve()
 			//save?
 			return sptr(CollectRes(goal.objectType.as<GameResID>(), goal.value));
 		case EventCondition::HAVE_CREATURES:
-			return sptr(GatherTroops(goal.objectType.as<CreatureID>(), goal.value));
+			return sptr(GatherTroops(goal.objectType.as<CreatureID>().getNum(), goal.value));
 		case EventCondition::TRANSPORT:
 		{
 			//TODO. merge with bring Grail to town? So AI will first dig grail, then transport it using this goal and builds it

+ 4 - 4
AI/VCAI/MapObjectsEvaluator.cpp

@@ -68,7 +68,7 @@ std::optional<int> MapObjectsEvaluator::getObjectValue(const CGObjectInstance *
 	{
 		//special case handling: in-game heroes have hero ID as object subID, but when reading configs available hero object subID's are hero classes
 		auto hero = dynamic_cast<const CGHeroInstance*>(obj);
-		return getObjectValue(obj->ID, hero->getHeroClassID());
+		return getObjectValue(obj->ID.getNum(), hero->getHeroClassID().getNum());
 	}
 	else if(obj->ID == Obj::PRISON)
 	{
@@ -92,7 +92,7 @@ std::optional<int> MapObjectsEvaluator::getObjectValue(const CGObjectInstance *
 	else if(obj->ID == Obj::ARTIFACT)
 	{
 		auto artifactObject = dynamic_cast<const CGArtifact *>(obj);
-		switch(artifactObject->storedArtifact->getType()->aClass)
+		switch(artifactObject->getArtifactInstance()->getType()->aClass)
 		{
 		case CArtifact::EartClass::ART_TREASURE:
 			return 2000;
@@ -111,7 +111,7 @@ std::optional<int> MapObjectsEvaluator::getObjectValue(const CGObjectInstance *
 	else if(obj->ID == Obj::SPELL_SCROLL)
 	{
 		auto scrollObject = dynamic_cast<const CGArtifact *>(obj);
-		auto spell = scrollObject->storedArtifact->getScrollSpellID().toSpell();
+		auto spell = scrollObject->getArtifactInstance()->getScrollSpellID().toSpell();
 		if(spell)
 		{
 			switch(spell->getLevel())
@@ -126,7 +126,7 @@ std::optional<int> MapObjectsEvaluator::getObjectValue(const CGObjectInstance *
 			}
 		}
 		else
-			logAi->warn("AI found spell scroll with invalid spell ID: %s", scrollObject->storedArtifact->getScrollSpellID());
+			logAi->warn("AI found spell scroll with invalid spell ID: %s", scrollObject->getArtifactInstance()->getScrollSpellID());
 	}
 
 	return getObjectValue(obj->ID, obj->subID);

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

@@ -88,7 +88,7 @@ bool AINodeStorage::isBattleNode(const CGPathNode * node) const
 
 std::optional<AIPathNode *> AINodeStorage::getOrCreateNode(const int3 & pos, const EPathfindingLayer layer, int chainNumber)
 {
-	auto chains = nodes[layer][pos.z][pos.x][pos.y];
+	auto chains = nodes[layer.getNum()][pos.z][pos.x][pos.y];
 
 	for(AIPathNode & node : chains)
 	{
@@ -111,7 +111,7 @@ std::optional<AIPathNode *> AINodeStorage::getOrCreateNode(const int3 & pos, con
 std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
 {
 	auto hpos = hero->visitablePos();
-	auto initialNode = getOrCreateNode(hpos, hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND, NORMAL_CHAIN).value();
+	auto initialNode = getOrCreateNode(hpos, hero->inBoat() ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND, NORMAL_CHAIN).value();
 
 	initialNode->turns = 0;
 	initialNode->moveRemains = hero->movementPointsRemaining();
@@ -125,7 +125,7 @@ void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, EPath
 {
 	for(int i = 0; i < NUM_CHAINS; i++)
 	{
-		AIPathNode & heroNode = nodes[layer][coord.z][coord.x][coord.y][i];
+		AIPathNode & heroNode = nodes[layer.getNum()][coord.z][coord.x][coord.y][i];
 
 		heroNode.chainMask = 0;
 		heroNode.danger = 0;
@@ -267,7 +267,7 @@ void AINodeStorage::calculateTownPortalTeleportations(
 
 		for(const CGTownInstance * targetTown : towns)
 		{
-			if(targetTown->visitingHero)
+			if(targetTown->getVisitingHero())
 				continue;
 
 			auto nodeOptional = getOrCreateNode(targetTown->visitablePos(), EPathfindingLayer::LAND, srcNode->chainMask | CAST_CHAIN);
@@ -326,7 +326,7 @@ bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNode
 
 bool AINodeStorage::isTileAccessible(const int3 & pos, const EPathfindingLayer layer) const
 {
-	return nodes[layer][pos.z][pos.x][pos.y][0].action != EPathNodeAction::UNKNOWN;
+	return nodes[layer.getNum()][pos.z][pos.x][pos.y][0].action != EPathNodeAction::UNKNOWN;
 }
 
 std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand) const

+ 2 - 2
AI/VCAI/Pathfinding/AIPathfinderConfig.cpp

@@ -39,7 +39,7 @@ namespace AIPathfinding
 		CPlayerSpecificInfoCallback * cb,
 		VCAI * ai,
 		std::shared_ptr<AINodeStorage> nodeStorage)
-		:PathfinderConfig(nodeStorage, cb, makeRuleset(cb, ai, nodeStorage)), hero(nodeStorage->getHero())
+		:PathfinderConfig(nodeStorage, *cb, makeRuleset(cb, ai, nodeStorage)), hero(nodeStorage->getHero())
 	{
 		options.ignoreGuards = false;
 		options.useEmbarkAndDisembark = true;
@@ -51,7 +51,7 @@ namespace AIPathfinding
 
 	AIPathfinderConfig::~AIPathfinderConfig() = default;
 
-	CPathfinderHelper * AIPathfinderConfig::getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs)
+	CPathfinderHelper * AIPathfinderConfig::getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState & gs)
 	{
 		if(!helper)
 		{

+ 1 - 1
AI/VCAI/Pathfinding/AIPathfinderConfig.h

@@ -30,6 +30,6 @@ namespace AIPathfinding
 
 		~AIPathfinderConfig();
 
-		CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs) override;
+		CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState & gs) override;
 	};
 }

+ 4 - 2
AI/VCAI/Pathfinding/PathfindingManager.cpp

@@ -12,7 +12,9 @@
 #include "AIPathfinder.h"
 #include "AIPathfinderConfig.h"
 #include "../Goals/Goals.h"
+#include "../Goals/CompleteQuest.h"
 #include "../../../lib/CGameInfoCallback.h"
+#include "../../../lib/gameState/QuestInfo.h"
 #include "../../../lib/mapping/CMapDefines.h"
 #include "../../../lib/mapObjects/CQuest.h"
 
@@ -190,7 +192,7 @@ Goals::TSubgoal PathfindingManager::clearWayTo(HeroPtr hero, int3 firstTileToGet
 	if(isBlockedBorderGate(firstTileToGet))
 	{
 		//FIXME: this way we'll not visit gate and activate quest :?
-		return sptr(Goals::FindObj(Obj::KEYMASTER, cb->getTile(firstTileToGet)->visitableObjects.back()->getObjTypeIndex()));
+		return sptr(Goals::FindObj(Obj::KEYMASTER, cb->getTopObj(firstTileToGet)->getObjTypeIndex()));
 	}
 
 	auto topObj = cb->getTopObj(firstTileToGet);
@@ -224,7 +226,7 @@ Goals::TSubgoal PathfindingManager::clearWayTo(HeroPtr hero, int3 firstTileToGet
 
 			if(questObj)
 			{
-				auto questInfo = QuestInfo(questObj->quest, topObj, topObj->visitablePos());
+				auto questInfo = QuestInfo(topObj->id);
 
 				return sptr(Goals::CompleteQuest(questInfo));
 			}

+ 38 - 39
AI/VCAI/VCAI.cpp

@@ -13,6 +13,7 @@
 #include "ResourceManager.h"
 #include "BuildingManager.h"
 #include "Goals/Goals.h"
+#include "Goals/CompleteQuest.h"
 
 #include "../../lib/ArtifactUtils.h"
 #include "../../lib/AsyncRunner.h"
@@ -29,6 +30,7 @@
 #include "../../lib/bonuses/Updaters.h"
 #include "../../lib/bonuses/Propagators.h"
 #include "../../lib/entities/building/CBuilding.h"
+#include "../../lib/mapObjects/CQuest.h"
 #include "../../lib/networkPacks/PacksForClient.h"
 #include "../../lib/networkPacks/PacksForClientBattle.h"
 #include "../../lib/networkPacks/PacksForServer.h"
@@ -136,7 +138,7 @@ void VCAI::heroMoved(const TryMoveHero & details, bool verbose)
 	else if(details.result == TryMoveHero::EMBARK && hero)
 	{
 		//make sure AI not attempt to visit used boat
-		validateObject(hero->boat);
+		validateObject(hero->getBoat());
 	}
 	else if(details.result == TryMoveHero::DISEMBARK && o1)
 	{
@@ -362,7 +364,7 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q
 
 void VCAI::heroPrimarySkillChanged(const CGHeroInstance * hero, PrimarySkill which, si64 val)
 {
-	LOG_TRACE_PARAMS(logAi, "which '%i', val '%i'", static_cast<int>(which) % val);
+	LOG_TRACE_PARAMS(logAi, "which '%i', val '%i'", which.getNum() % val);
 	NET_EVENT_HANDLER;
 }
 
@@ -446,13 +448,13 @@ void VCAI::objectRemoved(const CGObjectInstance * obj, const PlayerColor & initi
 	//TODO: Find better way to handle hero boat removal
 	if(auto hero = dynamic_cast<const CGHeroInstance *>(obj))
 	{
-		if(hero->boat)
+		if(hero->inBoat())
 		{
-			vstd::erase_if_present(visitableObjs, hero->boat);
-			vstd::erase_if_present(alreadyVisited, hero->boat);
+			vstd::erase_if_present(visitableObjs, hero->getBoat());
+			vstd::erase_if_present(alreadyVisited, hero->getBoat());
 
 			for(auto h : cb->getHeroesInfo())
-				unreserveObject(h, hero->boat);
+				unreserveObject(h, hero->getBoat());
 		}
 	}
 
@@ -486,8 +488,8 @@ void VCAI::playerBonusChanged(const Bonus & bonus, bool gain)
 void VCAI::heroCreated(const CGHeroInstance * h)
 {
 	LOG_TRACE(logAi);
-	if(h->visitedTown)
-		townVisitsThisWeek[HeroPtr(h)].insert(h->visitedTown);
+	if(h->getVisitedTown())
+		townVisitsThisWeek[HeroPtr(h)].insert(h->getVisitedTown());
 	NET_EVENT_HANDLER;
 }
 
@@ -625,7 +627,7 @@ void VCAI::initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<C
 	playerID = *myCb->getPlayerID();
 	myCb->waitTillRealize = true;
 	myCb->unlockGsWhenWaiting = true;
-	pathfinderCache = std::make_unique<PathfinderCache>(myCb.get(), PathfinderOptions(myCb.get()));
+	pathfinderCache = std::make_unique<PathfinderCache>(myCb.get(), PathfinderOptions(*myCb));
 
 	if(!fh)
 		fh = new FuzzyHelper();
@@ -671,7 +673,7 @@ void VCAI::commanderGotLevel(const CCommanderInstance * commander, std::vector<u
 {
 	LOG_TRACE_PARAMS(logAi, "queryID '%i'", queryID);
 	NET_EVENT_HANDLER;
-	status.addQuery(queryID, boost::str(boost::format("Commander %s of %s got level %d") % commander->name % commander->armyObj->nodeName() % (int)commander->level));
+	status.addQuery(queryID, boost::str(boost::format("Commander %s of %s got level %d") % commander->name % commander->getArmy()->nodeName() % static_cast<int>(commander->level)));
 	executeActionAsync("commanderGotLevel", [this, queryID](){ answerQuery(queryID, 0); });
 }
 
@@ -1073,12 +1075,12 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
 	{
 	case Obj::TOWN:
 		moveCreaturesToHero(dynamic_cast<const CGTownInstance *>(obj));
-		if(h->visitedTown) //we are inside, not just attacking
+		if(h->getVisitedTown()) //we are inside, not just attacking
 		{
-			townVisitsThisWeek[h].insert(h->visitedTown);
+			townVisitsThisWeek[h].insert(h->getVisitedTown());
 			if(!h->hasSpellbook() && ah->freeGold() >= GameConstants::SPELLBOOK_GOLD_COST)
 			{
-				if(h->visitedTown->hasBuilt(BuildingID::MAGES_GUILD_1))
+				if(h->getVisitedTown()->hasBuilt(BuildingID::MAGES_GUILD_1))
 					cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK);
 			}
 		}
@@ -1089,9 +1091,9 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
 
 void VCAI::moveCreaturesToHero(const CGTownInstance * t)
 {
-	if(t->visitingHero && t->armedGarrison() && t->visitingHero->tempOwner == t->tempOwner)
+	if(t->getVisitingHero() && t->armedGarrison() && t->getVisitingHero()->tempOwner == t->tempOwner)
 	{
-		pickBestCreatures(t->visitingHero, t);
+		pickBestCreatures(t->getVisitingHero(), t);
 	}
 }
 
@@ -1175,22 +1177,22 @@ void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * ot
 			{
 				for(auto p : h->artifactsWorn)
 				{
-					if(p.second.artifact)
+					if(p.second.getArt())
 						allArtifacts.push_back(ArtifactLocation(h->id, p.first));
 				}
 			}
 			for(auto slot : h->artifactsInBackpack)
-				allArtifacts.push_back(ArtifactLocation(h->id, h->getArtPos(slot.artifact)));
+				allArtifacts.push_back(ArtifactLocation(h->id, h->getArtPos(slot.getArt())));
 
 			if(otherh)
 			{
 				for(auto p : otherh->artifactsWorn)
 				{
-					if(p.second.artifact)
+					if(p.second.getArt())
 						allArtifacts.push_back(ArtifactLocation(otherh->id, p.first));
 				}
 				for(auto slot : otherh->artifactsInBackpack)
-					allArtifacts.push_back(ArtifactLocation(otherh->id, otherh->getArtPos(slot.artifact)));
+					allArtifacts.push_back(ArtifactLocation(otherh->id, otherh->getArtPos(slot.getArt())));
 			}
 			//we give stuff to one hero or another, depending on giveStuffToFirstHero
 
@@ -1211,7 +1213,7 @@ void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * ot
 				auto s = cb->getHero(location.artHolder)->getSlot(location.slot);
 				if(!s || s->locked) //we can't move locks
 					continue;
-				auto artifact = s->artifact;
+				auto artifact = s->getArt();
 				if(!artifact)
 					continue;
 				//FIXME: why are the above possible to be null?
@@ -1233,13 +1235,13 @@ void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * ot
 					for(auto slot : artifact->getType()->getPossibleSlots().at(target->bearerType()))
 					{
 						auto otherSlot = target->getSlot(slot);
-						if(otherSlot && otherSlot->artifact) //we need to exchange artifact for better one
+						if(otherSlot && otherSlot->getArt()) //we need to exchange artifact for better one
 						{
 							//if that artifact is better than what we have, pick it
-							if(compareArtifacts(artifact, otherSlot->artifact) && artifact->canBePutAt(target, slot, true)) //combined artifacts are not always allowed to move
+							if(compareArtifacts(artifact, otherSlot->getArt()) && artifact->canBePutAt(target, slot, true)) //combined artifacts are not always allowed to move
 							{
 								ArtifactLocation destLocation(target->id, slot);
-								cb->swapArtifacts(location, ArtifactLocation(target->id, target->getArtPos(otherSlot->artifact)));
+								cb->swapArtifacts(location, ArtifactLocation(target->id, target->getArtPos(otherSlot->getArt())));
 								changeMade = true;
 								break;
 							}
@@ -1363,10 +1365,10 @@ void VCAI::wander(HeroPtr h)
 {
 	auto visitTownIfAny = [this](HeroPtr h) -> bool
 	{
-		if (h->visitedTown)
+		if (h->getVisitedTown())
 		{
-			townVisitsThisWeek[h].insert(h->visitedTown);
-			buildArmyIn(h->visitedTown);
+			townVisitsThisWeek[h].insert(h->getVisitedTown());
+			buildArmyIn(h->getVisitedTown());
 			return true;
 		}
 		return false;
@@ -1434,7 +1436,7 @@ void VCAI::wander(HeroPtr h)
 			std::vector<const CGTownInstance *> townsNotReachable;
 			for(const CGTownInstance * t : cb->getTownsInfo())
 			{
-				if(!t->visitingHero && !vstd::contains(townVisitsThisWeek[h], t))
+				if(!t->getVisitingHero() && !vstd::contains(townVisitsThisWeek[h], t))
 				{
 					if(isAccessibleForHero(t->visitablePos(), h))
 						townsReachable.push_back(t);
@@ -1762,7 +1764,7 @@ const CGObjectInstance * VCAI::lookForArt(ArtifactID aid) const
 {
 	for(const CGObjectInstance * obj : ai->visitableObjs)
 	{
-		if(obj->ID == Obj::ARTIFACT && dynamic_cast<const CGArtifact *>(obj)->getArtifact()  == aid)
+		if(obj->ID == Obj::ARTIFACT && dynamic_cast<const CGArtifact *>(obj)->getArtifactType()  == aid)
 			return obj;
 	}
 
@@ -1866,10 +1868,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 
 		auto getObj = [&](int3 coord, bool ignoreHero)
 		{
-			auto tile = cb->getTile(coord, false);
-			assert(tile);
-			return tile->topVisitableObj(ignoreHero);
-			//return cb->getTile(coord,false)->topVisitableObj(ignoreHero);
+			return cb->getObj(cb->getTile(coord)->topVisitableObj(ignoreHero));
 		};
 
 		auto isTeleportAction = [&](EPathNodeAction action) -> bool
@@ -2157,7 +2156,7 @@ void VCAI::tryRealize(Goals::Trade & g) //trade
 
 				int toGive;
 				int toGet;
-				m->getOffer(res, g.resID, toGive, toGet, EMarketMode::RESOURCE_RESOURCE);
+				m->getOffer(res.getNum(), g.resID, toGive, toGet, EMarketMode::RESOURCE_RESOURCE);
 				toGive = static_cast<int>(toGive * (it->resVal / toGive)); //round down
 				//TODO trade only as much as needed
 				if (toGive) //don't try to sell 0 resources
@@ -2251,7 +2250,7 @@ void VCAI::tryRealize(Goals::AbstractGoal & g)
 const CGTownInstance * VCAI::findTownWithTavern() const
 {
 	for(const CGTownInstance * t : cb->getTownsInfo())
-		if(t->hasBuilt(BuildingID::TAVERN) && !t->visitingHero)
+		if(t->hasBuilt(BuildingID::TAVERN) && !t->getVisitingHero())
 			return t;
 
 	return nullptr;
@@ -2455,7 +2454,7 @@ void VCAI::performTypicalActions()
 
 void VCAI::buildArmyIn(const CGTownInstance * t)
 {
-	makePossibleUpgrades(t->visitingHero);
+	makePossibleUpgrades(t->getVisitingHero());
 	makePossibleUpgrades(t);
 	recruitCreatures(t, t->getUpperArmy());
 	moveCreaturesToHero(t);
@@ -2808,7 +2807,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
 	{
 		for(auto q : ai->myCb->getMyQuests())
 		{
-			if(q.obj == obj)
+			if(q.getObject(cb) == obj)
 			{
 				return false; // do not visit guards or gates when wandering
 			}
@@ -2822,9 +2821,9 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
 	{
 		for(auto q : ai->myCb->getMyQuests())
 		{
-			if(q.obj == obj)
+			if(q.getObject(cb) == obj)
 			{
-				if(q.quest->checkQuest(h.h))
+				if(q.getQuest(cb)->checkQuest(h.h))
 					return true; //we completed the quest
 				else
 					return false; //we can't complete this quest
@@ -2850,7 +2849,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
 	}
 	case Obj::HILL_FORT:
 	{
-		for(auto slot : h->Slots())
+		for(const auto & slot : h->Slots())
 		{
 			if(slot.second->getType()->hasUpgrades())
 				return true; //TODO: check price?

+ 6 - 9
CCallback.cpp

@@ -273,7 +273,7 @@ void CCallback::spellResearch( const CGTownInstance *town, SpellID spellAtSlot,
 
 void CCallback::swapGarrisonHero( const CGTownInstance *town )
 {
-	if(town->tempOwner == *player || (town->garrisonHero && town->garrisonHero->tempOwner == *player ))
+	if(town->tempOwner == *player || (town->getGarrisonHero() && town->getGarrisonHero()->tempOwner == *player ))
 	{
 		GarrisonHeroSwap pack(town->id);
 		sendRequest(pack);
@@ -365,11 +365,10 @@ void CCallback::buildBoat( const IShipyard *obj )
 	sendRequest(bb);
 }
 
-CCallback::CCallback(CGameState * GS, std::optional<PlayerColor> Player, CClient * C)
+CCallback::CCallback(std::shared_ptr<CGameState> gamestate, std::optional<PlayerColor> Player, CClient * C)
 	: CBattleCallback(Player, C)
+	, gamestate(gamestate)
 {
-	gs = GS;
-
 	waitTillRealize = false;
 	unlockGsWhenWaiting = false;
 }
@@ -379,7 +378,7 @@ CCallback::~CCallback() = default;
 bool CCallback::canMoveBetween(const int3 &a, const int3 &b)
 {
 	//bidirectional
-	return gs->getMap().canMoveBetween(a, b);
+	return gameState().getMap().canMoveBetween(a, b);
 }
 
 std::optional<PlayerColor> CCallback::getPlayerID() const
@@ -389,10 +388,10 @@ std::optional<PlayerColor> CCallback::getPlayerID() const
 
 int3 CCallback::getGuardingCreaturePosition(int3 tile)
 {
-	if (!gs->getMap().isInTheMap(tile))
+	if (!gameState().getMap().isInTheMap(tile))
 		return int3(-1,-1,-1);
 
-	return gs->getMap().guardingCreaturePositions[tile.z][tile.x][tile.y];
+	return gameState().getMap().guardingCreaturePositions[tile.z][tile.x][tile.y];
 }
 
 void CCallback::dig( const CGObjectInstance *hero )
@@ -437,7 +436,6 @@ CBattleCallback::CBattleCallback(std::optional<PlayerColor> player, CClient * C)
 
 void CBattleCallback::battleMakeUnitAction(const BattleID & battleID, const BattleAction & action)
 {
-	assert(!cl->gs->getBattle(battleID)->tacticDistance);
 	MakeAction ma;
 	ma.ba = action;
 	ma.battleID = battleID;
@@ -446,7 +444,6 @@ void CBattleCallback::battleMakeUnitAction(const BattleID & battleID, const Batt
 
 void CBattleCallback::battleMakeTacticAction(const BattleID & battleID, const BattleAction & action )
 {
-	assert(cl->gs->getBattle(battleID)->tacticDistance);
 	MakeAction ma;
 	ma.ba = action;
 	ma.battleID = battleID;

+ 6 - 1
CCallback.h

@@ -150,8 +150,13 @@ public:
 
 class CCallback : public CPlayerSpecificInfoCallback, public CBattleCallback, public IGameActionCallback
 {
+	std::shared_ptr<CGameState> gamestate;
+
+	CGameState & gameState() final { return *gamestate; }
+	const CGameState & gameState() const final { return *gamestate; }
+
 public:
-	CCallback(CGameState * GS, std::optional<PlayerColor> Player, CClient * C);
+	CCallback(std::shared_ptr<CGameState> gamestate, std::optional<PlayerColor> Player, CClient * C);
 	virtual ~CCallback();
 
 	//client-specific functionalities (pathfinding)

+ 2 - 4
Global.h

@@ -247,8 +247,6 @@ using TLockGuardRec = std::lock_guard<std::recursive_mutex>;
 #  define DLL_LINKAGE DLL_IMPORT
 #endif
 
-#define THROW_FORMAT(message, formatting_elems)  throw std::runtime_error(boost::str(boost::format(message) % formatting_elems))
-
 // old iOS SDKs compatibility
 #ifdef VCMI_IOS
 #include <AvailabilityVersions.h>
@@ -574,7 +572,7 @@ namespace vstd
 
 	//Returns iterator to the element for which the value of ValueFunction is minimal
 	template<class ForwardRange, class ValueFunction>
-	auto minElementByFun(const ForwardRange& rng, ValueFunction vf) -> decltype(std::begin(rng))
+	auto minElementByFun(const ForwardRange& rng, ValueFunction vf)
 	{
 		/* Clang crashes when instantiating this function template and having PCH compilation enabled.
 		 * There is a bug report here: http://llvm.org/bugs/show_bug.cgi?id=18744
@@ -589,7 +587,7 @@ namespace vstd
 
 	//Returns iterator to the element for which the value of ValueFunction is maximal
 	template<class ForwardRange, class ValueFunction>
-	auto maxElementByFun(const ForwardRange& rng, ValueFunction vf) -> decltype(std::begin(rng))
+	auto maxElementByFun(const ForwardRange& rng, ValueFunction vf)
 	{
 		/* Clang crashes when instantiating this function template and having PCH compilation enabled.
 		 * There is a bug report here: http://llvm.org/bugs/show_bug.cgi?id=18744

+ 6 - 5
client/ArtifactsUIController.cpp

@@ -12,17 +12,18 @@
 #include "ArtifactsUIController.h"
 #include "CPlayerInterface.h"
 
-#include "../CCallback.h"
-#include "../lib/ArtifactUtils.h"
-#include "../lib/texts/CGeneralTextHandler.h"
-#include "../lib/mapObjects/CGHeroInstance.h"
-
 #include "GameEngine.h"
 #include "GameInstance.h"
 #include "gui/WindowHandler.h"
 #include "widgets/CComponent.h"
 #include "windows/CWindowWithArtifacts.h"
 
+#include "../CCallback.h"
+#include "../lib/ArtifactUtils.h"
+#include "../lib/GameLibrary.h"
+#include "../lib/mapObjects/CGHeroInstance.h"
+#include "../lib/texts/CGeneralTextHandler.h"
+
 ArtifactsUIController::ArtifactsUIController()
 {
 	numOfMovedArts = 0;

+ 10 - 22
client/CPlayerInterface.cpp

@@ -118,18 +118,6 @@
 
 std::shared_ptr<BattleInterface> CPlayerInterface::battleInt;
 
-struct HeroObjectRetriever
-{
-	const CGHeroInstance * operator()(const ConstTransitivePtr<CGHeroInstance> &h) const
-	{
-		return h;
-	}
-	const CGHeroInstance * operator()(const ConstTransitivePtr<CStackInstance> &s) const
-	{
-		return nullptr;
-	}
-};
-
 CPlayerInterface::CPlayerInterface(PlayerColor Player):
 	localState(std::make_unique<PlayerLocalState>(*this)),
 	movementController(std::make_unique<HeroMovementController>()),
@@ -165,7 +153,7 @@ void CPlayerInterface::initGameInterface(std::shared_ptr<Environment> ENV, std::
 	cb = CB;
 	env = ENV;
 
-	pathfinderCache = std::make_unique<PathfinderCache>(cb.get(), PathfinderOptions(cb.get()));
+	pathfinderCache = std::make_unique<PathfinderCache>(cb.get(), PathfinderOptions(*cb));
 	ENGINE->music().loadTerrainMusicThemes();
 	initializeHeroTownList();
 
@@ -542,18 +530,18 @@ void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 
-	if(town->garrisonHero) //wandering hero moved to the garrison
+	if(town->getGarrisonHero()) //wandering hero moved to the garrison
 	{
 		// This method also gets called on hero recruitment -> garrisoned hero is already in garrison
-		if(town->garrisonHero->tempOwner == playerID && vstd::contains(localState->getWanderingHeroes(), town->garrisonHero))
-			localState->removeWanderingHero(town->garrisonHero);
+		if(town->getGarrisonHero()->tempOwner == playerID && vstd::contains(localState->getWanderingHeroes(), town->getGarrisonHero()))
+			localState->removeWanderingHero(town->getGarrisonHero());
 	}
 
-	if(town->visitingHero) //hero leaves garrison
+	if(town->getVisitingHero()) //hero leaves garrison
 	{
 		// This method also gets called on hero recruitment -> wandering heroes already contains new hero
-		if(town->visitingHero->tempOwner == playerID && !vstd::contains(localState->getWanderingHeroes(), town->visitingHero))
-			localState->addWanderingHero(town->visitingHero);
+		if(town->getVisitingHero()->tempOwner == playerID && !vstd::contains(localState->getWanderingHeroes(), town->getVisitingHero()))
+			localState->addWanderingHero(town->getVisitingHero());
 	}
 	adventureInt->onHeroChanged(nullptr);
 	adventureInt->onTownChanged(town);
@@ -611,8 +599,8 @@ void CPlayerInterface::garrisonsChanged(std::vector<const CArmedInstance *> objs
 			localState->verifyPath(hero);
 
 			adventureInt->onHeroChanged(hero);
-			if(hero->inTownGarrison && hero->visitedTown != town)
-				adventureInt->onTownChanged(hero->visitedTown);
+			if(hero->isGarrisoned() && hero->getVisitedTown() != town)
+				adventureInt->onTownChanged(hero->getVisitedTown());
 		}
 	}
 
@@ -1363,7 +1351,7 @@ void CPlayerInterface::initializeHeroTownList()
 	{
 		for(auto & hero : cb->getHeroesInfo())
 		{
-			if(!hero->inTownGarrison)
+			if(!hero->isGarrisoned())
 				localState->addWanderingHero(hero);
 		}
 	}

+ 4 - 4
client/CServerHandler.cpp

@@ -606,7 +606,7 @@ void CServerHandler::startMapAfterConnection(std::shared_ptr<CMapInfo> to)
 	mapToStart = to;
 }
 
-void CServerHandler::startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameState)
+void CServerHandler::startGameplay(std::shared_ptr<CGameState> gameState)
 {
 	if(GAME->mainmenu())
 		GAME->mainmenu()->disable();
@@ -634,11 +634,11 @@ void CServerHandler::startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameSta
 
 void CServerHandler::showHighScoresAndEndGameplay(PlayerColor player, bool victory, const StatisticDataSet & statistic)
 {
-	HighScoreParameter param = HighScore::prepareHighScores(client->gameState(), player, victory);
+	HighScoreParameter param = HighScore::prepareHighScores(&client->gameState(), player, victory);
 
-	if(victory && client->gameState()->getStartInfo()->campState)
+	if(victory && client->gameState().getStartInfo()->campState)
 	{
-		startCampaignScenario(param, client->gameState()->getStartInfo()->campState, statistic);
+		startCampaignScenario(param, client->gameState().getStartInfo()->campState, statistic);
 	}
 	else
 	{

+ 1 - 1
client/CServerHandler.h

@@ -200,7 +200,7 @@ public:
 	bool validateGameStart(bool allowOnlyAI = false) const;
 	void debugStartTest(std::string filename, bool save = false);
 
-	void startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameState = nullptr);
+	void startGameplay(std::shared_ptr<CGameState> gameState);
 	void showHighScoresAndEndGameplay(PlayerColor player, bool victory, const StatisticDataSet & statistic);
 	void endNetwork();
 	void endGameplay();

+ 30 - 36
client/Client.cpp

@@ -83,7 +83,6 @@ const CPlayerEnvironment::GameCb * CPlayerEnvironment::game() const
 CClient::CClient()
 {
 	waitingRequest.clear();
-	gs = nullptr;
 }
 
 CClient::~CClient() = default;
@@ -113,20 +112,14 @@ events::EventBus * CClient::eventBus() const
 	return clientEventBus.get();
 }
 
-void CClient::newGame(CGameState * initializedGameState)
+void CClient::newGame(std::shared_ptr<CGameState> initializedGameState)
 {
 	GAME->server().th->update();
 	CMapService mapService;
 	assert(initializedGameState);
-	gs = initializedGameState;
-	gs->preInit(LIBRARY, this);
+	gamestate = initializedGameState;
+	gamestate->preInit(LIBRARY);
 	logNetwork->trace("\tCreating gamestate: %i", GAME->server().th->getDiff());
-	if(!initializedGameState)
-	{
-		Load::ProgressAccumulator progressTracking;
-		gs->init(&mapService, GAME->server().si.get(), progressTracking, settings["general"]["saveRandomMaps"].Bool());
-	}
-	logNetwork->trace("Initializing GameState (together): %d ms", GAME->server().th->getDiff());
 
 	initMapHandler();
 	reinitScripting();
@@ -134,15 +127,14 @@ void CClient::newGame(CGameState * initializedGameState)
 	initPlayerInterfaces();
 }
 
-void CClient::loadGame(CGameState * initializedGameState)
+void CClient::loadGame(std::shared_ptr<CGameState> initializedGameState)
 {
 	logNetwork->info("Loading procedure started!");
 
 	logNetwork->info("Game state was transferred over network, loading.");
-	gs = initializedGameState;
-
-	gs->preInit(LIBRARY, this);
-	gs->updateOnLoad(GAME->server().si.get());
+	gamestate = initializedGameState;
+	gamestate->preInit(LIBRARY);
+	gamestate->updateOnLoad(GAME->server().si.get());
 	logNetwork->info("Game loaded, initialize interfaces.");
 
 	initMapHandler();
@@ -155,7 +147,7 @@ void CClient::loadGame(CGameState * initializedGameState)
 
 void CClient::save(const std::string & fname)
 {
-	if(!gs->currentBattles.empty())
+	if(!gameState().currentBattles.empty())
 	{
 		logNetwork->error("Game cannot be saved during battle!");
 		return;
@@ -199,7 +191,7 @@ void CClient::endGame()
 	removeGUI();
 
 	GAME->setMapInstance(nullptr);
-	vstd::clear_pointer(gs);
+	gamestate.reset();
 
 	logNetwork->info("Deleted mapHandler and gameState.");
 
@@ -219,7 +211,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->getMap()));
+		GAME->setMapInstance(std::make_unique<CMapHandler>(&gameState().getMap()));
 		logNetwork->trace("Creating mapHandler: %d ms", GAME->server().th->getDiff());
 	}
 }
@@ -233,9 +225,9 @@ void CClient::initPlayerEnvironments()
 	for(auto & color : allPlayers)
 	{
 		logNetwork->info("Preparing environment for player %s", color.toString());
-		playerEnvironments[color] = std::make_shared<CPlayerEnvironment>(color, this, std::make_shared<CCallback>(gs, color, this));
+		playerEnvironments[color] = std::make_shared<CPlayerEnvironment>(color, this, std::make_shared<CCallback>(gamestate, color, this));
 		
-		if(!hasHumanPlayer && gs->players[color].isHuman())
+		if(color.isValidPlayer() && !hasHumanPlayer && gameState().players.at(color).isHuman())
 			hasHumanPlayer = true;
 	}
 
@@ -249,13 +241,13 @@ void CClient::initPlayerEnvironments()
 	
 	if(settings["session"]["spectate"].Bool())
 	{
-		playerEnvironments[PlayerColor::SPECTATOR] = std::make_shared<CPlayerEnvironment>(PlayerColor::SPECTATOR, this, std::make_shared<CCallback>(gs, std::nullopt, this));
+		playerEnvironments[PlayerColor::SPECTATOR] = std::make_shared<CPlayerEnvironment>(PlayerColor::SPECTATOR, this, std::make_shared<CCallback>(gamestate, std::nullopt, this));
 	}
 }
 
 void CClient::initPlayerInterfaces()
 {
-	for(auto & playerInfo : gs->getStartInfo()->playerInfos)
+	for(const auto & playerInfo : gameState().getStartInfo()->playerInfos)
 	{
 		PlayerColor color = playerInfo.first;
 		if(!vstd::contains(GAME->server().getAllClientPlayers(GAME->server().logicConnection->connectionID), color))
@@ -267,8 +259,8 @@ void CClient::initPlayerInterfaces()
 			if(playerInfo.second.isControlledByAI() || settings["session"]["onlyai"].Bool())
 			{
 				bool alliedToHuman = false;
-				for(auto & allyInfo : gs->getStartInfo()->playerInfos)
-					if (gs->getPlayerTeam(allyInfo.first) == gs->getPlayerTeam(playerInfo.first) && allyInfo.second.isControlledByHuman())
+				for(const auto & allyInfo : gameState().getStartInfo()->playerInfos)
+					if (gameState().getPlayerTeam(allyInfo.first) == gameState().getPlayerTeam(playerInfo.first) && allyInfo.second.isControlledByHuman())
 						alliedToHuman = true;
 
 				auto AiToGive = aiNameForPlayer(playerInfo.second, false, alliedToHuman);
@@ -326,7 +318,7 @@ void CClient::installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInte
 	playerint[color] = gameInterface;
 
 	logGlobal->trace("\tInitializing the interface for player %s", color.toString());
-	auto cb = std::make_shared<CCallback>(gs, color, this);
+	auto cb = std::make_shared<CCallback>(gamestate, color, this);
 	battleCallbacks[color] = cb;
 	gameInterface->initGameInterface(playerEnvironments.at(color), cb);
 
@@ -348,14 +340,14 @@ void CClient::installNewBattleInterface(std::shared_ptr<CBattleGameInterface> ba
 
 void CClient::handlePack(CPackForClient & pack)
 {
-	ApplyClientNetPackVisitor afterVisitor(*this, *gameState());
-	ApplyFirstClientNetPackVisitor beforeVisitor(*this, *gameState());
+	ApplyClientNetPackVisitor afterVisitor(*this, gameState());
+	ApplyFirstClientNetPackVisitor beforeVisitor(*this, gameState());
 
 	pack.visit(beforeVisitor);
 	logNetwork->trace("\tMade first apply on cl: %s", typeid(pack).name());
 	{
 		std::unique_lock lock(CGameState::mutex);
-		gs->apply(pack);
+		gameState().apply(pack);
 	}
 	logNetwork->trace("\tApplied on gs: %s", typeid(pack).name());
 	pack.visit(afterVisitor);
@@ -379,8 +371,10 @@ int CClient::sendRequest(const CPackForServer & request, PlayerColor player)
 	return requestID;
 }
 
-void CClient::battleStarted(const BattleInfo * info)
+void CClient::battleStarted(const BattleID & battleID)
 {
+	const BattleInfo * info = gameState().getBattle(battleID);
+
 	std::shared_ptr<CPlayerInterface> att;
 	std::shared_ptr<CPlayerInterface> def;
 	const auto & leftSide = info->getSide(BattleSide::LEFT_SIDE);
@@ -396,7 +390,7 @@ void CClient::battleStarted(const BattleInfo * info)
 	auto callBattleStart = [&](PlayerColor color, BattleSide side)
 	{
 		if(vstd::contains(battleints, color))
-			battleints[color]->battleStart(info->battleID, leftSide.armyObject, rightSide.armyObject, info->tile, leftSide.hero, rightSide.hero, side, info->replayAllowed);
+			battleints[color]->battleStart(info->battleID, leftSide.getArmy(), rightSide.getArmy(), info->tile, leftSide.getHero(), rightSide.getHero(), side, info->replayAllowed);
 	};
 	
 	callBattleStart(leftSide.color, BattleSide::LEFT_SIDE);
@@ -441,14 +435,14 @@ void CClient::battleStarted(const BattleInfo * info)
 	{
 		if(att || def)
 		{
-			CPlayerInterface::battleInt = std::make_shared<BattleInterface>(info->getBattleID(), leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero, att, def);
+			CPlayerInterface::battleInt = std::make_shared<BattleInterface>(info->getBattleID(), leftSide.getArmy(), rightSide.getArmy(), leftSide.getHero(), rightSide.getHero(), att, def);
 		}
 		else if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
 		{
 			//TODO: This certainly need improvement
 			auto spectratorInt = std::dynamic_pointer_cast<CPlayerInterface>(playerint[PlayerColor::SPECTATOR]);
 			spectratorInt->cb->onBattleStarted(info);
-			CPlayerInterface::battleInt = std::make_shared<BattleInterface>(info->getBattleID(), leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero, att, def, spectratorInt);
+			CPlayerInterface::battleInt = std::make_shared<BattleInterface>(info->getBattleID(), leftSide.getArmy(), rightSide.getArmy(), leftSide.getHero(), rightSide.getHero(), att, def, spectratorInt);
 		}
 	}
 
@@ -465,8 +459,8 @@ void CClient::battleFinished(const BattleID & battleID)
 {
 	for(auto side : { BattleSide::ATTACKER, BattleSide::DEFENDER })
 	{
-		if(battleCallbacks.count(gs->getBattle(battleID)->getSide(side).color))
-			battleCallbacks[gs->getBattle(battleID)->getSide(side).color]->onBattleEnded(battleID);
+		if(battleCallbacks.count(gameState().getBattle(battleID)->getSide(side).color))
+			battleCallbacks[gameState().getBattle(battleID)->getSide(side).color]->onBattleEnded(battleID);
 	}
 
 	if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
@@ -484,11 +478,11 @@ void CClient::startPlayerBattleAction(const BattleID & battleID, PlayerColor col
 	{
 		// we want to avoid locking gamestate and causing UI to freeze while AI is making turn
 		auto unlockInterface = vstd::makeUnlockGuard(ENGINE->interfaceMutex);
-		battleint->activeStack(battleID, gs->getBattle(battleID)->battleGetStackByID(gs->getBattle(battleID)->activeStack, false));
+		battleint->activeStack(battleID, gameState().getBattle(battleID)->battleGetStackByID(gameState().getBattle(battleID)->activeStack, false));
 	}
 	else
 	{
-		battleint->activeStack(battleID, gs->getBattle(battleID)->battleGetStackByID(gs->getBattle(battleID)->activeStack, false));
+		battleint->activeStack(battleID, gameState().getBattle(battleID)->battleGetStackByID(gameState().getBattle(battleID)->activeStack, false));
 	}
 }
 

+ 7 - 3
client/Client.h

@@ -122,6 +122,7 @@ public:
 /// Class which handles client - server logic
 class CClient : public IGameCallback, public Environment
 {
+	std::shared_ptr<CGameState> gamestate;
 public:
 	std::map<PlayerColor, std::shared_ptr<CGameInterface>> playerint;
 	std::map<PlayerColor, std::shared_ptr<CBattleGameInterface>> battleints;
@@ -139,8 +140,11 @@ public:
 	vstd::CLoggerBase * logger() const override;
 	events::EventBus * eventBus() const override;
 
-	void newGame(CGameState * gameState);
-	void loadGame(CGameState * gameState);
+	CGameState & gameState() final { return *gamestate; }
+	const CGameState & gameState() const final { return *gamestate; }
+
+	void newGame(std::shared_ptr<CGameState> gameState);
+	void loadGame(std::shared_ptr<CGameState> gameState);
 
 	void save(const std::string & fname);
 	void endNetwork();
@@ -160,7 +164,7 @@ public:
 	void handlePack(CPackForClient & pack); //applies the given pack and deletes it
 	int sendRequest(const CPackForServer & request, PlayerColor player); //returns ID given to that request
 
-	void battleStarted(const BattleInfo * info);
+	void battleStarted(const BattleID & battle);
 	void battleFinished(const BattleID & battleID);
 	void startPlayerBattleAction(const BattleID & battleID, PlayerColor color);
 

+ 2 - 2
client/ClientCommandManager.cpp

@@ -140,7 +140,7 @@ void ClientCommandManager::handleControlaiCommand(std::istringstream& singleWord
 	if(GAME->interface())
 		color = GAME->interface()->playerID;
 
-	for(auto & elem : GAME->server().client->gameState()->players)
+	for(auto & elem : GAME->server().client->gameState().players)
 	{
 		if(!elem.first.isValidPlayer()
 			|| elem.second.human
@@ -557,7 +557,7 @@ void ClientCommandManager::giveTurn(const PlayerColor &colorIdentifier)
 	yt.player = colorIdentifier;
 	yt.queryID = QueryID::NONE;
 
-	ApplyClientNetPackVisitor visitor(*GAME->server().client, *GAME->server().client->gameState());
+	ApplyClientNetPackVisitor visitor(*GAME->server().client, GAME->server().client->gameState());
 	yt.visit(visitor);
 }
 

+ 28 - 21
client/NetPacksClient.cpp

@@ -100,16 +100,16 @@ void callAllInterfaces(CClient & cl, void (T::*ptr)(Args...), Args2 && ...args)
 template<typename T, typename ... Args, typename ... Args2>
 void callBattleInterfaceIfPresentForBothSides(CClient & cl, const BattleID & battleID, void (T::*ptr)(Args...), Args2 && ...args)
 {
-	assert(cl.gameState()->getBattle(battleID));
+	assert(cl.gameState().getBattle(battleID));
 
-	if(!cl.gameState()->getBattle(battleID))
+	if(!cl.gameState().getBattle(battleID))
 	{
 		logGlobal->error("Attempt to call battle interface without ongoing battle!");
 		return;
 	}
 
-	callOnlyThatBattleInterface(cl, cl.gameState()->getBattle(battleID)->getSide(BattleSide::ATTACKER).color, ptr, std::forward<Args2>(args)...);
-	callOnlyThatBattleInterface(cl, cl.gameState()->getBattle(battleID)->getSide(BattleSide::DEFENDER).color, ptr, std::forward<Args2>(args)...);
+	callOnlyThatBattleInterface(cl, cl.gameState().getBattle(battleID)->getSide(BattleSide::ATTACKER).color, ptr, std::forward<Args2>(args)...);
+	callOnlyThatBattleInterface(cl, cl.gameState().getBattle(battleID)->getSide(BattleSide::DEFENDER).color, ptr, std::forward<Args2>(args)...);
 	if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool() && GAME->interface()->battleInt)
 	{
 		callOnlyThatBattleInterface(cl, PlayerColor::SPECTATOR, ptr, std::forward<Args2>(args)...);
@@ -431,7 +431,7 @@ void ApplyClientNetPackVisitor::visitPlayerReinitInterface(PlayerReinitInterface
 
 		for(PlayerColor player(0); player < PlayerColor::PLAYER_LIMIT; ++player)
 		{
-			if(cl.gameState()->isPlayerMakingTurn(player))
+			if(cl.gameState().isPlayerMakingTurn(player))
 			{
 				callAllInterfaces(cl, &IGameEventsReceiver::playerStartsTurn, player);
 				callOnlyThatInterface(cl, player, &CGameInterface::yourTurn, QueryID::NONE);
@@ -595,7 +595,7 @@ void ApplyClientNetPackVisitor::visitSetAvailableCreatures(SetAvailableCreatures
 
 	PlayerColor p;
 	if(dw->ID == Obj::WAR_MACHINE_FACTORY) //War Machines Factory is not flaggable, it's "owned" by visitor
-		p = cl.getTile(dw->visitablePos())->visitableObjects.back()->tempOwner;
+		p = cl.getObjInstance(cl.getTile(dw->visitablePos())->visitableObjects.back())->getOwner();
 	else
 		p = dw->tempOwner;
 
@@ -625,7 +625,7 @@ void ApplyClientNetPackVisitor::visitSetHeroesInTown(SetHeroesInTown & pack)
 
 void ApplyClientNetPackVisitor::visitHeroRecruited(HeroRecruited & pack)
 {
-	CGHeroInstance *h = gs.getMap().heroesOnMap.back();
+	auto * h = gs.getMap().getHero(pack.hid);
 	if(h->getHeroTypeID() != pack.hid)
 	{
 		logNetwork->error("Something wrong with hero recruited!");
@@ -703,9 +703,9 @@ void ApplyClientNetPackVisitor::visitCommanderLevelUp(CommanderLevelUp & pack)
 {
 	const CGHeroInstance * hero = cl.getHero(pack.heroId);
 	assert(hero);
-	const CCommanderInstance * commander = hero->commander;
+	const auto & commander = hero->getCommander();
 	assert(commander);
-	assert(commander->armyObj); //is it possible for Commander to exist beyond armed instance?
+	assert(commander->getArmy()); //is it possible for Commander to exist beyond armed instance?
 	callOnlyThatInterface(cl, pack.player, &CGameInterface::commanderGotLevel, commander, pack.skills, pack.queryID);
 }
 
@@ -744,17 +744,17 @@ void ApplyClientNetPackVisitor::visitMapObjectSelectDialog(MapObjectSelectDialog
 void ApplyFirstClientNetPackVisitor::visitBattleStart(BattleStart & pack)
 {
 	// Cannot use the usual code because curB is not set yet
-	callOnlyThatBattleInterface(cl, pack.info->getSide(BattleSide::ATTACKER).color, &IBattleEventsReceiver::battleStartBefore, pack.battleID, pack.info->getSide(BattleSide::ATTACKER).armyObject, pack.info->getSide(BattleSide::DEFENDER).armyObject,
-		pack.info->tile, pack.info->getSide(BattleSide::ATTACKER).hero, pack.info->getSide(BattleSide::DEFENDER).hero);
-	callOnlyThatBattleInterface(cl, pack.info->getSide(BattleSide::DEFENDER).color, &IBattleEventsReceiver::battleStartBefore, pack.battleID, pack.info->getSide(BattleSide::ATTACKER).armyObject, pack.info->getSide(BattleSide::DEFENDER).armyObject,
-		pack.info->tile, pack.info->getSide(BattleSide::ATTACKER).hero, pack.info->getSide(BattleSide::DEFENDER).hero);
-	callOnlyThatBattleInterface(cl, PlayerColor::SPECTATOR, &IBattleEventsReceiver::battleStartBefore, pack.battleID, pack.info->getSide(BattleSide::ATTACKER).armyObject, pack.info->getSide(BattleSide::DEFENDER).armyObject,
-		pack.info->tile, pack.info->getSide(BattleSide::ATTACKER).hero, pack.info->getSide(BattleSide::DEFENDER).hero);
+	callOnlyThatBattleInterface(cl, pack.info->getSide(BattleSide::ATTACKER).color, &IBattleEventsReceiver::battleStartBefore, pack.battleID, pack.info->getSideArmy(BattleSide::ATTACKER), pack.info->getSideArmy(BattleSide::DEFENDER),
+		pack.info->tile, pack.info->getSideHero(BattleSide::ATTACKER), pack.info->getSideHero(BattleSide::DEFENDER));
+	callOnlyThatBattleInterface(cl, pack.info->getSide(BattleSide::DEFENDER).color, &IBattleEventsReceiver::battleStartBefore, pack.battleID, pack.info->getSideArmy(BattleSide::ATTACKER), pack.info->getSideArmy(BattleSide::DEFENDER),
+		pack.info->tile, pack.info->getSideHero(BattleSide::ATTACKER), pack.info->getSideHero(BattleSide::DEFENDER));
+	callOnlyThatBattleInterface(cl, PlayerColor::SPECTATOR, &IBattleEventsReceiver::battleStartBefore, pack.battleID, pack.info->getSideArmy(BattleSide::ATTACKER), pack.info->getSideArmy(BattleSide::DEFENDER),
+		pack.info->tile, pack.info->getSideHero(BattleSide::ATTACKER), pack.info->getSideHero(BattleSide::DEFENDER));
 }
 
 void ApplyClientNetPackVisitor::visitBattleStart(BattleStart & pack)
 {
-	cl.battleStarted(pack.info);
+	cl.battleStarted(pack.battleID);
 }
 
 void ApplyFirstClientNetPackVisitor::visitBattleNextRound(BattleNextRound & pack)
@@ -1016,7 +1016,9 @@ void ApplyClientNetPackVisitor::visitOpenWindow(OpenWindow & pack)
 			const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.object));
 			const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor));
 			const auto market = cl.getMarket(pack.object);
-			callInterfaceIfPresent(cl, cl.getTile(obj->visitablePos())->visitableObjects.back()->tempOwner, &IGameEventsReceiver::showMarketWindow, market, hero, pack.queryID);
+			const auto * tile = cl.getTile(obj->visitablePos());
+			const auto * topObject = cl.getObjInstance(tile->visitableObjects.back());
+			callInterfaceIfPresent(cl, topObject->getOwner(), &IGameEventsReceiver::showMarketWindow, market, hero, pack.queryID);
 		}
 		break;
 	case EOpenWindowMode::HILL_FORT_WINDOW:
@@ -1025,7 +1027,9 @@ void ApplyClientNetPackVisitor::visitOpenWindow(OpenWindow & pack)
 			//displays Hill fort window
 			const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.object));
 			const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor));
-			callInterfaceIfPresent(cl, cl.getTile(obj->visitablePos())->visitableObjects.back()->tempOwner, &IGameEventsReceiver::showHillFortWindow, obj, hero);
+			const auto * tile = cl.getTile(obj->visitablePos());
+			const auto * topObject = cl.getObjInstance(tile->visitableObjects.back());
+			callInterfaceIfPresent(cl, topObject->getOwner(), &IGameEventsReceiver::showHillFortWindow, obj, hero);
 		}
 		break;
 	case EOpenWindowMode::PUZZLE_MAP:
@@ -1054,7 +1058,7 @@ void ApplyClientNetPackVisitor::visitNewObject(NewObject & pack)
 {
 	callAllInterfaces(cl, &CGameInterface::invalidatePaths);
 
-	const CGObjectInstance *obj = pack.newObject;
+	const CGObjectInstance * obj = pack.newObject.get();
 	GAME->map().onObjectFadeIn(obj, pack.initiator);
 
 	for(auto i=cl.playerint.begin(); i!=cl.playerint.end(); i++)
@@ -1068,7 +1072,7 @@ void ApplyClientNetPackVisitor::visitNewObject(NewObject & pack)
 
 void ApplyClientNetPackVisitor::visitSetAvailableArtifacts(SetAvailableArtifacts & pack)
 {
-	if(pack.id < 0) //artifact merchants globally
+	if(!pack.id.hasValue()) //artifact merchants globally
 	{
 		callAllInterfaces(cl, &IGameEventsReceiver::availableArtifactsChanged, nullptr);
 	}
@@ -1076,7 +1080,10 @@ void ApplyClientNetPackVisitor::visitSetAvailableArtifacts(SetAvailableArtifacts
 	{
 		const CGBlackMarket *bm = dynamic_cast<const CGBlackMarket *>(cl.getObj(ObjectInstanceID(pack.id)));
 		assert(bm);
-		callInterfaceIfPresent(cl, cl.getTile(bm->visitablePos())->visitableObjects.back()->tempOwner, &IGameEventsReceiver::availableArtifactsChanged, bm);
+		const auto * tile = cl.getTile(bm->visitablePos());
+		const auto * topObject = cl.getObjInstance(tile->visitableObjects.back());
+
+		callInterfaceIfPresent(cl, topObject->getOwner(), &IGameEventsReceiver::availableArtifactsChanged, bm);
 	}
 }
 

+ 3 - 3
client/PlayerLocalState.cpp

@@ -315,14 +315,14 @@ void PlayerLocalState::serialize(JsonNode & dest) const
 	for (auto const * town : ownedTowns)
 	{
 		JsonNode record;
-		record["id"].Integer() = town->id;
+		record["id"].Integer() = town->id.getNum();
 		dest["towns"].Vector().push_back(record);
 	}
 
 	for (auto const * hero : wanderingHeroes)
 	{
 		JsonNode record;
-		record["id"].Integer() = hero->id;
+		record["id"].Integer() = hero->id.getNum();
 		if (vstd::contains(sleepingHeroes, hero))
 			record["sleeping"].Bool() = true;
 
@@ -340,7 +340,7 @@ void PlayerLocalState::serialize(JsonNode & dest) const
 	dest["spellbook"]["tabAdvmap"].Integer() = spellbookSettings.spellbookLastTabAdvmap;
 
 	if (currentSelection)
-		dest["currentSelection"].Integer() = currentSelection->id;
+		dest["currentSelection"].Integer() = currentSelection->id.getNum();
 }
 
 void PlayerLocalState::deserialize(const JsonNode & source)

+ 1 - 1
client/adventureMap/AdventureMapInterface.cpp

@@ -606,7 +606,7 @@ void AdventureMapInterface::onTileHovered(const int3 &targetPosition)
 
 	if(spellBeingCasted)
 	{
-		switch(spellBeingCasted->id)
+		switch(spellBeingCasted->id.toEnum())
 		{
 		case SpellID::SCUTTLE_BOAT:
 			if(isValidAdventureSpellTarget(targetPosition))

+ 2 - 1
client/adventureMap/AdventureMapShortcuts.cpp

@@ -32,6 +32,7 @@
 
 #include "../../CCallback.h"
 #include "../../lib/CConfigHandler.h"
+#include "../../lib/CPlayerState.h"
 #include "../../lib/texts/CGeneralTextHandler.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapObjects/CGTownInstance.h"
@@ -553,7 +554,7 @@ void AdventureMapShortcuts::moveHeroDirectional(const Point & direction)
 
 bool AdventureMapShortcuts::optionCanViewQuests()
 {
-	return optionInMapView() && !GAME->map().getMap()->quests.empty();
+	return optionInMapView() && !GAME->interface()->cb->getPlayerState(GAME->interface()->playerID)->quests.empty();
 }
 
 bool AdventureMapShortcuts::optionCanToggleLevel()

+ 1 - 0
client/adventureMap/CInfoBar.cpp

@@ -29,6 +29,7 @@
 
 #include "../../CCallback.h"
 #include "../../lib/CConfigHandler.h"
+#include "../../lib/GameLibrary.h"
 #include "../../lib/texts/CGeneralTextHandler.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapObjects/CGTownInstance.h"

+ 1 - 0
client/adventureMap/CList.cpp

@@ -30,6 +30,7 @@
 
 #include "../../lib/texts/CGeneralTextHandler.h"
 #include "../../lib/IGameSettings.h"
+#include "../../lib/GameLibrary.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapObjects/CGTownInstance.h"
 

+ 2 - 1
client/adventureMap/CMinimap.cpp

@@ -39,8 +39,9 @@ ColorRGBA CMinimapInstance::getTileColor(const int3 & pos) const
 		return Colors::BLACK;
 
 	// if object at tile is owned - it will be colored as its owner
-	for (const CGObjectInstance *obj : tile->blockingObjects)
+	for (const ObjectInstanceID objectID : tile->blockingObjects)
 	{
+		const auto * obj = GAME->interface()->cb->getObj(objectID);
 		PlayerColor player = obj->getOwner();
 		if(player == PlayerColor::NEUTRAL)
 			return graphics->neutralColor;

+ 3 - 4
client/adventureMap/MapAudioPlayer.cpp

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

+ 4 - 4
client/adventureMap/TurnTimerWidget.cpp

@@ -55,18 +55,18 @@ TurnTimerWidget::TurnTimerWidget(const Point & position, PlayerColor player)
 		pos.w = 76;
 
 		pos.h += 20;
-		playerLabelsMain[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 10, FONT_BIG, ETextAlignment::CENTER, graphics->playerColors[player], "");
+		playerLabelsMain[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 10, FONT_BIG, ETextAlignment::CENTER, graphics->playerColors[player.getNum()], "");
 
 		if (timers.battleTimer != 0)
 		{
 			pos.h += 20;
-			playerLabelsBattle[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 10, FONT_BIG, ETextAlignment::CENTER, graphics->playerColors[player], "");
+			playerLabelsBattle[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 10, FONT_BIG, ETextAlignment::CENTER, graphics->playerColors[player.getNum()], "");
 		}
 
 		if (!timers.accumulatingUnitTimer && timers.unitTimer != 0)
 		{
 			pos.h += 20;
-			playerLabelsUnit[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 10, FONT_BIG, ETextAlignment::CENTER, graphics->playerColors[player], "");
+			playerLabelsUnit[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 10, FONT_BIG, ETextAlignment::CENTER, graphics->playerColors[player.getNum()], "");
 		}
 
 		updateTextLabel(player, GAME->interface()->cb->getPlayerTurnTime(player));
@@ -87,7 +87,7 @@ TurnTimerWidget::TurnTimerWidget(const Point & position, PlayerColor player)
 				continue;
 
 			pos.h += 20;
-			playerLabelsMain[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 10, FONT_BIG, ETextAlignment::CENTER, graphics->playerColors[player], "");
+			playerLabelsMain[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 10, FONT_BIG, ETextAlignment::CENTER, graphics->playerColors[player.getNum()], "");
 
 			updateTextLabel(player, GAME->interface()->cb->getPlayerTurnTime(player));
 		}

+ 1 - 0
client/battle/BattleActionsController.cpp

@@ -28,6 +28,7 @@
 
 #include "../../CCallback.h"
 #include "../../lib/CConfigHandler.h"
+#include "../../lib/GameLibrary.h"
 #include "../../lib/texts/CGeneralTextHandler.h"
 #include "../../lib/CRandomGenerator.h"
 #include "../../lib/CStack.h"

+ 4 - 2
client/battle/BattleEffectsController.cpp

@@ -26,11 +26,13 @@
 #include "../render/Graphics.h"
 
 #include "../../CCallback.h"
+
+#include "../../lib/CStack.h"
+#include "../../lib/GameLibrary.h"
+#include "../../lib/IGameEventsReceiver.h"
 #include "../../lib/battle/BattleAction.h"
 #include "../../lib/filesystem/ResourcePath.h"
 #include "../../lib/networkPacks/PacksForClientBattle.h"
-#include "../../lib/CStack.h"
-#include "../../lib/IGameEventsReceiver.h"
 #include "../../lib/texts/CGeneralTextHandler.h"
 
 BattleEffectsController::BattleEffectsController(BattleInterface & owner):

+ 7 - 5
client/battle/BattleInterface.cpp

@@ -35,16 +35,18 @@
 #include "../adventureMap/AdventureMapInterface.h"
 
 #include "../../CCallback.h"
+
 #include "../../lib/BattleFieldHandler.h"
-#include "../../lib/CStack.h"
 #include "../../lib/CConfigHandler.h"
-#include "../../lib/texts/CGeneralTextHandler.h"
+#include "../../lib/CStack.h"
+#include "../../lib/CThreadHelper.h"
+#include "../../lib/GameLibrary.h"
+#include "../../lib/TerrainHandler.h"
+#include "../../lib/UnlockGuard.h"
 #include "../../lib/gameState/InfoAboutArmy.h"
 #include "../../lib/mapObjects/CGTownInstance.h"
 #include "../../lib/networkPacks/PacksForClientBattle.h"
-#include "../../lib/UnlockGuard.h"
-#include "../../lib/TerrainHandler.h"
-#include "../../lib/CThreadHelper.h"
+#include "../../lib/texts/CGeneralTextHandler.h"
 
 BattleInterface::BattleInterface(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2,
 		const CGHeroInstance *hero1, const CGHeroInstance *hero2,

+ 4 - 3
client/battle/BattleInterfaceClasses.cpp

@@ -52,6 +52,7 @@
 #include "../../lib/CCreatureHandler.h"
 #include "../../lib/entities/hero/CHeroClass.h"
 #include "../../lib/entities/hero/CHero.h"
+#include "../../lib/GameLibrary.h"
 #include "../../lib/gameState/InfoAboutArmy.h"
 #include "../../lib/texts/CGeneralTextHandler.h"
 #include "../../lib/texts/TextOperations.h"
@@ -630,7 +631,7 @@ void StackInfoBasicPanel::initializeData(const CStack * stack)
 {
 	OBJECT_CONSTRUCTION;
 
-	icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("TWCRPORT"), stack->creatureId() + 2, 0, 10, 6));
+	icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("TWCRPORT"), stack->creatureId().getNum() + 2, 0, 10, 6));
 	labels.push_back(std::make_shared<CLabel>(10 + 58, 6 + 64, FONT_MEDIUM, ETextAlignment::BOTTOMRIGHT, Colors::WHITE, TextOperations::formatMetric(stack->getCount(), 4)));
 
 	int damageMultiplier = 1;
@@ -704,7 +705,7 @@ void StackInfoBasicPanel::initializeData(const CStack * stack)
 
 			int duration = spellBonuses->front()->turnsRemain;
 
-			icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SpellInt"), effect + 1, 0, firstPos.x + offset.x * printed, firstPos.y + offset.y * printed));
+			icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SpellInt"), effect.getNum() + 1, 0, firstPos.x + offset.x * printed, firstPos.y + offset.y * printed));
 			if(settings["general"]["enableUiEnhancements"].Bool())
 				labels.push_back(std::make_shared<CLabel>(firstPos.x + offset.x * printed + 46, firstPos.y + offset.y * printed + 36, EFonts::FONT_TINY, ETextAlignment::BOTTOMRIGHT, Colors::WHITE, std::to_string(duration)));
 			if(++printed >= 3 || (printed == 2 && spells.size() > 3)) // interface limit reached
@@ -836,7 +837,7 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface
 			int yPos = 344 + static_cast<int>(step) * 97;
 			for(auto & elem : br.casualties[step])
 			{
-				auto creature = LIBRARY->creatures()->getByIndex(elem.first);
+				auto creature = elem.first.toEntity(LIBRARY);
 				if (creature->getId() == CreatureID::ARROW_TOWERS )
 					continue; // do not show destroyed towers in battle results
 

+ 7 - 5
client/battle/BattleStacksController.cpp

@@ -33,13 +33,15 @@
 #include "../render/IFont.h"
 
 #include "../../CCallback.h"
-#include "../../lib/spells/ISpellMechanics.h"
-#include "../../lib/battle/BattleAction.h"
-#include "../../lib/battle/BattleHex.h"
-#include "../../lib/texts/TextOperations.h"
+
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CRandomGenerator.h"
 #include "../../lib/CStack.h"
+#include "../../lib/GameLibrary.h"
+#include "../../lib/battle/BattleAction.h"
+#include "../../lib/battle/BattleHex.h"
+#include "../../lib/spells/ISpellMechanics.h"
+#include "../../lib/texts/TextOperations.h"
 
 static void onAnimationFinished(const CStack *stack, std::weak_ptr<CreatureAnimation> anim)
 {
@@ -262,7 +264,7 @@ std::shared_ptr<IImage> BattleStacksController::getStackAmountBox(const CStack *
 
 	for(const auto & spellID : activeSpells)
 	{
-		auto positiveness = LIBRARY->spells()->getByIndex(spellID)->getPositiveness();
+		auto positiveness = spellID.toEntity(LIBRARY)->getPositiveness();
 		if(!boost::logic::indeterminate(positiveness))
 		{
 			if(positiveness)

+ 9 - 7
client/battle/BattleWindow.cpp

@@ -32,18 +32,20 @@
 #include "../render/IRenderHandler.h"
 #include "../adventureMap/CInGameConsole.h"
 #include "../adventureMap/TurnTimerWidget.h"
+#include "../windows/settings/SettingsMainWindow.h"
 
 #include "../../CCallback.h"
-#include "../../lib/texts/CGeneralTextHandler.h"
-#include "../../lib/gameState/InfoAboutArmy.h"
-#include "../../lib/mapObjects/CGHeroInstance.h"
-#include "../../lib/CStack.h"
+
 #include "../../lib/CConfigHandler.h"
-#include "../../lib/filesystem/ResourcePath.h"
+#include "../../lib/CPlayerState.h"
+#include "../../lib/CStack.h"
+#include "../../lib/GameLibrary.h"
 #include "../../lib/StartInfo.h"
 #include "../../lib/battle/BattleInfo.h"
-#include "../../lib/CPlayerState.h"
-#include "../windows/settings/SettingsMainWindow.h"
+#include "../../lib/filesystem/ResourcePath.h"
+#include "../../lib/gameState/InfoAboutArmy.h"
+#include "../../lib/mapObjects/CGHeroInstance.h"
+#include "../../lib/texts/CGeneralTextHandler.h"
 
 BattleWindow::BattleWindow(BattleInterface & Owner):
 	owner(Owner),

+ 10 - 11
client/lobby/CBonusSelection.cpp

@@ -43,7 +43,9 @@
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CCreatureHandler.h"
 #include "../../lib/CSkillHandler.h"
+#include "../../lib/GameLibrary.h"
 #include "../../lib/StartInfo.h"
+#include "../../lib/campaign/CampaignState.h"
 #include "../../lib/entities/building/CBuilding.h"
 #include "../../lib/entities/building/CBuildingHandler.h"
 #include "../../lib/entities/faction/CFaction.h"
@@ -51,14 +53,11 @@
 #include "../../lib/entities/faction/CTownHandler.h"
 #include "../../lib/entities/hero/CHeroHandler.h"
 #include "../../lib/filesystem/Filesystem.h"
-#include "../../lib/texts/CGeneralTextHandler.h"
-
-#include "../../lib/campaign/CampaignState.h"
-#include "../../lib/mapping/CMapService.h"
-#include "../../lib/mapping/CMapInfo.h"
-#include "../../lib/mapping/CMapHeader.h"
-
 #include "../../lib/mapObjects/CGHeroInstance.h"
+#include "../../lib/mapping/CMapHeader.h"
+#include "../../lib/mapping/CMapInfo.h"
+#include "../../lib/mapping/CMapService.h"
+#include "../../lib/texts/CGeneralTextHandler.h"
 
 std::shared_ptr<CampaignState> CBonusSelection::getCampaign()
 {
@@ -203,14 +202,14 @@ void CBonusSelection::createBonusesIcons()
 				}
 
 			}
-			assert(faction != -1);
+			assert(faction.hasValue());
 
 			BuildingID buildID;
 			if(getCampaign()->formatVCMI())
 				buildID = BuildingID(bonDescs[i].info1);
 			else
 				buildID = CBuildingHandler::campToERMU(bonDescs[i].info1, faction, std::set<BuildingID>());
-			picName = graphics->ERMUtoPicture[faction][buildID];
+			picName = graphics->ERMUtoPicture[faction.getNum()][buildID.getNum()];
 			picNumber = -1;
 
 			if(vstd::contains((*LIBRARY->townh)[faction]->town->buildings, buildID))
@@ -305,7 +304,7 @@ void CBonusSelection::createBonusesIcons()
 		}
 
 		case CampaignBonusType::HERO:
-			if(bonDescs[i].info2 == HeroTypeID::CAMP_RANDOM)
+			if(bonDescs[i].info2 == HeroTypeID::CAMP_RANDOM.getNum())
 			{
 				desc.appendLocalString(EMetaText::GENERAL_TXT, 720); // Start with random hero
 				picNumber = -1;
@@ -426,7 +425,7 @@ void CBonusSelection::startMap()
 	{
 		auto exitCb = []()
 		{
-			logGlobal->info("Starting scenario %d", static_cast<int>(GAME->server().campaignMap));
+			logGlobal->info("Starting scenario %d", GAME->server().campaignMap.getNum());
 			GAME->server().sendStartGame();
 		};
 

+ 1 - 1
client/lobby/CSavingScreen.cpp

@@ -54,7 +54,7 @@ const CMapInfo * CSavingScreen::getMapInfo()
 const StartInfo * CSavingScreen::getStartInfo()
 {
 	if (localMi)
-		return localMi->scenarioOptionsOfSave;
+		return localMi->scenarioOptionsOfSave.get();
 	return GAME->interface()->cb->getStartInfo();
 }
 

+ 2 - 2
client/lobby/OptionsTab.cpp

@@ -822,7 +822,7 @@ OptionsTab::HandicapWindow::HandicapWindow()
 		INCOME = 1000,
 		GROWTH = 2000,
 	};
-	auto columns = std::vector<EGameResID>{EGameResID::GOLD, EGameResID::WOOD, EGameResID::MERCURY, EGameResID::ORE, EGameResID::SULFUR, EGameResID::CRYSTAL, EGameResID::GEMS, Columns::INCOME, Columns::GROWTH};
+	auto columns = std::vector<int>{EGameResID::GOLD, EGameResID::WOOD, EGameResID::MERCURY, EGameResID::ORE, EGameResID::SULFUR, EGameResID::CRYSTAL, EGameResID::GEMS, Columns::INCOME, Columns::GROWTH};
 
 	int i = 0;
 	for(auto & pInfo : SEL->getStartInfo()->playerInfos)
@@ -847,7 +847,7 @@ OptionsTab::HandicapWindow::HandicapWindow()
 				else if(isGrowth)
 					labels.push_back(std::make_shared<CLabel>(xPos, 38, FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, LIBRARY->generaltexth->translate("core.genrltxt.194")));
 				else
-					anim.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SMALRES"), GameResID(resource), 0, 15 + xPos + (j == 0 ? 10 : 0), 35));
+					anim.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SMALRES"), GameResID(resource).getNum(), 0, 15 + xPos + (j == 0 ? 10 : 0), 35));
 			}
 
 			auto area = Rect(xPos, 60 + i * 30, j == 0 ? 60 : 50, 16);

+ 1 - 1
client/lobby/RandomMapTab.cpp

@@ -229,7 +229,7 @@ void RandomMapTab::updateMapInfoByHost()
 		playerInfo.team = team;
 		playerInfo.hasMainTown = true;
 		playerInfo.generateHeroAtMainTown = true;
-		mapInfo->mapHeader->players[player.first] = playerInfo;
+		mapInfo->mapHeader->players[player.first.getNum()] = playerInfo;
 		vstd::erase(availableColors, player.first);
 	}
 

+ 6 - 8
client/mainmenu/CCampaignScreen.cpp

@@ -29,19 +29,17 @@
 #include "../windows/InfoWindows.h"
 #include "../windows/CWindowObject.h"
 
-#include "../../lib/filesystem/Filesystem.h"
-#include "../../lib/texts/CGeneralTextHandler.h"
-
 #include "../../lib/CArtHandler.h"
-#include "../../lib/spells/CSpellHandler.h"
 #include "../../lib/CConfigHandler.h"
-#include "../../lib/CSkillHandler.h"
 #include "../../lib/CCreatureHandler.h"
-
+#include "../../lib/CSkillHandler.h"
+#include "../../lib/GameLibrary.h"
 #include "../../lib/campaign/CampaignHandler.h"
-#include "../../lib/mapping/CMapService.h"
-
+#include "../../lib/filesystem/Filesystem.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
+#include "../../lib/mapping/CMapService.h"
+#include "../../lib/spells/CSpellHandler.h"
+#include "../../lib/texts/CGeneralTextHandler.h"
 
 CCampaignScreen::CCampaignScreen(const JsonNode & config, std::string name)
 	: CWindowObject(BORDERED), campaignSet(name)

+ 7 - 7
client/mapView/MapRenderer.cpp

@@ -152,7 +152,7 @@ void MapRendererTerrain::renderTile(IMapRendererContext & context, Canvas & targ
 {
 	const TerrainTile & mapTile = context.getMapTile(coordinates);
 
-	int32_t terrainIndex = mapTile.getTerrainID();
+	int32_t terrainIndex = mapTile.getTerrainID().getNum();
 	int32_t imageIndex = mapTile.terView;
 	int32_t rotationIndex = mapTile.extTileFlags % 4;
 
@@ -194,7 +194,7 @@ void MapRendererRiver::renderTile(IMapRendererContext & context, Canvas & target
 	if(!mapTile.hasRiver())
 		return;
 
-	int32_t terrainIndex = mapTile.getRiverID();
+	int32_t terrainIndex = mapTile.getRiverID().getNum();
 	int32_t imageIndex = mapTile.riverDir;
 	int32_t rotationIndex = (mapTile.extTileFlags >> 2) % 4;
 
@@ -231,7 +231,7 @@ void MapRendererRoad::renderTile(IMapRendererContext & context, Canvas & target,
 		const TerrainTile & mapTileAbove = context.getMapTile(coordinatesAbove);
 		if(mapTileAbove.hasRoad())
 		{
-			int32_t terrainIndex = mapTileAbove.getRoadID();
+			int32_t terrainIndex = mapTileAbove.getRoadID().getNum();
 			int32_t imageIndex = mapTileAbove.roadDir;
 			int32_t rotationIndex = (mapTileAbove.extTileFlags >> 4) % 4;
 
@@ -243,7 +243,7 @@ void MapRendererRoad::renderTile(IMapRendererContext & context, Canvas & target,
 	const TerrainTile & mapTile = context.getMapTile(coordinates);
 	if(mapTile.hasRoad())
 	{
-		int32_t terrainIndex = mapTile.getRoadID();
+		int32_t terrainIndex = mapTile.getRoadID().getNum();
 		int32_t imageIndex = mapTile.roadDir;
 		int32_t rotationIndex = (mapTile.extTileFlags >> 4) % 4;
 
@@ -439,8 +439,8 @@ std::shared_ptr<CAnimation> MapRendererObjects::getFlagAnimation(const CGObjectI
 	if(obj->ID == Obj::BOAT)
 	{
 		const auto * boat = dynamic_cast<const CGBoat *>(obj);
-		if(boat && boat->hero && !boat->flagAnimations[boat->hero->tempOwner.getNum()].empty())
-			return getAnimation(boat->flagAnimations[boat->hero->tempOwner.getNum()], true, false);
+		if(boat && boat->getBoardedHero() && !boat->flagAnimations[boat->getBoardedHero()->tempOwner.getNum()].empty())
+			return getAnimation(boat->flagAnimations[boat->getBoardedHero()->tempOwner.getNum()], true, false);
 	}
 
 	return nullptr;
@@ -452,7 +452,7 @@ std::shared_ptr<CAnimation> MapRendererObjects::getOverlayAnimation(const CGObje
 	{
 		// Boats have additional animation with waves around boat
 		const auto * boat = dynamic_cast<const CGBoat *>(obj);
-		if(boat && boat->hero && !boat->overlayAnimation.empty())
+		if(boat && boat->getBoardedHero() && !boat->overlayAnimation.empty())
 			return getAnimation(boat->overlayAnimation, true, false);
 	}
 	return nullptr;

+ 10 - 8
client/mapView/MapRendererContext.cpp

@@ -45,8 +45,8 @@ uint32_t MapRendererBaseContext::getObjectRotation(ObjectInstanceID objectID) co
 	{
 		const auto * boat = dynamic_cast<const CGBoat *>(obj);
 
-		if(boat->hero)
-			return boat->hero->moveDir;
+		if(boat->getBoardedHero())
+			return boat->getBoardedHero()->moveDir;
 		return boat->direction;
 	}
 	return 0;
@@ -103,7 +103,7 @@ const MapRendererBaseContext::MapObjectsList & MapRendererBaseContext::getObject
 
 const CGObjectInstance * MapRendererBaseContext::getObject(ObjectInstanceID objectID) const
 {
-	return GAME->map().getMap()->objects.at(objectID.getNum());
+	return GAME->map().getMap()->getObject(objectID);
 }
 
 const CGPath * MapRendererBaseContext::currentPath() const
@@ -132,10 +132,10 @@ double MapRendererBaseContext::objectTransparency(ObjectInstanceID objectID, con
 	{
 		const auto * hero = dynamic_cast<const CGHeroInstance *>(object);
 
-		if(hero->inTownGarrison)
+		if(hero->isGarrisoned())
 			return 0;
 
-		if(hero->boat)
+		if(hero->inBoat())
 			return 0;
 	}
 	return 1;
@@ -278,10 +278,12 @@ std::string MapRendererAdventureContext::overlayText(const int3 & coordinates) c
 	if (!tile.visitable())
 		return {};
 
-	if ( tile.visitableObjects.back()->ID == Obj::EVENT)
+	const auto * object = getObject(tile.visitableObjects.back());
+
+	if ( object->ID == Obj::EVENT)
 		return {};
 
-	return tile.visitableObjects.back()->getObjectName();
+	return object->getObjectName();
 }
 
 ColorRGBA MapRendererAdventureContext::overlayTextColor(const int3 & coordinates) const
@@ -294,7 +296,7 @@ ColorRGBA MapRendererAdventureContext::overlayTextColor(const int3 & coordinates
 	if (!tile.visitable())
 		return {};
 
-	const auto * object = tile.visitableObjects.back();
+	const auto * object = getObject(tile.visitableObjects.back());
 
 	if (object->getOwner() == GAME->interface()->playerID)
 		return { 0, 192, 0};

+ 2 - 2
client/mapView/MapRendererContextState.cpp

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

+ 16 - 16
client/mapView/MapViewController.cpp

@@ -182,7 +182,7 @@ void MapViewController::tick(uint32_t timeDelta)
 		assert(boat || hero);
 
 		if(!hero)
-			hero = boat->hero;
+			hero = boat->getBoardedHero();
 
 		double heroMoveTime = GAME->interface()->playerID == hero->getOwner() ?
 			settings["adventure"]["heroMoveTime"].Float() :
@@ -249,7 +249,7 @@ void MapViewController::afterRender()
 		assert(boat || hero);
 
 		if(!hero)
-			hero = boat->hero;
+			hero = boat->getBoardedHero();
 
 		if(movementContext->progress >= 0.999)
 		{
@@ -372,8 +372,8 @@ void MapViewController::fadeOutObject(const CGObjectInstance * obj)
 	if (obj->ID == Obj::HERO)
 	{
 		auto * hero = dynamic_cast<const CGHeroInstance*>(obj);
-		if (hero->boat)
-			movingObject = hero->boat;
+		if (hero->inBoat())
+			movingObject = hero->getBoat();
 	}
 
 	fadingOutContext->target = movingObject->id;
@@ -393,8 +393,8 @@ void MapViewController::fadeInObject(const CGObjectInstance * obj)
 	if (obj->ID == Obj::HERO)
 	{
 		auto * hero = dynamic_cast<const CGHeroInstance*>(obj);
-		if (hero->boat)
-			movingObject = hero->boat;
+		if (hero->inBoat())
+			movingObject = hero->getBoat();
 	}
 
 	fadingInContext->target = movingObject->id;
@@ -406,20 +406,20 @@ void MapViewController::removeObject(const CGObjectInstance * obj)
 	if (obj->ID == Obj::BOAT)
 	{
 		auto * boat = dynamic_cast<const CGBoat*>(obj);
-		if (boat->hero)
+		if (boat->getBoardedHero())
 		{
-			view->invalidate(context, boat->hero->id);
-			state->removeObject(boat->hero);
+			view->invalidate(context, boat->getBoardedHero()->id);
+			state->removeObject(boat->getBoardedHero());
 		}
 	}
 
 	if (obj->ID == Obj::HERO)
 	{
 		auto * hero = dynamic_cast<const CGHeroInstance*>(obj);
-		if (hero->boat)
+		if (hero->inBoat())
 		{
-			view->invalidate(context, hero->boat->id);
-			state->removeObject(hero->boat);
+			view->invalidate(context, hero->getBoat()->id);
+			state->removeObject(hero->getBoat());
 		}
 	}
 
@@ -514,8 +514,8 @@ void MapViewController::onAfterHeroTeleported(const CGHeroInstance * obj, const
 	assert(!hasOngoingAnimations());
 
 	const CGObjectInstance * movingObject = obj;
-	if(obj->boat)
-		movingObject = obj->boat;
+	if(obj->inBoat())
+		movingObject = obj->getBoat();
 
 	removeObject(movingObject);
 	addObject(movingObject);
@@ -541,8 +541,8 @@ void MapViewController::onHeroMoved(const CGHeroInstance * obj, const int3 & fro
 		return;
 
 	const CGObjectInstance * movingObject = obj;
-	if(obj->boat)
-		movingObject = obj->boat;
+	if(obj->inBoat())
+		movingObject = obj->getBoat();
 
 	removeObject(movingObject);
 

+ 2 - 2
client/mapView/mapHandler.cpp

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

+ 1 - 1
client/render/Graphics.cpp

@@ -129,7 +129,7 @@ void Graphics::setPlayerPalette(SDL_Palette * targetPalette, PlayerColor player)
 	if(player.isValidPlayer())
 	{
 		for(int i=0; i<32; ++i)
-			palette[i] = CSDL_Ext::toSDL(playerColorPalette[player][i]);
+			palette[i] = CSDL_Ext::toSDL(playerColorPalette[player.getNum()][i]);
 	}
 	else
 	{

+ 5 - 5
client/widgets/CArtifactsOfHeroBackpack.cpp

@@ -152,10 +152,10 @@ void CArtifactsOfHeroQuickBackpack::setHero(const CGHeroInstance * hero)
 
 		std::map<const ArtifactID, const CArtifactInstance*> filteredArts;
 		for(auto & slotInfo : curHero->artifactsInBackpack)
-			if(slotInfo.artifact->getTypeId() != artInSlotId &&	!slotInfo.artifact->isScroll() &&
-				slotInfo.artifact->getType()->canBePutAt(curHero, filterBySlot, true))
+			if(slotInfo.getArt()->getTypeId() != artInSlotId &&	!slotInfo.getArt()->isScroll() &&
+				slotInfo.getArt()->getType()->canBePutAt(curHero, filterBySlot, true))
 			{
-				filteredArts.insert(std::pair(slotInfo.artifact->getTypeId(), slotInfo.artifact));
+				filteredArts.insert(std::pair(slotInfo.getArt()->getTypeId(), slotInfo.getArt()));
 			}
 
 		std::map<const SpellID, const CArtifactInstance*> filteredScrolls;
@@ -164,8 +164,8 @@ void CArtifactsOfHeroQuickBackpack::setHero(const CGHeroInstance * hero)
 		{
 			for(auto & slotInfo : curHero->artifactsInBackpack)
 			{
-				if(slotInfo.artifact->isScroll() && slotInfo.artifact->getScrollSpellID() != scrollInSlotSpellId)
-					filteredScrolls.insert(std::pair(slotInfo.artifact->getScrollSpellID(), slotInfo.artifact));
+				if(slotInfo.getArt()->isScroll() && slotInfo.getArt()->getScrollSpellID() != scrollInSlotSpellId)
+					filteredScrolls.insert(std::pair(slotInfo.getArt()->getScrollSpellID(), slotInfo.getArt()));
 			}
 		}
 

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.