Selaa lähdekoodia

Make IObjectInterface::cb non-static

Ivan Savenko 1 vuosi sitten
vanhempi
sitoutus
a15366f5a5
89 muutettua tiedostoa jossa 502 lisäystä ja 445 poistoa
  1. 1 1
      AI/Nullkiller/Analyzers/ArmyManager.cpp
  2. 1 1
      AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp
  3. 1 1
      AI/Nullkiller/Engine/FuzzyHelper.cpp
  4. 1 1
      AI/Nullkiller/Goals/CompleteQuest.cpp
  5. 1 1
      AI/Nullkiller/Pathfinding/Actors.h
  6. 1 1
      AI/VCAI/FuzzyHelper.cpp
  7. 1 1
      AI/VCAI/Goals/CompleteQuest.cpp
  8. 3 7
      client/Client.cpp
  9. 2 2
      client/ClientCommandManager.cpp
  10. 2 2
      client/windows/CMapOverview.cpp
  11. 2 2
      client/windows/CQuestLog.cpp
  12. 1 0
      cmake_modules/VCMI_lib.cmake
  13. 24 0
      lib/GameCallbackHolder.h
  14. 34 38
      lib/JsonRandom.cpp
  15. 40 20
      lib/JsonRandom.h
  16. 6 6
      lib/campaign/CampaignState.cpp
  17. 4 3
      lib/campaign/CampaignState.h
  18. 12 18
      lib/gameState/CGameState.cpp
  19. 3 2
      lib/gameState/CGameState.h
  20. 3 3
      lib/gameState/CGameStateCampaign.cpp
  21. 2 1
      lib/mapObjectConstructors/AObjectTypeHandler.h
  22. 10 72
      lib/mapObjectConstructors/CBankInstanceConstructor.cpp
  23. 1 5
      lib/mapObjectConstructors/CBankInstanceConstructor.h
  24. 4 4
      lib/mapObjectConstructors/CDefaultObjectTypeHandler.h
  25. 2 2
      lib/mapObjectConstructors/CRewardableConstructor.cpp
  26. 1 1
      lib/mapObjectConstructors/CRewardableConstructor.h
  27. 7 6
      lib/mapObjectConstructors/CommonConstructors.cpp
  28. 1 1
      lib/mapObjectConstructors/CommonConstructors.h
  29. 3 3
      lib/mapObjectConstructors/DwellingInstanceConstructor.cpp
  30. 0 5
      lib/mapObjectConstructors/IObjectInfo.h
  31. 4 3
      lib/mapObjects/CArmedInstance.cpp
  32. 2 2
      lib/mapObjects/CArmedInstance.h
  33. 4 2
      lib/mapObjects/CBank.cpp
  34. 1 1
      lib/mapObjects/CBank.h
  35. 2 0
      lib/mapObjects/CGCreature.h
  36. 4 1
      lib/mapObjects/CGDwelling.cpp
  37. 1 1
      lib/mapObjects/CGDwelling.h
  38. 5 4
      lib/mapObjects/CGHeroInstance.cpp
  39. 3 1
      lib/mapObjects/CGHeroInstance.h
  40. 3 3
      lib/mapObjects/CGMarket.cpp
  41. 5 1
      lib/mapObjects/CGMarket.h
  42. 2 1
      lib/mapObjects/CGObjectInstance.cpp
  43. 1 1
      lib/mapObjects/CGObjectInstance.h
  44. 1 1
      lib/mapObjects/CGPandoraBox.cpp
  45. 4 0
      lib/mapObjects/CGPandoraBox.h
  46. 24 3
      lib/mapObjects/CGTownBuilding.cpp
  47. 7 4
      lib/mapObjects/CGTownBuilding.h
  48. 2 1
      lib/mapObjects/CGTownInstance.cpp
  49. 1 1
      lib/mapObjects/CGTownInstance.h
  50. 14 14
      lib/mapObjects/CQuest.cpp
  51. 18 5
      lib/mapObjects/CQuest.h
  52. 2 1
      lib/mapObjects/CRewardableObject.cpp
  53. 1 1
      lib/mapObjects/CRewardableObject.h
  54. 2 4
      lib/mapObjects/IObjectInterface.cpp
  55. 3 2
      lib/mapObjects/IObjectInterface.h
  56. 4 3
      lib/mapObjects/MiscObjects.cpp
  57. 39 3
      lib/mapObjects/MiscObjects.h
  58. 3 2
      lib/mapping/CMap.cpp
  59. 3 2
      lib/mapping/CMap.h
  60. 4 4
      lib/mapping/CMapService.cpp
  61. 7 6
      lib/mapping/CMapService.h
  62. 23 23
      lib/mapping/MapFormatH3M.cpp
  63. 1 1
      lib/mapping/MapFormatH3M.h
  64. 11 11
      lib/mapping/MapFormatJson.cpp
  65. 1 1
      lib/mapping/MapFormatJson.h
  66. 1 1
      lib/mapping/ObstacleProxy.cpp
  67. 1 1
      lib/networkPacks/NetPacksLib.cpp
  68. 45 41
      lib/rewardable/Info.cpp
  69. 3 3
      lib/rewardable/Limiter.cpp
  70. 2 2
      lib/rmg/CMapGenerator.cpp
  71. 2 1
      lib/rmg/CMapGenerator.h
  72. 2 2
      lib/rmg/RmgMap.cpp
  73. 1 1
      lib/rmg/RmgMap.h
  74. 4 4
      lib/rmg/modificators/ConnectionsPlacer.cpp
  75. 2 2
      lib/rmg/modificators/MinePlacer.cpp
  76. 2 2
      lib/rmg/modificators/ObjectDistributor.cpp
  77. 1 1
      lib/rmg/modificators/ObjectManager.cpp
  78. 1 1
      lib/rmg/modificators/QuestArtifactPlacer.cpp
  79. 1 1
      lib/rmg/modificators/RiverPlacer.cpp
  80. 2 2
      lib/rmg/modificators/TownPlacer.cpp
  81. 18 18
      lib/rmg/modificators/TreasurePlacer.cpp
  82. 3 3
      lib/rmg/modificators/WaterProxy.cpp
  83. 17 6
      lib/serializer/BinaryDeserializer.h
  84. 1 14
      lib/spells/ISpellMechanics.cpp
  85. 0 7
      lib/spells/ISpellMechanics.h
  86. 2 2
      mapeditor/mainwindow.cpp
  87. 1 1
      mapeditor/mapview.cpp
  88. 2 2
      mapeditor/windownewmap.cpp
  89. 2 3
      server/CGameHandler.cpp

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

@@ -128,7 +128,7 @@ class TemporaryArmy : public CArmedInstance
 public:
 	void armyChanged() override {}
 	TemporaryArmy()
-		:CArmedInstance(true)
+		:CArmedInstance(nullptr, true)
 	{
 	}
 };

+ 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(nullptr);
 			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;

+ 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();
 }

+ 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();
 }

+ 3 - 7
client/Client.cpp

@@ -139,14 +139,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,7 +174,7 @@ void CClient::newGame(CGameState * initializedGameState)
 	CSH->th->update();
 	CMapService mapService;
 	gs = initializedGameState ? initializedGameState : new CGameState();
-	gs->preInit(VLC);
+	gs->preInit(VLC, this);
 	logNetwork->trace("\tCreating gamestate: %i", CSH->th->getDiff());
 	if(!initializedGameState)
 	{
@@ -200,7 +196,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.");
 

+ 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();

+ 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

@@ -619,6 +619,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

+ 24 - 0
lib/GameCallbackHolder.h

@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+	GameCallbackHolder(IGameCallback *cb):
+		cb(cb)
+	{}
+};

+ 34 - 38
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(std::string variableGroup, const std::string & value, const Variables & variables, si32 defaultValue)
 	{
 		if (value.empty() || value[0] != '@')
 		{
@@ -51,7 +49,7 @@ 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;
@@ -79,7 +77,7 @@ namespace JsonRandom
 	}
 
 	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())
@@ -532,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())
@@ -543,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(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

+ 6 - 6
lib/campaign/CampaignState.cpp

@@ -224,7 +224,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 +316,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 +327,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 +355,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 +363,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;
 

+ 12 - 18
lib/gameState/CGameState.cpp

@@ -169,14 +169,16 @@ CGameState::~CGameState()
 	initialOpts.dellNull();
 }
 
-void CGameState::preInit(Services * services)
+void CGameState::preInit(Services * services, IGameCallback * callback)
 {
 	this->services = services;
+	this->callback = callback;
 }
 
 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();
@@ -284,21 +286,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 +301,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 +364,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 +555,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 +629,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());
+			CGBoat * boat = dynamic_cast<CGBoat*>(handler->create(callback, nullptr));
 			handler->configureObject(boat, gs->getRandomGenerator());
 
 			boat->pos = hero->pos;
@@ -673,7 +667,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();
@@ -936,7 +930,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
 }

+ 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();

+ 3 - 3
lib/gameState/CGameStateCampaign.cpp

@@ -402,7 +402,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 +427,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 +599,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

+ 10 - 72
lib/mapObjectConstructors/CBankInstanceConstructor.cpp

@@ -37,15 +37,16 @@ void CBankInstanceConstructor::initTypeData(const JsonNode & input)
 BankConfig CBankInstanceConstructor::generateConfig(const JsonNode & level, CRandomGenerator & rng) const
 {
 	BankConfig bc;
+	JsonRandom randomizer(nullptr);
 	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;
 }
@@ -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;
 
 
@@ -191,12 +128,13 @@ std::vector<PossibleReward<TResources>> CBankInfo::getPossibleResourcesReward()
 std::vector<PossibleReward<CStackBasicDescriptor>> CBankInfo::getPossibleCreaturesReward() const
 {
 	JsonRandom::Variables emptyVariables;
+	JsonRandom randomizer(nullptr);
 	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)
 		{

+ 1 - 5
lib/mapObjectConstructors/CBankInstanceConstructor.h

@@ -55,14 +55,10 @@ 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;
-
 	bool givesResources() const override;
 	bool givesArtifacts() const override;
 	bool givesCreatures() const override;

+ 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);
 	}
 };
 

+ 2 - 2
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;

+ 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;
 

+ 7 - 6
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;
 }
@@ -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);
 	}
 }

+ 1 - 1
lib/mapObjectConstructors/CommonConstructors.h

@@ -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;
 

+ 3 - 3
lib/mapObjectConstructors/DwellingInstanceConstructor.cpp

@@ -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));
 		}

+ 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; }

+ 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 - 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
 	};

+ 4 - 1
lib/mapObjects/CGDwelling.cpp

@@ -43,7 +43,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:

+ 5 - 4
lib/mapObjects/CGHeroInstance.cpp

@@ -274,7 +274,8 @@ 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),
 	tacticFormationEnabled(false),
 	inTownGarrison(false),
 	moveDir(4),
@@ -789,7 +790,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 +854,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
@@ -1760,7 +1761,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;

+ 3 - 1
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;
 
@@ -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;

+ 2 - 1
lib/mapObjects/CGObjectInstance.cpp

@@ -28,7 +28,8 @@
 VCMI_LIB_NAMESPACE_BEGIN
 
 //TODO: remove constructor
-CGObjectInstance::CGObjectInstance():
+CGObjectInstance::CGObjectInstance(IGameCallback *cb):
+	IObjectInterface(cb),
 	pos(-1,-1,-1),
 	ID(Obj::NO_OBJ),
 	subID(-1),

+ 1 - 1
lib/mapObjects/CGObjectInstance.h

@@ -44,7 +44,7 @@ public:
 	std::string typeName;
 	std::string subTypeName;
 
-	CGObjectInstance(); //TODO: remove constructor
+	CGObjectInstance(IGameCallback *cb);
 	~CGObjectInstance() override;
 
 	MapObjectID getObjGroupIndex() const override;

+ 1 - 1
lib/mapObjects/CGPandoraBox.cpp

@@ -66,7 +66,7 @@ void CGPandoraBox::grantRewardWithMessage(const CGHeroInstance * h, int index, b
 		return text;
 	};
 	
