2
0
Эх сурвалжийг харах

Merge remote-tracking branch 'vcmi/develop' into lobby

Ivan Savenko 1 жил өмнө
parent
commit
fc4ae3bd8c
100 өөрчлөгдсөн 613 нэмэгдсэн , 677 устгасан
  1. 5 4
      AI/BattleAI/BattleEvaluator.cpp
  2. 1 3
      AI/Nullkiller/AIUtility.cpp
  3. 0 1
      AI/Nullkiller/AIUtility.h
  4. 9 9
      AI/Nullkiller/Analyzers/ArmyManager.cpp
  5. 1 1
      AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp
  6. 1 1
      AI/Nullkiller/Engine/FuzzyHelper.cpp
  7. 2 2
      AI/Nullkiller/Engine/PriorityEvaluator.cpp
  8. 2 2
      AI/Nullkiller/Goals/BuyArmy.cpp
  9. 1 1
      AI/Nullkiller/Goals/CompleteQuest.cpp
  10. 2 2
      AI/Nullkiller/Pathfinding/Actors.cpp
  11. 1 1
      AI/Nullkiller/Pathfinding/Actors.h
  12. 1 1
      AI/VCAI/FuzzyHelper.cpp
  13. 1 1
      AI/VCAI/Goals/CompleteQuest.cpp
  14. 0 1
      AI/VCAI/VCAI.cpp
  15. 1 0
      CCallback.cpp
  16. 1 4
      client/CGameInfo.cpp
  17. 16 15
      client/CGameInfo.h
  18. 1 1
      client/CMT.cpp
  19. 0 1
      client/CPlayerInterface.cpp
  20. 10 2
      client/CServerHandler.cpp
  21. 6 1
      client/CServerHandler.h
  22. 7 10
      client/Client.cpp
  23. 2 2
      client/ClientCommandManager.cpp
  24. 1 0
      client/NetPacksClient.cpp
  25. 1 1
      client/battle/BattleInterfaceClasses.cpp
  26. 1 1
      client/battle/BattleWindow.cpp
  27. 1 0
      client/mapView/MapRendererContext.cpp
  28. 1 1
      client/windows/CCreatureWindow.cpp
  29. 2 2
      client/windows/CMapOverview.cpp
  30. 2 2
      client/windows/CQuestLog.cpp
  31. 1 0
      cmake_modules/VCMI_lib.cmake
  32. 1 0
      docs/Readme.md
  33. 3 3
      lib/ArtifactUtils.cpp
  34. 1 1
      lib/ArtifactUtils.h
  35. 5 5
      lib/CArtHandler.cpp
  36. 4 4
      lib/CArtHandler.h
  37. 4 4
      lib/CArtifactInstance.cpp
  38. 3 3
      lib/CArtifactInstance.h
  39. 1 12
      lib/CCreatureHandler.cpp
  40. 0 2
      lib/CCreatureHandler.h
  41. 5 6
      lib/CCreatureSet.cpp
  42. 7 5
      lib/CGameInfoCallback.cpp
  43. 18 10
      lib/CHeroHandler.cpp
  44. 4 4
      lib/CHeroHandler.h
  45. 0 2
      lib/CPlayerState.cpp
  46. 0 2
      lib/CPlayerState.h
  47. 1 1
      lib/CStack.cpp
  48. 6 6
      lib/CTownHandler.cpp
  49. 3 3
      lib/CTownHandler.h
  50. 26 0
      lib/GameCallbackHolder.h
  51. 3 1
      lib/IGameCallback.cpp
  52. 18 16
      lib/IHandlerBase.h
  53. 2 3
      lib/JsonNode.cpp
  54. 40 43
      lib/JsonRandom.cpp
  55. 40 20
      lib/JsonRandom.h
  56. 1 1
      lib/RiverHandler.cpp
  57. 1 1
      lib/RoadHandler.cpp
  58. 1 1
      lib/StartInfo.cpp
  59. 21 93
      lib/VCMI_Lib.cpp
  60. 25 30
      lib/VCMI_Lib.h
  61. 3 3
      lib/battle/BattleInfo.cpp
  62. 2 3
      lib/battle/CBattleInfoEssentials.cpp
  63. 0 1
      lib/bonuses/Bonus.cpp
  64. 12 12
      lib/bonuses/BonusSelector.cpp
  65. 6 6
      lib/bonuses/BonusSelector.h
  66. 94 87
      lib/bonuses/CBonusSystemNode.cpp
  67. 16 14
      lib/bonuses/CBonusSystemNode.h
  68. 1 1
      lib/bonuses/IBonusBearer.cpp
  69. 7 6
      lib/campaign/CampaignState.cpp
  70. 4 3
      lib/campaign/CampaignState.h
  71. 10 0
      lib/constants/EntityIdentifiers.cpp
  72. 5 0
      lib/constants/EntityIdentifiers.h
  73. 26 38
      lib/gameState/CGameState.cpp
  74. 3 2
      lib/gameState/CGameState.h
  75. 6 5
      lib/gameState/CGameStateCampaign.cpp
  76. 2 1
      lib/mapObjectConstructors/AObjectTypeHandler.h
  77. 13 75
      lib/mapObjectConstructors/CBankInstanceConstructor.cpp
  78. 3 7
      lib/mapObjectConstructors/CBankInstanceConstructor.h
  79. 4 4
      lib/mapObjectConstructors/CDefaultObjectTypeHandler.h
  80. 3 3
      lib/mapObjectConstructors/CRewardableConstructor.cpp
  81. 1 1
      lib/mapObjectConstructors/CRewardableConstructor.h
  82. 8 7
      lib/mapObjectConstructors/CommonConstructors.cpp
  83. 3 3
      lib/mapObjectConstructors/CommonConstructors.h
  84. 4 4
      lib/mapObjectConstructors/DwellingInstanceConstructor.cpp
  85. 1 0
      lib/mapObjectConstructors/DwellingInstanceConstructor.h
  86. 1 0
      lib/mapObjectConstructors/HillFortInstanceConstructor.h
  87. 0 5
      lib/mapObjectConstructors/IObjectInfo.h
  88. 1 0
      lib/mapObjectConstructors/ShipyardInstanceConstructor.h
  89. 4 3
      lib/mapObjects/CArmedInstance.cpp
  90. 2 2
      lib/mapObjects/CArmedInstance.h
  91. 4 2
      lib/mapObjects/CBank.cpp
  92. 1 1
      lib/mapObjects/CBank.h
  93. 2 2
      lib/mapObjects/CGCreature.cpp
  94. 2 0
      lib/mapObjects/CGCreature.h
  95. 5 1
      lib/mapObjects/CGDwelling.cpp
  96. 1 1
      lib/mapObjects/CGDwelling.h
  97. 16 13
      lib/mapObjects/CGHeroInstance.cpp
  98. 4 2
      lib/mapObjects/CGHeroInstance.h
  99. 3 3
      lib/mapObjects/CGMarket.cpp
  100. 5 1
      lib/mapObjects/CGMarket.h

+ 5 - 4
AI/BattleAI/BattleEvaluator.cpp

@@ -350,10 +350,11 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
 	LOGL("Casting spells sounds like fun. Let's see...");
 	//Get all spells we can cast
 	std::vector<const CSpell*> possibleSpells;
-	vstd::copy_if(VLC->spellh->objects, std::back_inserter(possibleSpells), [hero, this](const CSpell *s) -> bool
-	{
-		return s->canBeCast(cb->getBattle(battleID).get(), spells::Mode::HERO, hero);
-	});
+
+	for (auto const & s : VLC->spellh->objects)
+		if (s->canBeCast(cb->getBattle(battleID).get(), spells::Mode::HERO, hero))
+			possibleSpells.push_back(s.get());
+
 	LOGFL("I can cast %d spells.", possibleSpells.size());
 
 	vstd::erase_if(possibleSpells, [](const CSpell *s)

+ 1 - 3
AI/Nullkiller/AIUtility.cpp

@@ -276,12 +276,10 @@ creInfo infoFromDC(const dwellingContent & dc)
 	ci.creID = dc.second.size() ? dc.second.back() : CreatureID(-1); //should never be accessed
 	if (ci.creID != CreatureID::NONE)
 	{
-		ci.cre = VLC->creatures()->getById(ci.creID);
-		ci.level = ci.cre->getLevel(); //this is creature tier, while tryRealize expects dwelling level. Ignore.
+		ci.level = ci.creID.toCreature()->getLevel(); //this is creature tier, while tryRealize expects dwelling level. Ignore.
 	}
 	else
 	{
-		ci.cre = nullptr;
 		ci.level = 0;
 	}
 	return ci;

+ 0 - 1
AI/Nullkiller/AIUtility.h

@@ -163,7 +163,6 @@ struct creInfo
 {
 	int count;
 	CreatureID creID;
-	const Creature * cre;
 	int level;
 };
 creInfo infoFromDC(const dwellingContent & dc);

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

@@ -63,9 +63,9 @@ std::vector<SlotInfo> ArmyManager::toSlotInfo(std::vector<creInfo> army) const
 	{
 		SlotInfo slot;
 
-		slot.creature = VLC->creh->objects[i.cre->getId()];
+		slot.creature = i.creID.toCreature();
 		slot.count = i.count;
-		slot.power = evaluateStackPower(i.cre, i.count);
+		slot.power = evaluateStackPower(i.creID.toCreature(), i.count);
 
 		result.push_back(slot);
 	}
@@ -128,7 +128,7 @@ class TemporaryArmy : public CArmedInstance
 public:
 	void armyChanged() override {}
 	TemporaryArmy()
-		:CArmedInstance(true)
+		:CArmedInstance(nullptr, true)
 	{
 	}
 };
@@ -259,7 +259,7 @@ std::shared_ptr<CCreatureSet> ArmyManager::getArmyAvailableToBuyAsCCreatureSet(
 		if(!ci.count || ci.creID == CreatureID::NONE)
 			continue;
 
-		vstd::amin(ci.count, availableRes / ci.cre->getFullRecruitCost()); //max count we can afford
+		vstd::amin(ci.count, availableRes / ci.creID.toCreature()->getFullRecruitCost()); //max count we can afford
 
 		if(!ci.count)
 			continue;
@@ -270,7 +270,7 @@ std::shared_ptr<CCreatureSet> ArmyManager::getArmyAvailableToBuyAsCCreatureSet(
 			break;
 
 		army->setCreature(dst, ci.creID, ci.count);
-		availableRes -= ci.cre->getFullRecruitCost() * ci.count;
+		availableRes -= ci.creID.toCreature()->getFullRecruitCost() * ci.count;
 	}
 
 	return army;
@@ -287,7 +287,7 @@ ui64 ArmyManager::howManyReinforcementsCanBuy(
 
 	for(const creInfo & ci : army)
 	{
-		aivalue += ci.count * ci.cre->getAIValue();
+		aivalue += ci.count * ci.creID.toCreature()->getAIValue();
 	}
 
 	return aivalue;
@@ -320,7 +320,7 @@ std::vector<creInfo> ArmyManager::getArmyAvailableToBuy(
 
 		if(i < GameConstants::CREATURES_PER_TOWN && countGrowth)
 		{
-			ci.count += town ? town->creatureGrowth(i) : ci.cre->getGrowth();
+			ci.count += town ? town->creatureGrowth(i) : ci.creID.toCreature()->getGrowth();
 		}
 
 		if(!ci.count) continue;
@@ -334,13 +334,13 @@ std::vector<creInfo> ArmyManager::getArmyAvailableToBuy(
 				freeHeroSlots--; //new slot will be occupied
 		}
 
-		vstd::amin(ci.count, availableRes / ci.cre->getFullRecruitCost()); //max count we can afford
+		vstd::amin(ci.count, availableRes / ci.creID.toCreature()->getFullRecruitCost()); //max count we can afford
 
 		if(!ci.count) continue;
 
 		ci.level = i; //this is important for Dungeon Summoning Portal
 		creaturesInDwellings.push_back(ci);
-		availableRes -= ci.cre->getFullRecruitCost() * ci.count;
+		availableRes -= ci.creID.toCreature()->getFullRecruitCost() * ci.count;
 	}
 
 	return creaturesInDwellings;

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

@@ -165,7 +165,7 @@ void DangerHitMapAnalyzer::calculateTileOwners()
 
 	auto addTownHero = [&](const CGTownInstance * town)
 	{
-			auto townHero = new CGHeroInstance();
+			auto townHero = new CGHeroInstance(town->cb);
 			CRandomGenerator rng;
 			auto visitablePos = town->visitablePos();
 			

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

@@ -30,7 +30,7 @@ ui64 FuzzyHelper::estimateBankDanger(const CBank * bank)
 
 	ui64 totalStrength = 0;
 	ui8 totalChance = 0;
-	for(auto config : bankInfo->getPossibleGuards())
+	for(auto config : bankInfo->getPossibleGuards(bank->cb))
 	{
 		totalStrength += config.second.totalStrength * config.first;
 		totalChance += config.first;

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

@@ -141,7 +141,7 @@ uint64_t getCreatureBankArmyReward(const CGObjectInstance * target, const CGHero
 {
 	auto objectInfo = target->getObjectHandler()->getObjectInfo(target->appearance);
 	CBankInfo * bankInfo = dynamic_cast<CBankInfo *>(objectInfo.get());
-	auto creatures = bankInfo->getPossibleCreaturesReward();
+	auto creatures = bankInfo->getPossibleCreaturesReward(target->cb);
 	uint64_t result = 0;
 
 	const auto& slots = hero->Slots();
@@ -236,7 +236,7 @@ int getDwellingArmyCost(const CGObjectInstance * target)
 	return cost;
 }
 
-uint64_t evaluateArtifactArmyValue(CArtifactInstance * art)
+static uint64_t evaluateArtifactArmyValue(const CArtifactInstance * art)
 {
 	if(art->artType->getId() == ArtifactID::SPELL_SCROLL)
 		return 1500;

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

@@ -54,12 +54,12 @@ void BuyArmy::accept(AIGateway * ai)
 		if(objid != CreatureID::NONE && ci.creID.getNum() != objid)
 			continue;
 
-		vstd::amin(ci.count, res / ci.cre->getFullRecruitCost());
+		vstd::amin(ci.count, res / ci.creID.toCreature()->getFullRecruitCost());
 
 		if(ci.count)
 		{
 			cb->recruitCreatures(town, town->getUpperArmy(), ci.creID, ci.count, ci.level);
-			valueBought += ci.count * ci.cre->getAIValue();
+			valueBought += ci.count * ci.creID.toCreature()->getAIValue();
 		}
 	}
 

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

@@ -98,7 +98,7 @@ std::string CompleteQuest::questToString() const
 		return "inactive quest";
 
 	MetaString ms;
-	q.quest->getRolloverText(ms, false);
+	q.quest->getRolloverText(q.obj->cb, ms, false);
 
 	return ms.toString();
 }

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

@@ -373,10 +373,10 @@ HeroExchangeArmy * HeroExchangeMap::tryUpgrade(
 
 		for(auto & creatureToBuy : buyArmy)
 		{
-			auto targetSlot = target->getSlotFor(dynamic_cast<const CCreature*>(creatureToBuy.cre));
+			auto targetSlot = target->getSlotFor(creatureToBuy.creID.toCreature());
 
 			target->addToSlot(targetSlot, creatureToBuy.creID, creatureToBuy.count);
-			target->armyCost += creatureToBuy.cre->getFullRecruitCost() * creatureToBuy.count;
+			target->armyCost += creatureToBuy.creID.toCreature()->getFullRecruitCost() * creatureToBuy.count;
 			target->requireBuyArmy = true;
 		}
 	}

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

@@ -31,7 +31,7 @@ public:
 	virtual bool needsLastStack() const override;
 	std::shared_ptr<SpecialAction> getActorAction() const;
 
-	HeroExchangeArmy(): CArmedInstance(true), requireBuyArmy(false) {}
+	HeroExchangeArmy(): CArmedInstance(nullptr, true), requireBuyArmy(false) {}
 };
 
 struct ExchangeResult

+ 1 - 1
AI/VCAI/FuzzyHelper.cpp

@@ -72,7 +72,7 @@ ui64 FuzzyHelper::estimateBankDanger(const CBank * bank)
 
 	ui64 totalStrength = 0;
 	ui8 totalChance = 0;
-	for(auto config : bankInfo->getPossibleGuards())
+	for(auto config : bankInfo->getPossibleGuards(bank->cb))
 	{
 		totalStrength += config.second.totalStrength * config.first;
 		totalChance += config.first;

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

@@ -103,7 +103,7 @@ std::string CompleteQuest::questToString() const
 		return "inactive quest";
 
 	MetaString ms;
-	q.quest->getRolloverText(ms, false);
+	q.quest->getRolloverText(q.obj->cb, ms, false);
 
 	return ms.toString();
 }

+ 0 - 1
AI/VCAI/VCAI.cpp

@@ -22,7 +22,6 @@
 #include "../../lib/CHeroHandler.h"
 #include "../../lib/GameSettings.h"
 #include "../../lib/gameState/CGameState.h"
-#include "../../lib/bonuses/CBonusSystemNode.h"
 #include "../../lib/bonuses/Limiters.h"
 #include "../../lib/bonuses/Updaters.h"
 #include "../../lib/bonuses/Propagators.h"

+ 1 - 0
CCallback.cpp

@@ -16,6 +16,7 @@
 #include "client/Client.h"
 #include "lib/mapping/CMap.h"
 #include "lib/mapObjects/CGHeroInstance.h"
+#include "lib/mapObjects/CGTownInstance.h"
 #include "lib/CBuildingHandler.h"
 #include "lib/CGeneralTextHandler.h"
 #include "lib/CHeroHandler.h"

+ 1 - 4
client/CGameInfo.cpp

@@ -12,16 +12,13 @@
 
 #include "../lib/VCMI_Lib.h"
 
-const CGameInfo * CGI;
+CGameInfo * CGI;
 CClientState * CCS = nullptr;
 CServerHandler * CSH;
 
 
 CGameInfo::CGameInfo()
 {
-	generaltexth = nullptr;
-	mh = nullptr;
-	townh = nullptr;
 	globalServices = nullptr;
 }
 

+ 16 - 15
client/CGameInfo.h

@@ -56,7 +56,7 @@ extern CClientState * CCS;
 
 /// CGameInfo class
 /// for allowing different functions for accessing game informations
-class CGameInfo : public Services
+class CGameInfo final : public Services
 {
 public:
 	const ArtifactService * artifacts() const override;
@@ -78,19 +78,20 @@ public:
 	const spells::effects::Registry * spellEffects() const override;
 	spells::effects::Registry * spellEffects() override;
 
-	ConstTransitivePtr<CModHandler> modh; //public?
-	ConstTransitivePtr<BattleFieldHandler> battleFieldHandler;
-	ConstTransitivePtr<CHeroHandler> heroh;
-	ConstTransitivePtr<CCreatureHandler> creh;
-	ConstTransitivePtr<CSpellHandler> spellh;
-	ConstTransitivePtr<CSkillHandler> skillh;
-	ConstTransitivePtr<CObjectHandler> objh;
-	ConstTransitivePtr<TerrainTypeHandler> terrainTypeHandler;
-	ConstTransitivePtr<CObjectClassesHandler> objtypeh;
-	ConstTransitivePtr<ObstacleHandler> obstacleHandler;
-	CGeneralTextHandler * generaltexth;
-	CMapHandler * mh;
-	CTownHandler * townh;
+	std::shared_ptr<const CModHandler> modh;
+	std::shared_ptr<const BattleFieldHandler> battleFieldHandler;
+	std::shared_ptr<const CHeroHandler> heroh;
+	std::shared_ptr<const CCreatureHandler> creh;
+	std::shared_ptr<const CSpellHandler> spellh;
+	std::shared_ptr<const CSkillHandler> skillh;
+	std::shared_ptr<const CObjectHandler> objh;
+	std::shared_ptr<const TerrainTypeHandler> terrainTypeHandler;
+	std::shared_ptr<const CObjectClassesHandler> objtypeh;
+	std::shared_ptr<const ObstacleHandler> obstacleHandler;
+	std::shared_ptr<const CGeneralTextHandler> generaltexth;
+	std::shared_ptr<const CTownHandler> townh;
+
+	std::shared_ptr<CMapHandler> mh;
 
 	void setFromLib();
 
@@ -98,4 +99,4 @@ public:
 private:
 	const Services * globalServices;
 };
-extern const CGameInfo* CGI;
+extern CGameInfo* CGI;

+ 1 - 1
client/CMT.cpp

@@ -72,7 +72,7 @@ void init()
 	CStopWatch tmh;
 
 	loadDLLClasses();
-	const_cast<CGameInfo*>(CGI)->setFromLib();
+	CGI->setFromLib();
 
 	logGlobal->info("Initializing VCMI_Lib: %d ms", tmh.getDiff());
 

+ 0 - 1
client/CPlayerInterface.cpp

@@ -82,7 +82,6 @@
 #include "../lib/UnlockGuard.h"
 #include "../lib/VCMIDirs.h"
 
-#include "../lib/bonuses/CBonusSystemNode.h"
 #include "../lib/bonuses/Limiters.h"
 #include "../lib/bonuses/Propagators.h"
 #include "../lib/bonuses/Updaters.h"

+ 10 - 2
client/CServerHandler.cpp

@@ -277,6 +277,9 @@ void CServerHandler::onConnectionFailed(const std::string & errorMessage)
 		// retry - local server might be still starting up
 		logNetwork->debug("\nCannot establish connection. %s. Retrying...", errorMessage);
 		networkHandler->createTimer(*this, std::chrono::milliseconds(100));
+
+			nextClient = std::make_unique<CClient>();
+			c->setCallback(nextClient.get());
 	}
 	else
 	{
@@ -633,7 +636,8 @@ void CServerHandler::startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameSta
 {
 	if(CMM)
 		CMM->disable();
-	client = new CClient();
+
+	std::swap(client, nextClient);
 
 	highScoreCalc = nullptr;
 
@@ -667,7 +671,7 @@ void CServerHandler::endGameplay(bool closeConnection, bool restart)
 	}
 
 	client->endGame();
-	vstd::clear_pointer(client);
+	client.reset();
 
 	if(!restart)
 	{
@@ -683,7 +687,11 @@ void CServerHandler::endGameplay(bool closeConnection, bool restart)
 	}
 	
 	if(c)
+	{
+		nextClient = std::make_unique<CClient>();
+		c->setCallback(nextClient.get());
 		c->enterLobbyConnectionMode();
+	}
 }
 
 void CServerHandler::startCampaignScenario(HighScoreParameter param, std::shared_ptr<CampaignState> cs)

+ 6 - 1
client/CServerHandler.h

@@ -95,6 +95,11 @@ class CServerHandler final : public IServerAPI, public LobbyInfo, public INetwor
 
 	void threadRunNetwork();
 	void threadRunServer(bool connectToLobby);
+
+	/// temporary helper member that exists while game in lobby mode
+	/// required to correctly deserialize gamestate using client-side game callback
+	std::unique_ptr<CClient> nextClient;
+
 	void onServerFinished();
 	void sendLobbyPack(const CPackForLobby & pack) const override;
 
@@ -131,7 +136,7 @@ public:
 	std::unique_ptr<boost::thread> threadRunLocalServer;
 	std::unique_ptr<boost::thread> threadNetwork;
 
-	CClient * client;
+	std::unique_ptr<CClient> client;
 
 	CondSh<bool> campaignServerRestartLock;
 

+ 7 - 10
client/Client.cpp

@@ -140,14 +140,10 @@ CClient::CClient()
 	waitingRequest.clear();
 	applier = std::make_shared<CApplier<CBaseForCLApply>>();
 	registerTypesClientPacks(*applier);
-	IObjectInterface::cb = this;
 	gs = nullptr;
 }
 
-CClient::~CClient()
-{
-	IObjectInterface::cb = nullptr;
-}
+CClient::~CClient() = default;
 
 const Services * CClient::services() const
 {
@@ -178,8 +174,9 @@ void CClient::newGame(CGameState * initializedGameState)
 {
 	CSH->th->update();
 	CMapService mapService;
-	gs = initializedGameState ? initializedGameState : new CGameState();
-	gs->preInit(VLC);
+	assert(initializedGameState);
+	gs = initializedGameState;
+	gs->preInit(VLC, this);
 	logNetwork->trace("\tCreating gamestate: %i", CSH->th->getDiff());
 	if(!initializedGameState)
 	{
@@ -201,7 +198,7 @@ void CClient::loadGame(CGameState * initializedGameState)
 	logNetwork->info("Game state was transferred over network, loading.");
 	gs = initializedGameState;
 
-	gs->preInit(VLC);
+	gs->preInit(VLC, this);
 	gs->updateOnLoad(CSH->si.get());
 	logNetwork->info("Game loaded, initialize interfaces.");
 
@@ -371,7 +368,7 @@ void CClient::endGame()
 		logNetwork->info("Ending current game!");
 		removeGUI();
 
-		vstd::clear_pointer(const_cast<CGameInfo *>(CGI)->mh);
+		CGI->mh.reset();
 		vstd::clear_pointer(gs);
 
 		logNetwork->info("Deleted mapHandler and gameState.");
@@ -393,7 +390,7 @@ void CClient::initMapHandler()
 	// During loading CPlayerInterface from serialized state it's depend on MH
 	if(!settings["session"]["headless"].Bool())
 	{
-		const_cast<CGameInfo *>(CGI)->mh = new CMapHandler(gs->map);
+		CGI->mh = std::make_shared<CMapHandler>(gs->map);
 		logNetwork->trace("Creating mapHandler: %d ms", CSH->th->getDiff());
 	}
 

+ 2 - 2
client/ClientCommandManager.cpp

@@ -203,7 +203,7 @@ void ClientCommandManager::handleConvertTextCommand()
 		try
 		{
 			// load and drop loaded map - we only need loader to run over all maps
-			mapService.loadMap(mapName);
+			mapService.loadMap(mapName, nullptr);
 		}
 		catch(std::exception & e)
 		{
@@ -216,7 +216,7 @@ void ClientCommandManager::handleConvertTextCommand()
 	{
 		auto state = CampaignHandler::getCampaign(campaignName.getName());
 		for (auto const & part : state->allScenarios())
-			state->getMap(part);
+			state->getMap(part, nullptr);
 	}
 
 	VLC->generaltexth->dumpAllTexts();

+ 1 - 0
client/NetPacksClient.cpp

@@ -39,6 +39,7 @@
 #include "../lib/StartInfo.h"
 #include "../lib/CConfigHandler.h"
 #include "../lib/mapObjects/CGMarket.h"
+#include "../lib/mapObjects/CGTownInstance.h"
 #include "../lib/gameState/CGameState.h"
 #include "../lib/CStack.h"
 #include "../lib/battle/BattleInfo.h"

+ 1 - 1
client/battle/BattleInterfaceClasses.cpp

@@ -530,7 +530,7 @@ void StackInfoBasicPanel::initializeData(const CStack * stack)
 		if (hasGraphics)
 		{
 			//FIXME: support permanent duration
-			int duration = stack->getBonusLocalFirst(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(effect)))->turnsRemain;
+			int duration = stack->getFirstBonus(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(effect)))->turnsRemain;
 
 			icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SpellInt"), effect + 1, 0, firstPos.x + offset.x * printed, firstPos.y + offset.y * printed));
 			if(settings["general"]["enableUiEnhancements"].Bool())

+ 1 - 1
client/battle/BattleWindow.cpp

@@ -601,7 +601,7 @@ void BattleWindow::bSpellf()
 	{
 		//TODO: move to spell mechanics, add more information to spell cast problem
 		//Handle Orb of Inhibition-like effects -> we want to display dialog with info, why casting is impossible
-		auto blockingBonus = owner.currentHero()->getBonusLocalFirst(Selector::type()(BonusType::BLOCK_ALL_MAGIC));
+		auto blockingBonus = owner.currentHero()->getFirstBonus(Selector::type()(BonusType::BLOCK_ALL_MAGIC));
 		if (!blockingBonus)
 			return;
 

+ 1 - 0
client/mapView/MapRendererContext.cpp

@@ -21,6 +21,7 @@
 
 #include "../../lib/Point.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
+#include "../../lib/mapObjects/MiscObjects.h"
 #include "../../lib/spells/CSpellHandler.h"
 #include "../../lib/mapping/CMap.h"
 #include "../../lib/pathfinder/CGPathNode.h"

+ 1 - 1
client/windows/CCreatureWindow.cpp

@@ -225,7 +225,7 @@ CStackWindow::ActiveSpellsSection::ActiveSpellsSection(CStackWindow * owner, int
 			spellText = CGI->generaltexth->allTexts[610]; //"%s, duration: %d rounds."
 			boost::replace_first(spellText, "%s", spell->getNameTranslated());
 			//FIXME: support permanent duration
-			int duration = battleStack->getBonusLocalFirst(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(effect)))->turnsRemain;
+			int duration = battleStack->getFirstBonus(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(effect)))->turnsRemain;
 			boost::replace_first(spellText, "%d", std::to_string(duration));
 
 			spellIcons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SpellInt"), effect + 1, 0, firstPos.x + offset.x * printed, firstPos.y + offset.y * printed));

+ 2 - 2
client/windows/CMapOverview.cpp

@@ -104,7 +104,7 @@ std::vector<Canvas> CMapOverviewWidget::createMinimaps(ResourcePath resource) co
 	std::unique_ptr<CMap> map;
 	try
 	{
-		map = mapService.loadMap(resource);
+		map = mapService.loadMap(resource, nullptr);
 	}
 	catch (const std::exception & e)
 	{
@@ -169,7 +169,7 @@ CMapOverviewWidget::CMapOverviewWidget(CMapOverview& parent):
 			lf >> *(mapHeader) >> startInfo;
 
 			if(startInfo->campState)
-				campaignMap = startInfo->campState->getMap(*startInfo->campState->currentScenario());
+				campaignMap = startInfo->campState->getMap(*startInfo->campState->currentScenario(), nullptr);
 			res = ResourcePath(startInfo->fileURI, EResType::MAP);
 		}
 		if(!campaignMap)

+ 2 - 2
client/windows/CQuestLog.cpp

@@ -160,7 +160,7 @@ void CQuestLog::recreateLabelList()
 		}
 
 		MetaString text;
-		quests[i].quest->getRolloverText (text, false);
+		quests[i].quest->getRolloverText (quests[i].obj->cb, text, false);
 		if (quests[i].obj)
 		{
 			if (auto seersHut = dynamic_cast<const CGSeerHut *>(quests[i].obj))
@@ -236,7 +236,7 @@ void CQuestLog::selectQuest(int which, int labelId)
 
 	MetaString text;
 	std::vector<Component> components;
-	currentQuest->quest->getVisitText(text, components, true);
+	currentQuest->quest->getVisitText(currentQuest->obj->cb, text, components, true);
 	if(description->slider)
 		description->slider->scrollToMin(); // scroll text to start position
 	description->setText(text.toString()); //TODO: use special log entry text

+ 1 - 0
cmake_modules/VCMI_lib.cmake

@@ -629,6 +629,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/CThreadHelper.h
 		${MAIN_LIB_DIR}/CTownHandler.h
 		${MAIN_LIB_DIR}/FunctionList.h
+		${MAIN_LIB_DIR}/GameCallbackHolder.h
 		${MAIN_LIB_DIR}/GameConstants.h
 		${MAIN_LIB_DIR}/GameSettings.h
 		${MAIN_LIB_DIR}/IBonusTypeHandler.h

+ 1 - 0
docs/Readme.md

@@ -24,6 +24,7 @@ VCMI is an open-source recreation of Heroes of Might & Magic III engine, giving
  * Bugtracker: https://github.com/vcmi/vcmi/issues
  * Slack:      https://slack.vcmi.eu/
  * Discord:    https://discord.gg/chBT42V
+ * GPT Store:  https://chat.openai.com/g/g-1kNhX0mlO-vcmi-assistant
 
 ## Latest release
 

+ 3 - 3
lib/ArtifactUtils.cpp

@@ -187,14 +187,14 @@ DLL_LINKAGE std::vector<const CArtifact*> ArtifactUtils::assemblyPossibilities(
 
 DLL_LINKAGE CArtifactInstance * ArtifactUtils::createScroll(const SpellID & sid)
 {
-	auto ret = new CArtifactInstance(VLC->arth->objects[ArtifactID::SPELL_SCROLL]);
+	auto ret = new CArtifactInstance(ArtifactID(ArtifactID::SPELL_SCROLL).toArtifact());
 	auto bonus = std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::SPELL,
 		BonusSource::ARTIFACT_INSTANCE, -1, BonusSourceID(ArtifactID(ArtifactID::SPELL_SCROLL)), BonusSubtypeID(sid));
 	ret->addNewBonus(bonus);
 	return ret;
 }
 
-DLL_LINKAGE CArtifactInstance * ArtifactUtils::createNewArtifactInstance(CArtifact * art)
+DLL_LINKAGE CArtifactInstance * ArtifactUtils::createNewArtifactInstance(const CArtifact * art)
 {
 	assert(art);
 
@@ -216,7 +216,7 @@ DLL_LINKAGE CArtifactInstance * ArtifactUtils::createNewArtifactInstance(CArtifa
 
 DLL_LINKAGE CArtifactInstance * ArtifactUtils::createNewArtifactInstance(const ArtifactID & aid)
 {
-	return ArtifactUtils::createNewArtifactInstance((*VLC->arth)[aid]);
+	return ArtifactUtils::createNewArtifactInstance(aid.toArtifact());
 }
 
 DLL_LINKAGE CArtifactInstance * ArtifactUtils::createArtifact(CMap * map, const ArtifactID & aid, SpellID spellID)

+ 1 - 1
lib/ArtifactUtils.h

@@ -40,7 +40,7 @@ namespace ArtifactUtils
 	DLL_LINKAGE bool isBackpackFreeSlots(const CArtifactSet * target, const size_t reqSlots = 1);
 	DLL_LINKAGE std::vector<const CArtifact*> assemblyPossibilities(const CArtifactSet * artSet, const ArtifactID & aid);
 	DLL_LINKAGE CArtifactInstance * createScroll(const SpellID & sid);
-	DLL_LINKAGE CArtifactInstance * createNewArtifactInstance(CArtifact * art);
+	DLL_LINKAGE CArtifactInstance * createNewArtifactInstance(const CArtifact * art);
 	DLL_LINKAGE CArtifactInstance * createNewArtifactInstance(const ArtifactID & aid);
 	DLL_LINKAGE CArtifactInstance * createArtifact(CMap * map, const ArtifactID & aid, SpellID spellID = SpellID::NONE);
 	DLL_LINKAGE void insertScrrollSpellName(std::string & description, const SpellID & sid);

+ 5 - 5
lib/CArtHandler.cpp

@@ -49,12 +49,12 @@ bool CCombinedArtifact::isCombined() const
 	return !(constituents.empty());
 }
 
-const std::vector<CArtifact*> & CCombinedArtifact::getConstituents() const
+const std::vector<const CArtifact*> & CCombinedArtifact::getConstituents() const
 {
 	return constituents;
 }
 
-const std::vector<CArtifact*> & CCombinedArtifact::getPartOf() const
+const std::vector<const CArtifact*> & CCombinedArtifact::getPartOf() const
 {
 	return partOf;
 }
@@ -328,7 +328,7 @@ std::vector<JsonNode> CArtHandler::loadLegacyData()
 	const std::vector<std::string> artSlots = { ART_POS_LIST };
 	#undef ART_POS
 
-	static std::map<char, std::string> classes =
+	static const std::map<char, std::string> classes =
 		{{'S',"SPECIAL"}, {'T',"TREASURE"},{'N',"MINOR"},{'J',"MAJOR"},{'R',"RELIC"},};
 
 	CLegacyConfigParser parser(TextPath::builtin("DATA/ARTRAITS.TXT"));
@@ -353,7 +353,7 @@ std::vector<JsonNode> CArtHandler::loadLegacyData()
 				artData["slot"].Vector().back().String() = artSlot;
 			}
 		}
-		artData["class"].String() = classes[parser.readString()[0]];
+		artData["class"].String() = classes.at(parser.readString()[0]);
 		artData["text"]["description"].String() = parser.readString();
 
 		parser.endLine();
@@ -597,7 +597,7 @@ void CArtHandler::loadComponents(CArtifact * art, const JsonNode & node)
 			{
 				// when this code is called both combinational art as well as component are loaded
 				// so it is safe to access any of them
-				art->constituents.push_back(objects[id]);
+				art->constituents.push_back(ArtifactID(id).toArtifact());
 				objects[id]->partOf.push_back(art);
 			});
 		}

+ 4 - 4
lib/CArtHandler.h

@@ -47,12 +47,12 @@ class DLL_LINKAGE CCombinedArtifact
 protected:
 	CCombinedArtifact() = default;
 
-	std::vector<CArtifact*> constituents; // Artifacts IDs a combined artifact consists of, or nullptr.
-	std::vector<CArtifact*> partOf; // Reverse map of constituents - combined arts that include this art
+	std::vector<const CArtifact*> constituents; // Artifacts IDs a combined artifact consists of, or nullptr.
+	std::vector<const CArtifact*> partOf; // Reverse map of constituents - combined arts that include this art
 public:
 	bool isCombined() const;
-	const std::vector<CArtifact*> & getConstituents() const;
-	const std::vector<CArtifact*> & getPartOf() const;
+	const std::vector<const CArtifact*> & getConstituents() const;
+	const std::vector<const CArtifact*> & getPartOf() const;
 };
 
 class DLL_LINKAGE CScrollArtifact

+ 4 - 4
lib/CArtifactInstance.cpp

@@ -62,7 +62,7 @@ void CCombinedArtifactInstance::addPlacementMap(CArtifactSet::ArtPlacementMap &
 SpellID CScrollArtifactInstance::getScrollSpellID() const
 {
 	auto artInst = static_cast<const CArtifactInstance*>(this);
-	const auto bonus = artInst->getBonusLocalFirst(Selector::type()(BonusType::SPELL));
+	const auto bonus = artInst->getFirstBonus(Selector::type()(BonusType::SPELL));
 	if(!bonus)
 		return SpellID::NONE;
 	return bonus->subtype.as<SpellID>();
@@ -107,7 +107,7 @@ void CArtifactInstance::init()
 	setNodeType(ARTIFACT_INSTANCE);
 }
 
-CArtifactInstance::CArtifactInstance(CArtifact * art)
+CArtifactInstance::CArtifactInstance(const CArtifact * art)
 {
 	init();
 	setType(art);
@@ -118,10 +118,10 @@ CArtifactInstance::CArtifactInstance()
 	init();
 }
 
-void CArtifactInstance::setType(CArtifact * art)
+void CArtifactInstance::setType(const CArtifact * art)
 {
 	artType = art;
-	attachTo(*art);
+	attachToSource(*art);
 }
 
 std::string CArtifactInstance::nodeName() const

+ 3 - 3
lib/CArtifactInstance.h

@@ -73,11 +73,11 @@ protected:
 
 	ArtifactInstanceID id;
 public:
-	ConstTransitivePtr<CArtifact> artType;
+	const CArtifact * artType = nullptr;
 
-	CArtifactInstance(CArtifact * art);
+	CArtifactInstance(const CArtifact * art);
 	CArtifactInstance();
-	void setType(CArtifact * art);
+	void setType(const CArtifact * art);
 	std::string nodeName() const override;
 	std::string getDescription() const;
 	ArtifactID getTypeId() const;

+ 1 - 12
lib/CCreatureHandler.cpp

@@ -407,20 +407,9 @@ void CCreature::serializeJson(JsonSerializeFormat & handler)
 CCreatureHandler::CCreatureHandler()
 	: expAfterUpgrade(0)
 {
-	VLC->creh = this;
 	loadCommanders();
 }
 
-const CCreature * CCreatureHandler::getCreature(const std::string & scope, const std::string & identifier) const
-{
-	std::optional<si32> index = VLC->identifiers()->getIdentifier(scope, "creature", identifier);
-
-	if(!index)
-		throw std::runtime_error("Creature not found "+identifier);
-
-	return objects[*index];
-}
-
 void CCreatureHandler::loadCommanders()
 {
 	auto configResource = JsonPath::builtin("config/commanders.json");
@@ -797,7 +786,7 @@ void CCreatureHandler::loadCrExpBon(CBonusSystemNode & globalEffects)
 			bl.clear();
 			loadStackExp(b, bl, parser);
 			for(const auto & b : bl)
-				(*this)[sid]->addNewBonus(b); //add directly to CCreature Node
+				objects[sid.getNum()]->addNewBonus(b); //add directly to CCreature Node
 		}
 		while (parser.endLine());
 

+ 0 - 2
lib/CCreatureHandler.h

@@ -222,8 +222,6 @@ public:
 	std::vector< std::vector <ui8> > skillLevels; //how much of a bonus will be given to commander with every level. SPELL_POWER also gives CASTS and RESISTANCE
 	std::vector <std::pair <std::shared_ptr<Bonus>, std::pair <ui8, ui8> > > skillRequirements; // first - Bonus, second - which two skills are needed to use it
 
-	const CCreature * getCreature(const std::string & scope, const std::string & identifier) const;
-
 	CreatureID pickRandomMonster(CRandomGenerator & rand, int tier = -1) const; //tier <1 - CREATURES_PER_TOWN> or -1 for any
 
 	CCreatureHandler();

+ 5 - 6
lib/CCreatureSet.cpp

@@ -739,11 +739,10 @@ void CStackInstance::giveStackExp(TExpType exp)
 	if (!vstd::iswithin(level, 1, 7))
 		level = 0;
 
-	CCreatureHandler * creh = VLC->creh;
-	ui32 maxExp = creh->expRanks[level].back();
+	ui32 maxExp = VLC->creh->expRanks[level].back();
 
 	vstd::amin(exp, static_cast<TExpType>(maxExp)); //prevent exp overflow due to different types
-	vstd::amin(exp, (maxExp * creh->maxExpPerBattle[level])/100);
+	vstd::amin(exp, (maxExp * VLC->creh->maxExpPerBattle[level])/100);
 	vstd::amin(experience += exp, maxExp); //can't get more exp than this limit
 }
 