-	auto sendInfoWindow = [h](const MetaString & text, const Rewardable::Reward & reward)
+	auto sendInfoWindow = [&](const MetaString & text, const Rewardable::Reward & reward)
 	{
 		InfoWindow iw;
 		iw.player = h->tempOwner;

+ 4 - 0
lib/mapObjects/CGPandoraBox.h

@@ -19,6 +19,8 @@ struct InfoWindow;
 class DLL_LINKAGE CGPandoraBox : public CRewardableObject
 {
 public:
+	using CRewardableObject::CRewardableObject;
+
 	MetaString message;
 
 	void initObj(CRandomGenerator & rand) override;
@@ -41,6 +43,8 @@ protected:
 class DLL_LINKAGE CGEvent : public CGPandoraBox  //event objects
 {
 public:
+	using CGPandoraBox::CGPandoraBox;
+
 	bool removeAfterVisit = false; //true if event is removed after occurring
 	std::set<PlayerColor> availableFor; //players whom this event is available for
 	bool computerActivate = false; //true if computer player can activate this event

+ 24 - 3
lib/mapObjects/CGTownBuilding.cpp

@@ -19,6 +19,16 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
+CGTownBuilding::CGTownBuilding(IGameCallback * cb)
+	: IObjectInterface(cb)
+	, town(nullptr)
+{}
+
+CGTownBuilding::CGTownBuilding(CGTownInstance * town)
+	: IObjectInterface(town->cb)
+	, town(town)
+{}
+
 PlayerColor CGTownBuilding::getOwner() const
 {
 	return town->getOwner();
@@ -111,12 +121,15 @@ std::string CGTownBuilding::getCustomBonusGreeting(const Bonus & bonus) const
 	return greeting;
 }
 
+COPWBonus::COPWBonus(IGameCallback *cb)
+	: CGTownBuilding(cb)
+{}
 
 COPWBonus::COPWBonus(const BuildingID & bid, BuildingSubID::EBuildingSubID subId, CGTownInstance * cgTown)
+	: CGTownBuilding(cgTown)
 {
 	bID = bid;
 	bType = subId;
-	town = cgTown;
 	indexOnTV = static_cast<si32>(town->bonusingBuildings.size());
 }
 
@@ -175,11 +188,15 @@ void COPWBonus::onHeroVisit (const CGHeroInstance * h) const
 	}
 }
 
+CTownBonus::CTownBonus(IGameCallback *cb)
+	: CGTownBuilding(cb)
+{}
+
 CTownBonus::CTownBonus(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * cgTown)
+	: CGTownBuilding(cgTown)
 {
 	bID = index;
 	bType = subId;
-	town = cgTown;
 	indexOnTV = static_cast<si32>(town->bonusingBuildings.size());
 }
 
@@ -286,11 +303,15 @@ void CTownBonus::applyBonuses(CGHeroInstance * h, const BonusList & bonuses) con
 		town->addHeroToStructureVisitors(h, indexOnTV);
 }
 
+CTownRewardableBuilding::CTownRewardableBuilding(IGameCallback *cb)
+	: CGTownBuilding(cb)
+{}
+
 CTownRewardableBuilding::CTownRewardableBuilding(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * cgTown, CRandomGenerator & rand)
+	: CGTownBuilding(cgTown)
 {
 	bID = index;
 	bType = subId;
-	town = cgTown;
 	indexOnTV = static_cast<si32>(town->bonusingBuildings.size());
 	initObj(rand);
 }

+ 7 - 4
lib/mapObjects/CGTownBuilding.h

@@ -22,9 +22,12 @@ class DLL_LINKAGE CGTownBuilding : public IObjectInterface
 {
 ///basic class for town structures handled as map objects
 public:
+	CGTownBuilding(CGTownInstance * town);
+	CGTownBuilding(IGameCallback *cb);
+
 	si32 indexOnTV = 0; //identifies its index on towns vector
 	
-	CGTownInstance * town = nullptr;
+	CGTownInstance * town;
 
 	STRONG_INLINE
 	BuildingSubID::EBuildingSubID getBuildingSubtype() const
@@ -74,7 +77,7 @@ public:
 	void onHeroVisit (const CGHeroInstance * h) const override;
 
 	COPWBonus(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * TOWN);
-	COPWBonus() = default;
+	COPWBonus(IGameCallback *cb);
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -93,7 +96,7 @@ public:
 	void onHeroVisit (const CGHeroInstance * h) const override;
 
 	CTownBonus(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * TOWN);
-	CTownBonus() = default;
+	CTownBonus(IGameCallback *cb);
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -131,7 +134,7 @@ public:
 	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
 	
 	CTownRewardableBuilding(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * town, CRandomGenerator & rand);
-	CTownRewardableBuilding() = default;
+	CTownRewardableBuilding(IGameCallback *cb);
 	
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{

+ 2 - 1
lib/mapObjects/CGTownInstance.cpp

@@ -228,7 +228,8 @@ bool CGTownInstance::hasCapitol() const
 	return hasBuilt(BuildingID::CAPITOL);
 }
 
-CGTownInstance::CGTownInstance():
+CGTownInstance::CGTownInstance(IGameCallback *cb):
+	CGDwelling(cb),
 	town(nullptr),
 	builded(0),
 	destroyed(0),

+ 1 - 1
lib/mapObjects/CGTownInstance.h

@@ -200,7 +200,7 @@ public:
 	FactionID getFaction() const override;
 	TerrainId getNativeTerrain() const override;
 
-	CGTownInstance();
+	CGTownInstance(IGameCallback *cb);
 	virtual ~CGTownInstance();
 
 	///IObjectInterface overrides

+ 14 - 14
lib/mapObjects/CQuest.cpp

@@ -130,7 +130,7 @@ bool CQuest::checkQuest(const CGHeroInstance * h) const
 	
 	if(killTarget != ObjectInstanceID::NONE)
 	{
-		if(CGHeroInstance::cb->getObjByQuestIdentifier(killTarget))
+		if(h->cb->getObjByQuestIdentifier(killTarget))
 			return false;
 	}
 	
@@ -167,7 +167,7 @@ void CQuest::completeQuest(IGameCallback * cb, const CGHeroInstance *h) const
 	cb->giveResources(h->getOwner(), mission.resources);
 }
 
-void CQuest::addTextReplacements(MetaString & text, std::vector<Component> & components) const
+void CQuest::addTextReplacements(IGameCallback * cb, MetaString & text, std::vector<Component> & components) const
 {
 	if(mission.heroLevel > 0)
 		text.replaceNumber(mission.heroLevel);
@@ -263,10 +263,10 @@ void CQuest::addTextReplacements(MetaString & text, std::vector<Component> & com
 	}
 	
 	if(lastDay >= 0)
-		text.replaceNumber(lastDay - IObjectInterface::cb->getDate(Date::DAY));
+		text.replaceNumber(lastDay - cb->getDate(Date::DAY));
 }
 
-void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components, bool firstVisit, const CGHeroInstance * h) const
+void CQuest::getVisitText(IGameCallback * cb, MetaString &iwText, std::vector<Component> &components, bool firstVisit, const CGHeroInstance * h) const
 {
 	bool failRequirements = (h ? !checkQuest(h) : true);
 	mission.loadComponents(components, h);
@@ -279,10 +279,10 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components
 	if(lastDay >= 0)
 		iwText.appendTextID(TextIdentifier("core", "seerhut", "time", textOption).get());
 	
-	addTextReplacements(iwText, components);
+	addTextReplacements(cb, iwText, components);
 }
 
-void CQuest::getRolloverText(MetaString &ms, bool onHover) const
+void CQuest::getRolloverText(IGameCallback * cb, MetaString &ms, bool onHover) const
 {
 	if(onHover)
 		ms.appendRawString("\n\n");
@@ -292,15 +292,15 @@ void CQuest::getRolloverText(MetaString &ms, bool onHover) const
 	ms.appendTextID(TextIdentifier("core", "seerhut", "quest", questName, questState, textOption).get());
 
 	std::vector<Component> components;
-	addTextReplacements(ms, components);
+	addTextReplacements(cb, ms, components);
 }
 
-void CQuest::getCompletionText(MetaString &iwText) const
+void CQuest::getCompletionText(IGameCallback * cb, MetaString &iwText) const
 {
 	iwText.appendRawString(completedText.toString());
 	
 	std::vector<Component> components;
-	addTextReplacements(iwText, components);
+	addTextReplacements(cb, iwText, components);
 }
 
 void CQuest::defineQuestName()
@@ -411,9 +411,9 @@ bool IQuestObject::checkQuest(const CGHeroInstance* h) const
 	return quest->checkQuest(h);
 }
 
-void IQuestObject::getVisitText(MetaString &text, std::vector<Component> &components, bool FirstVisit, const CGHeroInstance * h) const
+void CGSeerHut::getVisitText(MetaString &text, std::vector<Component> &components, bool FirstVisit, const CGHeroInstance * h) const
 {
-	quest->getVisitText(text, components, FirstVisit, h);
+	quest->getVisitText(cb, text, components, FirstVisit, h);
 }
 
 void IQuestObject::afterAddToMapCommon(CMap * map) const
@@ -479,14 +479,14 @@ void CGSeerHut::initObj(CRandomGenerator & rand)
 			quest->completedText.appendTextID(TextIdentifier("core", "seerhut", "quest", quest-> questName, quest->missionState(2), quest->textOption).get());
 	}
 	
-	quest->getCompletionText(configuration.onSelect);
+	quest->getCompletionText(cb, configuration.onSelect);
 	for(auto & i : configuration.info)
-		quest->getCompletionText(i.message);
+		quest->getCompletionText(cb, i.message);
 }
 
 void CGSeerHut::getRolloverText(MetaString &text, bool onHover) const
 {
-	quest->getRolloverText(text, onHover);//TODO: simplify?
+	quest->getRolloverText(cb, text, onHover);//TODO: simplify?
 	if(!onHover)
 		text.replaceRawString(seerName);
 }

+ 18 - 5
lib/mapObjects/CQuest.h

@@ -56,11 +56,11 @@ public:
 
 	static bool checkMissionArmy(const CQuest * q, const CCreatureSet * army);
 	virtual bool checkQuest(const CGHeroInstance * h) const; //determines whether the quest is complete or not
-	virtual void getVisitText(MetaString &text, std::vector<Component> & components, bool FirstVisit, const CGHeroInstance * h = nullptr) const;
-	virtual void getCompletionText(MetaString &text) const;
-	virtual void getRolloverText (MetaString &text, bool onHover) const; //hover or quest log entry
+	virtual void getVisitText(IGameCallback * cb, MetaString &text, std::vector<Component> & components, bool FirstVisit, const CGHeroInstance * h = nullptr) const;
+	virtual void getCompletionText(IGameCallback * cb, MetaString &text) const;
+	virtual void getRolloverText (IGameCallback * cb, MetaString &text, bool onHover) const; //hover or quest log entry
 	virtual void completeQuest(IGameCallback *, const CGHeroInstance * h) const;
-	virtual void addTextReplacements(MetaString &out, std::vector<Component> & components) const;
+	virtual void addTextReplacements(IGameCallback * cb, MetaString &out, std::vector<Component> & components) const;
 	virtual void addKillTargetReplacements(MetaString &out) const;
 	void defineQuestName();
 
@@ -103,7 +103,7 @@ public:
 	///Information about quest should remain accessible even if IQuestObject removed from map
 	///All CQuest objects are freed in CMap destructor
 	virtual ~IQuestObject() = default;
-	virtual void getVisitText (MetaString &text, std::vector<Component> &components, bool FirstVisit, const CGHeroInstance * h = nullptr) const;
+	virtual void getVisitText (MetaString &text, std::vector<Component> &components, bool FirstVisit, const CGHeroInstance * h = nullptr) const = 0;
 	virtual bool checkQuest (const CGHeroInstance * h) const;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
@@ -117,6 +117,8 @@ protected:
 class DLL_LINKAGE CGSeerHut : public CRewardableObject, public IQuestObject
 {
 public:
+	using CRewardableObject::CRewardableObject;
+
 	std::string seerName;
 
 	void initObj(CRandomGenerator & rand) override;
@@ -129,6 +131,7 @@ public:
 	void newTurn(CRandomGenerator & rand) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
+	void getVisitText (MetaString &text, std::vector<Component> &components, bool FirstVisit, const CGHeroInstance * h = nullptr) const override;
 
 	virtual void init(CRandomGenerator & rand);
 	int checkDirection() const; //calculates the region of map where monster is placed
@@ -154,6 +157,8 @@ protected:
 class DLL_LINKAGE CGQuestGuard : public CGSeerHut
 {
 public:
+	using CGSeerHut::CGSeerHut;
+
 	void init(CRandomGenerator & rand) override;
 	
 	void onHeroVisit(const CGHeroInstance * h) const override;
@@ -170,6 +175,8 @@ protected:
 class DLL_LINKAGE CGKeys : public CGObjectInstance //Base class for Keymaster and guards
 {
 public:
+	using CGObjectInstance::CGObjectInstance;
+
 	bool wasMyColorVisited(const PlayerColor & player) const;
 
 	std::string getObjectName() const override; //depending on color
@@ -184,6 +191,8 @@ public:
 class DLL_LINKAGE CGKeymasterTent : public CGKeys
 {
 public:
+	using CGKeys::CGKeys;
+
 	bool wasVisited (PlayerColor player) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 
@@ -196,6 +205,8 @@ public:
 class DLL_LINKAGE CGBorderGuard : public CGKeys, public IQuestObject
 {
 public:
+	using CGKeys::CGKeys;
+
 	void initObj(CRandomGenerator & rand) override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
@@ -216,6 +227,8 @@ public:
 class DLL_LINKAGE CGBorderGate : public CGBorderGuard
 {
 public:
+	using CGBorderGuard::CGBorderGuard;
+
 	void onHeroVisit(const CGHeroInstance * h) const override;
 
 	bool passableFor(PlayerColor color) const override;

+ 2 - 1
lib/mapObjects/CRewardableObject.cpp

@@ -385,7 +385,8 @@ void CRewardableObject::initObj(CRandomGenerator & rand)
 	getObjectHandler()->configureObject(this, rand);
 }
 
-CRewardableObject::CRewardableObject()
+CRewardableObject::CRewardableObject(IGameCallback *cb)
+	:CArmedInstance(cb)
 {}
 
 void CRewardableObject::serializeJsonOptions(JsonSerializeFormat & handler)

+ 1 - 1
lib/mapObjects/CRewardableObject.h

@@ -67,7 +67,7 @@ public:
 	
 	void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;
 
-	CRewardableObject();
+	CRewardableObject(IGameCallback *cb);
 	
 	std::string getHoverText(PlayerColor player) const override;
 	std::string getHoverText(const CGHeroInstance * hero) const override;

+ 2 - 4
lib/mapObjects/IObjectInterface.cpp

@@ -21,8 +21,6 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-IGameCallback * IObjectInterface::cb = nullptr;
-
 void IObjectInterface::showInfoDialog(const ui32 txtID, const ui16 soundID, EInfoWindowMode mode) const
 {
 	InfoWindow iw;
@@ -30,7 +28,7 @@ void IObjectInterface::showInfoDialog(const ui32 txtID, const ui16 soundID, EInf
 	iw.player = getOwner();
 	iw.type = mode;
 	iw.text.appendLocalString(EMetaText::ADVOB_TXT,txtID);
-	IObjectInterface::cb->sendAndApply(&iw);
+	cb->sendAndApply(&iw);
 }
 
 ///IObjectInterface
@@ -117,7 +115,7 @@ IBoatGenerator::EGeneratorState IBoatGenerator::shipyardStatus() const
 	if(!tile.valid())
 		return TILE_BLOCKED; //no available water
 
-	const TerrainTile *t = IObjectInterface::cb->getTile(tile);
+	const TerrainTile *t = getObject()->cb->getTile(tile);
 	if(!t)
 		return TILE_BLOCKED; //no available water
 

+ 3 - 2
lib/mapObjects/IObjectInterface.h

@@ -12,6 +12,7 @@
 #include "../networkPacks/EInfoWindowMode.h"
 #include "../networkPacks/ObjProperty.h"
 #include "../constants/EntityIdentifiers.h"
+#include "../GameCallbackHolder.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -28,10 +29,10 @@ class int3;
 class MetaString;
 class PlayerColor;
 
-class DLL_LINKAGE IObjectInterface
+class DLL_LINKAGE IObjectInterface : public GameCallbackHolder
 {
 public:
-	static IGameCallback *cb;
+	using GameCallbackHolder::GameCallbackHolder;
 
 	virtual ~IObjectInterface() = default;
 

+ 4 - 3
lib/mapObjects/MiscObjects.cpp

@@ -584,13 +584,13 @@ void CGSubterraneanGate::initObj(CRandomGenerator & rand)
 	type = BOTH;
 }
 
-void CGSubterraneanGate::postInit() //matches subterranean gates into pairs
+void CGSubterraneanGate::postInit(IGameCallback * cb) //matches subterranean gates into pairs
 {
 	//split on underground and surface gates
 	std::vector<CGSubterraneanGate *> gatesSplit[2]; //surface and underground gates
 	for(auto & obj : cb->gameState()->map->objects)
 	{
-		if(!obj) // FIXME: Find out why there are nullptr objects right after initialization
+		if(!obj)
 			continue;
 
 		auto * hlp = dynamic_cast<CGSubterraneanGate *>(cb->gameState()->getObjInstance(obj->id));
@@ -1081,7 +1081,8 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const
 	}
 }
 
-CGBoat::CGBoat()
+CGBoat::CGBoat(IGameCallback * cb)
+	: CGObjectInstance(cb)
 {
 	hero = nullptr;
 	direction = 4;

+ 39 - 3
lib/mapObjects/MiscObjects.h

@@ -24,6 +24,8 @@ using TTeleportExitsList = std::vector<std::pair<ObjectInstanceID, int3>>;
 class DLL_LINKAGE CTeamVisited: public CGObjectInstance
 {
 public:
+	using CGObjectInstance::CGObjectInstance;
+
 	std::set<PlayerColor> players; //players that visited this object
 
 	bool wasVisited (const CGHeroInstance * h) const override;
@@ -41,6 +43,8 @@ public:
 class DLL_LINKAGE CGSignBottle : public CGObjectInstance //signs and ocean bottles
 {
 public:
+	using CGObjectInstance::CGObjectInstance;
+
 	MetaString message;
 
 	void onHeroVisit(const CGHeroInstance * h) const override;
@@ -58,6 +62,8 @@ protected:
 class DLL_LINKAGE CGGarrison : public CArmedInstance
 {
 public:
+	using CArmedInstance::CArmedInstance;
+
 	bool removableUnits;
 
 	void initObj(CRandomGenerator &rand) override;
@@ -78,6 +84,8 @@ protected:
 class DLL_LINKAGE CGArtifact : public CArmedInstance
 {
 public:
+	using CArmedInstance::CArmedInstance;
+
 	CArtifactInstance * storedArtifact = nullptr;
 	MetaString message;
 
@@ -112,6 +120,8 @@ protected:
 class DLL_LINKAGE CGResource : public CArmedInstance
 {
 public:
+	using CArmedInstance::CArmedInstance;
+
 	static constexpr ui32 RANDOM_AMOUNT = 0;
 	ui32 amount = RANDOM_AMOUNT; //0 if random
 	
@@ -148,6 +158,8 @@ public:
 	ResourceSet dailyIncome() const;
 
 private:
+	using CArmedInstance::CArmedInstance;
+
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
 	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
@@ -204,6 +216,8 @@ protected:
 	std::vector<ObjectInstanceID> getAllExits(bool excludeCurrent = false) const;
 
 public:
+	using CGObjectInstance::CGObjectInstance;
+
 	TeleportChannelID channel;
 
 	bool isEntrance() const;
@@ -236,6 +250,8 @@ protected:
 	void initObj(CRandomGenerator & rand) override;
 
 public:
+	using CGTeleport::CGTeleport;
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CGTeleport&>(*this);
@@ -248,7 +264,9 @@ class DLL_LINKAGE CGSubterraneanGate : public CGMonolith
 	void initObj(CRandomGenerator & rand) override;
 
 public:
-	static void postInit();
+	using CGMonolith::CGMonolith;
+
+	static void postInit(IGameCallback * cb);
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -263,6 +281,8 @@ class DLL_LINKAGE CGWhirlpool : public CGMonolith
 	static bool isProtected( const CGHeroInstance * h );
 
 public:
+	using CGMonolith::CGMonolith;
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CGMonolith&>(*this);
@@ -272,6 +292,8 @@ public:
 class DLL_LINKAGE CGSirens : public CGObjectInstance
 {
 public:
+	using CGObjectInstance::CGObjectInstance;
+
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	std::string getHoverText(const CGHeroInstance * hero) const override;
 	void initObj(CRandomGenerator & rand) override;
@@ -285,6 +307,8 @@ public:
 class DLL_LINKAGE CGBoat : public CGObjectInstance, public CBonusSystemNode
 {
 public:
+	using CGObjectInstance::CGObjectInstance;
+
 	ui8 direction;
 	const CGHeroInstance *hero;  //hero on board
 	bool onboardAssaultAllowed; //if true, hero can attack units from transport
@@ -296,7 +320,7 @@ public:
 	AnimationPath overlayAnimation; //waves animations
 	std::array<AnimationPath, PlayerColor::PLAYER_LIMIT_I> flagAnimations;
 
-	CGBoat();
+	CGBoat(IGameCallback * cb);
 	bool isCoastVisitable() const override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
@@ -327,6 +351,8 @@ protected:
 	BoatId getBoatType() const override;
 
 public:
+	using CGObjectInstance::CGObjectInstance;
+
 	template<typename Handler> void serialize(Handler & h, const int version)
 	{
 		h & static_cast<CGObjectInstance&>(*this);
@@ -340,6 +366,8 @@ protected:
 class DLL_LINKAGE CGMagi : public CGObjectInstance
 {
 public:
+	using CGObjectInstance::CGObjectInstance;
+
 	void initObj(CRandomGenerator & rand) override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 
@@ -352,11 +380,15 @@ public:
 class DLL_LINKAGE CGDenOfthieves : public CGObjectInstance
 {
 	void onHeroVisit(const CGHeroInstance * h) const override;
+public:
+	using CGObjectInstance::CGObjectInstance;
 };
 
 class DLL_LINKAGE CGObelisk : public CTeamVisited
 {
 public:
+	using CTeamVisited::CTeamVisited;
+
 	static ui8 obeliskCount; //how many obelisks are on map
 	static std::map<TeamID, ui8> visited; //map: team_id => how many obelisks has been visited
 
@@ -376,6 +408,8 @@ protected:
 class DLL_LINKAGE CGLighthouse : public CGObjectInstance
 {
 public:
+	using CGObjectInstance::CGObjectInstance;
+
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void initObj(CRandomGenerator & rand) override;
 
@@ -392,7 +426,7 @@ protected:
 class DLL_LINKAGE CGTerrainPatch : public CGObjectInstance
 {
 public:
-	CGTerrainPatch() = default;
+	using CGObjectInstance::CGObjectInstance;
 
 	virtual bool isTile2Terrain() const override
 	{
@@ -411,6 +445,8 @@ protected:
 	void fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const override;
 
 public:
+	using CGObjectInstance::CGObjectInstance;
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CGObjectInstance&>(*this);

+ 3 - 2
lib/mapping/CMap.cpp

@@ -160,8 +160,9 @@ bool TerrainTile::isWater() const
 	return terType->isWater();
 }
 
-CMap::CMap()
-	: checksum(0)
+CMap::CMap(IGameCallback * cb)
+	: GameCallbackHolder(cb)
+	, checksum(0)
 	, grailPos(-1, -1, -1)
 	, grailRadius(0)
 	, uidCounter(0)

+ 3 - 2
lib/mapping/CMap.h

@@ -11,6 +11,7 @@
 #pragma once
 
 #include "CMapHeader.h"
+#include "../GameCallbackHolder.h"
 #include "../MetaString.h"
 #include "../mapObjects/MiscObjects.h" // To serialize static props
 #include "../mapObjects/CQuest.h" // To serialize static props
@@ -73,10 +74,10 @@ struct DLL_LINKAGE DisposedHero
 };
 
 /// The map contains the map header, the tiles of the terrain, objects, heroes, towns, rumors...
-class DLL_LINKAGE CMap : public CMapHeader
+class DLL_LINKAGE CMap : public CMapHeader, public GameCallbackHolder
 {
 public:
-	CMap();
+	explicit CMap(IGameCallback *cb);
 	~CMap();
 	void initTerrain();
 

+ 4 - 4
lib/mapping/CMapService.cpp

@@ -30,14 +30,14 @@
 VCMI_LIB_NAMESPACE_BEGIN
 
 
-std::unique_ptr<CMap> CMapService::loadMap(const ResourcePath & name) const
+std::unique_ptr<CMap> CMapService::loadMap(const ResourcePath & name, IGameCallback * cb) const
 {
 	std::string modName = VLC->modh->findResourceOrigin(name);
 	std::string language = VLC->modh->getModLanguage(modName);
 	std::string encoding = Languages::getLanguageOptions(language).encoding;
 
 	auto stream = getStreamFromFS(name);
-	return getMapLoader(stream, name.getName(), modName, encoding)->loadMap();
+	return getMapLoader(stream, name.getName(), modName, encoding)->loadMap(cb);
 }
 
 std::unique_ptr<CMapHeader> CMapService::loadMapHeader(const ResourcePath & name) const
@@ -50,10 +50,10 @@ std::unique_ptr<CMapHeader> CMapService::loadMapHeader(const ResourcePath & name
 	return getMapLoader(stream, name.getName(), modName, encoding)->loadMapHeader();
 }
 
-std::unique_ptr<CMap> CMapService::loadMap(const uint8_t * buffer, int size, const std::string & name,  const std::string & modName, const std::string & encoding) const
+std::unique_ptr<CMap> CMapService::loadMap(const uint8_t * buffer, int size, const std::string & name,  const std::string & modName, const std::string & encoding, IGameCallback * cb) const
 {
 	auto stream = getStreamFromMem(buffer, size);
-	std::unique_ptr<CMap> map(getMapLoader(stream, name, modName, encoding)->loadMap());
+	std::unique_ptr<CMap> map(getMapLoader(stream, name, modName, encoding)->loadMap(cb));
 	std::unique_ptr<CMapHeader> header(map.get());
 
 	//might be original campaign and require patch

+ 7 - 6
lib/mapping/CMapService.h

@@ -10,7 +10,7 @@
 
 #pragma once
 
-#include "../modding/CModInfo.h"
+#include "../modding/ModVerificationInfo.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -22,6 +22,7 @@ class CInputStream;
 
 class IMapLoader;
 class IMapPatcher;
+class IGameCallback;
 
 using ModCompatibilityInfo = std::map<std::string, ModVerificationInfo>;
 
@@ -40,7 +41,7 @@ public:
 	 * @param name the name of the map
 	 * @return a unique ptr to the loaded map class
 	 */
-	virtual std::unique_ptr<CMap> loadMap(const ResourcePath & name) const = 0;
+	virtual std::unique_ptr<CMap> loadMap(const ResourcePath & name, IGameCallback * cb) const = 0;
 
 	/**
 	 * Loads the VCMI/H3 map header specified by the name.
@@ -57,7 +58,7 @@ public:
 	 * @param name indicates name of file that will be used during map header patching
 	 * @return a unique ptr to the loaded map class
 	 */
-	virtual std::unique_ptr<CMap> loadMap(const uint8_t * buffer, int size, const std::string & name, const std::string & modName, const std::string & encoding) const = 0;
+	virtual std::unique_ptr<CMap> loadMap(const uint8_t * buffer, int size, const std::string & name, const std::string & modName, const std::string & encoding, IGameCallback * cb) const = 0;
 
 	/**
 	 * Loads the VCMI/H3 map header from a buffer. This method is temporarily
@@ -82,9 +83,9 @@ public:
 	CMapService() = default;
 	virtual ~CMapService() = default;
 
-	std::unique_ptr<CMap> loadMap(const ResourcePath & name) const override;
+	std::unique_ptr<CMap> loadMap(const ResourcePath & name, IGameCallback * cb) const override;
 	std::unique_ptr<CMapHeader> loadMapHeader(const ResourcePath & name) const override;
-	std::unique_ptr<CMap> loadMap(const uint8_t * buffer, int size, const std::string & name, const std::string & modName, const std::string & encoding) const override;
+	std::unique_ptr<CMap> loadMap(const uint8_t * buffer, int size, const std::string & name, const std::string & modName, const std::string & encoding, IGameCallback * cb) const override;
 	std::unique_ptr<CMapHeader> loadMapHeader(const uint8_t * buffer, int size, const std::string & name, const std::string & modName, const std::string & encoding) const override;
 	void saveMap(const std::unique_ptr<CMap> & map, boost::filesystem::path fullPath) const override;
 	
@@ -142,7 +143,7 @@ public:
 	 *
 	 * @return a unique ptr of the loaded map class
 	 */
-	virtual std::unique_ptr<CMap> loadMap() = 0;
+	virtual std::unique_ptr<CMap> loadMap(IGameCallback * cb) = 0;
 
 	/**
 	 * Loads the VCMI/H3 map header.

+ 23 - 23
lib/mapping/MapFormatH3M.cpp

@@ -71,10 +71,10 @@ CMapLoaderH3M::CMapLoaderH3M(const std::string & mapName, const std::string & mo
 //must be instantiated in .cpp file for access to complete types of all member fields
 CMapLoaderH3M::~CMapLoaderH3M() = default;
 
-std::unique_ptr<CMap> CMapLoaderH3M::loadMap()
+std::unique_ptr<CMap> CMapLoaderH3M::loadMap(IGameCallback * cb)
 {
 	// Init map object by parsing the input buffer
-	map = new CMap();
+	map = new CMap(cb);
 	mapHeader = std::unique_ptr<CMapHeader>(dynamic_cast<CMapHeader *>(map));
 	init();
 
@@ -831,7 +831,7 @@ void CMapLoaderH3M::readPredefinedHeroes()
 		if(!custom)
 			continue;
 
-		auto * hero = new CGHeroInstance();
+		auto * hero = new CGHeroInstance(map->cb);
 		hero->ID = Obj::HERO;
 		hero->subID = heroID;
 
@@ -1005,7 +1005,7 @@ void CMapLoaderH3M::readObjectTemplates()
 
 CGObjectInstance * CMapLoaderH3M::readEvent(const int3 & mapPosition, const ObjectInstanceID & idToBeGiven)
 {
-	auto * object = new CGEvent();
+	auto * object = new CGEvent(map->cb);
 
 	readBoxContent(object, mapPosition, idToBeGiven);
 
@@ -1025,7 +1025,7 @@ CGObjectInstance * CMapLoaderH3M::readEvent(const int3 & mapPosition, const Obje
 
 CGObjectInstance * CMapLoaderH3M::readPandora(const int3 & mapPosition, const ObjectInstanceID & idToBeGiven)
 {
-	auto * object = new CGPandoraBox();
+	auto * object = new CGPandoraBox(map->cb);
 	readBoxContent(object, mapPosition, idToBeGiven);
 	return object;
 }
@@ -1080,7 +1080,7 @@ void CMapLoaderH3M::readBoxContent(CGPandoraBox * object, const int3 & mapPositi
 
 CGObjectInstance * CMapLoaderH3M::readMonster(const int3 & mapPosition, const ObjectInstanceID & objectInstanceID)
 {
-	auto * object = new CGCreature();
+	auto * object = new CGCreature(map->cb);
 
 	if(features.levelAB)
 	{
@@ -1134,7 +1134,7 @@ CGObjectInstance * CMapLoaderH3M::readMonster(const int3 & mapPosition, const Ob
 
 CGObjectInstance * CMapLoaderH3M::readSign(const int3 & mapPosition)
 {
-	auto * object = new CGSignBottle();
+	auto * object = new CGSignBottle(map->cb);
 	object->message.appendTextID(readLocalizedString(TextIdentifier("sign", mapPosition.x, mapPosition.y, mapPosition.z, "message")));
 	reader->skipZero(4);
 	return object;
@@ -1260,7 +1260,7 @@ CGObjectInstance * CMapLoaderH3M::readScholar(const int3 & position, std::shared
 
 CGObjectInstance * CMapLoaderH3M::readGarrison(const int3 & mapPosition)
 {
-	auto * object = new CGGarrison();
+	auto * object = new CGGarrison(map->cb);
 
 	setOwnerAndValidate(mapPosition, object, reader->readPlayer32());
 	readCreatureSet(object, 7);
@@ -1277,7 +1277,7 @@ CGObjectInstance * CMapLoaderH3M::readArtifact(const int3 & mapPosition, std::sh
 {
 	ArtifactID artID = ArtifactID::NONE; //random, set later
 	SpellID spellID = SpellID::NONE;
-	auto * object = new CGArtifact();
+	auto * object = new CGArtifact(map->cb);
 
 	readMessageAndGuards(object->message, object, mapPosition);
 
@@ -1298,7 +1298,7 @@ CGObjectInstance * CMapLoaderH3M::readArtifact(const int3 & mapPosition, std::sh
 
 CGObjectInstance * CMapLoaderH3M::readResource(const int3 & mapPosition, std::shared_ptr<const ObjectTemplate> objectTemplate)
 {
-	auto * object = new CGResource();
+	auto * object = new CGResource(map->cb);
 
 	readMessageAndGuards(object->message, object, mapPosition);
 
@@ -1314,7 +1314,7 @@ CGObjectInstance * CMapLoaderH3M::readResource(const int3 & mapPosition, std::sh
 
 CGObjectInstance * CMapLoaderH3M::readMine(const int3 & mapPosition, std::shared_ptr<const ObjectTemplate> objectTemplate)
 {
-	auto * object = new CGMine();
+	auto * object = new CGMine(map->cb);
 	if(objectTemplate->subid < 7)
 	{
 		setOwnerAndValidate(mapPosition, object, reader->readPlayer32());
@@ -1329,14 +1329,14 @@ CGObjectInstance * CMapLoaderH3M::readMine(const int3 & mapPosition, std::shared
 
 CGObjectInstance * CMapLoaderH3M::readDwelling(const int3 & position)
 {
-	auto * object = new CGDwelling();
+	auto * object = new CGDwelling(map->cb);
 	setOwnerAndValidate(position, object, reader->readPlayer32());
 	return object;
 }
 
 CGObjectInstance * CMapLoaderH3M::readDwellingRandom(const int3 & mapPosition, std::shared_ptr<const ObjectTemplate> objectTemplate)
 {
-	auto * object = new CGDwelling();
+	auto * object = new CGDwelling(map->cb);
 
 	setOwnerAndValidate(mapPosition, object, reader->readPlayer32());
 
@@ -1395,7 +1395,7 @@ CGObjectInstance * CMapLoaderH3M::readShrine(const int3 & position, std::shared_
 
 CGObjectInstance * CMapLoaderH3M::readHeroPlaceholder(const int3 & mapPosition)
 {
-	auto * object = new CGHeroPlaceholder();
+	auto * object = new CGHeroPlaceholder(map->cb);
 
 	setOwnerAndValidate(mapPosition, object, reader->readPlayer());
 
@@ -1433,23 +1433,23 @@ CGObjectInstance * CMapLoaderH3M::readGrail(const int3 & mapPosition, std::share
 CGObjectInstance * CMapLoaderH3M::readGeneric(const int3 & mapPosition, std::shared_ptr<const ObjectTemplate> objectTemplate)
 {
 	if(VLC->objtypeh->knownSubObjects(objectTemplate->id).count(objectTemplate->subid))
-		return VLC->objtypeh->getHandlerFor(objectTemplate->id, objectTemplate->subid)->create(objectTemplate);
+		return VLC->objtypeh->getHandlerFor(objectTemplate->id, objectTemplate->subid)->create(map->cb, objectTemplate);
 
 	logGlobal->warn("Map '%s': Unrecognized object %d:%d ('%s') at %s found!", mapName, objectTemplate->id.toEnum(), objectTemplate->subid, objectTemplate->animationFile.getOriginalName(), mapPosition.toString());
-	return new CGObjectInstance();
+	return new CGObjectInstance(map->cb);
 }
 
 CGObjectInstance * CMapLoaderH3M::readPyramid(const int3 & mapPosition, std::shared_ptr<const ObjectTemplate> objectTemplate)
 {
 	if(objectTemplate->subid == 0)
-		return new CBank();
+		return new CBank(map->cb);
 
-	return new CGObjectInstance();
+	return new CGObjectInstance(map->cb);
 }
 
 CGObjectInstance * CMapLoaderH3M::readQuestGuard(const int3 & mapPosition)
 {
-	auto * guard = new CGQuestGuard();
+	auto * guard = new CGQuestGuard(map->cb);
 	readQuest(guard, mapPosition);
 	return guard;
 }
@@ -1463,7 +1463,7 @@ CGObjectInstance * CMapLoaderH3M::readShipyard(const int3 & mapPosition, std::sh
 
 CGObjectInstance * CMapLoaderH3M::readLighthouse(const int3 & mapPosition)
 {
-	auto * object = new CGLighthouse();
+	auto * object = new CGLighthouse(map->cb);
 	setOwnerAndValidate(mapPosition, object, reader->readPlayer32());
 	return object;
 }
@@ -1733,7 +1733,7 @@ void CMapLoaderH3M::setOwnerAndValidate(const int3 & mapPosition, CGObjectInstan
 
 CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const ObjectInstanceID & objectInstanceID)
 {
-	auto * object = new CGHeroInstance();
+	auto * object = new CGHeroInstance(map->cb);
 
 	if(features.levelAB)
 	{
@@ -1892,7 +1892,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec
 
 CGObjectInstance * CMapLoaderH3M::readSeerHut(const int3 & position, const ObjectInstanceID & idToBeGiven)
 {
-	auto * hut = new CGSeerHut();
+	auto * hut = new CGSeerHut(map->cb);
 
 	uint32_t questsCount = 1;
 
@@ -2176,7 +2176,7 @@ int CMapLoaderH3M::readQuest(IQuestObject * guard, const int3 & position)
 
 CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_ptr<const ObjectTemplate> objectTemplate)
 {
-	auto * object = new CGTownInstance();
+	auto * object = new CGTownInstance(map->cb);
 	if(features.levelAB)
 		object->identifier = reader->readUInt32();
 

+ 1 - 1
lib/mapping/MapFormatH3M.h

@@ -55,7 +55,7 @@ public:
 	 *
 	 * @return a unique ptr of the loaded map class
 	 */
-	std::unique_ptr<CMap> loadMap() override;
+	std::unique_ptr<CMap> loadMap(IGameCallback * cb) override;
 
 	/**
 	 * Loads the VCMI/H3 map header.

+ 11 - 11
lib/mapping/MapFormatJson.cpp

@@ -677,19 +677,19 @@ void CMapFormatJson::serializeTimedEvents(JsonSerializeFormat & handler)
 
 void CMapFormatJson::serializePredefinedHeroes(JsonSerializeFormat & handler)
 {
-    //todo:serializePredefinedHeroes
+	//todo:serializePredefinedHeroes
 
-    if(handler.saving)
+	if(handler.saving)
 	{
 		if(!map->predefinedHeroes.empty())
 		{
 			auto predefinedHeroes = handler.enterStruct("predefinedHeroes");
 
-            for(auto & hero : map->predefinedHeroes)
+			for(auto & hero : map->predefinedHeroes)
 			{
-                auto predefinedHero = handler.enterStruct(hero->getHeroTypeName());
+				auto predefinedHero = handler.enterStruct(hero->getHeroTypeName());
 
-                hero->serializeJsonDefinition(handler);
+				hero->serializeJsonDefinition(handler);
 			}
 		}
 	}
@@ -697,13 +697,13 @@ void CMapFormatJson::serializePredefinedHeroes(JsonSerializeFormat & handler)
 	{
 		auto predefinedHeroes = handler.enterStruct("predefinedHeroes");
 
-        const JsonNode & data = handler.getCurrent();
+		const JsonNode & data = handler.getCurrent();
 
-        for(const auto & p : data.Struct())
+		for(const auto & p : data.Struct())
 		{
 			auto predefinedHero = handler.enterStruct(p.first);
 
-			auto * hero = new CGHeroInstance();
+			auto * hero = new CGHeroInstance(map->cb);
 			hero->ID = Obj::HERO;
 			hero->setHeroTypeName(p.first);
 			hero->serializeJsonDefinition(handler);
@@ -777,10 +777,10 @@ CMapLoaderJson::CMapLoaderJson(CInputStream * stream)
 {
 }
 
-std::unique_ptr<CMap> CMapLoaderJson::loadMap()
+std::unique_ptr<CMap> CMapLoaderJson::loadMap(IGameCallback * cb)
 {
 	LOG_TRACE(logGlobal);
-	auto result = std::make_unique<CMap>();
+	auto result = std::make_unique<CMap>(cb);
 	map = result.get();
 	mapHeader = map;
 	readMap();
@@ -1081,7 +1081,7 @@ void CMapLoaderJson::MapObjectLoader::construct()
 	appearance->readJson(configuration["template"], false);
 
 	// Will be destroyed soon and replaced with shared template
-	instance = handler->create(std::shared_ptr<const ObjectTemplate>(appearance));
+	instance = handler->create(owner->map->cb, std::shared_ptr<const ObjectTemplate>(appearance));
 
 	instance->id = ObjectInstanceID(static_cast<si32>(owner->map->objects.size()));
 	instance->instanceName = jsonKey;

+ 1 - 1
lib/mapping/MapFormatJson.h

@@ -168,7 +168,7 @@ public:
 	 *
 	 * @return a unique ptr of the loaded map class
 	 */
-	std::unique_ptr<CMap> loadMap() override;
+	std::unique_ptr<CMap> loadMap(IGameCallback * cb) override;
 
 	/**
 	 * Loads the VCMI/Json map header.

+ 1 - 1
lib/mapping/ObstacleProxy.cpp

@@ -79,7 +79,7 @@ int ObstacleProxy::getWeightedObjects(const int3 & tile, CRandomGenerator & rand
 		for(const auto & temp : shuffledObstacles)
 		{
 			auto handler = VLC->objtypeh->getHandlerFor(temp->id, temp->subid);
-			auto * obj = handler->create(temp);
+			auto * obj = handler->create(nullptr, temp);
 			allObjects.emplace_back(*obj);
 			rmg::Object * rmgObject = &allObjects.back();
 			for(const auto & offset : obj->getBlockedOffsets())

+ 1 - 1
lib/networkPacks/NetPacksLib.cpp

@@ -1497,7 +1497,7 @@ void NewObject::applyGs(CGameState *gs)
 
 	auto handler = VLC->objtypeh->getHandlerFor(ID, subID);
 
-	CGObjectInstance * o = handler->create();
+	CGObjectInstance * o = handler->create(gs->callback, nullptr);
 	handler->configureObject(o, gs->getRandomGenerator());
 	assert(o->ID == this->ID);
 	

+ 45 - 41
lib/rewardable/Info.cpp

@@ -123,28 +123,29 @@ Rewardable::LimitersList Rewardable::Info::configureSublimiters(Rewardable::Conf
 void Rewardable::Info::configureLimiter(Rewardable::Configuration & object, CRandomGenerator & rng, Rewardable::Limiter & limiter, const JsonNode & source) const
 {
 	auto const & variables = object.variables.values;
+	JsonRandom randomizer(nullptr);
 
-	limiter.dayOfWeek = JsonRandom::loadValue(source["dayOfWeek"], rng, variables);
-	limiter.daysPassed = JsonRandom::loadValue(source["daysPassed"], rng, variables);
-	limiter.heroExperience = JsonRandom::loadValue(source["heroExperience"], rng, variables);
-	limiter.heroLevel = JsonRandom::loadValue(source["heroLevel"], rng, variables);
+	limiter.dayOfWeek = randomizer.loadValue(source["dayOfWeek"], rng, variables);
+	limiter.daysPassed = randomizer.loadValue(source["daysPassed"], rng, variables);
+	limiter.heroExperience = randomizer.loadValue(source["heroExperience"], rng, variables);
+	limiter.heroLevel = randomizer.loadValue(source["heroLevel"], rng, variables);
 	limiter.canLearnSkills = source["canLearnSkills"].Bool();
 
-	limiter.manaPercentage = JsonRandom::loadValue(source["manaPercentage"], rng, variables);
-	limiter.manaPoints = JsonRandom::loadValue(source["manaPoints"], rng, variables);
+	limiter.manaPercentage = randomizer.loadValue(source["manaPercentage"], rng, variables);
+	limiter.manaPoints = randomizer.loadValue(source["manaPoints"], rng, variables);
 
-	limiter.resources = JsonRandom::loadResources(source["resources"], rng, variables);
+	limiter.resources = randomizer.loadResources(source["resources"], rng, variables);
 
-	limiter.primary = JsonRandom::loadPrimaries(source["primary"], rng, variables);
-	limiter.secondary = JsonRandom::loadSecondaries(source["secondary"], rng, variables);
-	limiter.artifacts = JsonRandom::loadArtifacts(source["artifacts"], rng, variables);
-	limiter.spells  = JsonRandom::loadSpells(source["spells"], rng, variables);
-	limiter.canLearnSpells  = JsonRandom::loadSpells(source["canLearnSpells"], rng, variables);
-	limiter.creatures = JsonRandom::loadCreatures(source["creatures"], rng, variables);
+	limiter.primary = randomizer.loadPrimaries(source["primary"], rng, variables);
+	limiter.secondary = randomizer.loadSecondaries(source["secondary"], rng, variables);
+	limiter.artifacts = randomizer.loadArtifacts(source["artifacts"], rng, variables);
+	limiter.spells  = randomizer.loadSpells(source["spells"], rng, variables);
+	limiter.canLearnSpells  = randomizer.loadSpells(source["canLearnSpells"], rng, variables);
+	limiter.creatures = randomizer.loadCreatures(source["creatures"], rng, variables);
 	
-	limiter.players = JsonRandom::loadColors(source["colors"], rng, variables);
-	limiter.heroes = JsonRandom::loadHeroes(source["heroes"], rng);
-	limiter.heroClasses = JsonRandom::loadHeroClasses(source["heroClasses"], rng);
+	limiter.players = randomizer.loadColors(source["colors"], rng, variables);
+	limiter.heroes = randomizer.loadHeroes(source["heroes"], rng);
+	limiter.heroClasses = randomizer.loadHeroClasses(source["heroClasses"], rng);
 
 	limiter.allOf  = configureSublimiters(object, rng, source["allOf"] );
 	limiter.anyOf  = configureSublimiters(object, rng, source["anyOf"] );
@@ -154,31 +155,32 @@ void Rewardable::Info::configureLimiter(Rewardable::Configuration & object, CRan
 void Rewardable::Info::configureReward(Rewardable::Configuration & object, CRandomGenerator & rng, Rewardable::Reward & reward, const JsonNode & source) const
 {
 	auto const & variables = object.variables.values;
+	JsonRandom randomizer(nullptr);
 
-	reward.resources = JsonRandom::loadResources(source["resources"], rng, variables);
+	reward.resources = randomizer.loadResources(source["resources"], rng, variables);
 
-	reward.heroExperience = JsonRandom::loadValue(source["heroExperience"], rng, variables);
-	reward.heroLevel = JsonRandom::loadValue(source["heroLevel"], rng, variables);
+	reward.heroExperience = randomizer.loadValue(source["heroExperience"], rng, variables);
+	reward.heroLevel = randomizer.loadValue(source["heroLevel"], rng, variables);
 
-	reward.manaDiff = JsonRandom::loadValue(source["manaPoints"], rng, variables);
-	reward.manaOverflowFactor = JsonRandom::loadValue(source["manaOverflowFactor"], rng, variables);
-	reward.manaPercentage = JsonRandom::loadValue(source["manaPercentage"], rng, variables, -1);
+	reward.manaDiff = randomizer.loadValue(source["manaPoints"], rng, variables);
+	reward.manaOverflowFactor = randomizer.loadValue(source["manaOverflowFactor"], rng, variables);
+	reward.manaPercentage = randomizer.loadValue(source["manaPercentage"], rng, variables, -1);
 
-	reward.movePoints = JsonRandom::loadValue(source["movePoints"], rng, variables);
-	reward.movePercentage = JsonRandom::loadValue(source["movePercentage"], rng, variables, -1);
+	reward.movePoints = randomizer.loadValue(source["movePoints"], rng, variables);
+	reward.movePercentage = randomizer.loadValue(source["movePercentage"], rng, variables, -1);
 
 	reward.removeObject = source["removeObject"].Bool();
-	reward.bonuses = JsonRandom::loadBonuses(source["bonuses"]);
+	reward.bonuses = randomizer.loadBonuses(source["bonuses"]);
 
-	reward.primary = JsonRandom::loadPrimaries(source["primary"], rng, variables);
-	reward.secondary = JsonRandom::loadSecondaries(source["secondary"], rng, variables);
+	reward.primary = randomizer.loadPrimaries(source["primary"], rng, variables);
+	reward.secondary = randomizer.loadSecondaries(source["secondary"], rng, variables);
 
-	reward.artifacts = JsonRandom::loadArtifacts(source["artifacts"], rng, variables);
-	reward.spells = JsonRandom::loadSpells(source["spells"], rng, variables);
-	reward.creatures = JsonRandom::loadCreatures(source["creatures"], rng, variables);
+	reward.artifacts = randomizer.loadArtifacts(source["artifacts"], rng, variables);
+	reward.spells = randomizer.loadSpells(source["spells"], rng, variables);
+	reward.creatures = randomizer.loadCreatures(source["creatures"], rng, variables);
 	if(!source["spellCast"].isNull() && source["spellCast"].isStruct())
 	{
-		reward.spellCast.first = JsonRandom::loadSpell(source["spellCast"]["spell"], rng, variables);
+		reward.spellCast.first = randomizer.loadSpell(source["spellCast"]["spell"], rng, variables);
 		reward.spellCast.second = source["spellCast"]["schoolLevel"].Integer();
 	}
 
@@ -187,13 +189,13 @@ void Rewardable::Info::configureReward(Rewardable::Configuration & object, CRand
 		auto const & entry = source["revealTiles"];
 
 		reward.revealTiles = RewardRevealTiles();
-		reward.revealTiles->radius = JsonRandom::loadValue(entry["radius"], rng, variables);
+		reward.revealTiles->radius = randomizer.loadValue(entry["radius"], rng, variables);
 		reward.revealTiles->hide = entry["hide"].Bool();
 
-		reward.revealTiles->scoreSurface = JsonRandom::loadValue(entry["surface"], rng, variables);
-		reward.revealTiles->scoreSubterra = JsonRandom::loadValue(entry["subterra"], rng, variables);
-		reward.revealTiles->scoreWater = JsonRandom::loadValue(entry["water"], rng, variables);
-		reward.revealTiles->scoreRock = JsonRandom::loadValue(entry["rock"], rng, variables);
+		reward.revealTiles->scoreSurface = randomizer.loadValue(entry["surface"], rng, variables);
+		reward.revealTiles->scoreSubterra = randomizer.loadValue(entry["subterra"], rng, variables);
+		reward.revealTiles->scoreWater = randomizer.loadValue(entry["water"], rng, variables);
+		reward.revealTiles->scoreRock = randomizer.loadValue(entry["rock"], rng, variables);
 	}
 
 	for ( auto node : source["changeCreatures"].Struct() )
@@ -216,6 +218,8 @@ void Rewardable::Info::configureResetInfo(Rewardable::Configuration & object, CR
 
 void Rewardable::Info::configureVariables(Rewardable::Configuration & object, CRandomGenerator & rng, const JsonNode & source) const
 {
+	JsonRandom randomizer(nullptr);
+
 	for(const auto & category : source.Struct())
 	{
 		for(const auto & entry : category.second.Struct())
@@ -225,19 +229,19 @@ void Rewardable::Info::configureVariables(Rewardable::Configuration & object, CR
 			int32_t value = -1;
 
 			if (category.first == "number")
-				value = JsonRandom::loadValue(input, rng, object.variables.values);
+				value = randomizer.loadValue(input, rng, object.variables.values);
 
 			if (category.first == "artifact")
-				value = JsonRandom::loadArtifact(input, rng, object.variables.values).getNum();
+				value = randomizer.loadArtifact(input, rng, object.variables.values).getNum();
 
 			if (category.first == "spell")
-				value = JsonRandom::loadSpell(input, rng, object.variables.values).getNum();
+				value = randomizer.loadSpell(input, rng, object.variables.values).getNum();
 
 			if (category.first == "primarySkill")
-				value = JsonRandom::loadPrimary(input, rng, object.variables.values).getNum();
+				value = randomizer.loadPrimary(input, rng, object.variables.values).getNum();
 
 			if (category.first == "secondarySkill")
-				value = JsonRandom::loadSecondary(input, rng, object.variables.values).getNum();
+				value = randomizer.loadSecondary(input, rng, object.variables.values).getNum();
 
 			object.initVariable(category.first, entry.first, value);
 		}

+ 3 - 3
lib/rewardable/Limiter.cpp

@@ -69,13 +69,13 @@ bool Rewardable::Limiter::heroAllowed(const CGHeroInstance * hero) const
 {
 	if(dayOfWeek != 0)
 	{
-		if (IObjectInterface::cb->getDate(Date::DAY_OF_WEEK) != dayOfWeek)
+		if (hero->cb->getDate(Date::DAY_OF_WEEK) != dayOfWeek)
 			return false;
 	}
 
 	if(daysPassed != 0)
 	{
-		if (IObjectInterface::cb->getDate(Date::DAY) < daysPassed)
+		if (hero->cb->getDate(Date::DAY) < daysPassed)
 			return false;
 	}
 
@@ -92,7 +92,7 @@ bool Rewardable::Limiter::heroAllowed(const CGHeroInstance * hero) const
 			return false;
 	}
 
-	if(!IObjectInterface::cb->getPlayerState(hero->tempOwner)->resources.canAfford(resources))
+	if(!hero->cb->getPlayerState(hero->tempOwner)->resources.canAfford(resources))
 		return false;
 
 	if(heroLevel > static_cast<si32>(hero->level))

+ 2 - 2
lib/rmg/CMapGenerator.cpp

@@ -33,14 +33,14 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-CMapGenerator::CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed) :
+CMapGenerator::CMapGenerator(CMapGenOptions& mapGenOptions, IGameCallback * cb, int RandomSeed) :
 	mapGenOptions(mapGenOptions), randomSeed(RandomSeed),
 	monolithIndex(0)
 {
 	loadConfig();
 	rand.setSeed(this->randomSeed);
 	mapGenOptions.finalize(rand);
-	map = std::make_unique<RmgMap>(mapGenOptions);
+	map = std::make_unique<RmgMap>(mapGenOptions, cb);
 	placer = std::make_shared<CZonePlacer>(*map);
 }
 

+ 2 - 1
lib/rmg/CMapGenerator.h

@@ -26,6 +26,7 @@ class RmgMap;
 class CMap;
 class Zone;
 class CZonePlacer;
+class IGameCallback;
 
 using JsonVector = std::vector<JsonNode>;
 
@@ -56,7 +57,7 @@ public:
 		bool singleThread;
 	};
 	
-	explicit CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed = std::time(nullptr));
+	explicit CMapGenerator(CMapGenOptions& mapGenOptions, IGameCallback * cb, int RandomSeed);
 	~CMapGenerator(); // required due to std::unique_ptr
 	
 	const Config & getConfig() const;

+ 2 - 2
lib/rmg/RmgMap.cpp

@@ -38,10 +38,10 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-RmgMap::RmgMap(const CMapGenOptions& mapGenOptions) :
+RmgMap::RmgMap(const CMapGenOptions& mapGenOptions, IGameCallback * cb) :
 	mapGenOptions(mapGenOptions), zonesTotal(0)
 {
-	mapInstance = std::make_unique<CMap>();
+	mapInstance = std::make_unique<CMap>(cb);
 	mapProxy = std::make_shared<MapProxy>(*this);
 	getEditManager()->getUndoManager().setUndoRedoLimit(0);
 }

+ 1 - 1
lib/rmg/RmgMap.h

@@ -33,7 +33,7 @@ public:
 	std::shared_ptr<MapProxy> getMapProxy() const;
 	CMap & getMap(const CMapGenerator *) const; //limited access
 	
-	RmgMap(const CMapGenOptions& mapGenOptions);
+	RmgMap(const CMapGenOptions& mapGenOptions, IGameCallback * cb);
 	~RmgMap() = default;
 
 	CMapEditManager* getEditManager() const;

+ 4 - 4
lib/rmg/modificators/ConnectionsPlacer.cpp

@@ -314,8 +314,8 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
 			auto & managerOther = *otherZone->getModificator<ObjectManager>();
 			
 			auto factory = VLC->objtypeh->getHandlerFor(Obj::SUBTERRANEAN_GATE, 0);
-			auto * gate1 = factory->create();
-			auto * gate2 = factory->create();
+			auto * gate1 = factory->create(map.mapInstance->cb, nullptr);
+			auto * gate2 = factory->create(map.mapInstance->cb, nullptr);
 			rmg::Object rmgGate1(*gate1);
 			rmg::Object rmgGate2(*gate2);
 			rmgGate1.setTemplate(zone.getTerrainType(), zone.getRand());
@@ -368,8 +368,8 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
 	if(!success)
 	{
 		auto factory = VLC->objtypeh->getHandlerFor(Obj::MONOLITH_TWO_WAY, generator.getNextMonlithIndex());
-		auto * teleport1 = factory->create();
-		auto * teleport2 = factory->create();
+		auto * teleport1 = factory->create(map.mapInstance->cb, nullptr);
+		auto * teleport2 = factory->create(map.mapInstance->cb, nullptr);
 
 		RequiredObjectInfo obj1(teleport1, connection.getGuardStrength(), allowRoad);
 		RequiredObjectInfo obj2(teleport2, connection.getGuardStrength(), allowRoad);

+ 2 - 2
lib/rmg/modificators/MinePlacer.cpp

@@ -56,7 +56,7 @@ bool MinePlacer::placeMines(ObjectManager & manager)
 		{
 			auto mineHandler = VLC->objtypeh->getHandlerFor(Obj::MINE, res);
 			const auto & rmginfo = mineHandler->getRMGInfo();
-			auto * mine = dynamic_cast<CGMine *>(mineHandler->create());
+			auto * mine = dynamic_cast<CGMine *>(mineHandler->create(map.mapInstance->cb, nullptr));
 			mine->producedResource = res;
 			mine->tempOwner = PlayerColor::NEUTRAL;
 			mine->producedQuantity = mine->defaultResProduction();
@@ -87,7 +87,7 @@ bool MinePlacer::placeMines(ObjectManager & manager)
 		{
 			for(int rc = zone.getRand().nextInt(1, extraRes); rc > 0; --rc)
 			{
-				auto * resource = dynamic_cast<CGResource *>(VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create());
+				auto * resource = dynamic_cast<CGResource *>(VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create(map.mapInstance->cb, nullptr));
 				resource->amount = CGResource::RANDOM_AMOUNT;
 
 				RequiredObjectInfo roi;

+ 2 - 2
lib/rmg/modificators/ObjectDistributor.cpp

@@ -79,9 +79,9 @@ void ObjectDistributor::distributeLimitedObjects()
 					RandomGeneratorUtil::randomShuffle(matchingZones, zone.getRand());
 					for (auto& zone : matchingZones)
 					{
-						oi.generateObject = [primaryID, secondaryID]() -> CGObjectInstance *
+						oi.generateObject = [this, primaryID, secondaryID]() -> CGObjectInstance *
 						{
-							return VLC->objtypeh->getHandlerFor(primaryID, secondaryID)->create();
+							return VLC->objtypeh->getHandlerFor(primaryID, secondaryID)->create(map.mapInstance->cb, nullptr);
 						};
 						
 						oi.value = rmgInfo.value;

+ 1 - 1
lib/rmg/modificators/ObjectManager.cpp

@@ -716,7 +716,7 @@ CGCreature * ObjectManager::chooseGuard(si32 strength, bool zoneGuard)
 	
 	auto guardFactory = VLC->objtypeh->getHandlerFor(Obj::MONSTER, creId);
 
-	auto * guard = dynamic_cast<CGCreature *>(guardFactory->create());
+	auto * guard = dynamic_cast<CGCreature *>(guardFactory->create(map.mapInstance->cb, nullptr));
 	guard->character = CGCreature::HOSTILE;
 	auto * hlp = new CStackInstance(creId, amount);
 	//will be set during initialization

+ 1 - 1
lib/rmg/modificators/QuestArtifactPlacer.cpp

@@ -99,7 +99,7 @@ void QuestArtifactPlacer::placeQuestArtifacts(CRandomGenerator & rand)
 
 			//Update appearance. Terrain is irrelevant.
 			auto handler = VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, artifactToPlace);
-			auto newObj = handler->create();
+			auto newObj = handler->create(map.mapInstance->cb, nullptr);
 			auto templates = handler->getTemplates();
 			//artifactToReplace->appearance = templates.front();
 			newObj->appearance  = templates.front();

+ 1 - 1
lib/rmg/modificators/RiverPlacer.cpp

@@ -395,7 +395,7 @@ void RiverPlacer::connectRiver(const int3 & tile)
 			{
 				if(templ->animationFile == targetTemplateName)
 				{
-					auto * obj = handler->create(templ);
+					auto * obj = handler->create(map.mapInstance->cb, templ);
 					rmg::Object deltaObj(*obj, deltaPositions[pos]);
 					deltaObj.finalize(map, zone.getRand());
 				}

+ 2 - 2
lib/rmg/modificators/TownPlacer.cpp

@@ -71,7 +71,7 @@ void TownPlacer::placeTowns(ObjectManager & manager)
 		
 		auto townFactory = VLC->objtypeh->getHandlerFor(Obj::TOWN, zone.getTownType());
 
-		CGTownInstance * town = dynamic_cast<CGTownInstance *>(townFactory->create());
+		CGTownInstance * town = dynamic_cast<CGTownInstance *>(townFactory->create(map.mapInstance->cb, nullptr));
 		town->tempOwner = player;
 		town->builtBuildings.insert(BuildingID::FORT);
 		town->builtBuildings.insert(BuildingID::DEFAULT);
@@ -193,7 +193,7 @@ void TownPlacer::addNewTowns(int count, bool hasFort, const PlayerColor & player
 		}
 		
 		auto townFactory = VLC->objtypeh->getHandlerFor(Obj::TOWN, subType);
-		auto * town = dynamic_cast<CGTownInstance *>(townFactory->create());
+		auto * town = dynamic_cast<CGTownInstance *>(townFactory->create(map.mapInstance->cb, nullptr));
 		town->ID = Obj::TOWN;
 		
 		town->tempOwner = player;

+ 18 - 18
lib/rmg/modificators/TreasurePlacer.cpp

@@ -80,9 +80,9 @@ void TreasurePlacer::addAllPossibleObjects()
 					continue;
 				}
 
-				oi.generateObject = [primaryID, secondaryID]() -> CGObjectInstance *
+				oi.generateObject = [this, primaryID, secondaryID]() -> CGObjectInstance *
 				{
-					return VLC->objtypeh->getHandlerFor(primaryID, secondaryID)->create();
+					return VLC->objtypeh->getHandlerFor(primaryID, secondaryID)->create(map.mapInstance->cb, nullptr);
 				};
 				oi.value = rmgInfo.value;
 				oi.probability = rmgInfo.rarity;
@@ -133,7 +133,7 @@ void TreasurePlacer::addAllPossibleObjects()
 				};
 
 				auto factory = VLC->objtypeh->getHandlerFor(Obj::PRISON, 0);
-				auto* obj = dynamic_cast<CGHeroInstance*>(factory->create());
+				auto* obj = dynamic_cast<CGHeroInstance*>(factory->create(map.mapInstance->cb, nullptr));
 
 				obj->setHeroType(hid); //will be initialized later
 				obj->exp = generator.getConfig().prisonExperience[i];
@@ -197,9 +197,9 @@ void TreasurePlacer::addAllPossibleObjects()
 				oi.value = static_cast<ui32>(cre->getAIValue() * cre->getGrowth() * (1 + (nativeZonesCount / map.getTotalZoneCount()) + (nativeZonesCount / 2)));
 				oi.probability = 40;
 				
-				oi.generateObject = [secondaryID, dwellingType]() -> CGObjectInstance *
+				oi.generateObject = [this, secondaryID, dwellingType]() -> CGObjectInstance *
 				{
-					auto * obj = VLC->objtypeh->getHandlerFor(dwellingType, secondaryID)->create();
+					auto * obj = VLC->objtypeh->getHandlerFor(dwellingType, secondaryID)->create(map.mapInstance->cb, nullptr);
 					obj->tempOwner = PlayerColor::NEUTRAL;
 					return obj;
 				};
@@ -215,7 +215,7 @@ void TreasurePlacer::addAllPossibleObjects()
 		oi.generateObject = [i, this]() -> CGObjectInstance *
 		{
 			auto factory = VLC->objtypeh->getHandlerFor(Obj::SPELL_SCROLL, 0);
-			auto * obj = dynamic_cast<CGArtifact *>(factory->create());
+			auto * obj = dynamic_cast<CGArtifact *>(factory->create(map.mapInstance->cb, nullptr));
 			std::vector<SpellID> out;
 			
 			for(auto spell : VLC->spellh->objects) //spellh size appears to be greater (?)
@@ -239,10 +239,10 @@ void TreasurePlacer::addAllPossibleObjects()
 	//pandora box with gold
 	for(int i = 1; i < 5; i++)
 	{
-		oi.generateObject = [i]() -> CGObjectInstance *
+		oi.generateObject = [this, i]() -> CGObjectInstance *
 		{
 			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
-			auto * obj = dynamic_cast<CGPandoraBox *>(factory->create());
+			auto * obj = dynamic_cast<CGPandoraBox *>(factory->create(map.mapInstance->cb, nullptr));
 			
 			Rewardable::VisitInfo reward;
 			reward.reward.resources[EGameResID::GOLD] = i * 5000;
@@ -261,10 +261,10 @@ void TreasurePlacer::addAllPossibleObjects()
 	//pandora box with experience
 	for(int i = 1; i < 5; i++)
 	{
-		oi.generateObject = [i]() -> CGObjectInstance *
+		oi.generateObject = [this, i]() -> CGObjectInstance *
 		{
 			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
-			auto * obj = dynamic_cast<CGPandoraBox *>(factory->create());
+			auto * obj = dynamic_cast<CGPandoraBox *>(factory->create(map.mapInstance->cb, nullptr));
 			
 			Rewardable::VisitInfo reward;
 			reward.reward.heroExperience = i * 5000;
@@ -323,10 +323,10 @@ void TreasurePlacer::addAllPossibleObjects()
 		if(!creaturesAmount)
 			continue;
 		
-		oi.generateObject = [creature, creaturesAmount]() -> CGObjectInstance *
+		oi.generateObject = [this, creature, creaturesAmount]() -> CGObjectInstance *
 		{
 			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
-			auto * obj = dynamic_cast<CGPandoraBox *>(factory->create());
+			auto * obj = dynamic_cast<CGPandoraBox *>(factory->create(map.mapInstance->cb, nullptr));
 			
 			Rewardable::VisitInfo reward;
 			reward.reward.creatures.emplace_back(creature, creaturesAmount);
@@ -348,7 +348,7 @@ void TreasurePlacer::addAllPossibleObjects()
 		oi.generateObject = [i, this]() -> CGObjectInstance *
 		{
 			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
-			auto * obj = dynamic_cast<CGPandoraBox *>(factory->create());
+			auto * obj = dynamic_cast<CGPandoraBox *>(factory->create(map.mapInstance->cb, nullptr));
 
 			std::vector <const CSpell *> spells;
 			for(auto spell : VLC->spellh->objects)
@@ -381,7 +381,7 @@ void TreasurePlacer::addAllPossibleObjects()
 		oi.generateObject = [i, this]() -> CGObjectInstance *
 		{
 			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
-			auto * obj = dynamic_cast<CGPandoraBox *>(factory->create());
+			auto * obj = dynamic_cast<CGPandoraBox *>(factory->create(map.mapInstance->cb, nullptr));
 
 			std::vector <const CSpell *> spells;
 			for(auto spell : VLC->spellh->objects)
@@ -413,7 +413,7 @@ void TreasurePlacer::addAllPossibleObjects()
 	oi.generateObject = [this]() -> CGObjectInstance *
 	{
 		auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
-		auto * obj = dynamic_cast<CGPandoraBox *>(factory->create());
+		auto * obj = dynamic_cast<CGPandoraBox *>(factory->create(map.mapInstance->cb, nullptr));
 
 		std::vector <const CSpell *> spells;
 		for(auto spell : VLC->spellh->objects)
@@ -491,7 +491,7 @@ void TreasurePlacer::addAllPossibleObjects()
 			oi.generateObject = [creature, creaturesAmount, randomAppearance, setRandomArtifact]() -> CGObjectInstance *
 			{
 				auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
-				auto * obj = dynamic_cast<CGSeerHut *>(factory->create());
+				auto * obj = dynamic_cast<CGSeerHut *>(factory->create(map.mapInstance->cb, nullptr));
 				
 				Rewardable::VisitInfo reward;
 				reward.reward.creatures.emplace_back(creature->getId(), creaturesAmount);
@@ -535,7 +535,7 @@ void TreasurePlacer::addAllPossibleObjects()
 			oi.generateObject = [i, randomAppearance, this, setRandomArtifact]() -> CGObjectInstance *
 			{
 				auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
-				auto * obj = dynamic_cast<CGSeerHut *>(factory->create());
+				auto * obj = dynamic_cast<CGSeerHut *>(factory->create(map.mapInstance->cb, nullptr));
 				
 				Rewardable::VisitInfo reward;
 				reward.reward.heroExperience = generator.getConfig().questRewardValues[i];
@@ -553,7 +553,7 @@ void TreasurePlacer::addAllPossibleObjects()
 			oi.generateObject = [i, randomAppearance, this, setRandomArtifact]() -> CGObjectInstance *
 			{
 				auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
-				auto * obj = dynamic_cast<CGSeerHut *>(factory->create());
+				auto * obj = dynamic_cast<CGSeerHut *>(factory->create(map.mapInstance->cb, nullptr));
 				
 				Rewardable::VisitInfo reward;
 				reward.reward.resources[EGameResID::GOLD] = generator.getConfig().questRewardValues[i];

+ 3 - 3
lib/rmg/modificators/WaterProxy.cpp

@@ -240,7 +240,7 @@ bool WaterProxy::placeBoat(Zone & land, const Lake & lake, bool createRoad, Rout
 	for(auto subObj : subObjects)
 	{
 		//making a temporary object
-		std::unique_ptr<CGObjectInstance> obj(VLC->objtypeh->getHandlerFor(Obj::BOAT, subObj)->create());
+		std::unique_ptr<CGObjectInstance> obj(VLC->objtypeh->getHandlerFor(Obj::BOAT, subObj)->create(map.mapInstance->cb, nullptr));
 		if(auto * testBoat = dynamic_cast<CGBoat *>(obj.get()))
 		{
 			if(testBoat->layer == EPathfindingLayer::SAIL)
@@ -251,7 +251,7 @@ bool WaterProxy::placeBoat(Zone & land, const Lake & lake, bool createRoad, Rout
 	if(sailingBoatTypes.empty())
 		return false;
 	
-	auto * boat = dynamic_cast<CGBoat *>(VLC->objtypeh->getHandlerFor(Obj::BOAT, *RandomGeneratorUtil::nextItem(sailingBoatTypes, zone.getRand()))->create());
+	auto * boat = dynamic_cast<CGBoat *>(VLC->objtypeh->getHandlerFor(Obj::BOAT, *RandomGeneratorUtil::nextItem(sailingBoatTypes, zone.getRand()))->create(map.mapInstance->cb, nullptr));
 
 	rmg::Object rmgObject(*boat);
 	rmgObject.setTemplate(zone.getTerrainType(), zone.getRand());
@@ -315,7 +315,7 @@ bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, bool
 		return false;
 	
 	int subtype = chooseRandomAppearance(zone.getRand(), Obj::SHIPYARD, land.getTerrainType());
-	auto * shipyard = dynamic_cast<CGShipyard *>(VLC->objtypeh->getHandlerFor(Obj::SHIPYARD, subtype)->create());
+	auto * shipyard = dynamic_cast<CGShipyard *>(VLC->objtypeh->getHandlerFor(Obj::SHIPYARD, subtype)->create(map.mapInstance->cb, nullptr));
 	shipyard->tempOwner = PlayerColor::NEUTRAL;
 	
 	rmg::Object rmgObject(*shipyard);

+ 17 - 6
lib/serializer/BinaryDeserializer.h

@@ -69,22 +69,33 @@ class DLL_LINKAGE BinaryDeserializer : public CLoaderBase
 	template <typename T, typename Enable = void>
 	struct ClassObjectCreator
 	{
-		static T *invoke()
+		static T *invoke(IGameCallback *cb)
 		{
-			static_assert(!std::is_abstract<T>::value, "Cannot call new upon abstract classes!");
+			static_assert(!std::is_base_of_v<GameCallbackHolder, T>, "Cannot call new upon map objects!");
+			static_assert(!std::is_abstract_v<T>, "Cannot call new upon abstract classes!");
 			return new T();
 		}
 	};
 
 	template<typename T>
-	struct ClassObjectCreator<T, typename std::enable_if<std::is_abstract<T>::value>::type>
+	struct ClassObjectCreator<T, typename std::enable_if_t<std::is_abstract_v<T>>>
 	{
-		static T *invoke()
+		static T *invoke(IGameCallback *cb)
 		{
 			throw std::runtime_error("Something went really wrong during deserialization. Attempted creating an object of an abstract class " + std::string(typeid(T).name()));
 		}
 	};
 
+	template<typename T>
+	struct ClassObjectCreator<T, typename std::enable_if_t<std::is_base_of_v<GameCallbackHolder, T> && !std::is_abstract_v<T>>>
+	{
+		static T *invoke(IGameCallback *cb)
+		{
+			static_assert(!std::is_abstract<T>::value, "Cannot call new upon abstract classes!");
+			return new T(cb);
+		}
+	};
+
 	STRONG_INLINE ui32 readAndCheckLength()
 	{
 		ui32 length;
@@ -121,7 +132,7 @@ class DLL_LINKAGE BinaryDeserializer : public CLoaderBase
 			auto & s = static_cast<BinaryDeserializer &>(ar);
 
 			//create new object under pointer
-			Type * ptr = ClassObjectCreator<Type>::invoke(); //does new npT or throws for abstract classes
+			Type * ptr = ClassObjectCreator<Type>::invoke(nullptr); //does new npT or throws for abstract classes
 			s.ptrAllocated(ptr, pid);
 
 			assert(s.fileVersion != 0);
@@ -281,7 +292,7 @@ public:
 		{
 			typedef typename std::remove_pointer<T>::type npT;
 			typedef typename std::remove_const<npT>::type ncpT;
-			data = ClassObjectCreator<ncpT>::invoke();
+			data = ClassObjectCreator<ncpT>::invoke(nullptr);
 			ptrAllocated(data, pid);
 			load(*data);
 		}

+ 1 - 14
lib/spells/ISpellMechanics.cpp

@@ -139,7 +139,6 @@ public:
 BattleCast::BattleCast(const CBattleInfoCallback * cb_, const Caster * caster_, const Mode mode_, const CSpell * spell_):
 	spell(spell_),
 	cb(cb_),
-	gameCb(IObjectInterface::cb), //FIXME: pass player callback (problem is that BattleAI do not have one)
 	caster(caster_),
 	mode(mode_),
 	smart(boost::logic::indeterminate),
@@ -150,7 +149,6 @@ BattleCast::BattleCast(const CBattleInfoCallback * cb_, const Caster * caster_,
 BattleCast::BattleCast(const BattleCast & orig, const Caster * caster_)
 	: spell(orig.spell),
 	cb(orig.cb),
-	gameCb(orig.gameCb),
 	caster(caster_),
 	mode(Mode::MAGIC_MIRROR),
 	magicSkillLevel(orig.magicSkillLevel),
@@ -184,11 +182,6 @@ const CBattleInfoCallback * BattleCast::getBattle() const
 	return cb;
 }
 
-const IGameInfoCallback * BattleCast::getGame() const
-{
-	return gameCb;
-}
-
 BattleCast::OptionalValue BattleCast::getSpellLevel() const
 {
 	return magicSkillLevel;
@@ -417,8 +410,7 @@ BaseMechanics::BaseMechanics(const IBattleCast * event):
 	mode(event->getMode()),
 	smart(event->isSmart()),
 	massive(event->isMassive()),
-	cb(event->getBattle()),
-	gameCb(event->getGame())
+	cb(event->getBattle())
 {
 	caster = event->getCaster();
 
@@ -696,11 +688,6 @@ const Service * BaseMechanics::spells() const
 	return VLC->spells(); //todo: redirect
 }
 
-const IGameInfoCallback * BaseMechanics::game() const
-{
-	return gameCb;
-}
-
 const CBattleInfoCallback * BaseMechanics::battle() const
 {
 	return cb;

+ 0 - 7
lib/spells/ISpellMechanics.h

@@ -27,7 +27,6 @@ class CreatureService;
 class CMap;
 class CGameInfoCallback;
 class CBattleInfoCallback;
-class IGameInfoCallback;
 class JsonNode;
 class CStack;
 class CGObjectInstance;
@@ -81,7 +80,6 @@ public:
 	virtual Mode getMode() const = 0;
 	virtual const Caster * getCaster() const = 0;
 	virtual const CBattleInfoCallback * getBattle() const = 0;
-	virtual const IGameInfoCallback * getGame() const = 0;
 
 	virtual OptionalValue getSpellLevel() const = 0;
 
@@ -114,7 +112,6 @@ public:
 	Mode getMode() const override;
 	const Caster * getCaster() const override;
 	const CBattleInfoCallback * getBattle() const override;
-	const IGameInfoCallback * getGame() const override;
 
 	OptionalValue getSpellLevel() const override;
 
@@ -162,7 +159,6 @@ private:
 	Mode mode;
 	const CSpell * spell;
 	const CBattleInfoCallback * cb;
-	const IGameInfoCallback * gameCb;
 	const Caster * caster;
 };
 
@@ -252,7 +248,6 @@ public:
 #endif
 	virtual const Service * spells() const = 0;
 
-	virtual const IGameInfoCallback * game() const = 0;
 	virtual const CBattleInfoCallback * battle() const = 0;
 
 	const Caster * caster;
@@ -311,7 +306,6 @@ public:
 #endif
 	const Service * spells() const override;
 
-	const IGameInfoCallback * game() const override;
 	const CBattleInfoCallback * battle() const override;
 
 protected:
@@ -335,7 +329,6 @@ private:
 	boost::logic::tribool smart;
 	boost::logic::tribool massive;
 
-	const IGameInfoCallback * gameCb;
 	const CBattleInfoCallback * cb;
 };
 

+ 2 - 2
mapeditor/mainwindow.cpp

@@ -347,7 +347,7 @@ std::unique_ptr<CMap> MainWindow::openMapInternal(const QString & filenameSelect
 		if(!modList.empty())
 			throw ModIncompatibility(modList);
 		
-		return mapService.loadMap(resId);
+		return mapService.loadMap(resId, nullptr);
 	}
 	else
 		throw std::runtime_error("Corrupted map");
@@ -546,7 +546,7 @@ void MainWindow::addGroupIntoCatalog(const std::string & groupName, bool useCust
 			}
 			
 			//create object to extract name
-			std::unique_ptr<CGObjectInstance> temporaryObj(factory->create(templ));
+			std::unique_ptr<CGObjectInstance> temporaryObj(factory->create(nullptr, templ));
 			QString translated = useCustomName ? QString::fromStdString(temporaryObj->getObjectName().c_str()) : subGroupName;
 			itemType->setText(translated);
 			

+ 1 - 1
mapeditor/mapview.cpp

@@ -593,7 +593,7 @@ void MapView::dragEnterEvent(QDragEnterEvent * event)
 				auto factory = VLC->objtypeh->getHandlerFor(objId, objSubId);
 				auto templ = factory->getTemplates()[templateId];
 				controller->discardObject(sc->level);
-				controller->createObject(sc->level, factory->create(templ));
+				controller->createObject(sc->level, factory->create(nullptr, templ));
 			}
 		}
 		

+ 2 - 2
mapeditor/windownewmap.cpp

@@ -231,7 +231,7 @@ void generateRandomMap(CMapGenerator & gen, MainWindow * window)
 
 std::unique_ptr<CMap> generateEmptyMap(CMapGenOptions & options)
 {
-	std::unique_ptr<CMap> map(new CMap);
+	auto map = std::make_unique<CMap>(nullptr);
 	map->version = EMapFormat::VCMI;
 	map->width = options.getWidth();
 	map->height = options.getHeight();
@@ -281,7 +281,7 @@ void WindowNewMap::on_okButton_clicked()
 		if(ui->checkSeed->isChecked() && !ui->lineSeed->text().isEmpty())
 			seed = ui->lineSeed->text().toInt();
 			
-		CMapGenerator generator(mapGenOptions, seed);
+		CMapGenerator generator(mapGenOptions, nullptr, seed);
 		auto progressBarWnd = new GeneratorProgress(generator, this);
 		progressBarWnd->show();
 	

+ 2 - 3
server/CGameHandler.cpp

@@ -511,7 +511,6 @@ CGameHandler::CGameHandler(CVCMIServer * lobby)
 	, turnTimerHandler(*this)
 {
 	QID = 1;
-	IObjectInterface::cb = this;
 	applier = std::make_shared<CApplier<CBaseForGHApply>>();
 	registerTypesServerPacks(*applier);
 
@@ -541,7 +540,7 @@ void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTrack
 	}
 	CMapService mapService;
 	gs = new CGameState();
-	gs->preInit(VLC);
+	gs->preInit(VLC, this);
 	logGlobal->info("Gamestate created!");
 	gs->init(&mapService, si, progressTracking);
 	logGlobal->info("Gamestate initialized!");
@@ -1838,7 +1837,7 @@ bool CGameHandler::load(const std::string & filename)
 		lobby->announceMessage(std::string("Failed to load game: ") + e.what());
 		return false;
 	}
-	gs->preInit(VLC);
+	gs->preInit(VLC, this);
 	gs->updateOnLoad(lobby->si.get());
 	return true;
 }