@@ -759,7 +758,7 @@ void CStackInstance::setType(const CCreature *c)
 {
 	if(type)
 	{
-		detachFrom(const_cast<CCreature&>(*type));
+		detachFromSource(*type);
 		if (type->isMyUpgrade(c) && VLC->settings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE))
 			experience = static_cast<TExpType>(experience * VLC->creh->expAfterUpgrade / 100.0);
 	}
@@ -767,7 +766,7 @@ void CStackInstance::setType(const CCreature *c)
 	CStackBasicDescriptor::setType(c);
 
 	if(type)
-		attachTo(const_cast<CCreature&>(*type));
+		attachToSource(*type);
 }
 std::string CStackInstance::bonusToString(const std::shared_ptr<Bonus>& bonus, bool description) const
 {
@@ -1055,7 +1054,7 @@ void CStackBasicDescriptor::serializeJson(JsonSerializeFormat & handler)
 		std::string typeName;
 		handler.serializeString("type", typeName);
 		if(!typeName.empty())
-			setType(VLC->creh->getCreature(ModScope::scopeMap(), typeName));
+			setType(CreatureID(CreatureID::decode(typeName)).toCreature());
 	}
 }
 

+ 7 - 5
lib/CGameInfoCallback.cpp

@@ -16,6 +16,8 @@
 #include "gameState/TavernHeroesPool.h"
 #include "gameState/QuestInfo.h"
 #include "mapObjects/CGHeroInstance.h"
+#include "mapObjects/CGTownInstance.h"
+#include "mapObjects/MiscObjects.h"
 #include "networkPacks/ArtifactLocation.h"
 #include "CGeneralTextHandler.h"
 #include "StartInfo.h" // for StartInfo
@@ -382,7 +384,7 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero
 				if(creature->getFaction() == factionIndex && static_cast<int>(creature->getAIValue()) > maxAIValue)
 				{
 					maxAIValue = creature->getAIValue();
-					mostStrong = creature;
+					mostStrong = creature.get();
 				}
 			}
 
@@ -791,7 +793,7 @@ int CPlayerSpecificInfoCallback::getHeroSerial(const CGHeroInstance * hero, bool
 
 int3 CPlayerSpecificInfoCallback::getGrailPos( double *outKnownRatio )
 {
-	if (!getPlayerID() || CGObelisk::obeliskCount == 0)
+	if (!getPlayerID() || gs->map->obeliskCount == 0)
 	{
 		*outKnownRatio = 0.0;
 	}
@@ -799,10 +801,10 @@ int3 CPlayerSpecificInfoCallback::getGrailPos( double *outKnownRatio )
 	{
 		TeamID t = gs->getPlayerTeam(*getPlayerID())->id;
 		double visited = 0.0;
-		if(CGObelisk::visited.count(t))
-			visited = static_cast<double>(CGObelisk::visited[t]);
+		if(gs->map->obelisksVisited.count(t))
+			visited = static_cast<double>(gs->map->obelisksVisited[t]);
 
-		*outKnownRatio = visited / CGObelisk::obeliskCount;
+		*outKnownRatio = visited / gs->map->obeliskCount;
 	}
 	return gs->map->grailPos;
 }

+ 18 - 10
lib/CHeroHandler.cpp

@@ -155,6 +155,14 @@ bool CHeroClass::isMagicHero() const
 	return affinity == MAGIC;
 }
 
+int CHeroClass::tavernProbability(FactionID targetFaction) const
+{
+	auto it = selectionProbability.find(targetFaction);
+	if (it != selectionProbability.end())
+		return it->second;
+	return 0;
+}
+
 EAlignment CHeroClass::getAlignment() const
 {
 	return VLC->factions()->getById(faction)->getAlignment();
@@ -292,7 +300,7 @@ CHeroClass * CHeroClassHandler::loadFromJson(const std::string & scope, const Js
 	VLC->identifiers()->requestIdentifier ("creature", node["commander"],
 	[=](si32 commanderID)
 	{
-		heroClass->commander = VLC->creh->objects[commanderID];
+		heroClass->commander = CreatureID(commanderID).toCreature();
 	});
 
 	heroClass->defaultTavernChance = static_cast<ui32>(node["defaultTavern"].Float());
@@ -369,9 +377,9 @@ std::vector<JsonNode> CHeroClassHandler::loadLegacyData()
 void CHeroClassHandler::afterLoadFinalization()
 {
 	// for each pair <class, town> set selection probability if it was not set before in tavern entries
-	for(CHeroClass * heroClass : objects)
+	for(auto & heroClass : objects)
 	{
-		for(CFaction * faction : VLC->townh->objects)
+		for(auto & faction : VLC->townh->objects)
 		{
 			if (!faction->town)
 				continue;
@@ -394,9 +402,9 @@ void CHeroClassHandler::afterLoadFinalization()
 		}
 	}
 
-	for(CHeroClass * hc : objects)
+	for(const auto & hc : objects)
 	{
-		if (!hc->imageMapMale.empty())
+		if(!hc->imageMapMale.empty())
 		{
 			JsonNode templ;
 			templ["animation"].String() = hc->imageMapMale;
@@ -454,7 +462,7 @@ CHero * CHeroHandler::loadFromJson(const std::string & scope, const JsonNode & n
 	VLC->identifiers()->requestIdentifier("heroClass", node["class"],
 	[=](si32 classID)
 	{
-		hero->heroClass = classes[HeroClassID(classID)];
+		hero->heroClass = HeroClassID(classID).toHeroClass();
 	});
 
 	return hero;
@@ -532,7 +540,7 @@ static std::vector<std::shared_ptr<Bonus>> createCreatureSpecialty(CreatureID ba
 	{
 		std::set<CreatureID> oldTargets = targets;
 
-		for (auto const & upgradeSourceID : oldTargets)
+		for(const auto & upgradeSourceID : oldTargets)
 		{
 			const CCreature * upgradeSource = upgradeSourceID.toCreature();
 			targets.insert(upgradeSource->upgrades.begin(), upgradeSource->upgrades.end());
@@ -544,7 +552,7 @@ static std::vector<std::shared_ptr<Bonus>> createCreatureSpecialty(CreatureID ba
 
 	for(CreatureID cid : targets)
 	{
-		auto const & specCreature = *cid.toCreature();
+		const auto & specCreature = *cid.toCreature();
 		int stepSize = specCreature.getLevel() ? specCreature.getLevel() : 5;
 
 		{
@@ -604,7 +612,7 @@ void CHeroHandler::beforeValidate(JsonNode & object)
 
 void CHeroHandler::afterLoadFinalization()
 {
-	for (auto const & functor : callAfterLoadFinalization)
+	for(const auto & functor : callAfterLoadFinalization)
 		functor();
 
 	callAfterLoadFinalization.clear();
@@ -790,7 +798,7 @@ std::set<HeroTypeID> CHeroHandler::getDefaultAllowed() const
 {
 	std::set<HeroTypeID> result;
 
-	for(const CHero * hero : objects)
+	for(auto & hero : objects)
 		if (hero && !hero->special)
 			result.insert(hero->getId());
 

+ 4 - 4
lib/CHeroHandler.h

@@ -57,7 +57,7 @@ public:
 
 	std::vector<InitialArmyStack> initialArmy;
 
-	CHeroClass * heroClass{};
+	const CHeroClass * heroClass = nullptr;
 	std::vector<std::pair<SecondarySkill, ui8> > secSkillsInit; //initial secondary skills; first - ID of skill, second - level of skill (1 - basic, 2 - adv., 3 - expert)
 	BonusList specialty;
 	std::set<SpellID> spells;
@@ -121,7 +121,7 @@ public:
 	// resulting chance = sqrt(town.chance * heroClass.chance)
 	ui32 defaultTavernChance;
 
-	CCreature * commander;
+	const CCreature * commander;
 
 	std::vector<int> primarySkillInitial;  // initial primary skills
 	std::vector<int> primarySkillLowLevel; // probability (%) of getting point of primary skill when getting level
@@ -154,6 +154,8 @@ public:
 	void serializeJson(JsonSerializeFormat & handler);
 
 	EAlignment getAlignment() const;
+
+	int tavernProbability(FactionID faction) const;
 };
 
 class DLL_LINKAGE CHeroClassHandler : public CHandlerBase<HeroClassID, HeroClass, CHeroClass, HeroClassService>
@@ -189,8 +191,6 @@ class DLL_LINKAGE CHeroHandler : public CHandlerBase<HeroTypeID, HeroType, CHero
 	std::vector<std::function<void()>> callAfterLoadFinalization;
 
 public:
-	CHeroClassHandler classes;
-
 	ui32 level(TExpType experience) const; //calculates level corresponding to given experience amount
 	TExpType reqExp(ui32 level) const; //calculates experience required for given level
 	ui32 maxSupportedLevel() const;

+ 0 - 2
lib/CPlayerState.cpp

@@ -23,8 +23,6 @@ PlayerState::PlayerState()
 	setNodeType(PLAYER);
 }
 
-PlayerState::PlayerState(PlayerState && other) noexcept = default;
-
 PlayerState::~PlayerState() = default;
 
 std::string PlayerState::nodeName() const

+ 0 - 2
lib/CPlayerState.h

@@ -67,7 +67,6 @@ public:
 	TurnTimerInfo turnTimer;
 
 	PlayerState();
-	PlayerState(PlayerState && other) noexcept;
 	~PlayerState();
 
 	std::string nodeName() const override;
@@ -123,7 +122,6 @@ public:
 	std::unique_ptr<boost::multi_array<ui8, 3>> fogOfWarMap; //[z][x][y] true - visible, false - hidden
 
 	TeamState();
-	TeamState(TeamState && other) noexcept;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{

+ 1 - 1
lib/CStack.cpp

@@ -72,7 +72,7 @@ void CStack::localInit(BattleInfo * battleInfo)
 		CArmedInstance * army = battle->battleGetArmyObject(side);
 		assert(army);
 		attachTo(*army);
-		attachTo(const_cast<CCreature&>(*type));
+		attachToSource(*type);
 	}
 	nativeTerrain = getNativeTerrain(); //save nativeTerrain in the variable on the battle start to avoid dead lock
 	CUnitState::localInit(this); //it causes execution of the CStack::isOnNativeTerrain where nativeTerrain will be considered

+ 6 - 6
lib/CTownHandler.cpp

@@ -336,9 +336,9 @@ JsonNode readBuilding(CLegacyConfigParser & parser)
 	return ret;
 }
 
-TPropagatorPtr & CTownHandler::emptyPropagator()
+const TPropagatorPtr & CTownHandler::emptyPropagator()
 {
-	static TPropagatorPtr emptyProp(nullptr);
+	static const TPropagatorPtr emptyProp(nullptr);
 	return emptyProp;
 }
 
@@ -534,7 +534,7 @@ R CTownHandler::getMappedValue(const JsonNode & node, const R defval, const std:
 void CTownHandler::addBonusesForVanilaBuilding(CBuilding * building) const
 {
 	std::shared_ptr<Bonus> b;
-	static TPropagatorPtr playerPropagator = std::make_shared<CPropagatorNodeType>(CBonusSystemNode::ENodeTypes::PLAYER);
+	static const TPropagatorPtr playerPropagator = std::make_shared<CPropagatorNodeType>(CBonusSystemNode::ENodeTypes::PLAYER);
 
 	if(building->bid == BuildingID::TAVERN)
 	{
@@ -578,7 +578,7 @@ std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, BonusType ty
 	return createBonus(build, type, val, subtype, emptyPropagator());
 }
 
-std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, BonusType type, int val, BonusSubtypeID subtype, TPropagatorPtr & prop) const
+std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, BonusType type, int val, BonusSubtypeID subtype, const TPropagatorPtr & prop) const
 {
 	std::ostringstream descr;
 	descr << build->getNameTranslated();
@@ -589,7 +589,7 @@ std::shared_ptr<Bonus> CTownHandler::createBonusImpl(const BuildingID & building
 													 const FactionID & faction,
 													 BonusType type,
 													 int val,
-													 TPropagatorPtr & prop,
+													 const TPropagatorPtr & prop,
 													 const std::string & description,
 													 BonusSubtypeID subtype) const
 {
@@ -991,7 +991,7 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source)
 
 		VLC->identifiers()->requestIdentifier(node.second.meta, "heroClass",node.first, [=](si32 classID)
 		{
-			VLC->heroh->classes[HeroClassID(classID)]->selectionProbability[town->faction->getId()] = chance;
+			VLC->heroclassesh->objects[classID]->selectionProbability[town->faction->getId()] = chance;
 		});
 	}
 

+ 3 - 3
lib/CTownHandler.h

@@ -290,7 +290,7 @@ class DLL_LINKAGE CTownHandler : public CHandlerBase<FactionID, Faction, CFactio
 	std::vector<BuildingRequirementsHelper> requirementsToLoad;
 	std::vector<BuildingRequirementsHelper> overriddenBidsToLoad; //list of buildings, which bonuses should be overridden.
 
-	static TPropagatorPtr & emptyPropagator();
+	static const TPropagatorPtr & emptyPropagator();
 
 	void initializeRequirements();
 	void initializeOverridden();
@@ -303,12 +303,12 @@ class DLL_LINKAGE CTownHandler : public CHandlerBase<FactionID, Faction, CFactio
 
 	std::shared_ptr<Bonus> createBonus(CBuilding * build, BonusType type, int val) const;
 	std::shared_ptr<Bonus> createBonus(CBuilding * build, BonusType type, int val, BonusSubtypeID subtype) const;
-	std::shared_ptr<Bonus> createBonus(CBuilding * build, BonusType type, int val, BonusSubtypeID subtype, TPropagatorPtr & prop) const;
+	std::shared_ptr<Bonus> createBonus(CBuilding * build, BonusType type, int val, BonusSubtypeID subtype, const TPropagatorPtr & prop) const;
 	std::shared_ptr<Bonus> createBonusImpl(const BuildingID & building,
 										   const FactionID & faction,
 												  BonusType type,
 												  int val,
-												  TPropagatorPtr & prop,
+												  const TPropagatorPtr & prop,
 												  const std::string & description,
 												  BonusSubtypeID subtype) const;
 

+ 26 - 0
lib/GameCallbackHolder.h

@@ -0,0 +1,26 @@
+/*
+ * GameCallbackHolder.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class IGameCallback;
+
+class DLL_LINKAGE GameCallbackHolder
+{
+public:
+	IGameCallback * const cb;
+
+	explicit GameCallbackHolder(IGameCallback *cb):
+		cb(cb)
+	{}
+};
+
+VCMI_LIB_NAMESPACE_END

+ 3 - 1
lib/IGameCallback.cpp

@@ -16,7 +16,6 @@
 #include "CBonusTypeHandler.h"
 #include "BattleFieldHandler.h"
 #include "ObstacleHandler.h"
-#include "bonuses/CBonusSystemNode.h"
 #include "bonuses/Limiters.h"
 #include "bonuses/Propagators.h"
 #include "bonuses/Updaters.h"
@@ -26,7 +25,10 @@
 #include "rmg/CMapGenOptions.h"
 #include "mapObjectConstructors/AObjectTypeHandler.h"
 #include "mapObjectConstructors/CObjectClassesHandler.h"
+#include "mapObjects/CGTownInstance.h"
 #include "mapObjects/CObjectHandler.h"
+#include "mapObjects/CQuest.h"
+#include "mapObjects/MiscObjects.h"
 #include "mapObjects/ObjectTemplate.h"
 #include "campaign/CampaignState.h"
 #include "StartInfo.h"

+ 18 - 16
lib/IHandlerBase.h

@@ -44,11 +44,21 @@ public:
 	/// allows handler to do post-loading step for validation or integration of loaded data
 	virtual void afterLoadFinalization(){};
 
-	virtual ~IHandlerBase(){}
+	virtual ~IHandlerBase() = default;
 };
 
 template <class _ObjectID, class _ObjectBase, class _Object, class _ServiceBase> class CHandlerBase : public _ServiceBase, public IHandlerBase
 {
+	const _Object * getObjectImpl(const int32_t index) const
+	{
+		if(index < 0 || index >= objects.size())
+		{
+			logMod->error("%s id %d is invalid", getTypeNames()[0], index);
+			throw std::runtime_error("Attempt to access invalid index " + std::to_string(index) + " of type " + getTypeNames().front());
+		}
+		return objects[index].get();
+	}
+
 public:
 	virtual ~CHandlerBase()
 	{
@@ -56,22 +66,21 @@ public:
 		{
 			o.dellNull();
 		}
-
 	}
 
 	const Entity * getBaseByIndex(const int32_t index) const override
 	{
-		return getByIndex(index);
+		return getObjectImpl(index);
 	}
 
 	const _ObjectBase * getById(const _ObjectID & id) const override
 	{
-		return (*this)[id].get();
+		return getObjectImpl(id.getNum());
 	}
 
 	const _ObjectBase * getByIndex(const int32_t index) const override
 	{
-		return (*this)[_ObjectID(index)].get();
+		return getObjectImpl(index);
 	}
 
 	void forEachBase(const std::function<void(const Entity * entity, bool & stop)> & cb) const override
@@ -105,21 +114,14 @@ public:
 			registerObject(scope, type_name, name, object->getIndex());
 	}
 
-	ConstTransitivePtr<_Object> operator[] (const _ObjectID id) const
+	const _Object * operator[] (const _ObjectID id) const
 	{
-		const int32_t raw_id = id.getNum();
-		return operator[](raw_id);
+		return getObjectImpl(id.getNum());
 	}
 
-	ConstTransitivePtr<_Object> operator[] (int32_t index) const
+	const _Object * operator[] (int32_t index) const
 	{
-		if(index < 0 || index >= objects.size())
-		{
-			logMod->error("%s id %d is invalid", getTypeNames()[0], index);
-			throw std::runtime_error("Attempt to access invalid index " + std::to_string(index) + " of type " + getTypeNames().front());
-		}
-
-		return objects[index];
+		return getObjectImpl(index);
 	}
 
 	void updateEntity(int32_t index, const JsonNode & data)

+ 2 - 3
lib/JsonNode.cpp

@@ -694,7 +694,6 @@ std::shared_ptr<Bonus> JsonUtils::parseBonus(const JsonVector & ability_vec)
 template <typename T>
 const T parseByMap(const std::map<std::string, T> & map, const JsonNode * val, const std::string & err)
 {
-	static T defaultValue = T();
 	if (!val->isNull())
 	{
 		const std::string & type = val->String();
@@ -702,7 +701,7 @@ const T parseByMap(const std::map<std::string, T> & map, const JsonNode * val, c
 		if (it == map.end())
 		{
 			logMod->error("Error: invalid %s%s.", err, type);
-			return defaultValue;
+			return {};
 		}
 		else
 		{
@@ -710,7 +709,7 @@ const T parseByMap(const std::map<std::string, T> & map, const JsonNode * val, c
 		}
 	}
 	else
-		return defaultValue;
+		return {};
 }
 
 template <typename T>

+ 40 - 43
lib/JsonRandom.cpp

@@ -31,9 +31,7 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-namespace JsonRandom
-{
-	si32 loadVariable(std::string variableGroup, const std::string & value, const Variables & variables, si32 defaultValue)
+	si32 JsonRandom::loadVariable(const std::string & variableGroup, const std::string & value, const Variables & variables, si32 defaultValue)
 	{
 		if (value.empty() || value[0] != '@')
 		{
@@ -51,12 +49,12 @@ namespace JsonRandom
 		return variables.at(variableID);
 	}
 
-	si32 loadValue(const JsonNode & value, CRandomGenerator & rng, const Variables & variables, si32 defaultValue)
+	si32 JsonRandom::loadValue(const JsonNode & value, CRandomGenerator & rng, const Variables & variables, si32 defaultValue)
 	{
 		if(value.isNull())
 			return defaultValue;
 		if(value.isNumber())
-			return static_cast<si32>(value.Float());
+			return value.Integer();
 		if (value.isString())
 			return loadVariable("number", value.String(), variables, defaultValue);
 
@@ -70,16 +68,16 @@ namespace JsonRandom
 		if(value.isStruct())
 		{
 			if (!value["amount"].isNull())
-				return static_cast<si32>(loadValue(value["amount"], rng, variables, defaultValue));
-			si32 min = static_cast<si32>(loadValue(value["min"], rng, variables, 0));
-			si32 max = static_cast<si32>(loadValue(value["max"], rng, variables, 0));
+				return loadValue(value["amount"], rng, variables, defaultValue);
+			si32 min = loadValue(value["min"], rng, variables, 0);
+			si32 max = loadValue(value["max"], rng, variables, 0);
 			return rng.getIntRange(min, max)();
 		}
 		return defaultValue;
 	}
 
 	template<typename IdentifierType>
-	IdentifierType decodeKey(const std::string & modScope, const std::string & value, const Variables & variables)
+	IdentifierType JsonRandom::decodeKey(const std::string & modScope, const std::string & value, const Variables & variables)
 	{
 		if (value.empty() || value[0] != '@')
 			return IdentifierType(*VLC->identifiers()->getIdentifier(modScope, IdentifierType::entityType(), value));
@@ -88,7 +86,7 @@ namespace JsonRandom
 	}
 
 	template<typename IdentifierType>
-	IdentifierType decodeKey(const JsonNode & value, const Variables & variables)
+	IdentifierType JsonRandom::decodeKey(const JsonNode & value, const Variables & variables)
 	{
 		if (value.String().empty() || value.String()[0] != '@')
 			return IdentifierType(*VLC->identifiers()->getIdentifier(IdentifierType::entityType(), value));
@@ -97,19 +95,19 @@ namespace JsonRandom
 	}
 
 	template<>
-	PlayerColor decodeKey(const JsonNode & value, const Variables & variables)
+	PlayerColor JsonRandom::decodeKey(const JsonNode & value, const Variables & variables)
 	{
 		return PlayerColor(*VLC->identifiers()->getIdentifier("playerColor", value));
 	}
 
 	template<>
-	PrimarySkill decodeKey(const JsonNode & value, const Variables & variables)
+	PrimarySkill JsonRandom::decodeKey(const JsonNode & value, const Variables & variables)
 	{
 		return PrimarySkill(*VLC->identifiers()->getIdentifier("primarySkill", value));
 	}
 
 	template<>
-	PrimarySkill decodeKey(const std::string & modScope, const std::string & value, const Variables & variables)
+	PrimarySkill JsonRandom::decodeKey(const std::string & modScope, const std::string & value, const Variables & variables)
 	{
 		if (value.empty() || value[0] != '@')
 			return PrimarySkill(*VLC->identifiers()->getIdentifier(modScope, "primarySkill", value));
@@ -120,13 +118,13 @@ namespace JsonRandom
 	/// Method that allows type-specific object filtering
 	/// Default implementation is to accept all input objects
 	template<typename IdentifierType>
-	std::set<IdentifierType> filterKeysTyped(const JsonNode & value, const std::set<IdentifierType> & valuesSet)
+	std::set<IdentifierType> JsonRandom::filterKeysTyped(const JsonNode & value, const std::set<IdentifierType> & valuesSet)
 	{
 		return valuesSet;
 	}
 
 	template<>
-	std::set<ArtifactID> filterKeysTyped(const JsonNode & value, const std::set<ArtifactID> & valuesSet)
+	std::set<ArtifactID> JsonRandom::filterKeysTyped(const JsonNode & value, const std::set<ArtifactID> & valuesSet)
 	{
 		assert(value.isStruct());
 
@@ -164,7 +162,7 @@ namespace JsonRandom
 			if(!allowedClasses.empty() && !allowedClasses.count(art->aClass))
 				continue;
 
-			if(!IObjectInterface::cb->isAllowed(art->getId()))
+			if(!cb->isAllowed(art->getId()))
 				continue;
 
 			if(!allowedPositions.empty())
@@ -186,7 +184,7 @@ namespace JsonRandom
 	}
 
 	template<>
-	std::set<SpellID> filterKeysTyped(const JsonNode & value, const std::set<SpellID> & valuesSet)
+	std::set<SpellID> JsonRandom::filterKeysTyped(const JsonNode & value, const std::set<SpellID> & valuesSet)
 	{
 		std::set<SpellID> result = valuesSet;
 
@@ -213,7 +211,7 @@ namespace JsonRandom
 	}
 
 	template<typename IdentifierType>
-	std::set<IdentifierType> filterKeys(const JsonNode & value, const std::set<IdentifierType> & valuesSet, const Variables & variables)
+	std::set<IdentifierType> JsonRandom::filterKeys(const JsonNode & value, const std::set<IdentifierType> & valuesSet, const Variables & variables)
 	{
 		if(value.isString())
 			return { decodeKey<IdentifierType>(value, variables) };
@@ -257,7 +255,7 @@ namespace JsonRandom
 		return valuesSet;
 	}
 
-	TResources loadResources(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
+	TResources JsonRandom::loadResources(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
 	{
 		TResources ret;
 
@@ -275,7 +273,7 @@ namespace JsonRandom
 		return ret;
 	}
 
-	TResources loadResource(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
+	TResources JsonRandom::loadResource(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
 	{
 		std::set<GameResID> defaultResources{
 			GameResID::WOOD,
@@ -296,7 +294,7 @@ namespace JsonRandom
 		return ret;
 	}
 
-	PrimarySkill loadPrimary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
+	PrimarySkill JsonRandom::loadPrimary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
 	{
 		std::set<PrimarySkill> defaultSkills{
 			PrimarySkill::ATTACK,
@@ -308,7 +306,7 @@ namespace JsonRandom
 		return *RandomGeneratorUtil::nextItem(potentialPicks, rng);
 	}
 
-	std::vector<si32> loadPrimaries(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
+	std::vector<si32> JsonRandom::loadPrimaries(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
 	{
 		std::vector<si32> ret(GameConstants::PRIMARY_SKILLS, 0);
 		std::set<PrimarySkill> defaultSkills{
@@ -340,18 +338,18 @@ namespace JsonRandom
 		return ret;
 	}
 
-	SecondarySkill loadSecondary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
+	SecondarySkill JsonRandom::loadSecondary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
 	{
 		std::set<SecondarySkill> defaultSkills;
 		for(const auto & skill : VLC->skillh->objects)
-			if (IObjectInterface::cb->isAllowed(skill->getId()))
+			if (cb->isAllowed(skill->getId()))
 				defaultSkills.insert(skill->getId());
 
 		std::set<SecondarySkill> potentialPicks = filterKeys(value, defaultSkills, variables);
 		return *RandomGeneratorUtil::nextItem(potentialPicks, rng);
 	}
 
-	std::map<SecondarySkill, si32> loadSecondaries(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
+	std::map<SecondarySkill, si32> JsonRandom::loadSecondaries(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
 	{
 		std::map<SecondarySkill, si32> ret;
 		if(value.isStruct())
@@ -366,7 +364,7 @@ namespace JsonRandom
 		{
 			std::set<SecondarySkill> defaultSkills;
 			for(const auto & skill : VLC->skillh->objects)
-				if (IObjectInterface::cb->isAllowed(skill->getId()))
+				if (cb->isAllowed(skill->getId()))
 					defaultSkills.insert(skill->getId());
 
 			for(const auto & element : value.Vector())
@@ -381,19 +379,19 @@ namespace JsonRandom
 		return ret;
 	}
 
-	ArtifactID loadArtifact(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
+	ArtifactID JsonRandom::loadArtifact(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
 	{
 		std::set<ArtifactID> allowedArts;
 		for(const auto & artifact : VLC->arth->objects)
-			if (IObjectInterface::cb->isAllowed(artifact->getId()) && VLC->arth->legalArtifact(artifact->getId()))
+			if (cb->isAllowed(artifact->getId()) && VLC->arth->legalArtifact(artifact->getId()))
 				allowedArts.insert(artifact->getId());
 
 		std::set<ArtifactID> potentialPicks = filterKeys(value, allowedArts, variables);
 
-		return IObjectInterface::cb->gameState()->pickRandomArtifact(rng, potentialPicks);
+		return cb->gameState()->pickRandomArtifact(rng, potentialPicks);
 	}
 
-	std::vector<ArtifactID> loadArtifacts(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
+	std::vector<ArtifactID> JsonRandom::loadArtifacts(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
 	{
 		std::vector<ArtifactID> ret;
 		for (const JsonNode & entry : value.Vector())
@@ -403,11 +401,11 @@ namespace JsonRandom
 		return ret;
 	}
 
-	SpellID loadSpell(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
+	SpellID JsonRandom::loadSpell(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
 	{
 		std::set<SpellID> defaultSpells;
 		for(const auto & spell : VLC->spellh->objects)
-			if (IObjectInterface::cb->isAllowed(spell->getId()) && !spell->isSpecial())
+			if (cb->isAllowed(spell->getId()) && !spell->isSpecial())
 				defaultSpells.insert(spell->getId());
 
 		std::set<SpellID> potentialPicks = filterKeys(value, defaultSpells, variables);
@@ -420,7 +418,7 @@ namespace JsonRandom
 		return *RandomGeneratorUtil::nextItem(potentialPicks, rng);
 	}
 
-	std::vector<SpellID> loadSpells(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
+	std::vector<SpellID> JsonRandom::loadSpells(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
 	{
 		std::vector<SpellID> ret;
 		for (const JsonNode & entry : value.Vector())
@@ -430,7 +428,7 @@ namespace JsonRandom
 		return ret;
 	}
 
-	std::vector<PlayerColor> loadColors(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
+	std::vector<PlayerColor> JsonRandom::loadColors(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
 	{
 		std::vector<PlayerColor> ret;
 		std::set<PlayerColor> defaultPlayers;
@@ -446,7 +444,7 @@ namespace JsonRandom
 		return ret;
 	}
 
-	std::vector<HeroTypeID> loadHeroes(const JsonNode & value, CRandomGenerator & rng)
+	std::vector<HeroTypeID> JsonRandom::loadHeroes(const JsonNode & value, CRandomGenerator & rng)
 	{
 		std::vector<HeroTypeID> ret;
 		for(auto & entry : value.Vector())
@@ -456,7 +454,7 @@ namespace JsonRandom
 		return ret;
 	}
 
-	std::vector<HeroClassID> loadHeroClasses(const JsonNode & value, CRandomGenerator & rng)
+	std::vector<HeroClassID> JsonRandom::loadHeroClasses(const JsonNode & value, CRandomGenerator & rng)
 	{
 		std::vector<HeroClassID> ret;
 		for(auto & entry : value.Vector())
@@ -466,7 +464,7 @@ namespace JsonRandom
 		return ret;
 	}
 
-	CStackBasicDescriptor loadCreature(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
+	CStackBasicDescriptor JsonRandom::loadCreature(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
 	{
 		CStackBasicDescriptor stack;
 
@@ -495,7 +493,7 @@ namespace JsonRandom
 		return stack;
 	}
 
-	std::vector<CStackBasicDescriptor> loadCreatures(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
+	std::vector<CStackBasicDescriptor> JsonRandom::loadCreatures(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
 	{
 		std::vector<CStackBasicDescriptor> ret;
 		for (const JsonNode & node : value.Vector())
@@ -505,7 +503,7 @@ namespace JsonRandom
 		return ret;
 	}
 
-	std::vector<RandomStackInfo> evaluateCreatures(const JsonNode & value, const Variables & variables)
+	std::vector<JsonRandom::RandomStackInfo> JsonRandom::evaluateCreatures(const JsonNode & value, const Variables & variables)
 	{
 		std::vector<RandomStackInfo> ret;
 		for (const JsonNode & node : value.Vector())
@@ -519,7 +517,8 @@ namespace JsonRandom
 				info.minAmount = static_cast<si32>(node["min"].Float());
 				info.maxAmount = static_cast<si32>(node["max"].Float());
 			}
-			const CCreature * crea = VLC->creh->objects[VLC->identifiers()->getIdentifier("creature", node["type"]).value()];
+			CreatureID creatureID(VLC->identifiers()->getIdentifier("creature", node["type"]).value());
+			const CCreature * crea = creatureID.toCreature();
 			info.allowedCreatures.push_back(crea);
 			if (node["upgradeChance"].Float() > 0)
 			{
@@ -531,7 +530,7 @@ namespace JsonRandom
 		return ret;
 	}
 
-	std::vector<Bonus> DLL_LINKAGE loadBonuses(const JsonNode & value)
+	std::vector<Bonus> JsonRandom::loadBonuses(const JsonNode & value)
 	{
 		std::vector<Bonus> ret;
 		for (const JsonNode & entry : value.Vector())
@@ -542,6 +541,4 @@ namespace JsonRandom
 		return ret;
 	}
 
-}
-
 VCMI_LIB_NAMESPACE_END

+ 40 - 20
lib/JsonRandom.h

@@ -11,6 +11,7 @@
 
 #include "GameConstants.h"
 #include "ResourceSet.h"
+#include "GameCallbackHolder.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -22,10 +23,29 @@ struct Bonus;
 struct Component;
 class CStackBasicDescriptor;
 
-namespace JsonRandom
+class DLL_LINKAGE JsonRandom : public GameCallbackHolder
 {
+public:
 	using Variables = std::map<std::string, int>;
 
+private:
+	template<typename IdentifierType>
+	std::set<IdentifierType> filterKeys(const JsonNode & value, const std::set<IdentifierType> & valuesSet, const Variables & variables);
+
+	template<typename IdentifierType>
+	std::set<IdentifierType> filterKeysTyped(const JsonNode & value, const std::set<IdentifierType> & valuesSet);
+
+	template<typename IdentifierType>
+	IdentifierType decodeKey(const std::string & modScope, const std::string & value, const Variables & variables);
+
+	template<typename IdentifierType>
+	IdentifierType decodeKey(const JsonNode & value, const Variables & variables);
+
+	si32 loadVariable(const std::string & variableGroup, const std::string & value, const Variables & variables, si32 defaultValue);
+
+public:
+	using GameCallbackHolder::GameCallbackHolder;
+
 	struct DLL_LINKAGE RandomStackInfo
 	{
 		std::vector<const CCreature *> allowedCreatures;
@@ -33,30 +53,30 @@ namespace JsonRandom
 		si32 maxAmount;
 	};
 
-	DLL_LINKAGE si32 loadValue(const JsonNode & value, CRandomGenerator & rng, const Variables & variables, si32 defaultValue = 0);
+	si32 loadValue(const JsonNode & value, CRandomGenerator & rng, const Variables & variables, si32 defaultValue = 0);
 
-	DLL_LINKAGE TResources loadResources(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
-	DLL_LINKAGE TResources loadResource(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
-	DLL_LINKAGE PrimarySkill loadPrimary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
-	DLL_LINKAGE std::vector<si32> loadPrimaries(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
-	DLL_LINKAGE SecondarySkill loadSecondary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
-	DLL_LINKAGE std::map<SecondarySkill, si32> loadSecondaries(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
+	TResources loadResources(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
+	TResources loadResource(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
+	PrimarySkill loadPrimary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
+	std::vector<si32> loadPrimaries(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
+	SecondarySkill loadSecondary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
+	std::map<SecondarySkill, si32> loadSecondaries(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
 
-	DLL_LINKAGE ArtifactID loadArtifact(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
-	DLL_LINKAGE std::vector<ArtifactID> loadArtifacts(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
+	ArtifactID loadArtifact(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
+	std::vector<ArtifactID> loadArtifacts(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
 
-	DLL_LINKAGE SpellID loadSpell(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
-	DLL_LINKAGE std::vector<SpellID> loadSpells(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
+	SpellID loadSpell(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
+	std::vector<SpellID> loadSpells(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
 
-	DLL_LINKAGE CStackBasicDescriptor loadCreature(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
-	DLL_LINKAGE std::vector<CStackBasicDescriptor> loadCreatures(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
-	DLL_LINKAGE std::vector<RandomStackInfo> evaluateCreatures(const JsonNode & value, const Variables & variables);
+	CStackBasicDescriptor loadCreature(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
+	std::vector<CStackBasicDescriptor> loadCreatures(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
+	std::vector<RandomStackInfo> evaluateCreatures(const JsonNode & value, const Variables & variables);
 
-	DLL_LINKAGE std::vector<PlayerColor> loadColors(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
-	DLL_LINKAGE std::vector<HeroTypeID> loadHeroes(const JsonNode & value, CRandomGenerator & rng);
-	DLL_LINKAGE std::vector<HeroClassID> loadHeroClasses(const JsonNode & value, CRandomGenerator & rng);
+	std::vector<PlayerColor> loadColors(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
+	std::vector<HeroTypeID> loadHeroes(const JsonNode & value, CRandomGenerator & rng);
+	std::vector<HeroClassID> loadHeroClasses(const JsonNode & value, CRandomGenerator & rng);
 
-	DLL_LINKAGE std::vector<Bonus> loadBonuses(const JsonNode & value);
-}
+	static std::vector<Bonus> loadBonuses(const JsonNode & value);
+};
 
 VCMI_LIB_NAMESPACE_END

+ 1 - 1
lib/RiverHandler.cpp

@@ -18,7 +18,7 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 RiverTypeHandler::RiverTypeHandler()
 {
-	objects.push_back(new RiverType);
+	objects.push_back(new RiverType());
 
 	VLC->generaltexth->registerString("core", objects[0]->getNameTextID(), "");
 }

+ 1 - 1
lib/RoadHandler.cpp

@@ -18,7 +18,7 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 RoadTypeHandler::RoadTypeHandler()
 {
-	objects.push_back(new RoadType);
+	objects.push_back(new RoadType());
 
 	VLC->generaltexth->registerString("core", objects[0]->getNameTextID(), "");
 }

+ 1 - 1
lib/StartInfo.cpp

@@ -32,7 +32,7 @@ FactionID PlayerSettings::getCastleValidated() const
 {
 	if (!castle.isValid())
 		return FactionID(0);
-	if (castle.getNum() < VLC->townh->size() && VLC->townh->objects[castle.getNum()]->town != nullptr)
+	if (castle.getNum() < VLC->townh->size() && castle.toEntity(VLC)->hasTown())
 		return castle;
 
 	return FactionID(0);

+ 21 - 93
lib/VCMI_Lib.cpp

@@ -65,54 +65,54 @@ DLL_LINKAGE void loadDLLClasses(bool onlyEssential)
 
 const ArtifactService * LibClasses::artifacts() const
 {
-	return arth;
+	return arth.get();
 }
 
 const CreatureService * LibClasses::creatures() const
 {
-	return creh;
+	return creh.get();
 }
 
 const FactionService * LibClasses::factions() const
 {
-	return townh;
+	return townh.get();
 }
 
 const HeroClassService * LibClasses::heroClasses() const
 {
-	return &heroh->classes;
+	return heroclassesh.get();
 }
 
 const HeroTypeService * LibClasses::heroTypes() const
 {
-	return heroh;
+	return heroh.get();
 }
 
 #if SCRIPTING_ENABLED
 const scripting::Service * LibClasses::scripts() const
 {
-	return scriptHandler;
+	return scriptHandler.get();
 }
 #endif
 
 const spells::Service * LibClasses::spells() const
 {
-	return spellh;
+	return spellh.get();
 }
 
 const SkillService * LibClasses::skills() const
 {
-	return skillh;
+	return skillh.get();
 }
 
 const IBonusTypeHandler * LibClasses::getBth() const
 {
-	return bth;
+	return bth.get();
 }
 
 const CIdentifierStorage * LibClasses::identifiers() const
 {
-	return identifiersHandler;
+	return identifiersHandler.get();
 }
 
 const spells::effects::Registry * LibClasses::spellEffects() const
@@ -127,17 +127,17 @@ spells::effects::Registry * LibClasses::spellEffects()
 
 const BattleFieldService * LibClasses::battlefields() const
 {
-	return battlefieldsHandler;
+	return battlefieldsHandler.get();
 }
 
 const ObstacleService * LibClasses::obstacles() const
 {
-	return obstacleHandler;
+	return obstacleHandler.get();
 }
 
 const IGameSettings * LibClasses::settings() const
 {
-	return settingsHandler;
+	return settingsHandler.get();
 }
 
 void LibClasses::updateEntity(Metatype metatype, int32_t index, const JsonNode & data)
@@ -154,7 +154,7 @@ void LibClasses::updateEntity(Metatype metatype, int32_t index, const JsonNode &
 		townh->updateEntity(index, data);
 		break;
 	case Metatype::HERO_CLASS:
-		heroh->classes.updateEntity(index, data);
+		heroclassesh->updateEntity(index, data);
 		break;
 	case Metatype::HERO_TYPE:
 		heroh->updateEntity(index, data);
@@ -185,8 +185,8 @@ void LibClasses::loadFilesystem(bool extractArchives)
 void LibClasses::loadModFilesystem()
 {
 	CStopWatch loadTime;
-	modh = new CModHandler();
-	identifiersHandler = new CIdentifierStorage();
+	modh = std::make_unique<CModHandler>();
+	identifiersHandler = std::make_unique<CIdentifierStorage>();
 	modh->loadMods();
 	logGlobal->info("\tMod handler: %d ms", loadTime.getDiff());
 
@@ -199,9 +199,9 @@ static void logHandlerLoaded(const std::string & name, CStopWatch & timer)
 	logGlobal->info("\t\t %s handler: %d ms", name, timer.getDiff());
 }
 
-template <class Handler> void createHandler(Handler *&handler, const std::string &name, CStopWatch &timer)
+template <class Handler> void createHandler(std::shared_ptr<Handler> & handler, const std::string &name, CStopWatch &timer)
 {
-	handler = new Handler();
+	handler = std::make_shared<Handler>();
 	logHandlerLoaded(name, timer);
 }
 
@@ -219,6 +219,7 @@ void LibClasses::init(bool onlyEssential)
 	createHandler(riverTypeHandler, "River", pomtime);
 	createHandler(terrainTypeHandler, "Terrain", pomtime);
 	createHandler(heroh, "Hero", pomtime);
+	createHandler(heroclassesh, "Hero classes", pomtime);
 	createHandler(arth, "Artifact", pomtime);
 	createHandler(creh, "Creature", pomtime);
 	createHandler(townh, "Town", pomtime);
@@ -239,77 +240,6 @@ void LibClasses::init(bool onlyEssential)
 	modh->afterLoad(onlyEssential);
 }
 
-void LibClasses::clear()
-{
-	delete heroh;
-	delete arth;
-	delete creh;
-	delete townh;
-	delete objh;
-	delete objtypeh;
-	delete spellh;
-	delete skillh;
-	delete modh;
-	delete bth;
-	delete tplh;
-	delete terviewh;
-#if SCRIPTING_ENABLED
-	delete scriptHandler;
-#endif
-	delete battlefieldsHandler;
-	delete generaltexth;
-	delete identifiersHandler;
-	delete obstacleHandler;
-	delete terrainTypeHandler;
-	delete riverTypeHandler;
-	delete roadTypeHandler;
-	delete settingsHandler;
-	makeNull();
-}
-
-void LibClasses::makeNull()
-{
-	generaltexth = nullptr;
-	heroh = nullptr;
-	arth = nullptr;
-	creh = nullptr;
-	townh = nullptr;
-	objh = nullptr;
-	objtypeh = nullptr;
-	spellh = nullptr;
-	skillh = nullptr;
-	modh = nullptr;
-	bth = nullptr;
-	tplh = nullptr;
-	terviewh = nullptr;
-#if SCRIPTING_ENABLED
-	scriptHandler = nullptr;
-#endif
-	battlefieldsHandler = nullptr;
-	identifiersHandler = nullptr;
-	obstacleHandler = nullptr;
-	terrainTypeHandler = nullptr;
-	riverTypeHandler = nullptr;
-	roadTypeHandler = nullptr;
-	settingsHandler = nullptr;
-}
-
-LibClasses::LibClasses()
-{
-	//init pointers to handlers
-	makeNull();
-}
-
-void LibClasses::callWhenDeserializing()
-{
-	//FIXME: check if any of these are needed
-	//generaltexth = new CGeneralTextHandler();
-	//generaltexth->load();
-	//arth->load(true);
-	//modh->recreateHandlers();
-	//modh->loadConfigFromFile ("defaultMods"); //TODO: remember last saved config
-}
-
 #if SCRIPTING_ENABLED
 void LibClasses::scriptsLoaded()
 {
@@ -317,10 +247,8 @@ void LibClasses::scriptsLoaded()
 }
 #endif
 
-LibClasses::~LibClasses()
-{
-	clear();
-}
+LibClasses::LibClasses() = default;
+LibClasses::~LibClasses() = default;
 
 std::shared_ptr<CContentHandler> LibClasses::getContent() const
 {

+ 25 - 30
lib/VCMI_Lib.h

@@ -16,6 +16,7 @@ VCMI_LIB_NAMESPACE_BEGIN
 class CConsoleHandler;
 class CArtHandler;
 class CHeroHandler;
+class CHeroClassHandler;
 class CCreatureHandler;
 class CSpellHandler;
 class CSkillHandler;
@@ -47,20 +48,15 @@ namespace scripting
 }
 #endif
 
-
 /// Loads and constructs several handlers
-class DLL_LINKAGE LibClasses : public Services
+class DLL_LINKAGE LibClasses final : public Services
 {
-	CBonusTypeHandler * bth;
+	std::shared_ptr<CBonusTypeHandler> bth;
 
-	void callWhenDeserializing(); //should be called only by serialize !!!
-	void makeNull(); //sets all handler pointers to null
 	std::shared_ptr<CContentHandler> getContent() const;
 	void setContent(std::shared_ptr<CContentHandler> content);
 
 public:
-	bool IS_AI_ENABLED = false; //unused?
-
 	const ArtifactService * artifacts() const override;
 	const CreatureService * creatures() const override;
 	const FactionService * factions() const override;
@@ -83,35 +79,34 @@ public:
 	const IBonusTypeHandler * getBth() const; //deprecated
 	const CIdentifierStorage * identifiers() const;
 
-	CArtHandler * arth;
-	CHeroHandler * heroh;
-	CCreatureHandler * creh;
-	CSpellHandler * spellh;
-	CSkillHandler * skillh;
-	CObjectHandler * objh;
-	CObjectClassesHandler * objtypeh;
-	CTownHandler * townh;
-	CGeneralTextHandler * generaltexth;
-	CModHandler * modh;
-
-	TerrainTypeHandler * terrainTypeHandler;
-	RoadTypeHandler * roadTypeHandler;
-	RiverTypeHandler * riverTypeHandler;
-	CIdentifierStorage * identifiersHandler;
-
-	CTerrainViewPatternConfig * terviewh;
-	CRmgTemplateStorage * tplh;
-	BattleFieldHandler * battlefieldsHandler;
-	ObstacleHandler * obstacleHandler;
-	GameSettings * settingsHandler;
+	std::shared_ptr<CArtHandler> arth;
+	std::shared_ptr<CHeroHandler> heroh;
+	std::shared_ptr<CHeroClassHandler> heroclassesh;
+	std::shared_ptr<CCreatureHandler> creh;
+	std::shared_ptr<CSpellHandler> spellh;
+	std::shared_ptr<CSkillHandler> skillh;
+	std::shared_ptr<CObjectHandler> objh;
+	std::shared_ptr<CObjectClassesHandler> objtypeh;
+	std::shared_ptr<CTownHandler> townh;
+	std::shared_ptr<CGeneralTextHandler> generaltexth;
+	std::shared_ptr<CModHandler> modh;
+	std::shared_ptr<TerrainTypeHandler> terrainTypeHandler;
+	std::shared_ptr<RoadTypeHandler> roadTypeHandler;
+	std::shared_ptr<RiverTypeHandler> riverTypeHandler;
+	std::shared_ptr<CIdentifierStorage> identifiersHandler;
+	std::shared_ptr<CTerrainViewPatternConfig> terviewh;
+	std::shared_ptr<CRmgTemplateStorage> tplh;
+	std::shared_ptr<BattleFieldHandler> battlefieldsHandler;
+	std::shared_ptr<ObstacleHandler> obstacleHandler;
+	std::shared_ptr<GameSettings> settingsHandler;
+
 #if SCRIPTING_ENABLED
-	scripting::ScriptHandler * scriptHandler;
+	std::shared_ptr<scripting::ScriptHandler> scriptHandler;
 #endif
 
 	LibClasses(); //c-tor, loads .lods and NULLs handlers
 	~LibClasses();
 	void init(bool onlyEssential); //uses standard config file
-	void clear(); //deletes all handlers and its data
 
 	// basic initialization. should be called before init(). Can also extract original H3 archives
 	void loadFilesystem(bool extractArchives);

+ 3 - 3
lib/battle/BattleInfo.cpp

@@ -240,7 +240,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
 		{
 			try
 			{
-				RangeGenerator obidgen(0, VLC->obstacleHandler->objects.size() - 1, ourRand);
+				RangeGenerator obidgen(0, VLC->obstacleHandler->size() - 1, ourRand);
 				auto obstPtr = std::make_shared<CObstacleInstance>();
 				obstPtr->obstacleType = CObstacleInstance::ABSOLUTE_OBSTACLE;
 				obstPtr->ID = obidgen.getSuchNumber(appropriateAbsoluteObstacle);
@@ -262,7 +262,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
 		{
 			while(tilesToBlock > 0)
 			{
-				RangeGenerator obidgen(0, VLC->obstacleHandler->objects.size() - 1, ourRand);
+				RangeGenerator obidgen(0, VLC->obstacleHandler->size() - 1, ourRand);
 				auto tileAccessibility = curB->getAccesibility();
 				const int obid = obidgen.getSuchNumber(appropriateUsualObstacle);
 				const ObstacleInfo &obi = *Obstacle(obid).getInfo();
@@ -1009,7 +1009,7 @@ scripting::Pool * BattleInfo::getContextPool() const
 {
 	//this is real battle, use global scripting context pool
 	//TODO: make this line not ugly
-	return IObjectInterface::cb->getGlobalContextPool();
+	return battleGetFightingHero(0)->cb->getGlobalContextPool();
 }
 #endif
 

+ 2 - 3
lib/battle/CBattleInfoEssentials.cpp

@@ -401,10 +401,9 @@ PlayerColor CBattleInfoEssentials::battleGetOwner(const battle::Unit * unit) con
 
 	PlayerColor initialOwner = getBattle()->getSidePlayer(unit->unitSide());
 
-	static CSelector selector = Selector::type()(BonusType::HYPNOTIZED);
-	static std::string cachingString = "type_103s-1";
+	static const CSelector selector = Selector::type()(BonusType::HYPNOTIZED);
 
-	if(unit->hasBonus(selector, cachingString))
+	if(unit->hasBonus(selector))
 		return otherPlayer(initialOwner);
 	else
 		return initialOwner;

+ 0 - 1
lib/bonuses/Bonus.cpp

@@ -10,7 +10,6 @@
 
 #include "StdInc.h"
 #include "Bonus.h"
-#include "CBonusSystemNode.h"
 #include "Limiters.h"
 #include "Updaters.h"
 #include "Propagators.h"

+ 12 - 12
lib/bonuses/BonusSelector.cpp

@@ -15,39 +15,39 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 namespace Selector
 {
-	DLL_LINKAGE CSelectFieldEqual<BonusType> & type()
+	DLL_LINKAGE const CSelectFieldEqual<BonusType> & type()
 	{
-		static CSelectFieldEqual<BonusType> stype(&Bonus::type);
+		static const CSelectFieldEqual<BonusType> stype(&Bonus::type);
 		return stype;
 	}
 
-	DLL_LINKAGE CSelectFieldEqual<BonusSubtypeID> & subtype()
+	DLL_LINKAGE const CSelectFieldEqual<BonusSubtypeID> & subtype()
 	{
-		static CSelectFieldEqual<BonusSubtypeID> ssubtype(&Bonus::subtype);
+		static const CSelectFieldEqual<BonusSubtypeID> ssubtype(&Bonus::subtype);
 		return ssubtype;
 	}
 
-	DLL_LINKAGE CSelectFieldEqual<CAddInfo> & info()
+	DLL_LINKAGE const CSelectFieldEqual<CAddInfo> & info()
 	{
-		static CSelectFieldEqual<CAddInfo> sinfo(&Bonus::additionalInfo);
+		static const CSelectFieldEqual<CAddInfo> sinfo(&Bonus::additionalInfo);
 		return sinfo;
 	}
 
-	DLL_LINKAGE CSelectFieldEqual<BonusSource> & sourceType()
+	DLL_LINKAGE const CSelectFieldEqual<BonusSource> & sourceType()
 	{
-		static CSelectFieldEqual<BonusSource> ssourceType(&Bonus::source);
+		static const CSelectFieldEqual<BonusSource> ssourceType(&Bonus::source);
 		return ssourceType;
 	}
 
-	DLL_LINKAGE CSelectFieldEqual<BonusSource> & targetSourceType()
+	DLL_LINKAGE const CSelectFieldEqual<BonusSource> & targetSourceType()
 	{
-		static CSelectFieldEqual<BonusSource> ssourceType(&Bonus::targetSourceType);
+		static const CSelectFieldEqual<BonusSource> ssourceType(&Bonus::targetSourceType);
 		return ssourceType;
 	}
 
-	DLL_LINKAGE CSelectFieldEqual<BonusLimitEffect> & effectRange()
+	DLL_LINKAGE const CSelectFieldEqual<BonusLimitEffect> & effectRange()
 	{
-		static CSelectFieldEqual<BonusLimitEffect> seffectRange(&Bonus::effectRange);
+		static const CSelectFieldEqual<BonusLimitEffect> seffectRange(&Bonus::effectRange);
 		return seffectRange;
 	}
 

+ 6 - 6
lib/bonuses/BonusSelector.h

@@ -125,12 +125,12 @@ public:
 
 namespace Selector
 {
-	extern DLL_LINKAGE CSelectFieldEqual<BonusType> & type();
-	extern DLL_LINKAGE CSelectFieldEqual<BonusSubtypeID> & subtype();
-	extern DLL_LINKAGE CSelectFieldEqual<CAddInfo> & info();
-	extern DLL_LINKAGE CSelectFieldEqual<BonusSource> & sourceType();
-	extern DLL_LINKAGE CSelectFieldEqual<BonusSource> & targetSourceType();
-	extern DLL_LINKAGE CSelectFieldEqual<BonusLimitEffect> & effectRange();
+	extern DLL_LINKAGE const CSelectFieldEqual<BonusType> & type();
+	extern DLL_LINKAGE const CSelectFieldEqual<BonusSubtypeID> & subtype();
+	extern DLL_LINKAGE const CSelectFieldEqual<CAddInfo> & info();
+	extern DLL_LINKAGE const CSelectFieldEqual<BonusSource> & sourceType();
+	extern DLL_LINKAGE const CSelectFieldEqual<BonusSource> & targetSourceType();
+	extern DLL_LINKAGE const CSelectFieldEqual<BonusLimitEffect> & effectRange();
 	extern DLL_LINKAGE CWillLastTurns turns;
 	extern DLL_LINKAGE CWillLastDays days;
 

+ 94 - 87
lib/bonuses/CBonusSystemNode.cpp

@@ -20,18 +20,25 @@ VCMI_LIB_NAMESPACE_BEGIN
 std::atomic<int64_t> CBonusSystemNode::treeChanged(1);
 constexpr bool CBonusSystemNode::cachingEnabled = true;
 
-#define FOREACH_PARENT(pname) 	TNodes lparents; getParents(lparents); for(CBonusSystemNode *pname : lparents)
-#define FOREACH_RED_CHILD(pname) 	TNodes lchildren; getRedChildren(lchildren); for(CBonusSystemNode *pname : lchildren)
+std::shared_ptr<Bonus> CBonusSystemNode::getLocalBonus(const CSelector & selector)
+{
+	auto ret = bonuses.getFirst(selector);
+	if(ret)
+		return ret;
+	return nullptr;
+}
 
-std::shared_ptr<Bonus> CBonusSystemNode::getBonusLocalFirst(const CSelector & selector)
+std::shared_ptr<const Bonus> CBonusSystemNode::getFirstBonus(const CSelector & selector) const
 {
 	auto ret = bonuses.getFirst(selector);
 	if(ret)
 		return ret;
 
-	FOREACH_PARENT(pname)
+	TCNodes lparents;
+	getParents(lparents);
+	for(const CBonusSystemNode *pname : lparents)
 	{
-		ret = pname->getBonusLocalFirst(selector);
+		ret = pname->getFirstBonus(selector);
 		if (ret)
 			return ret;
 	}
@@ -39,28 +46,15 @@ std::shared_ptr<Bonus> CBonusSystemNode::getBonusLocalFirst(const CSelector & se
 	return nullptr;
 }
 
-std::shared_ptr<const Bonus> CBonusSystemNode::getBonusLocalFirst(const CSelector & selector) const
-{
-	return (const_cast<CBonusSystemNode*>(this))->getBonusLocalFirst(selector);
-}
-
 void CBonusSystemNode::getParents(TCNodes & out) const /*retrieves list of parent nodes (nodes to inherit bonuses from) */
 {
-	for(const auto * elem : parents)
+	for(const auto * elem : parentsToInherit)
 		out.insert(elem);
 }
 
-void CBonusSystemNode::getParents(TNodes &out)
-{
-	for (auto * elem : parents)
-	{
-		out.insert(elem);
-	}
-}
-
 void CBonusSystemNode::getAllParents(TCNodes & out) const //retrieves list of parent nodes (nodes to inherit bonuses from)
 {
-	for(auto * parent : parents)
+	for(auto * parent : parentsToInherit)
 	{
 		out.insert(parent);
 		parent->getAllParents(out);
@@ -227,39 +221,6 @@ CBonusSystemNode::CBonusSystemNode(ENodeTypes NodeType):
 {
 }
 
-CBonusSystemNode::CBonusSystemNode(CBonusSystemNode && other) noexcept:
-	bonuses(std::move(other.bonuses)),
-	exportedBonuses(std::move(other.exportedBonuses)),
-	nodeType(other.nodeType),
-	cachedLast(0),
-	isHypotheticNode(other.isHypotheticNode)
-{
-	std::swap(parents, other.parents);
-	std::swap(children, other.children);
-
-	//fixing bonus tree without recalculation
-
-	if(!isHypothetic())
-	{
-		for(CBonusSystemNode * n : parents)
-		{
-			n->children -= &other;
-			n->children.push_back(this);
-		}
-	}
-
-	for(CBonusSystemNode * n : children)
-	{
-		n->parents -= &other;
-		n->parents.push_back(this);
-	}
-
-	//cache ignored
-
-	//cachedBonuses
-	//cachedRequests
-}
-
 CBonusSystemNode::~CBonusSystemNode()
 {
 	detachFromAll();
@@ -273,14 +234,14 @@ CBonusSystemNode::~CBonusSystemNode()
 
 void CBonusSystemNode::attachTo(CBonusSystemNode & parent)
 {
-	assert(!vstd::contains(parents, &parent));
-	parents.push_back(&parent);
+	assert(!vstd::contains(parentsToPropagate, &parent));
+	parentsToPropagate.push_back(&parent);
+
+	attachToSource(parent);
 
 	if(!isHypothetic())
 	{
-		if(parent.actsAsBonusSourceOnly())
-			parent.newRedDescendant(*this);
-		else
+		if(!parent.actsAsBonusSourceOnly())
 			newRedDescendant(parent);
 
 		parent.newChildAttached(*this);
@@ -289,21 +250,35 @@ void CBonusSystemNode::attachTo(CBonusSystemNode & parent)
 	CBonusSystemNode::treeHasChanged();
 }
 
-void CBonusSystemNode::detachFrom(CBonusSystemNode & parent)
+void CBonusSystemNode::attachToSource(const CBonusSystemNode & parent)
 {
-	assert(vstd::contains(parents, &parent));
+	assert(!vstd::contains(parentsToInherit, &parent));
+	parentsToInherit.push_back(&parent);
 
 	if(!isHypothetic())
 	{
 		if(parent.actsAsBonusSourceOnly())
-			parent.removedRedDescendant(*this);
-		else
+			parent.newRedDescendant(*this);
+	}
+
+	CBonusSystemNode::treeHasChanged();
+}
+
+void CBonusSystemNode::detachFrom(CBonusSystemNode & parent)
+{
+	assert(vstd::contains(parentsToPropagate, &parent));
+
+	if(!isHypothetic())
+	{
+		if(!parent.actsAsBonusSourceOnly())
 			removedRedDescendant(parent);
 	}
 
-	if (vstd::contains(parents, &parent))
+	detachFromSource(parent);
+
+	if (vstd::contains(parentsToPropagate, &parent))
 	{
-		parents -= &parent;
+		parentsToPropagate -= &parent;
 	}
 	else
 	{
@@ -318,6 +293,30 @@ void CBonusSystemNode::detachFrom(CBonusSystemNode & parent)
 	CBonusSystemNode::treeHasChanged();
 }
 
+
+void CBonusSystemNode::detachFromSource(const CBonusSystemNode & parent)
+{
+	assert(vstd::contains(parentsToInherit, &parent));
+
+	if(!isHypothetic())
+	{
+		if(parent.actsAsBonusSourceOnly())
+			parent.removedRedDescendant(*this);
+	}
+
+	if (vstd::contains(parentsToInherit, &parent))
+	{
+		parentsToInherit -= &parent;
+	}
+	else
+	{
+		logBonus->error("Error on Detach. Node %s (nodeType=%d) has not parent %s (nodeType=%d)"
+			, nodeShortInfo(), nodeType, parent.nodeShortInfo(), parent.nodeType);
+	}
+
+	CBonusSystemNode::treeHasChanged();
+}
+
 void CBonusSystemNode::removeBonusesRecursive(const CSelector & s)
 {
 	removeBonuses(s);
@@ -405,8 +404,10 @@ void CBonusSystemNode::propagateBonus(const std::shared_ptr<Bonus> & b, const CB
 		logBonus->trace("#$# %s #propagated to# %s",  propagated->Description(), nodeName());
 	}
 
-	FOREACH_RED_CHILD(child)
-		child->propagateBonus(b, source);
+	TNodes lchildren;
+	getRedChildren(lchildren);
+	for(CBonusSystemNode *pname : lchildren)
+		pname->propagateBonus(b, source);
 }
 
 void CBonusSystemNode::unpropagateBonus(const std::shared_ptr<Bonus> & b)
@@ -417,8 +418,10 @@ void CBonusSystemNode::unpropagateBonus(const std::shared_ptr<Bonus> & b)
 		logBonus->trace("#$# %s #is no longer propagated to# %s",  b->Description(), nodeName());
 	}
 
-	FOREACH_RED_CHILD(child)
-		child->unpropagateBonus(b);
+	TNodes lchildren;
+	getRedChildren(lchildren);
+	for(CBonusSystemNode *pname : lchildren)
+		pname->unpropagateBonus(b);
 }
 
 void CBonusSystemNode::newChildAttached(CBonusSystemNode & child)
@@ -440,13 +443,16 @@ void CBonusSystemNode::childDetached(CBonusSystemNode & child)
 
 void CBonusSystemNode::detachFromAll()
 {
-	while(!parents.empty())
-		detachFrom(*parents.front());
+	while(!parentsToPropagate.empty())
+		detachFrom(*parentsToPropagate.front());
+
+	while(!parentsToInherit.empty())
+		detachFromSource(*parentsToInherit.front());
 }
 
 bool CBonusSystemNode::isIndependentNode() const
 {
-	return parents.empty() && children.empty();
+	return parentsToInherit.empty() && parentsToPropagate.empty() && children.empty();
 }
 
 std::string CBonusSystemNode::nodeName() const
@@ -464,12 +470,13 @@ std::string CBonusSystemNode::nodeShortInfo() const
 void CBonusSystemNode::deserializationFix()
 {
 	exportBonuses();
-
 }
 
-void CBonusSystemNode::getRedParents(TNodes & out)
+void CBonusSystemNode::getRedParents(TCNodes & out) const
 {
-	FOREACH_PARENT(pname)
+	TCNodes lparents;
+	getParents(lparents);
+	for(const CBonusSystemNode *pname : lparents)
 	{
 		if(pname->actsAsBonusSourceOnly())
 		{
@@ -479,7 +486,7 @@ void CBonusSystemNode::getRedParents(TNodes & out)
 
 	if(!actsAsBonusSourceOnly())
 	{
-		for(CBonusSystemNode *child : children)
+		for(const CBonusSystemNode *child : children)
 		{
 			out.insert(child);
 		}
@@ -488,7 +495,7 @@ void CBonusSystemNode::getRedParents(TNodes & out)
 
 void CBonusSystemNode::getRedChildren(TNodes &out)
 {
-	FOREACH_PARENT(pname)
+	for(CBonusSystemNode *pname : parentsToPropagate)
 	{
 		if(!pname->actsAsBonusSourceOnly())
 		{
@@ -505,17 +512,17 @@ void CBonusSystemNode::getRedChildren(TNodes &out)
 	}
 }
 
-void CBonusSystemNode::newRedDescendant(CBonusSystemNode & descendant)
+void CBonusSystemNode::newRedDescendant(CBonusSystemNode & descendant) const
 {
 	for(const auto & b : exportedBonuses)
 	{
 		if(b->propagator)
 			descendant.propagateBonus(b, *this);
 	}
-	TNodes redParents;
+	TCNodes redParents;
 	getRedAncestors(redParents); //get all red parents recursively
 
-	for(auto * parent : redParents)
+	for(const auto * parent : redParents)
 	{
 		for(const auto & b : parent->exportedBonuses)
 		{
@@ -525,13 +532,13 @@ void CBonusSystemNode::newRedDescendant(CBonusSystemNode & descendant)
 	}
 }
 
-void CBonusSystemNode::removedRedDescendant(CBonusSystemNode & descendant)
+void CBonusSystemNode::removedRedDescendant(CBonusSystemNode & descendant) const
 {
 	for(const auto & b : exportedBonuses)
 		if(b->propagator)
 			descendant.unpropagateBonus(b);
 
-	TNodes redParents;
+	TCNodes redParents;
 	getRedAncestors(redParents); //get all red parents recursively
 
 	for(auto * parent : redParents)
@@ -542,14 +549,14 @@ void CBonusSystemNode::removedRedDescendant(CBonusSystemNode & descendant)
 	}
 }
 
-void CBonusSystemNode::getRedAncestors(TNodes &out)
+void CBonusSystemNode::getRedAncestors(TCNodes &out) const
 {
 	getRedParents(out);
 
-	TNodes redParents; 
+	TCNodes redParents;
 	getRedParents(redParents);
 
-	for(CBonusSystemNode * parent : redParents)
+	for(const CBonusSystemNode * parent : redParents)
 		parent->getRedAncestors(out);
 }
 
@@ -574,9 +581,9 @@ CBonusSystemNode::ENodeTypes CBonusSystemNode::getNodeType() const
 	return nodeType;
 }
 
-const TNodesVector& CBonusSystemNode::getParentNodes() const
+const TCNodesVector& CBonusSystemNode::getParentNodes() const
 {
-	return parents;
+	return parentsToInherit;
 }
 
 void CBonusSystemNode::setNodeType(CBonusSystemNode::ENodeTypes type)
@@ -646,4 +653,4 @@ int64_t CBonusSystemNode::getTreeVersion() const
 	return treeChanged;
 }
 
-VCMI_LIB_NAMESPACE_END
+VCMI_LIB_NAMESPACE_END

+ 16 - 14
lib/bonuses/CBonusSystemNode.h

@@ -19,6 +19,7 @@ VCMI_LIB_NAMESPACE_BEGIN
 using TNodes = std::set<CBonusSystemNode *>;
 using TCNodes = std::set<const CBonusSystemNode *>;
 using TNodesVector = std::vector<CBonusSystemNode *>;
+using TCNodesVector = std::vector<const CBonusSystemNode *>;
 
 class DLL_LINKAGE CBonusSystemNode : public virtual IBonusBearer, public boost::noncopyable
 {
@@ -33,7 +34,8 @@ private:
 	BonusList bonuses; //wielded bonuses (local or up-propagated here)
 	BonusList exportedBonuses; //bonuses coming from this node (wielded or propagated away)
 
-	TNodesVector parents; //parents -> we inherit bonuses from them, we may attach our bonuses to them
+	TCNodesVector parentsToInherit; // we inherit bonuses from them
+	TNodesVector parentsToPropagate; // we may attach our bonuses to them
 	TNodesVector children;
 
 	ENodeTypes nodeType;
@@ -54,8 +56,8 @@ private:
 	TConstBonusListPtr getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr) const;
 	std::shared_ptr<Bonus> getUpdatedBonus(const std::shared_ptr<Bonus> & b, const TUpdaterPtr & updater) const;
 
-	void getRedParents(TNodes &out);  //retrieves list of red parent nodes (nodes bonuses propagate from)
-	void getRedAncestors(TNodes &out);
+	void getRedParents(TCNodes &out) const;  //retrieves list of red parent nodes (nodes bonuses propagate from)
+	void getRedAncestors(TCNodes &out) const;
 	void getRedChildren(TNodes &out);
 
 	void getAllParents(TCNodes & out) const;
@@ -66,8 +68,8 @@ private:
 	void unpropagateBonus(const std::shared_ptr<Bonus> & b);
 	bool actsAsBonusSourceOnly() const;
 
-	void newRedDescendant(CBonusSystemNode & descendant); //propagation needed
-	void removedRedDescendant(CBonusSystemNode & descendant); //de-propagation needed
+	void newRedDescendant(CBonusSystemNode & descendant) const; //propagation needed
+	void removedRedDescendant(CBonusSystemNode & descendant) const; //de-propagation needed
 
 	std::string nodeShortInfo() const;
 
@@ -80,21 +82,23 @@ protected:
 public:
 	explicit CBonusSystemNode(bool isHypotetic = false);
 	explicit CBonusSystemNode(ENodeTypes NodeType);
-	CBonusSystemNode(CBonusSystemNode && other) noexcept;
 	virtual ~CBonusSystemNode();
 
 	void limitBonuses(const BonusList &allBonuses, BonusList &out) const; //out will bo populed with bonuses that are not limited here
 	TBonusListPtr limitBonuses(const BonusList &allBonuses) const; //same as above, returns out by val for convienence
 	TConstBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr, const std::string &cachingStr = "") const override;
 	void getParents(TCNodes &out) const;  //retrieves list of parent nodes (nodes to inherit bonuses from),
-	std::shared_ptr<const Bonus> getBonusLocalFirst(const CSelector & selector) const;
 
-	//non-const interface
-	void getParents(TNodes &out);  //retrieves list of parent nodes (nodes to inherit bonuses from)
-	std::shared_ptr<Bonus> getBonusLocalFirst(const CSelector & selector);
+	/// Returns first bonus matching selector
+	std::shared_ptr<const Bonus> getFirstBonus(const CSelector & selector) const;
+
+	/// Provides write access to first bonus from this node that matches selector
+	std::shared_ptr<Bonus> getLocalBonus(const CSelector & selector);
 
 	void attachTo(CBonusSystemNode & parent);
+	void attachToSource(const CBonusSystemNode & parent);
 	void detachFrom(CBonusSystemNode & parent);
+	void detachFromSource(const CBonusSystemNode & parent);
 	void detachFromAll();
 	virtual void addNewBonus(const std::shared_ptr<Bonus>& b);
 	void accumulateBonus(const std::shared_ptr<Bonus>& b); //add value of bonus with same type/subtype or create new
@@ -115,7 +119,7 @@ public:
 	const BonusList & getExportedBonusList() const;
 	CBonusSystemNode::ENodeTypes getNodeType() const;
 	void setNodeType(CBonusSystemNode::ENodeTypes type);
-	const TNodesVector & getParentNodes() const;
+	const TCNodesVector & getParentNodes() const;
 
 	static void treeHasChanged();
 
@@ -128,14 +132,12 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-//		h & bonuses;
 		h & nodeType;
 		h & exportedBonuses;
 		BONUS_TREE_DESERIALIZATION_FIX
-		//h & parents & children;
 	}
 
 	friend class CBonusProxy;
 };
 
-VCMI_LIB_NAMESPACE_END
+VCMI_LIB_NAMESPACE_END

+ 1 - 1
lib/bonuses/IBonusBearer.cpp

@@ -10,7 +10,7 @@
 
 #include "StdInc.h"
 
-#include "CBonusSystemNode.h"
+#include "IBonusBearer.h"
 #include "BonusList.h"
 
 VCMI_LIB_NAMESPACE_BEGIN

+ 7 - 6
lib/campaign/CampaignState.cpp

@@ -11,6 +11,7 @@
 #include "CampaignState.h"
 
 #include "../JsonNode.h"
+#include "../Point.h"
 #include "../filesystem/ResourcePath.h"
 #include "../VCMI_Lib.h"
 #include "../CGeneralTextHandler.h"
@@ -224,7 +225,7 @@ std::set<HeroTypeID> CampaignState::getReservedHeroes() const
 
 const CGHeroInstance * CampaignState::strongestHero(CampaignScenarioID scenarioId, const PlayerColor & owner) const
 {
-	std::function<bool(const JsonNode & node)> isOwned = [owner](const JsonNode & node)
+	std::function<bool(const JsonNode & node)> isOwned = [&](const JsonNode & node)
 	{
 		auto * h = CampaignState::crossoverDeserialize(node, nullptr);
 		bool result = h->tempOwner == owner;
@@ -316,7 +317,7 @@ std::optional<ui8> CampaignState::getBonusID(CampaignScenarioID which) const
 	return chosenCampaignBonuses.at(which);
 }
 
-std::unique_ptr<CMap> CampaignState::getMap(CampaignScenarioID scenarioId) const
+std::unique_ptr<CMap> CampaignState::getMap(CampaignScenarioID scenarioId, IGameCallback * cb) const
 {
 	// FIXME: there is certainly better way to handle maps inside campaigns
 	if(scenarioId == CampaignScenarioID::NONE)
@@ -327,7 +328,7 @@ std::unique_ptr<CMap> CampaignState::getMap(CampaignScenarioID scenarioId) const
 	boost::to_lower(scenarioName);
 	scenarioName += ':' + std::to_string(scenarioId.getNum());
 	const auto & mapContent = mapPieces.find(scenarioId)->second;
-	return mapService.loadMap(mapContent.data(), mapContent.size(), scenarioName, getModName(), getEncoding());
+	return mapService.loadMap(mapContent.data(), mapContent.size(), scenarioName, getModName(), getEncoding(), cb);
 }
 
 std::unique_ptr<CMapHeader> CampaignState::getMapHeader(CampaignScenarioID scenarioId) const
@@ -355,7 +356,7 @@ std::shared_ptr<CMapInfo> CampaignState::getMapInfo(CampaignScenarioID scenarioI
 	return mapInfo;
 }
 
-JsonNode CampaignState::crossoverSerialize(CGHeroInstance * hero)
+JsonNode CampaignState::crossoverSerialize(CGHeroInstance * hero) const
 {
 	JsonNode node;
 	JsonSerializer handler(nullptr, node);
@@ -363,10 +364,10 @@ JsonNode CampaignState::crossoverSerialize(CGHeroInstance * hero)
 	return node;
 }
 
-CGHeroInstance * CampaignState::crossoverDeserialize(const JsonNode & node, CMap * map)
+CGHeroInstance * CampaignState::crossoverDeserialize(const JsonNode & node, CMap * map) const
 {
 	JsonDeserializer handler(nullptr, const_cast<JsonNode&>(node));
-	auto * hero = new CGHeroInstance();
+	auto * hero = new CGHeroInstance(map->cb);
 	hero->ID = Obj::HERO;
 	hero->serializeJsonOptions(handler);
 	if (map)

+ 4 - 3
lib/campaign/CampaignState.h

@@ -27,6 +27,7 @@ class CMapHeader;
 class CMapInfo;
 class JsonNode;
 class Point;
+class IGameCallback;
 
 class DLL_LINKAGE CampaignRegions
 {
@@ -277,7 +278,7 @@ public:
 	/// Returns true if all available scenarios have been completed and campaign is finished
 	bool isCampaignFinished() const;
 
-	std::unique_ptr<CMap> getMap(CampaignScenarioID scenarioId) const;
+	std::unique_ptr<CMap> getMap(CampaignScenarioID scenarioId, IGameCallback * cb) const;
 	std::unique_ptr<CMapHeader> getMapHeader(CampaignScenarioID scenarioId) const;
 	std::shared_ptr<CMapInfo> getMapInfo(CampaignScenarioID scenarioId) const;
 
@@ -298,8 +299,8 @@ public:
 	/// May return empty JsonNode if such hero was not found
 	const JsonNode & getHeroByType(HeroTypeID heroID) const;
 
-	static JsonNode crossoverSerialize(CGHeroInstance * hero);
-	static CGHeroInstance * crossoverDeserialize(const JsonNode & node, CMap * map);
+	JsonNode crossoverSerialize(CGHeroInstance * hero) const;
+	CGHeroInstance * crossoverDeserialize(const JsonNode & node, CMap * map) const;
 
 	std::string campaignSet;
 

+ 10 - 0
lib/constants/EntityIdentifiers.cpp

@@ -151,6 +151,16 @@ std::string HeroClassID::entityType()
 	return "heroClass";
 }
 
+const CHeroClass * HeroClassID::toHeroClass() const
+{
+	return dynamic_cast<const CHeroClass*>(toEntity(VLC));
+}
+
+const HeroClass * HeroClassID::toEntity(const Services * services) const
+{
+	return services->heroClasses()->getByIndex(num);
+}
+
 si32 ObjectInstanceID::decode(const std::string & identifier)
 {
 	return std::stoi(identifier);

+ 5 - 0
lib/constants/EntityIdentifiers.h

@@ -21,6 +21,8 @@ class Creature;
 class CreatureService;
 class HeroType;
 class CHero;
+class CHeroClass;
+class HeroClass;
 class HeroTypeService;
 class Faction;
 class Skill;
@@ -81,6 +83,9 @@ public:
 	DLL_LINKAGE static si32 decode(const std::string & identifier);
 	DLL_LINKAGE static std::string encode(const si32 index);
 	static std::string entityType();
+
+	const CHeroClass * toHeroClass() const;
+	const HeroClass * toEntity(const Services * services) const;
 };
 
 class DLL_LINKAGE HeroTypeID : public EntityIdentifier<HeroTypeID>

+ 26 - 38
lib/gameState/CGameState.cpp

@@ -36,6 +36,8 @@
 #include "../mapObjectConstructors/DwellingInstanceConstructor.h"
 #include "../mapObjects/CGHeroInstance.h"
 #include "../mapObjects/CGTownInstance.h"
+#include "../mapObjects/CQuest.h"
+#include "../mapObjects/MiscObjects.h"
 #include "../mapping/CMap.h"
 #include "../mapping/CMapEditManager.h"
 #include "../mapping/CMapService.h"
@@ -97,7 +99,7 @@ HeroTypeID CGameState::pickUnusedHeroTypeRandomly(const PlayerColor & owner)
 	const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(owner);
 	for(const HeroTypeID & hid : getUnusedAllowedHeroes())
 	{
-		if(VLC->heroh->objects[hid.getNum()]->heroClass->faction == ps.castle)
+		if(hid.toHeroType()->heroClass->faction == ps.castle)
 			factionHeroes.push_back(hid);
 		else
 			otherHeroes.push_back(hid);
@@ -169,14 +171,16 @@ CGameState::~CGameState()
 	initialOpts.dellNull();
 }
 
-void CGameState::preInit(Services * services)
+void CGameState::preInit(Services * newServices, IGameCallback * newCallback)
 {
-	this->services = services;
+	services = newServices;
+	callback = newCallback;
 }
 
 void CGameState::init(const IMapService * mapService, StartInfo * si, Load::ProgressAccumulator & progressTracking, bool allowSavingRandomMap)
 {
-	preInitAuto();
+	assert(services);
+	assert(callback);
 	logGlobal->info("\tUsing random seed: %d", si->seedToBeUsed);
 	getRandomGenerator().setSeed(si->seedToBeUsed);
 	scenarioOps = CMemorySerializer::deepCopy(*si).release();
@@ -224,7 +228,7 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, Load::Prog
 
 	for(auto & elem : teams)
 	{
-		CGObelisk::visited[elem.first] = 0;
+		map->obelisksVisited[elem.first] = 0;
 	}
 
 	logGlobal->debug("\tChecking objectives");
@@ -284,21 +288,13 @@ void CGameState::updateEntity(Metatype metatype, int32_t index, const JsonNode &
 
 void CGameState::updateOnLoad(StartInfo * si)
 {
-	preInitAuto();
+	assert(services);
+	assert(callback);
 	scenarioOps->playerInfos = si->playerInfos;
 	for(auto & i : si->playerInfos)
 		gs->players[i.first].human = i.second.isControlledByHuman();
 }
 
-void CGameState::preInitAuto()
-{
-	if(services == nullptr)
-	{
-		logGlobal->error("Game state preinit missing");
-		preInit(VLC);
-	}
-}
-
 void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRandomMap, Load::ProgressAccumulator & progressTracking)
 {
 	if(scenarioOps->createRandomMap())
@@ -307,7 +303,7 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan
 		CStopWatch sw;
 
 		// Gen map
-		CMapGenerator mapGenerator(*scenarioOps->mapGenOptions, scenarioOps->seedToBeUsed);
+		CMapGenerator mapGenerator(*scenarioOps->mapGenOptions, callback, scenarioOps->seedToBeUsed);
 		progressTracking.include(mapGenerator);
 
 		std::unique_ptr<CMap> randomMap = mapGenerator.generate();
@@ -370,7 +366,7 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan
 	{
 		logGlobal->info("Open map file: %s", scenarioOps->mapname);
 		const ResourcePath mapURI(scenarioOps->mapname, EResType::MAP);
-		map = mapService->loadMap(mapURI).release();
+		map = mapService->loadMap(mapURI, callback).release();
 	}
 }
 
@@ -561,7 +557,7 @@ void CGameState::placeStartingHero(const PlayerColor & playerColor, const HeroTy
 	}
 
 	auto handler = VLC->objtypeh->getHandlerFor(Obj::HERO, heroTypeId.toHeroType()->heroClass->getIndex());
-	CGObjectInstance * obj = handler->create(handler->getTemplates().front());
+	CGObjectInstance * obj = handler->create(callback, handler->getTemplates().front());
 	CGHeroInstance * hero = dynamic_cast<CGHeroInstance *>(obj);
 
 	hero->ID = Obj::HERO;
@@ -635,7 +631,7 @@ void CGameState::initHeroes()
 		if (tile.terType->isWater())
 		{
 			auto handler = VLC->objtypeh->getHandlerFor(Obj::BOAT, hero->getBoatType().getNum());
-			CGBoat * boat = dynamic_cast<CGBoat*>(handler->create());
+			auto boat = dynamic_cast<CGBoat*>(handler->create(callback, nullptr));
 			handler->configureObject(boat, gs->getRandomGenerator());
 
 			boat->pos = hero->pos;
@@ -673,7 +669,7 @@ void CGameState::initHeroes()
 
 	for(const HeroTypeID & htype : heroesToCreate) //all not used allowed heroes go with default state into the pool
 	{
-		auto * vhi = new CGHeroInstance();
+		auto * vhi = new CGHeroInstance(callback);
 		vhi->initHero(getRandomGenerator(), htype);
 
 		int typeID = htype.getNum();
@@ -772,11 +768,11 @@ void CGameState::initTowns()
 	if (campaign)
 		campaign->initTowns();
 
-	CGTownInstance::universitySkills.clear();
-	CGTownInstance::universitySkills.push_back(SecondarySkill(SecondarySkill::FIRE_MAGIC));
-	CGTownInstance::universitySkills.push_back(SecondarySkill(SecondarySkill::AIR_MAGIC));
-	CGTownInstance::universitySkills.push_back(SecondarySkill(SecondarySkill::WATER_MAGIC));
-	CGTownInstance::universitySkills.push_back(SecondarySkill(SecondarySkill::EARTH_MAGIC));
+	map->townUniversitySkills.clear();
+	map->townUniversitySkills.push_back(SecondarySkill(SecondarySkill::FIRE_MAGIC));
+	map->townUniversitySkills.push_back(SecondarySkill(SecondarySkill::AIR_MAGIC));
+	map->townUniversitySkills.push_back(SecondarySkill(SecondarySkill::WATER_MAGIC));
+	map->townUniversitySkills.push_back(SecondarySkill(SecondarySkill::EARTH_MAGIC));
 
 	for (auto & elem : map->towns)
 	{
@@ -936,7 +932,7 @@ void CGameState::initMapObjects()
 			}
 		}
 	}
-	CGSubterraneanGate::postInit(); //pairing subterranean gates
+	CGSubterraneanGate::postInit(callback); //pairing subterranean gates
 
 	map->calculateGuardingGreaturePositions(); //calculate once again when all the guards are placed and initialized
 }
@@ -1208,7 +1204,7 @@ int3 CGameState::guardingCreaturePosition (int3 pos) const
 
 void CGameState::updateRumor()
 {
-	static std::vector<RumorState::ERumorType> rumorTypes = {RumorState::TYPE_MAP, RumorState::TYPE_SPECIAL, RumorState::TYPE_RAND, RumorState::TYPE_RAND};
+	static const std::vector<RumorState::ERumorType> rumorTypes = {RumorState::TYPE_MAP, RumorState::TYPE_SPECIAL, RumorState::TYPE_RAND, RumorState::TYPE_RAND};
 	std::vector<RumorState::ERumorTypeSpecial> sRumorTypes = {
 		RumorState::RUMOR_OBELISKS, RumorState::RUMOR_ARTIFACTS, RumorState::RUMOR_ARMY, RumorState::RUMOR_INCOME};
 	if(map->grailPos.valid()) // Grail should always be on map, but I had related crash I didn't manage to reproduce
@@ -1722,10 +1718,10 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
 	}
 	if(level >= 3) //obelisks found
 	{
-		auto getObeliskVisited = [](const TeamID & t)
+		auto getObeliskVisited = [&](const TeamID & t)
 		{
-			if(CGObelisk::visited.count(t))
-				return CGObelisk::visited[t];
+			if(map->obelisksVisited.count(t))
+				return map->obelisksVisited[t];
 			else
 				return ui8(0);
 		};
@@ -1928,14 +1924,6 @@ TeamState::TeamState()
 	fogOfWarMap = std::make_unique<boost::multi_array<ui8, 3>>();
 }
 
-TeamState::TeamState(TeamState && other) noexcept:
-	CBonusSystemNode(std::move(other)),
-	id(other.id)
-{
-	std::swap(players, other.players);
-	std::swap(fogOfWarMap, other.fogOfWarMap);
-}
-
 CRandomGenerator & CGameState::getRandomGenerator()
 {
 	return rand;

+ 3 - 2
lib/gameState/CGameState.h

@@ -97,10 +97,12 @@ public:
 	/// list of players currently making turn. Usually - just one, except for simturns
 	std::set<PlayerColor> actingPlayers;
 
+	IGameCallback * callback;
+
 	CGameState();
 	virtual ~CGameState();
 
-	void preInit(Services * services);
+	void preInit(Services * services, IGameCallback * callback);
 
 	void init(const IMapService * mapService, StartInfo * si, Load::ProgressAccumulator &, bool allowSavingRandomMap = true);
 	void updateOnLoad(StartInfo * si);
@@ -193,7 +195,6 @@ public:
 
 private:
 	// ----- initialization -----
-	void preInitAuto();
 	void initNewGame(const IMapService * mapService, bool allowSavingRandomMap, Load::ProgressAccumulator & progressTracking);
 	void checkMapChecksum();
 	void initGlobalBonuses();

+ 6 - 5
lib/gameState/CGameStateCampaign.cpp

@@ -16,6 +16,7 @@
 #include "../campaign/CampaignState.h"
 #include "../mapping/CMapEditManager.h"
 #include "../mapObjects/CGHeroInstance.h"
+#include "../mapObjects/CGTownInstance.h"
 #include "../networkPacks/ArtifactLocation.h"
 #include "../mapObjectConstructors/AObjectTypeHandler.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
@@ -90,7 +91,7 @@ void CGameStateCampaign::trimCrossoverHeroesParameters(std::vector<CampaignHeroR
 					.And(Selector::subtype()(BonusSubtypeID(g)))
 					.And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL));
 
-				cgh->getBonusLocalFirst(sel)->val = cgh->type->heroClass->primarySkillInitial[g.getNum()];
+				cgh->getLocalBonus(sel)->val = cgh->type->heroClass->primarySkillInitial[g.getNum()];
 			}
 		}
 	}
@@ -347,7 +348,7 @@ void CGameStateCampaign::replaceHeroesPlaceholders(const std::vector<CampaignHer
 		if(heroPlaceholder->tempOwner.isValidPlayer())
 			heroToPlace->tempOwner = heroPlaceholder->tempOwner;
 		heroToPlace->pos = heroPlaceholder->pos;
-		heroToPlace->type = VLC->heroh->objects[heroToPlace->getHeroType().getNum()];
+		heroToPlace->type = heroToPlace->getHeroType().toHeroType();
 		heroToPlace->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, heroToPlace->type->heroClass->getIndex())->getTemplates().front();
 
 		gameState->map->removeBlockVisTiles(heroPlaceholder, true);
@@ -402,7 +403,7 @@ std::vector<CampaignHeroReplacement> CGameStateCampaign::generateCampaignHeroesT
 			continue;
 		}
 
-		CGHeroInstance * hero = CampaignState::crossoverDeserialize(node, gameState->map);
+		CGHeroInstance * hero = campaignState->crossoverDeserialize(node, gameState->map);
 
 		logGlobal->info("Hero crossover: Loading placeholder for %d (%s)", hero->getHeroType(), hero->getNameTranslated());
 
@@ -427,7 +428,7 @@ std::vector<CampaignHeroReplacement> CGameStateCampaign::generateCampaignHeroesT
 			if (nodeListIter == nodeList.end())
 				break;
 
-			CGHeroInstance * hero = CampaignState::crossoverDeserialize(*nodeListIter, gameState->map);
+			CGHeroInstance * hero = campaignState->crossoverDeserialize(*nodeListIter, gameState->map);
 			nodeListIter++;
 
 			logGlobal->info("Hero crossover: Loading placeholder as %d (%s)", hero->getHeroType(), hero->getNameTranslated());
@@ -599,7 +600,7 @@ bool CGameStateCampaign::playerHasStartingHero(PlayerColor playerColor) const
 
 std::unique_ptr<CMap> CGameStateCampaign::getCurrentMap() const
 {
-	return gameState->scenarioOps->campState->getMap(CampaignScenarioID::NONE);
+	return gameState->scenarioOps->campState->getMap(CampaignScenarioID::NONE, gameState->callback);
 }
 
 VCMI_LIB_NAMESPACE_END

+ 2 - 1
lib/mapObjectConstructors/AObjectTypeHandler.h

@@ -19,6 +19,7 @@ class ObjectTemplate;
 class CGObjectInstance;
 class CRandomGenerator;
 class IObjectInfo;
+class IGameCallback;
 
 /// Class responsible for creation of objects of specific type & subtype
 class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable
@@ -109,7 +110,7 @@ public:
 
 	/// Creates object and set up core properties (like ID/subID). Object is NOT initialized
 	/// to allow creating objects before game start (e.g. map loading)
-	virtual CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const = 0;
+	virtual CGObjectInstance * create(IGameCallback * cb, std::shared_ptr<const ObjectTemplate> tmpl) const = 0;
 
 	/// Configures object properties. Should be re-entrable, resetting state of the object if necessarily
 	/// This should set remaining properties, including randomized or depending on map

+ 13 - 75
lib/mapObjectConstructors/CBankInstanceConstructor.cpp

@@ -34,18 +34,19 @@ void CBankInstanceConstructor::initTypeData(const JsonNode & input)
 	coastVisitable = input["coastVisitable"].Bool();
 }
 
-BankConfig CBankInstanceConstructor::generateConfig(const JsonNode & level, CRandomGenerator & rng) const
+BankConfig CBankInstanceConstructor::generateConfig(IGameCallback * cb, const JsonNode & level, CRandomGenerator & rng) const
 {
 	BankConfig bc;
+	JsonRandom randomizer(cb);
 	JsonRandom::Variables emptyVariables;
 
 	bc.chance = static_cast<ui32>(level["chance"].Float());
-	bc.guards = JsonRandom::loadCreatures(level["guards"], rng, emptyVariables);
+	bc.guards = randomizer.loadCreatures(level["guards"], rng, emptyVariables);
 
 	bc.resources = ResourceSet(level["reward"]["resources"]);
-	bc.creatures = JsonRandom::loadCreatures(level["reward"]["creatures"], rng, emptyVariables);
-	bc.artifacts = JsonRandom::loadArtifacts(level["reward"]["artifacts"], rng, emptyVariables);
-	bc.spells = JsonRandom::loadSpells(level["reward"]["spells"], rng, emptyVariables);
+	bc.creatures = randomizer.loadCreatures(level["reward"]["creatures"], rng, emptyVariables);
+	bc.artifacts = randomizer.loadArtifacts(level["reward"]["artifacts"], rng, emptyVariables);
+	bc.spells = randomizer.loadSpells(level["reward"]["spells"], rng, emptyVariables);
 
 	return bc;
 }
@@ -70,7 +71,7 @@ void CBankInstanceConstructor::randomizeObject(CBank * bank, CRandomGenerator &
 		cumulativeChance += static_cast<int>(node["chance"].Float());
 		if(selectedChance < cumulativeChance)
 		{
-			bank->setConfig(generateConfig(node, rng));
+			bank->setConfig(generateConfig(bank->cb, node, rng));
 			break;
 		}
 	}
@@ -82,80 +83,16 @@ CBankInfo::CBankInfo(const JsonVector & Config) :
 	assert(!Config.empty());
 }
 
-static void addStackToArmy(IObjectInfo::CArmyStructure & army, const CCreature * crea, si32 amount)
-{
-	army.totalStrength += crea->getFightValue() * amount;
-
-	bool walker = true;
-	if(crea->hasBonusOfType(BonusType::SHOOTER))
-	{
-		army.shootersStrength += crea->getFightValue() * amount;
-		walker = false;
-	}
-	if(crea->hasBonusOfType(BonusType::FLYING))
-	{
-		army.flyersStrength += crea->getFightValue() * amount;
-		walker = false;
-	}
-	if(walker)
-		army.walkersStrength += crea->getFightValue() * amount;
-}
-
-IObjectInfo::CArmyStructure CBankInfo::minGuards() const
-{
-	JsonRandom::Variables emptyVariables;
-
-	std::vector<IObjectInfo::CArmyStructure> armies;
-	for(auto configEntry : config)
-	{
-		auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"], emptyVariables);
-		IObjectInfo::CArmyStructure army;
-		for(auto & stack : stacks)
-		{
-			assert(!stack.allowedCreatures.empty());
-			auto weakest = boost::range::min_element(stack.allowedCreatures, [](const CCreature * a, const CCreature * b)
-			{
-				return a->getFightValue() < b->getFightValue();
-			});
-			addStackToArmy(army, *weakest, stack.minAmount);
-		}
-		armies.push_back(army);
-	}
-	return *boost::range::min_element(armies);
-}
-
-IObjectInfo::CArmyStructure CBankInfo::maxGuards() const
-{
-	JsonRandom::Variables emptyVariables;
-
-	std::vector<IObjectInfo::CArmyStructure> armies;
-	for(auto configEntry : config)
-	{
-		auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"], emptyVariables);
-		IObjectInfo::CArmyStructure army;
-		for(auto & stack : stacks)
-		{
-			assert(!stack.allowedCreatures.empty());
-			auto strongest = boost::range::max_element(stack.allowedCreatures, [](const CCreature * a, const CCreature * b)
-			{
-				return a->getFightValue() < b->getFightValue();
-			});
-			addStackToArmy(army, *strongest, stack.maxAmount);
-		}
-		armies.push_back(army);
-	}
-	return *boost::range::max_element(armies);
-}
-
-TPossibleGuards CBankInfo::getPossibleGuards() const
+TPossibleGuards CBankInfo::getPossibleGuards(IGameCallback * cb) const
 {
 	JsonRandom::Variables emptyVariables;
+	JsonRandom randomizer(cb);
 	TPossibleGuards out;
 
 	for(const JsonNode & configEntry : config)
 	{
 		const JsonNode & guardsInfo = configEntry["guards"];
-		auto stacks = JsonRandom::evaluateCreatures(guardsInfo, emptyVariables);
+		auto stacks = randomizer.evaluateCreatures(guardsInfo, emptyVariables);
 		IObjectInfo::CArmyStructure army;
 
 
@@ -188,15 +125,16 @@ std::vector<PossibleReward<TResources>> CBankInfo::getPossibleResourcesReward()
 	return result;
 }
 
-std::vector<PossibleReward<CStackBasicDescriptor>> CBankInfo::getPossibleCreaturesReward() const
+std::vector<PossibleReward<CStackBasicDescriptor>> CBankInfo::getPossibleCreaturesReward(IGameCallback * cb) const
 {
 	JsonRandom::Variables emptyVariables;
+	JsonRandom randomizer(cb);
 	std::vector<PossibleReward<CStackBasicDescriptor>> aproximateReward;
 
 	for(const JsonNode & configEntry : config)
 	{
 		const JsonNode & guardsInfo = configEntry["reward"]["creatures"];
-		auto stacks = JsonRandom::evaluateCreatures(guardsInfo, emptyVariables);
+		auto stacks = randomizer.evaluateCreatures(guardsInfo, emptyVariables);
 
 		for(auto stack : stacks)
 		{

+ 3 - 7
lib/mapObjectConstructors/CBankInstanceConstructor.h

@@ -55,13 +55,9 @@ class DLL_LINKAGE CBankInfo : public IObjectInfo
 public:
 	CBankInfo(const JsonVector & Config);
 
-	TPossibleGuards getPossibleGuards() const;
+	TPossibleGuards getPossibleGuards(IGameCallback * cb) const;
 	std::vector<PossibleReward<TResources>> getPossibleResourcesReward() const;
-	std::vector<PossibleReward<CStackBasicDescriptor>> getPossibleCreaturesReward() const;
-
-	// These functions should try to evaluate minimal possible/max possible guards to give provide information on possible thread to AI
-	CArmyStructure minGuards() const override;
-	CArmyStructure maxGuards() const override;
+	std::vector<PossibleReward<CStackBasicDescriptor>> getPossibleCreaturesReward(IGameCallback * cb) const;
 
 	bool givesResources() const override;
 	bool givesArtifacts() const override;
@@ -71,7 +67,7 @@ public:
 
 class CBankInstanceConstructor : public CDefaultObjectTypeHandler<CBank>
 {
-	BankConfig generateConfig(const JsonNode & conf, CRandomGenerator & rng) const;
+	BankConfig generateConfig(IGameCallback * cb, const JsonNode & conf, CRandomGenerator & rng) const;
 
 	JsonVector levels;
 

+ 4 - 4
lib/mapObjectConstructors/CDefaultObjectTypeHandler.h

@@ -27,9 +27,9 @@ class CDefaultObjectTypeHandler : public AObjectTypeHandler
 		randomizeObject(castedObject, rng);
 	}
 
-	CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const final
+	CGObjectInstance * create(IGameCallback * cb, std::shared_ptr<const ObjectTemplate> tmpl) const final
 	{
-		ObjectType * result = createObject();
+		ObjectType * result = createObject(cb);
 
 		preInitObject(result);
 
@@ -44,9 +44,9 @@ class CDefaultObjectTypeHandler : public AObjectTypeHandler
 protected:
 	virtual void initializeObject(ObjectType * object) const {}
 	virtual void randomizeObject(ObjectType * object, CRandomGenerator & rng) const {}
-	virtual ObjectType * createObject() const
+	virtual ObjectType * createObject(IGameCallback * cb) const
 	{
-		return new ObjectType();
+		return new ObjectType(cb);
 	}
 };
 

+ 3 - 3
lib/mapObjectConstructors/CRewardableConstructor.cpp

@@ -31,9 +31,9 @@ bool CRewardableConstructor::hasNameTextID() const
 	return !objectInfo.getParameters()["name"].isNull();
 }
 
-CGObjectInstance * CRewardableConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const
+CGObjectInstance * CRewardableConstructor::create(IGameCallback * cb, std::shared_ptr<const ObjectTemplate> tmpl) const
 {
-	auto * ret = new CRewardableObject();
+	auto * ret = new CRewardableObject(cb);
 	preInitObject(ret);
 	ret->appearance = tmpl;
 	ret->blockVisit = blockVisit;
@@ -44,7 +44,7 @@ void CRewardableConstructor::configureObject(CGObjectInstance * object, CRandomG
 {
 	if(auto * rewardableObject = dynamic_cast<CRewardableObject*>(object))
 	{
-		objectInfo.configureObject(rewardableObject->configuration, rng);
+		objectInfo.configureObject(rewardableObject->configuration, rng, object->cb);
 		for(auto & rewardInfo : rewardableObject->configuration.info)
 		{
 			for (auto & bonus : rewardInfo.reward.bonuses)

+ 1 - 1
lib/mapObjectConstructors/CRewardableConstructor.h

@@ -25,7 +25,7 @@ class DLL_LINKAGE CRewardableConstructor : public AObjectTypeHandler
 public:
 	bool hasNameTextID() const override;
 
-	CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
+	CGObjectInstance * create(IGameCallback * cb, std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
 
 	void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
 

+ 8 - 7
lib/mapObjectConstructors/CommonConstructors.cpp

@@ -102,7 +102,7 @@ void CTownInstanceConstructor::initializeObject(CGTownInstance * obj) const
 
 void CTownInstanceConstructor::randomizeObject(CGTownInstance * object, CRandomGenerator & rng) const
 {
-	auto templ = getOverride(CGObjectInstance::cb->getTile(object->pos)->terType->getId(), object);
+	auto templ = getOverride(object->cb->getTile(object->pos)->terType->getId(), object);
 	if(templ)
 		object->appearance = templ;
 }
@@ -122,7 +122,7 @@ void CHeroInstanceConstructor::initTypeData(const JsonNode & input)
 	VLC->identifiers()->requestIdentifier(
 		"heroClass",
 		input["heroClass"],
-		[&](si32 index) { heroClass = VLC->heroh->classes[index]; });
+		[&](si32 index) { heroClass = HeroClassID(index).toHeroClass(); });
 
 	filtersJson = input["filters"];
 }
@@ -224,7 +224,7 @@ void MarketInstanceConstructor::initTypeData(const JsonNode & input)
 	speech = input["speech"].String();
 }
 
-CGMarket * MarketInstanceConstructor::createObject() const
+CGMarket * MarketInstanceConstructor::createObject(IGameCallback * cb) const
 {
 	if(marketModes.size() == 1)
 	{
@@ -232,13 +232,13 @@ CGMarket * MarketInstanceConstructor::createObject() const
 		{
 			case EMarketMode::ARTIFACT_RESOURCE:
 			case EMarketMode::RESOURCE_ARTIFACT:
-				return new CGBlackMarket;
+				return new CGBlackMarket(cb);
 
 			case EMarketMode::RESOURCE_SKILL:
-				return new CGUniversity;
+				return new CGUniversity(cb);
 		}
 	}
-	return new CGMarket;
+	return new CGMarket(cb);
 }
 
 void MarketInstanceConstructor::initializeObject(CGMarket * market) const
@@ -256,11 +256,12 @@ void MarketInstanceConstructor::initializeObject(CGMarket * market) const
 
 void MarketInstanceConstructor::randomizeObject(CGMarket * object, CRandomGenerator & rng) const
 {
+	JsonRandom randomizer(object->cb);
 	JsonRandom::Variables emptyVariables;
 
 	if(auto * university = dynamic_cast<CGUniversity *>(object))
 	{
-		for(auto skill : JsonRandom::loadSecondaries(predefinedOffer, rng, emptyVariables))
+		for(auto skill : randomizer.loadSecondaries(predefinedOffer, rng, emptyVariables))
 			university->skills.push_back(skill.first);
 	}
 }

+ 3 - 3
lib/mapObjectConstructors/CommonConstructors.h

@@ -57,7 +57,7 @@ protected:
 	void initTypeData(const JsonNode & input) override;
 
 public:
-	CFaction * faction = nullptr;
+	const CFaction * faction = nullptr;
 	std::map<std::string, LogicalExpression<BuildingID>> filters;
 
 	void initializeObject(CGTownInstance * object) const override;
@@ -76,7 +76,7 @@ protected:
 	void initTypeData(const JsonNode & input) override;
 
 public:
-	CHeroClass * heroClass = nullptr;
+	const CHeroClass * heroClass = nullptr;
 	std::map<std::string, LogicalExpression<HeroTypeID>> filters;
 
 	void initializeObject(CGHeroInstance * object) const override;
@@ -121,7 +121,7 @@ protected:
 	std::string speech;
 	
 public:
-	CGMarket * createObject() const override;
+	CGMarket * createObject(IGameCallback * cb) const override;
 	void initializeObject(CGMarket * object) const override;
 	void randomizeObject(CGMarket * object, CRandomGenerator & rng) const override;
 

+ 4 - 4
lib/mapObjectConstructors/DwellingInstanceConstructor.cpp

@@ -45,7 +45,7 @@ void DwellingInstanceConstructor::initTypeData(const JsonNode & input)
 		{
 			VLC->identifiers()->requestIdentifier("creature", creaturesOnLevel[currentCreature], [=] (si32 index)
 			{
-				availableCreatures[currentLevel][currentCreature] = VLC->creh->objects[index];
+				availableCreatures[currentLevel][currentCreature] = CreatureID(index).toCreature();
 			});
 		}
 		assert(!availableCreatures[currentLevel].empty());
@@ -68,9 +68,9 @@ void DwellingInstanceConstructor::initializeObject(CGDwelling * obj) const
 	}
 }
 
-void DwellingInstanceConstructor::randomizeObject(CGDwelling * object, CRandomGenerator &rng) const
+void DwellingInstanceConstructor::randomizeObject(CGDwelling * dwelling, CRandomGenerator &rng) const
 {
-	auto * dwelling = dynamic_cast<CGDwelling *>(object);
+	JsonRandom randomizer(dwelling->cb);
 
 	dwelling->creatures.clear();
 	dwelling->creatures.reserve(availableCreatures.size());
@@ -94,7 +94,7 @@ void DwellingInstanceConstructor::randomizeObject(CGDwelling * object, CRandomGe
 	else if(guards.getType() == JsonNode::JsonType::DATA_VECTOR) //custom guards (eg. Elemental Conflux)
 	{
 		JsonRandom::Variables emptyVariables;
-		for(auto & stack : JsonRandom::loadCreatures(guards, rng, emptyVariables))
+		for(auto & stack : randomizer.loadCreatures(guards, rng, emptyVariables))
 		{
 			dwelling->putStack(SlotID(dwelling->stacksCount()), new CStackInstance(stack.type->getId(), stack.count));
 		}

+ 1 - 0
lib/mapObjectConstructors/DwellingInstanceConstructor.h

@@ -10,6 +10,7 @@
 #pragma once
 
 #include "CDefaultObjectTypeHandler.h"
+#include "../mapObjects/CGDwelling.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 0
lib/mapObjectConstructors/HillFortInstanceConstructor.h

@@ -10,6 +10,7 @@
 #pragma once
 
 #include "CDefaultObjectTypeHandler.h"
+#include "../mapObjects/MiscObjects.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 0 - 5
lib/mapObjectConstructors/IObjectInfo.h

@@ -34,11 +34,6 @@ public:
 		}
 	};
 
-	/// Returns possible composition of guards. Actual guards would be
-	/// somewhere between these two values
-	virtual CArmyStructure minGuards() const { return CArmyStructure(); }
-	virtual CArmyStructure maxGuards() const { return CArmyStructure(); }
-
 	virtual bool givesResources() const { return false; }
 
 	virtual bool givesExperience() const { return false; }

+ 1 - 0
lib/mapObjectConstructors/ShipyardInstanceConstructor.h

@@ -10,6 +10,7 @@
 #pragma once
 
 #include "CDefaultObjectTypeHandler.h"
+#include "../mapObjects/MiscObjects.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 4 - 3
lib/mapObjects/CArmedInstance.cpp

@@ -40,12 +40,13 @@ void CArmedInstance::randomizeArmy(FactionID type)
 // Take Angelic Alliance troop-mixing freedom of non-evil units into account.
 CSelector CArmedInstance::nonEvilAlignmentMixSelector = Selector::type()(BonusType::NONEVIL_ALIGNMENT_MIX);
 
-CArmedInstance::CArmedInstance()
-	:CArmedInstance(false)
+CArmedInstance::CArmedInstance(IGameCallback *cb)
+	:CArmedInstance(cb, false)
 {
 }
 
-CArmedInstance::CArmedInstance(bool isHypothetic):
+CArmedInstance::CArmedInstance(IGameCallback *cb, bool isHypothetic):
+	CGObjectInstance(cb),
 	CBonusSystemNode(isHypothetic),
 	nonEvilAlignmentMix(this, nonEvilAlignmentMixSelector),
 	battle(nullptr)

+ 2 - 2
lib/mapObjects/CArmedInstance.h

@@ -42,8 +42,8 @@ public:
 	virtual CBonusSystemNode & whatShouldBeAttached();
 	//////////////////////////////////////////////////////////////////////////
 
-	CArmedInstance();
-	CArmedInstance(bool isHypothetic);
+	CArmedInstance(IGameCallback *cb);
+	CArmedInstance(IGameCallback *cb, bool isHypothetic);
 
 	PlayerColor getOwner() const override
 	{

+ 4 - 2
lib/mapObjects/CBank.cpp

@@ -37,8 +37,10 @@ static std::string visitedTxt(const bool visited)
 	return VLC->generaltexth->allTexts[id];
 }
 
-//must be instantiated in .cpp file for access to complete types of all member fields
-CBank::CBank() = default;
+CBank::CBank(IGameCallback *cb)
+	: CArmedInstance(cb)
+{}
+
 //must be instantiated in .cpp file for access to complete types of all member fields
 CBank::~CBank() = default;
 

+ 1 - 1
lib/mapObjects/CBank.h

@@ -27,7 +27,7 @@ class DLL_LINKAGE CBank : public CArmedInstance
 	void doVisit(const CGHeroInstance * hero) const;
 
 public:
-	CBank();
+	CBank(IGameCallback *cb);
 	~CBank() override;
 
 	void setConfig(const BankConfig & bc);

+ 2 - 2
lib/mapObjects/CGCreature.cpp

@@ -313,11 +313,11 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
 		powerFactor = -3;
 
 	std::set<CreatureID> myKindCres; //what creatures are the same kind as we
-	const CCreature * myCreature = VLC->creh->objects[getCreature().getNum()];
+	const CCreature * myCreature = getCreature().toCreature();
 	myKindCres.insert(myCreature->getId()); //we
 	myKindCres.insert(myCreature->upgrades.begin(), myCreature->upgrades.end()); //our upgrades
 
-	for(ConstTransitivePtr<CCreature> &crea : VLC->creh->objects)
+	for(auto const & crea : VLC->creh->objects)
 	{
 		if(vstd::contains(crea->upgrades, myCreature->getId())) //it's our base creatures
 			myKindCres.insert(crea->getId());

+ 2 - 0
lib/mapObjects/CGCreature.h

@@ -18,6 +18,8 @@ VCMI_LIB_NAMESPACE_BEGIN
 class DLL_LINKAGE CGCreature : public CArmedInstance //creatures on map
 {
 public:
+	using CArmedInstance::CArmedInstance;
+
 	enum Action {
 		FIGHT = -2, FLEE = -1, JOIN_FOR_FREE = 0 //values > 0 mean gold price
 	};

+ 5 - 1
lib/mapObjects/CGDwelling.cpp

@@ -16,6 +16,7 @@
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
 #include "../mapObjectConstructors/DwellingInstanceConstructor.h"
 #include "../mapObjects/CGHeroInstance.h"
+#include "../mapObjects/CGTownInstance.h"
 #include "../networkPacks/StackLocation.h"
 #include "../networkPacks/PacksForClient.h"
 #include "../networkPacks/PacksForClientBattle.h"
@@ -43,7 +44,10 @@ void CGDwellingRandomizationInfo::serializeJson(JsonSerializeFormat & handler)
 	}
 }
 
-CGDwelling::CGDwelling() = default;
+CGDwelling::CGDwelling(IGameCallback *cb):
+	CArmedInstance(cb)
+{}
+
 CGDwelling::~CGDwelling() = default;
 
 FactionID CGDwelling::randomizeFaction(CRandomGenerator & rand)

+ 1 - 1
lib/mapObjects/CGDwelling.h

@@ -38,7 +38,7 @@ public:
 	std::optional<CGDwellingRandomizationInfo> randomizationInfo; //random dwelling options; not serialized
 	TCreaturesSet creatures; //creatures[level] -> <vector of alternative ids (base creature and upgrades, creatures amount>
 
-	CGDwelling();
+	CGDwelling(IGameCallback *cb);
 	~CGDwelling() override;
 
 protected:

+ 16 - 13
lib/mapObjects/CGHeroInstance.cpp

@@ -33,6 +33,7 @@
 #include "../serializer/JsonSerializeFormat.h"
 #include "../mapObjectConstructors/AObjectTypeHandler.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
+#include "../mapObjects/MiscObjects.h"
 #include "../modding/ModScope.h"
 #include "../networkPacks/PacksForClient.h"
 #include "../networkPacks/PacksForClientBattle.h"
@@ -274,7 +275,9 @@ int CGHeroInstance::movementPointsLimitCached(bool onLand, const TurnInfo * ti)
 	return ti->valOfBonuses(BonusType::MOVEMENT, onLand ? BonusCustomSubtype::heroMovementLand : BonusCustomSubtype::heroMovementSea);
 }
 
-CGHeroInstance::CGHeroInstance():
+CGHeroInstance::CGHeroInstance(IGameCallback * cb)
+	: CArmedInstance(cb),
+	type(nullptr),
 	tacticFormationEnabled(false),
 	inTownGarrison(false),
 	moveDir(4),
@@ -316,7 +319,7 @@ void CGHeroInstance::initHero(CRandomGenerator & rand)
 {
 	assert(validTypes(true));
 	if(!type)
-		type = VLC->heroh->objects[getHeroType().getNum()];
+		type = getHeroType().toHeroType();
 
 	if (ID == Obj::HERO)
 		appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->getIndex())->getTemplates().front();
@@ -590,11 +593,11 @@ void CGHeroInstance::pickRandomObject(CRandomGenerator & rand)
 	{
 		ID = Obj::HERO;
 		subID = cb->gameState()->pickNextHeroType(getOwner());
-		type = VLC->heroh->objects[getHeroType().getNum()];
+		type = getHeroType().toHeroType();
 		randomizeArmy(type->heroClass->faction);
 	}
 	else
-		type = VLC->heroh->objects[getHeroType().getNum()];
+		type = getHeroType().toHeroType();
 
 	auto oldSubID = subID;
 
@@ -789,7 +792,7 @@ void CGHeroInstance::spendMana(ServerCallback * server, const int spellCost) con
 
 bool CGHeroInstance::canCastThisSpell(const spells::Spell * spell) const
 {
-	const bool isAllowed = IObjectInterface::cb->isAllowed(spell->getId());
+	const bool isAllowed = cb->isAllowed(spell->getId());
 
 	const bool inSpellBook = vstd::contains(spells, spell->getId()) && hasSpellbook();
 	const bool specificBonus = hasBonusOfType(BonusType::SPELL, BonusSubtypeID(spell->getId()));
@@ -853,7 +856,7 @@ bool CGHeroInstance::canLearnSpell(const spells::Spell * spell, bool allowBanned
 		return false;//creature abilities can not be learned
 	}
 
-	if(!allowBanned && !IObjectInterface::cb->isAllowed(spell->getId()))
+	if(!allowBanned && !cb->isAllowed(spell->getId()))
 	{
 		logGlobal->warn("Hero %s try to learn banned spell %s", nodeName(), spell->getNameTranslated());
 		return false;//banned spells should not be learned
@@ -879,7 +882,7 @@ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &b
 		double necromancySkill = valOfBonuses(BonusType::UNDEAD_RAISE_PERCENTAGE) / 100.0;
 		const ui8 necromancyLevel = valOfBonuses(BonusType::IMPROVED_NECROMANCY);
 		vstd::amin(necromancySkill, 1.0); //it's impossible to raise more creatures than all...
-		const std::map<ui32,si32> &casualties = battleResult.casualties[!battleResult.winner];
+		const std::map<CreatureID,si32> &casualties = battleResult.casualties[!battleResult.winner];
 		// figure out what to raise - pick strongest creature meeting requirements
 		CreatureID creatureTypeRaised = CreatureID::NONE; //now we always have IMPROVED_NECROMANCY, no need for hardcode
 		int requiredCasualtyLevel = 1;
@@ -888,7 +891,7 @@ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &b
 		{
 			int maxCasualtyLevel = 1;
 			for(const auto & casualty : casualties)
-				vstd::amax(maxCasualtyLevel, VLC->creatures()->getByIndex(casualty.first)->getLevel());
+				vstd::amax(maxCasualtyLevel, VLC->creatures()->getById(casualty.first)->getLevel());
 			// pick best bonus available
 			std::shared_ptr<Bonus> topPick;
 			for(const std::shared_ptr<Bonus> & newPick : *improvedNecromancy)
@@ -936,7 +939,7 @@ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &b
 		double raisedUnits = 0;
 		for(const auto & casualty : casualties)
 		{
-			const CCreature * c = VLC->creh->objects[casualty.first];
+			const CCreature * c = casualty.first.toCreature();
 			double raisedFromCasualty = std::min(c->getMaxHealth() / raisedUnitHealth, 1.0) * casualty.second * necromancySkill;
 			if(c->getLevel() < requiredCasualtyLevel)
 				raisedFromCasualty *= 0.5;
@@ -1258,7 +1261,7 @@ EDiggingStatus CGHeroInstance::diggingStatus() const
 {
 	if(static_cast<int>(movement) < movementPointsLimit(true))
 		return EDiggingStatus::LACK_OF_MOVEMENT;
-	if(!VLC->arth->objects[ArtifactID::GRAIL]->canBePutAt(this))
+	if(ArtifactID(ArtifactID::GRAIL).toArtifact()->canBePutAt(this))
 		return EDiggingStatus::BACKPACK_IS_FULL;
 	return cb->getTileDigStatus(visitablePos());
 }
@@ -1409,7 +1412,7 @@ void CGHeroInstance::setPrimarySkill(PrimarySkill primarySkill, si64 value, ui8
 {
 	if(primarySkill < PrimarySkill::EXPERIENCE)
 	{
-		auto skill = getBonusLocalFirst(Selector::type()(BonusType::PRIMARY_SKILL)
+		auto skill = getLocalBonus(Selector::type()(BonusType::PRIMARY_SKILL)
 			.And(Selector::subtype()(BonusSubtypeID(primarySkill)))
 			.And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL)));
 		assert(skill);
@@ -1742,7 +1745,7 @@ void CGHeroInstance::serializeJsonOptions(JsonSerializeFormat & handler)
 			if(!appearance)
 			{
 				// crossoverDeserialize
-				type = VLC->heroh->objects[getHeroType().getNum()];
+				type = getHeroType().toHeroType();
 				appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->getIndex())->getTemplates().front();
 			}
 
@@ -1760,7 +1763,7 @@ void CGHeroInstance::serializeJsonDefinition(JsonSerializeFormat & handler)
 
 bool CGHeroInstance::isMissionCritical() const
 {
-	for(const TriggeredEvent & event : IObjectInterface::cb->getMapHeader()->triggeredEvents)
+	for(const TriggeredEvent & event : cb->getMapHeader()->triggeredEvents)
 	{
 		if (event.effect.type != EventEffect::DEFEAT)
 			continue;

+ 4 - 2
lib/mapObjects/CGHeroInstance.h

@@ -28,6 +28,8 @@ enum class EHeroGender : uint8_t;
 class DLL_LINKAGE CGHeroPlaceholder : public CGObjectInstance
 {
 public:
+	using CGObjectInstance::CGObjectInstance;
+
 	/// if this is placeholder by power, then power rank of desired hero
 	std::optional<ui8> powerRank;
 
@@ -69,7 +71,7 @@ public:
 
 	//////////////////////////////////////////////////////////////////////////
 
-	ConstTransitivePtr<CHero> type;
+	const CHero * type;
 	TExpType exp; //experience points
 	ui32 level; //current level of hero
 
@@ -249,7 +251,7 @@ public:
 	/// If this hero perishes, the scenario is failed
 	bool isMissionCritical() const;
 
-	CGHeroInstance();
+	CGHeroInstance(IGameCallback *cb);
 	virtual ~CGHeroInstance();
 
 	PlayerColor getOwner() const override;

+ 3 - 3
lib/mapObjects/CGMarket.cpp

@@ -55,9 +55,9 @@ std::vector<TradeItemBuy> CGMarket::availableItemsIds(EMarketMode mode) const
 	return std::vector<TradeItemBuy>();
 }
 
-CGMarket::CGMarket()
-{
-}
+CGMarket::CGMarket(IGameCallback *cb):
+	CGObjectInstance(cb)
+{}
 
 std::vector<TradeItemBuy> CGBlackMarket::availableItemsIds(EMarketMode mode) const
 {

+ 5 - 1
lib/mapObjects/CGMarket.h

@@ -25,7 +25,7 @@ public:
 	std::string title;
 	std::string speech; //currently shown only in university
 	
-	CGMarket();
+	CGMarket(IGameCallback *cb);
 	///IObjectInterface
 	void onHeroVisit(const CGHeroInstance * h) const override; //open trading window
 	void initObj(CRandomGenerator & rand) override;//set skills for trade
@@ -49,6 +49,8 @@ public:
 class DLL_LINKAGE CGBlackMarket : public CGMarket
 {
 public:
+	using CGMarket::CGMarket;
+
 	std::vector<const CArtifact *> artifacts; //available artifacts
 
 	void newTurn(CRandomGenerator & rand) const override; //reset artifacts for black market every month
@@ -64,6 +66,8 @@ public:
 class DLL_LINKAGE CGUniversity : public CGMarket
 {
 public:
+	using CGMarket::CGMarket;
+
 	std::vector<TradeItemBuy> skills; //available skills
 
 	std::vector<TradeItemBuy> availableItemsIds(EMarketMode mode) const override;

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно