Bläddra i källkod

Refactoring: only use RNGs explicitly to avoid bug prone code in future

Now server-side code should always use CRandomGenerator::getDefault which is serialized in GH.
CGameState::getRandomGenerator should be only used from GS code and CPackForClient-based applyGs.
Arseniy Shestakov 9 år sedan
förälder
incheckning
c8faca8f39

+ 2 - 2
AI/BattleAI/BattleAI.cpp

@@ -653,7 +653,7 @@ AttackPossibility AttackPossibility::evaluate(const BattleAttackInfo &AttackInfo
 	for(int i  = 0; i < totalAttacks; i++)
 	{
 		std::pair<ui32, ui32> retaliation(0,0);
-		auto attackDmg = cbc->battleEstimateDamage(curBai, &retaliation);
+		auto attackDmg = cbc->battleEstimateDamage(CRandomGenerator::getDefault(), curBai, &retaliation);
 		ap.damageDealt = (attackDmg.first + attackDmg.second) / 2;
 		ap.damageReceived = (retaliation.first + retaliation.second) / 2;
 
@@ -737,7 +737,7 @@ int PotentialTargets::bestActionValue() const
 
 void EnemyInfo::calcDmg(const CStack * ourStack)
 {
-	TDmgRange retal, dmg = cbc->battleEstimateDamage(ourStack, s, &retal);
+	TDmgRange retal, dmg = cbc->battleEstimateDamage(CRandomGenerator::getDefault(), ourStack, s, &retal);
 	adi = (dmg.first + dmg.second) / 2;
 	adr = (retal.first + retal.second) / 2;
 }

+ 1 - 1
AI/StupidAI/StupidAI.cpp

@@ -44,7 +44,7 @@ struct EnemyInfo
 	{}
 	void calcDmg(const CStack * ourStack)
 	{
-		TDmgRange retal, dmg = cbc->battleEstimateDamage(ourStack, s, &retal);
+		TDmgRange retal, dmg = cbc->battleEstimateDamage(CRandomGenerator::getDefault(), ourStack, s, &retal);
 		adi = (dmg.first + dmg.second) / 2;
 		adr = (retal.first + retal.second) / 2;
 	}

+ 5 - 5
client/battle/CBattleInterface.cpp

@@ -1587,7 +1587,7 @@ void CBattleInterface::activateStack()
 		if(randomSpellcaster)
 			creatureSpellToCast = -1; //spell will be set later on cast
 
-		creatureSpellToCast = curInt->cb->battleGetRandomStackSpell(s, CBattleInfoCallback::RANDOM_AIMED); //faerie dragon can cast only one spell until their next move
+		creatureSpellToCast = curInt->cb->battleGetRandomStackSpell(CRandomGenerator::getDefault(), s, CBattleInfoCallback::RANDOM_AIMED); //faerie dragon can cast only one spell until their next move
 		//TODO: what if creature can cast BOTH random genie spell and aimed spell?
 	}
 	else
@@ -2101,7 +2101,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 			{
 				if (shere && ourStack && shere != sactive) //only positive spells for other allied creatures
 				{
-					int spellID = curInt->cb->battleGetRandomStackSpell(shere, CBattleInfoCallback::RANDOM_GENIE);
+					int spellID = curInt->cb->battleGetRandomStackSpell(CRandomGenerator::getDefault(), shere, CBattleInfoCallback::RANDOM_GENIE);
 					if (spellID > -1)
 					{
 						legalAction = true;
@@ -2264,7 +2264,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 					}
 				};
 
-				std::string estDmgText = formatDmgRange(curInt->cb->battleEstimateDamage(sactive, shere)); //calculating estimated dmg
+				std::string estDmgText = formatDmgRange(curInt->cb->battleEstimateDamage(CRandomGenerator::getDefault(), sactive, shere)); //calculating estimated dmg
 				consoleMsg = (boost::format(CGI->generaltexth->allTexts[36]) % shere->getName() % estDmgText).str(); //Attack %s (%s damage)
 			}
 				break;
@@ -2276,7 +2276,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 					cursorFrame = ECursor::COMBAT_SHOOT;
 
 				realizeAction = [=] {giveCommand(Battle::SHOOT, myNumber, activeStack->ID);};
-				std::string estDmgText = formatDmgRange(curInt->cb->battleEstimateDamage(sactive, shere)); //calculating estimated dmg
+				std::string estDmgText = formatDmgRange(curInt->cb->battleEstimateDamage(CRandomGenerator::getDefault(), sactive, shere)); //calculating estimated dmg
 				//printing - Shoot %s (%d shots left, %s damage)
 				consoleMsg = (boost::format(CGI->generaltexth->allTexts[296]) % shere->getName() % sactive->shots % estDmgText).str();
 			}
@@ -2425,7 +2425,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 					}
 					else //unknown random spell
 					{
-						giveCommand(Battle::MONSTER_SPELL, myNumber, sactive->ID, curInt->cb->battleGetRandomStackSpell(shere, CBattleInfoCallback::RANDOM_GENIE));
+						giveCommand(Battle::MONSTER_SPELL, myNumber, sactive->ID, curInt->cb->battleGetRandomStackSpell(CRandomGenerator::getDefault(), shere, CBattleInfoCallback::RANDOM_GENIE));
 					}
 				}
 				else

+ 11 - 16
lib/CBattleCallback.cpp

@@ -113,11 +113,6 @@ void CCallbackBase::setBattle(const BattleInfo *B)
 	battle = B;
 }
 
-CRandomGenerator & CCallbackBase::getRandomGenerator() const
-{
-	return rand;
-}
-
 boost::optional<PlayerColor> CCallbackBase::getPlayerID() const
 {
 	return player;
@@ -526,15 +521,15 @@ std::set<BattleHex> CBattleInfoCallback::battleGetAttackedHexes(const CStack* at
 	return attackedHexes;
 }
 
-SpellID CBattleInfoCallback::battleGetRandomStackSpell(const CStack * stack, ERandomSpell mode) const
+SpellID CBattleInfoCallback::battleGetRandomStackSpell(CRandomGenerator & rand, const CStack * stack, ERandomSpell mode) const
 {
 	switch (mode)
 	{
 	case RANDOM_GENIE:
-		return getRandomBeneficialSpell(stack); //target
+		return getRandomBeneficialSpell(rand, stack); //target
 		break;
 	case RANDOM_AIMED:
-		return getRandomCastedSpell(stack); //caster
+		return getRandomCastedSpell(rand, stack); //caster
 		break;
 	default:
 		logGlobal->errorStream() << "Incorrect mode of battleGetRandomSpell (" << mode <<")";
@@ -1081,15 +1076,15 @@ TDmgRange CBattleInfoCallback::calculateDmgRange( const CStack* attacker, const
 	return calculateDmgRange(bai);
 }
 
-TDmgRange CBattleInfoCallback::battleEstimateDamage(const CStack * attacker, const CStack * defender, TDmgRange * retaliationDmg) const
+TDmgRange CBattleInfoCallback::battleEstimateDamage(CRandomGenerator & rand, const CStack * attacker, const CStack * defender, TDmgRange * retaliationDmg) const
 {
 	RETURN_IF_NOT_BATTLE(std::make_pair(0, 0));
 	const bool shooting = battleCanShoot(attacker, defender->position);
 	const BattleAttackInfo bai(attacker, defender, shooting);
-	return battleEstimateDamage(bai, retaliationDmg);
+	return battleEstimateDamage(rand, bai, retaliationDmg);
 }
 
-std::pair<ui32, ui32> CBattleInfoCallback::battleEstimateDamage(const BattleAttackInfo &bai, std::pair<ui32, ui32> * retaliationDmg /*= nullptr*/) const
+std::pair<ui32, ui32> CBattleInfoCallback::battleEstimateDamage(CRandomGenerator & rand, const BattleAttackInfo &bai, std::pair<ui32, ui32> * retaliationDmg /*= nullptr*/) const
 {
 	RETURN_IF_NOT_BATTLE(std::make_pair(0, 0));
 
@@ -1111,7 +1106,7 @@ std::pair<ui32, ui32> CBattleInfoCallback::battleEstimateDamage(const BattleAtta
 			{
 				BattleStackAttacked bsa;
 				bsa.damageAmount = ret.*pairElems[i];
-				bai.defender->prepareAttacked(bsa, getRandomGenerator(), bai.defenderCount);
+				bai.defender->prepareAttacked(bsa, rand, bai.defenderCount);
 
 				auto retaliationAttack = bai.reverse();
 				retaliationAttack.attackerCount = bsa.newAmount;
@@ -1928,7 +1923,7 @@ std::set<const CStack*> CBattleInfoCallback:: batteAdjacentCreatures(const CStac
 	return stacks;
 }
 
-SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) const
+SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, const CStack * subject) const
 {
 	RETURN_IF_NOT_BATTLE(SpellID::NONE);
 	//This is complete list. No spells from mods.
@@ -2050,7 +2045,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) co
 
 	if(!beneficialSpells.empty())
 	{
-		return *RandomGeneratorUtil::nextItem(beneficialSpells, getRandomGenerator());
+		return *RandomGeneratorUtil::nextItem(beneficialSpells, rand);
 	}
 	else
 	{
@@ -2058,7 +2053,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) co
 	}
 }
 
-SpellID CBattleInfoCallback::getRandomCastedSpell(const CStack * caster) const
+SpellID CBattleInfoCallback::getRandomCastedSpell(CRandomGenerator & rand,const CStack * caster) const
 {
 	RETURN_IF_NOT_BATTLE(SpellID::NONE);
 
@@ -2070,7 +2065,7 @@ SpellID CBattleInfoCallback::getRandomCastedSpell(const CStack * caster) const
 	{
 		totalWeight += std::max(b->additionalInfo, 1); //minimal chance to cast is 1
 	}
-	int randomPos = getRandomGenerator().nextInt(totalWeight - 1);
+	int randomPos = rand.nextInt(totalWeight - 1);
 	for(Bonus * b : *bl)
 	{
 		randomPos -= std::max(b->additionalInfo, 1);

+ 6 - 8
lib/CBattleCallback.h

@@ -1,6 +1,5 @@
 #pragma once
 #include "BattleHex.h"
-#include "CRandomGenerator.h"
 
 /*
  * CBattleCallback.h, part of VCMI engine
@@ -23,6 +22,7 @@ struct CObstacleInstance;
 class IBonusBearer;
 struct InfoAboutHero;
 class CArmedInstance;
+class CRandomGenerator;
 
 namespace boost
 {class shared_mutex;}
@@ -49,7 +49,6 @@ class DLL_LINKAGE CCallbackBase
 
 protected:
 	CGameState *gs;
-	mutable CRandomGenerator rand;
 	boost::optional<PlayerColor> player; // not set gives access to all information, otherwise callback provides only information "visible" for player
 
 	CCallbackBase(CGameState *GS, boost::optional<PlayerColor> Player)
@@ -65,7 +64,6 @@ protected:
 public:
 	boost::shared_mutex &getGsMutex(); //just return a reference to mutex, does not lock nor anything
 	boost::optional<PlayerColor> getPlayerID() const;
-	CRandomGenerator & getRandomGenerator() const;
 
 	friend class CBattleInfoEssentials;
 };
@@ -269,8 +267,8 @@ public:
 	TDmgRange calculateDmgRange(const CStack* attacker, const CStack* defender, bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
 
 	//hextowallpart  //int battleGetWallUnderHex(BattleHex hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found
-	std::pair<ui32, ui32> battleEstimateDamage(const BattleAttackInfo &bai, std::pair<ui32, ui32> * retaliationDmg = nullptr) const; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair <min dmg, max dmg>
-	std::pair<ui32, ui32> battleEstimateDamage(const CStack * attacker, const CStack * defender, std::pair<ui32, ui32> * retaliationDmg = nullptr) const; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair <min dmg, max dmg>
+	std::pair<ui32, ui32> battleEstimateDamage(CRandomGenerator & rand, const BattleAttackInfo &bai, std::pair<ui32, ui32> * retaliationDmg = nullptr) const; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair <min dmg, max dmg>
+	std::pair<ui32, ui32> battleEstimateDamage(CRandomGenerator & rand, const CStack * attacker, const CStack * defender, std::pair<ui32, ui32> * retaliationDmg = nullptr) const; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair <min dmg, max dmg>
 	si8 battleHasDistancePenalty( const CStack * stack, BattleHex destHex ) const;
 	si8 battleHasDistancePenalty(const IBonusBearer *bonusBearer, BattleHex shooterPosition, BattleHex destHex ) const;
 	si8 battleHasWallPenalty(const CStack * stack, BattleHex destHex) const; //checks if given stack has wall penalty
@@ -289,9 +287,9 @@ public:
 	ESpellCastProblem::ESpellCastProblem battleCanCastThisSpellHere(const ISpellCaster * caster, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const; //checks if given player can cast given spell at given tile in given mode
 	std::vector<BattleHex> battleGetPossibleTargets(PlayerColor player, const CSpell *spell) const;
 
-	SpellID battleGetRandomStackSpell(const CStack * stack, ERandomSpell mode) const;
-	SpellID getRandomBeneficialSpell(const CStack * subject) const;
-	SpellID getRandomCastedSpell(const CStack * caster) const; //called at the beginning of turn for Faerie Dragon
+	SpellID battleGetRandomStackSpell(CRandomGenerator & rand, const CStack * stack, ERandomSpell mode) const;
+	SpellID getRandomBeneficialSpell(CRandomGenerator & rand, const CStack * subject) const;
+	SpellID getRandomCastedSpell(CRandomGenerator & rand, const CStack * caster) const; //called at the beginning of turn for Faerie Dragon
 
 	const CStack * getStackIf(std::function<bool(const CStack*)> pred) const;
 

+ 12 - 13
lib/CGameState.cpp

@@ -912,8 +912,8 @@ void CGameState::initDuel()
 			for(TSecSKill secSkill : ss.heroSecSkills)
 				h->setSecSkillLevel(SecondarySkill(secSkill.first), secSkill.second, 1);
 
-			h->initHero(HeroTypeID(h->subID));
-			obj->initObj();
+			h->initHero(getRandomGenerator(), HeroTypeID(h->subID));
+			obj->initObj(getRandomGenerator());
 		}
 		else
 		{
@@ -1239,7 +1239,7 @@ CGameState::CrossoverHeroesList CGameState::getCrossoverHeroesFromPreviousScenar
 	return crossoverHeroes;
 }
 
-void CGameState::prepareCrossoverHeroes(std::vector<CGameState::CampaignHeroReplacement> & campaignHeroReplacements, const CScenarioTravel & travelOptions) const
+void CGameState::prepareCrossoverHeroes(std::vector<CGameState::CampaignHeroReplacement> & campaignHeroReplacements, const CScenarioTravel & travelOptions)
 {
 	// create heroes list for convenience iterating
 	std::vector<CGHeroInstance *> crossoverHeroes;
@@ -1255,7 +1255,7 @@ void CGameState::prepareCrossoverHeroes(std::vector<CGameState::CampaignHeroRepl
 		//trimming experience
 		for(CGHeroInstance * cgh : crossoverHeroes)
 		{
-			cgh->initExp();
+			cgh->initExp(getRandomGenerator());
 		}
 	}
 
@@ -1462,7 +1462,7 @@ void CGameState::initHeroes()
 			continue;
 		}
 
-		hero->initHero();
+		hero->initHero(getRandomGenerator());
 		getPlayer(hero->getOwner())->heroes.push_back(hero);
 		map->allHeroes[hero->type->ID.getNum()] = hero;
 	}
@@ -1478,7 +1478,7 @@ void CGameState::initHeroes()
 	{
 		if(!vstd::contains(heroesToCreate, HeroTypeID(ph->subID)))
 			continue;
-		ph->initHero();
+		ph->initHero(getRandomGenerator());
 		hpool.heroesPool[ph->subID] = ph;
 		hpool.pavailable[ph->subID] = 0xff;
 		heroesToCreate.erase(ph->type->ID);
@@ -1489,7 +1489,7 @@ void CGameState::initHeroes()
 	for(HeroTypeID htype : heroesToCreate) //all not used allowed heroes go with default state into the pool
 	{
 		auto  vhi = new CGHeroInstance();
-		vhi->initHero(htype);
+		vhi->initHero(getRandomGenerator(), htype);
 
 		int typeID = htype.getNum();
 		map->allHeroes[typeID] = vhi;
@@ -1846,7 +1846,7 @@ void CGameState::initMapObjects()
 		if(obj)
 		{
 			logGlobal->traceStream() << boost::format ("Calling Init for object %d, %s, %s") % obj->id.getNum() % obj->typeName % obj->subTypeName;
-			obj->initObj();
+			obj->initObj(getRandomGenerator());
 		}
 	}
 	for(CGObjectInstance *obj : map->objects)
@@ -1906,7 +1906,7 @@ void CGameState::initVisitingAndGarrisonedHeroes()
 	}
 }
 
-BFieldType CGameState::battleGetBattlefieldType(int3 tile, CRandomGenerator & r)
+BFieldType CGameState::battleGetBattlefieldType(int3 tile, CRandomGenerator & rand)
 {
 	if(tile==int3() && curB)
 		tile = curB->tile;
@@ -1955,13 +1955,13 @@ BFieldType CGameState::battleGetBattlefieldType(int3 tile, CRandomGenerator & r)
 	switch(t.terType)
 	{
 	case ETerrainType::DIRT:
-		return BFieldType(r.nextInt(3, 5));
+		return BFieldType(rand.nextInt(3, 5));
 	case ETerrainType::SAND:
 		return BFieldType::SAND_MESAS; //TODO: coast support
 	case ETerrainType::GRASS:
-		return BFieldType(r.nextInt(6, 7));
+		return BFieldType(rand.nextInt(6, 7));
 	case ETerrainType::SNOW:
-		return BFieldType(r.nextInt(10, 11));
+		return BFieldType(rand.nextInt(10, 11));
 	case ETerrainType::SWAMP:
 		return BFieldType::SWAMP_TREES;
 	case ETerrainType::ROUGH:
@@ -3250,7 +3250,6 @@ TeamState::TeamState(TeamState && other):
 	std::swap(fogOfWarMap, other.fogOfWarMap);
 }
 
-
 CRandomGenerator & CGameState::getRandomGenerator()
 {
 	//if(scenarioOps && scenarioOps->seedPostInit)

+ 10 - 2
lib/CGameState.h

@@ -218,7 +218,7 @@ public:
 	void giveHeroArtifact(CGHeroInstance *h, ArtifactID aid);
 
 	void apply(CPack *pack);
-	BFieldType battleGetBattlefieldType(int3 tile, CRandomGenerator & r);
+	BFieldType battleGetBattlefieldType(int3 tile, CRandomGenerator & rand);
 	UpgradeInfo getUpgradeInfo(const CStackInstance &stack);
 	PlayerRelations::PlayerRelations getPlayerRelations(PlayerColor color1, PlayerColor color2);
 	bool checkForVisitableDir(const int3 & src, const int3 & dst) const; //check if src tile is visitable from dst tile
@@ -243,6 +243,14 @@ public:
 	int getDate(Date::EDateType mode=Date::DAY) const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month
 
 	// ----- getters, setters -----
+
+	/// This RNG should only be used inside GS or CPackForClient-derived applyGs
+	/// If this doesn't work for your code that mean you need a new netpack
+	///
+	/// Client-side must use CRandomGenerator::getDefault which is not serialized
+	///
+	/// CGameHandler have it's own getter for CRandomGenerator::getDefault
+	/// Any server-side code outside of GH must use CRandomGenerator::getDefault
 	CRandomGenerator & getRandomGenerator();
 
 	template <typename Handler> void serialize(Handler &h, const int version)
@@ -291,7 +299,7 @@ private:
 	std::vector<CampaignHeroReplacement> generateCampaignHeroesToReplace(CrossoverHeroesList & crossoverHeroes);
 
 	/// gets prepared and copied hero instances with crossover heroes from prev. scenario and travel options from current scenario
-	void prepareCrossoverHeroes(std::vector<CampaignHeroReplacement> & campaignHeroReplacements, const CScenarioTravel & travelOptions) const;
+	void prepareCrossoverHeroes(std::vector<CampaignHeroReplacement> & campaignHeroReplacements, const CScenarioTravel & travelOptions);
 
 	void replaceHeroesPlaceholders(const std::vector<CampaignHeroReplacement> & campaignHeroReplacements);
 	void placeStartingHeroes();

+ 2 - 2
lib/NetPacksLib.cpp

@@ -624,7 +624,7 @@ DLL_LINKAGE void HeroRecruited::applyGs( CGameState *gs )
 	h->attachTo(p);
 	if(fresh)
 	{
-		h->initObj();
+		h->initObj(gs->getRandomGenerator());
 	}
 	gs->map->addBlockVisTiles(h);
 
@@ -689,7 +689,7 @@ DLL_LINKAGE void NewObject::applyGs( CGameState *gs )
 
 	gs->map->objects.push_back(o);
 	gs->map->addBlockVisTiles(o);
-	o->initObj();
+	o->initObj(gs->getRandomGenerator());
 	gs->map->calculateGuardingGreaturePositions();
 
 	logGlobal->debugStream() << "added object id=" << id << "; address=" << (intptr_t)o << "; name=" << o->getObjectName();

+ 5 - 4
lib/mapObjects/CBank.cpp

@@ -34,11 +34,11 @@ CBank::~CBank()
 {
 }
 
-void CBank::initObj()
+void CBank::initObj(CRandomGenerator & rand)
 {
 	daycounter = 0;
 	resetDuration = 0;
-	VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, cb->gameState()->getRandomGenerator());
+	VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand);
 }
 
 std::string CBank::getHoverText(PlayerColor player) const
@@ -64,7 +64,8 @@ void CBank::setPropertyDer (ui8 what, ui32 val)
 				daycounter+=val;
 			break;
 		case ObjProperty::BANK_RESET:
-			initObj();
+			// FIXME: Object reset must be done by separate netpack from server
+			initObj(cb->gameState()->getRandomGenerator());
 			daycounter = 1; //yes, 1 since "today" daycounter won't be incremented
 			break;
 		case ObjProperty::BANK_CLEAR:
@@ -73,7 +74,7 @@ void CBank::setPropertyDer (ui8 what, ui32 val)
 	}
 }
 
-void CBank::newTurn() const
+void CBank::newTurn(CRandomGenerator & rand) const
 {
 	if (bc == nullptr)
 	{

+ 2 - 2
lib/mapObjects/CBank.h

@@ -31,9 +31,9 @@ public:
 
 	void setConfig(const BankConfig & bc);
 
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 	std::string getHoverText(PlayerColor player) const override;
-	void newTurn() const override;
+	void newTurn(CRandomGenerator & rand) const override;
 	bool wasVisited (PlayerColor player) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;

+ 17 - 18
lib/mapObjects/CGHeroInstance.cpp

@@ -244,10 +244,10 @@ CGHeroInstance::CGHeroInstance()
 	secSkills.push_back(std::make_pair(SecondarySkill::DEFAULT, -1));
 }
 
-void CGHeroInstance::initHero(HeroTypeID SUBID)
+void CGHeroInstance::initHero(CRandomGenerator & rand, HeroTypeID SUBID)
 {
 	subID = SUBID.getNum();
-	initHero();
+	initHero(rand);
 }
 
 void CGHeroInstance::setType(si32 ID, si32 subID)
@@ -259,7 +259,7 @@ void CGHeroInstance::setType(si32 ID, si32 subID)
 	randomizeArmy(type->heroClass->faction);
 }
 
-void CGHeroInstance::initHero()
+void CGHeroInstance::initHero(CRandomGenerator & rand)
 {
 	assert(validTypes(true));
 	if(!type)
@@ -302,17 +302,17 @@ void CGHeroInstance::initHero()
 	setFormation(false);
 	if (!stacksCount()) //standard army//initial army
 	{
-		initArmy(cb->gameState()->getRandomGenerator());
+		initArmy(rand);
 	}
 	assert(validTypes());
 
 	if(exp == 0xffffffff)
 	{
-		initExp();
+		initExp(rand);
 	}
 	else
 	{
-		levelUpAutomatically();
+		levelUpAutomatically(rand);
 	}
 
 	if (VLC->modh->modules.COMMANDERS && !commander)
@@ -483,7 +483,7 @@ void CGHeroInstance::SecondarySkillsInfo::resetWisdomCounter()
 	wisdomCounter = 1;
 }
 
-void CGHeroInstance::initObj()
+void CGHeroInstance::initObj(CRandomGenerator & rand)
 {
 	blockVisit = true;
 	auto  hs = new HeroSpecial();
@@ -491,9 +491,9 @@ void CGHeroInstance::initObj()
 	attachTo(hs); //do we ever need to detach it?
 
 	if(!type)
-		initHero(); //TODO: set up everything for prison before specialties are configured
+		initHero(rand); //TODO: set up everything for prison before specialties are configured
 
-	skillsInfo.rand.setSeed(cb->gameState()->getRandomGenerator().nextInt());
+	skillsInfo.rand.setSeed(rand.nextInt());
 	skillsInfo.resetMagicSchoolCounter();
 	skillsInfo.resetWisdomCounter();
 
@@ -1056,10 +1056,10 @@ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &b
  * @param raisedStack Pair where the first element represents ID of the raised creature
  * and the second element the amount.
  */
-void CGHeroInstance::showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const
+void CGHeroInstance::showNecromancyDialog(const CStackBasicDescriptor &raisedStack, CRandomGenerator & rand) const
 {
 	InfoWindow iw;
-	iw.soundID = soundBase::pickup01 + cb->getRandomGenerator().nextInt(6);
+	iw.soundID = soundBase::pickup01 + rand.nextInt(6);
 	iw.player = tempOwner;
 	iw.components.push_back(Component(raisedStack));
 
@@ -1162,9 +1162,9 @@ EAlignment::EAlignment CGHeroInstance::getAlignment() const
 	return type->heroClass->getAlignment();
 }
 
-void CGHeroInstance::initExp()
+void CGHeroInstance::initExp(CRandomGenerator & rand)
 {
-	exp = cb->gameState()->getRandomGenerator().nextInt(40, 89);
+	exp = rand.nextInt(40, 89);
 }
 
 std::string CGHeroInstance::nodeName() const
@@ -1356,7 +1356,7 @@ PrimarySkill::PrimarySkill CGHeroInstance::nextPrimarySkill(CRandomGenerator & r
 	return static_cast<PrimarySkill::PrimarySkill>(primarySkill);
 }
 
-boost::optional<SecondarySkill> CGHeroInstance::nextSecondarySkill() const
+boost::optional<SecondarySkill> CGHeroInstance::nextSecondarySkill(CRandomGenerator & rand) const
 {
 	assert(gainsLevel());
 
@@ -1373,7 +1373,6 @@ boost::optional<SecondarySkill> CGHeroInstance::nextSecondarySkill() const
 			}
 		}
 
-		auto & rand = cb->gameState()->getRandomGenerator();
 		if(learnedSecondarySkills.empty())
 		{
 			// there are only new skills to learn, so choose anyone of them
@@ -1452,16 +1451,16 @@ void CGHeroInstance::levelUp(std::vector<SecondarySkill> skills)
 	Updatespecialty();
 }
 
-void CGHeroInstance::levelUpAutomatically()
+void CGHeroInstance::levelUpAutomatically(CRandomGenerator & rand)
 {
 	while(gainsLevel())
 	{
-		const auto primarySkill = nextPrimarySkill(cb->gameState()->getRandomGenerator());
+		const auto primarySkill = nextPrimarySkill(rand);
 		setPrimarySkill(primarySkill, 1, false);
 
 		auto proposedSecondarySkills = getLevelUpProposedSecondarySkills();
 
-		const auto secondarySkill = nextSecondarySkill();
+		const auto secondarySkill = nextSecondarySkill(rand);
 		if(secondarySkill)
 		{
 			setSecSkillLevel(*secondarySkill, 1, false);

+ 7 - 7
lib/mapObjects/CGHeroInstance.h

@@ -166,7 +166,7 @@ public:
 	PrimarySkill::PrimarySkill nextPrimarySkill(CRandomGenerator & rand) const;
 
 	/// Returns the next secondary skill randomly on level up. Can only be called if hero can gain a level up.
-	boost::optional<SecondarySkill> nextSecondarySkill() const;
+	boost::optional<SecondarySkill> nextSecondarySkill(CRandomGenerator & rand) const;
 
 	/// Gets 0, 1 or 2 secondary skills which are proposed on hero level up.
 	std::vector<SecondarySkill> getLevelUpProposedSecondarySkills() const;
@@ -192,19 +192,19 @@ public:
 
 	bool canCastThisSpell(const CSpell * spell) const; //determines if this hero can cast given spell; takes into account existing spell in spellbook, existing spellbook and artifact bonuses
 	CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const;
-	void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const;
+	void showNecromancyDialog(const CStackBasicDescriptor &raisedStack, CRandomGenerator & rand) const;
 	EDiggingStatus diggingStatus() const;
 
 	//////////////////////////////////////////////////////////////////////////
 
 	void setType(si32 ID, si32 subID) override;
 
-	void initHero();
-	void initHero(HeroTypeID SUBID);
+	void initHero(CRandomGenerator & rand);
+	void initHero(CRandomGenerator & rand, HeroTypeID SUBID);
 
 	void putArtifact(ArtifactPosition pos, CArtifactInstance *art);
 	void putInBackpack(CArtifactInstance *art);
-	void initExp();
+	void initExp(CRandomGenerator & rand);
 	void initArmy(CRandomGenerator & rand, IArmyDescriptor *dst = nullptr);
 	//void giveArtifact (ui32 aid);
 	void pushPrimSkill(PrimarySkill::PrimarySkill which, int val);
@@ -248,7 +248,7 @@ public:
 
 	void deserializationFix();
 
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	std::string getObjectName() const override;
 protected:
@@ -256,7 +256,7 @@ protected:
 	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 
 private:
-	void levelUpAutomatically();
+	void levelUpAutomatically(CRandomGenerator & rand);
 
 public:
 	template <typename Handler> void serialize(Handler &h, const int version)

+ 4 - 4
lib/mapObjects/CGMarket.cpp

@@ -282,18 +282,18 @@ std::vector<int> CGBlackMarket::availableItemsIds(EMarketMode::EMarketMode mode)
 	}
 }
 
-void CGBlackMarket::newTurn() const
+void CGBlackMarket::newTurn(CRandomGenerator & rand) const
 {
 	if(cb->getDate(Date::DAY_OF_MONTH) != 1) //new month
 		return;
 
 	SetAvailableArtifacts saa;
 	saa.id = id.getNum();
-	cb->pickAllowedArtsSet(saa.arts, cb->getRandomGenerator());
+	cb->pickAllowedArtsSet(saa.arts, rand);
 	cb->sendAndApply(&saa);
 }
 
-void CGUniversity::initObj()
+void CGUniversity::initObj(CRandomGenerator & rand)
 {
 	std::vector<int> toChoose;
 	for(int i = 0; i < GameConstants::SKILL_QUANTITY; ++i)
@@ -313,7 +313,7 @@ void CGUniversity::initObj()
 	for(int i = 0; i < 4; ++i)
 	{
 		// move randomly one skill to selected and remove from list
-		auto it = RandomGeneratorUtil::nextItem(toChoose, cb->gameState()->getRandomGenerator());
+		auto it = RandomGeneratorUtil::nextItem(toChoose, rand);
 		skills.push_back(*it);
 		toChoose.erase(it);
 	}

+ 2 - 2
lib/mapObjects/CGMarket.h

@@ -61,7 +61,7 @@ class DLL_LINKAGE CGBlackMarket : public CGMarket
 public:
 	std::vector<const CArtifact *> artifacts; //available artifacts
 
-	void newTurn() const override; //reset artifacts for black market every month
+	void newTurn(CRandomGenerator & rand) const override; //reset artifacts for black market every month
 	std::vector<int> availableItemsIds(EMarketMode::EMarketMode mode) const override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
@@ -77,7 +77,7 @@ public:
 	std::vector<int> skills; //available skills
 
 	std::vector<int> availableItemsIds(EMarketMode::EMarketMode mode) const override;
-	void initObj() override;//set skills for trade
+	void initObj(CRandomGenerator & rand) override;//set skills for trade
 	void onHeroVisit(const CGHeroInstance * h) const override; //open window
 
 	template <typename Handler> void serialize(Handler &h, const int version)

+ 1 - 1
lib/mapObjects/CGPandoraBox.cpp

@@ -34,7 +34,7 @@ static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16
 	showInfoDialog(playerID,txtID,soundID);
 }
 
-void CGPandoraBox::initObj()
+void CGPandoraBox::initObj(CRandomGenerator & rand)
 {
 	blockVisit = (ID==Obj::PANDORAS_BOX); //block only if it's really pandora's box (events also derive from that class)
 	hasGuardians = stacks.size();

+ 1 - 1
lib/mapObjects/CGPandoraBox.h

@@ -36,7 +36,7 @@ public:
 	CCreatureSet creatures; //gained creatures
 
 	CGPandoraBox() : gainedExp(0), manaDiff(0), moraleDiff(0), luckDiff(0){};
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 	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;

+ 6 - 8
lib/mapObjects/CGTownInstance.cpp

@@ -25,14 +25,14 @@
 std::vector<const CArtifact *> CGTownInstance::merchantArtifacts;
 std::vector<int> CGTownInstance::universitySkills;
 
-void CGDwelling::initObj()
+void CGDwelling::initObj(CRandomGenerator & rand)
 {
 	switch(ID)
 	{
 	case Obj::CREATURE_GENERATOR1:
 	case Obj::CREATURE_GENERATOR4:
 		{
-			VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, cb->gameState()->getRandomGenerator());
+			VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand);
 
 			if (getOwner() != PlayerColor::NEUTRAL)
 				cb->gameState()->players[getOwner()].dwellings.push_back (this);
@@ -141,7 +141,7 @@ void CGDwelling::onHeroVisit( const CGHeroInstance * h ) const
 	cb->showBlockingDialog(&bd);
 }
 
-void CGDwelling::newTurn() const
+void CGDwelling::newTurn(CRandomGenerator & rand) const
 {
 	if(cb->getDate(Date::DAY_OF_WEEK) != 1) //not first day of week
 		return;
@@ -152,7 +152,7 @@ void CGDwelling::newTurn() const
 
 	if(ID == Obj::REFUGEE_CAMP) //if it's a refugee camp, we need to pick an available creature
 	{
-		cb->setObjProperty(id, ObjProperty::AVAILABLE_CREATURE, VLC->creh->pickRandomMonster(cb->getRandomGenerator()));
+		cb->setObjProperty(id, ObjProperty::AVAILABLE_CREATURE, VLC->creh->pickRandomMonster(rand));
 	}
 
 	bool change = false;
@@ -595,7 +595,7 @@ std::string CGTownInstance::getObjectName() const
 	return name + ", " + town->faction->name;
 }
 
-void CGTownInstance::initObj()
+void CGTownInstance::initObj(CRandomGenerator & rand)
 ///initialize town structures
 {
 	blockVisit = true;
@@ -637,12 +637,10 @@ void CGTownInstance::initObj()
 	updateAppearance();
 }
 
-void CGTownInstance::newTurn() const
+void CGTownInstance::newTurn(CRandomGenerator & rand) const
 {
 	if (cb->getDate(Date::DAY_OF_WEEK) == 1) //reset on new week
 	{
-		auto & rand = cb->getRandomGenerator();
-
 		//give resources for Rampart, Mystic Pond
 		if (hasBuilt(BuildingID::MYSTIC_POND, ETownType::RAMPART)
 			&& cb->getDate(Date::DAY) != 1 && (tempOwner < PlayerColor::PLAYER_LIMIT))

+ 4 - 4
lib/mapObjects/CGTownInstance.h

@@ -56,9 +56,9 @@ protected:
 	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 
 private:
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
-	void newTurn() const override;
+	void newTurn(CRandomGenerator & rand) const override;
 	void setPropertyDer(ui8 what, ui32 val) override;
 	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
 	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
@@ -249,10 +249,10 @@ public:
 	virtual ~CGTownInstance();
 
 	///IObjectInterface overrides
-	void newTurn() const override;
+	void newTurn(CRandomGenerator & rand) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void onHeroLeave(const CGHeroInstance * h) const override;
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
 	std::string getObjectName() const override;
 protected:

+ 3 - 3
lib/mapObjects/CObjectHandler.cpp

@@ -66,7 +66,7 @@ void IObjectInterface::onHeroVisit(const CGHeroInstance * h) const
 void IObjectInterface::onHeroLeave(const CGHeroInstance * h) const
 {}
 
-void IObjectInterface::newTurn () const
+void IObjectInterface::newTurn(CRandomGenerator & rand) const
 {}
 
 IObjectInterface::~IObjectInterface()
@@ -75,7 +75,7 @@ IObjectInterface::~IObjectInterface()
 IObjectInterface::IObjectInterface()
 {}
 
-void IObjectInterface::initObj()
+void IObjectInterface::initObj(CRandomGenerator & rand)
 {}
 
 void IObjectInterface::setProperty( ui8 what, ui32 val )
@@ -207,7 +207,7 @@ void CGObjectInstance::setType(si32 ID, si32 subID)
 	cb->gameState()->map->addBlockVisTiles(this);
 }
 
-void CGObjectInstance::initObj()
+void CGObjectInstance::initObj(CRandomGenerator & rand)
 {
 	switch(ID)
 	{

+ 4 - 3
lib/mapObjects/CObjectHandler.h

@@ -22,6 +22,7 @@ class CGObjectInstance;
 struct MetaString;
 struct BattleResult;
 class JsonSerializeFormat;
+class CRandomGenerator;
 
 // This one teleport-specific, but has to be available everywhere in callbacks and netpacks
 // For now it's will be there till teleports code refactored and moved into own file
@@ -37,8 +38,8 @@ public:
 
 	virtual void onHeroVisit(const CGHeroInstance * h) const;
 	virtual void onHeroLeave(const CGHeroInstance * h) const;
-	virtual void newTurn() const;
-	virtual void initObj(); //synchr
+	virtual void newTurn(CRandomGenerator & rand) const;
+	virtual void initObj(CRandomGenerator & rand); //synchr
 	virtual void setProperty(ui8 what, ui32 val);//synchr
 
 	//Called when queries created DURING HERO VISIT are resolved
@@ -166,7 +167,7 @@ public:
 
 	/** OVERRIDES OF IObjectInterface **/
 
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	/// method for synchronous update. Note: For new properties classes should override setPropertyDer instead
 	void setProperty(ui8 what, ui32 val) override final;

+ 11 - 11
lib/mapObjects/CQuest.cpp

@@ -420,16 +420,16 @@ void CGSeerHut::setObjToKill()
 	}
 }
 
-void CGSeerHut::init()
+void CGSeerHut::init(CRandomGenerator & rand)
 {
-	seerName = *RandomGeneratorUtil::nextItem(VLC->generaltexth->seerNames, cb->gameState()->getRandomGenerator());
-	quest->textOption = cb->gameState()->getRandomGenerator().nextInt(2);
-	quest->completedOption = cb->gameState()->getRandomGenerator().nextInt(1, 3);
+	seerName = *RandomGeneratorUtil::nextItem(VLC->generaltexth->seerNames, rand);
+	quest->textOption = rand.nextInt(2);
+	quest->completedOption = rand.nextInt(1, 3);
 }
 
-void CGSeerHut::initObj()
+void CGSeerHut::initObj(CRandomGenerator & rand)
 {
-	init();
+	init(rand);
 
 	quest->progress = CQuest::NOT_ACTIVE;
 	if(quest->missionType)
@@ -549,7 +549,7 @@ void CGSeerHut::setPropertyDer (ui8 what, ui32 val)
 	}
 }
 
-void CGSeerHut::newTurn() const
+void CGSeerHut::newTurn(CRandomGenerator & rand) const
 {
 	if(quest->lastDay >= 0 && quest->lastDay <= cb->getDate() - 1) //time is up
 	{
@@ -761,11 +761,11 @@ void CGSeerHut::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer)
 	finishQuest(hero, answer);
 }
 
-void CGQuestGuard::init()
+void CGQuestGuard::init(CRandomGenerator & rand)
 {
 	blockVisit = true;
-	quest->textOption = cb->gameState()->getRandomGenerator().nextInt(3, 5);
-	quest->completedOption = cb->gameState()->getRandomGenerator().nextInt(4, 5);
+	quest->textOption = rand.nextInt(3, 5);
+	quest->completedOption = rand.nextInt(4, 5);
 }
 
 void CGQuestGuard::completeQuest(const CGHeroInstance *h) const
@@ -825,7 +825,7 @@ void CGKeymasterTent::onHeroVisit( const CGHeroInstance * h ) const
 	showInfoDialog(h,txt_id,soundBase::CAVEHEAD);
 }
 
-void CGBorderGuard::initObj()
+void CGBorderGuard::initObj(CRandomGenerator & rand)
 {
 	//ui32 m13489val = subID; //store color as quest info
 	blockVisit = true;

+ 5 - 5
lib/mapObjects/CQuest.h

@@ -108,13 +108,13 @@ public:
 	std::string seerName;
 
 	CGSeerHut();
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 	std::string getHoverText(PlayerColor player) const override;
-	void newTurn() const override;
+	void newTurn(CRandomGenerator & rand) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
 
-	virtual void init();
+	virtual void init(CRandomGenerator & rand);
 	int checkDirection() const; //calculates the region of map where monster is placed
 	void setObjToKill(); //remember creatures / heroes to kill after they are initialized
 	const CGHeroInstance *getHeroToKill(bool allowNull = false) const;
@@ -139,7 +139,7 @@ class DLL_LINKAGE CGQuestGuard : public CGSeerHut
 {
 public:
 	CGQuestGuard() : CGSeerHut(){};
-	void init() override;
+	void init(CRandomGenerator & rand) override;
 	void completeQuest (const CGHeroInstance * h) const override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
@@ -185,7 +185,7 @@ class DLL_LINKAGE CGBorderGuard : public CGKeys, public IQuestObject
 {
 public:
 	CGBorderGuard() : IQuestObject(){};
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
 

+ 31 - 31
lib/mapObjects/CRewardableObject.cpp

@@ -158,7 +158,7 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const
 						grantRewardWithMessage(rewards[0]);
 						break;
 					case SELECT_RANDOM: // select one randomly //TODO: use weights
-						grantRewardWithMessage(rewards[cb->getRandomGenerator().nextInt(rewards.size()-1)]);
+						grantRewardWithMessage(rewards[CRandomGenerator::getDefault().nextInt(rewards.size()-1)]);
 						break;
 				}
 				return;
@@ -431,7 +431,7 @@ void CRewardableObject::setPropertyDer(ui8 what, ui32 val)
 	}
 }
 
-void CRewardableObject::newTurn() const
+void CRewardableObject::newTurn(CRandomGenerator & rand) const
 {
 	if (resetDuration != 0 && cb->getDate(Date::DAY) > 1 && (cb->getDate(Date::DAY) % resetDuration) == 1)
 		cb->setObjProperty(id, ObjProperty::REWARD_RESET, 0);
@@ -450,11 +450,11 @@ CRewardableObject::CRewardableObject():
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 /// Helper, selects random art class based on weights
-static int selectRandomArtClass(int treasure, int minor, int major, int relic)
+static int selectRandomArtClass(CRandomGenerator & rand, int treasure, int minor, int major, int relic)
 {
 	int total = treasure + minor + major + relic;
 	assert(total != 0);
-	int hlp = IObjectInterface::cb->gameState()->getRandomGenerator().nextInt(total - 1);
+	int hlp = rand.nextInt(total - 1);
 
 	if(hlp < treasure)
 		return CArtifact::ART_TREASURE;
@@ -466,10 +466,10 @@ static int selectRandomArtClass(int treasure, int minor, int major, int relic)
 }
 
 /// Helper, adds random artifact to reward selecting class based on weights
-static void loadRandomArtifact(CVisitInfo & info, int treasure, int minor, int major, int relic)
+static void loadRandomArtifact(CRandomGenerator & rand, CVisitInfo & info, int treasure, int minor, int major, int relic)
 {
-	int artClass = selectRandomArtClass(treasure, minor, major, relic);
-	ArtifactID artID = VLC->arth->pickRandomArtifact(IObjectInterface::cb->gameState()->getRandomGenerator(), artClass);
+	int artClass = selectRandomArtClass(rand, treasure, minor, major, relic);
+	ArtifactID artID = VLC->arth->pickRandomArtifact(rand, artClass);
 	info.reward.artifacts.push_back(artID);
 }
 
@@ -479,7 +479,7 @@ CGPickable::CGPickable()
 	selectMode = SELECT_PLAYER;
 }
 
-void CGPickable::initObj()
+void CGPickable::initObj(CRandomGenerator & rand)
 {
 	blockVisit = true;
 	switch(ID)
@@ -487,8 +487,8 @@ void CGPickable::initObj()
 	case Obj::CAMPFIRE:
 		{
 			soundID = soundBase::experience;
-			int givenRes = cb->gameState()->getRandomGenerator().nextInt(5);
-			int givenAmm = cb->gameState()->getRandomGenerator().nextInt(4, 6);
+			int givenRes = rand.nextInt(5);
+			int givenAmm = rand.nextInt(4, 6);
 
 			info.resize(1);
 			info[0].reward.resources[givenRes] = givenAmm;
@@ -499,7 +499,7 @@ void CGPickable::initObj()
 		}
 	case Obj::FLOTSAM:
 		{
-			int type = cb->gameState()->getRandomGenerator().nextInt(3);
+			int type = rand.nextInt(3);
 			soundID = soundBase::GENIE;
 			switch(type)
 			{
@@ -540,7 +540,7 @@ void CGPickable::initObj()
 	case Obj::SEA_CHEST:
 		{
 			soundID = soundBase::chest;
-			int hlp = cb->gameState()->getRandomGenerator().nextInt(99);
+			int hlp = rand.nextInt(99);
 			if(hlp < 20)
 			{
 				info.resize(1);
@@ -557,7 +557,7 @@ void CGPickable::initObj()
 			else
 			{
 				info.resize(1);
-				loadRandomArtifact(info[0], 100, 0, 0, 0);
+				loadRandomArtifact(rand, info[0], 100, 0, 0, 0);
 				info[0].reward.resources[Res::GOLD] = 1000;
 				info[0].message.addTxt(MetaString::ADVOB_TXT, 117);
 				info[0].message.addReplacement(MetaString::ART_NAMES, info[0].reward.artifacts.back());
@@ -569,7 +569,7 @@ void CGPickable::initObj()
 		{
 			soundID = soundBase::experience;
 			info.resize(1);
-			loadRandomArtifact(info[0], 55, 20, 20, 5);
+			loadRandomArtifact(rand, info[0], 55, 20, 20, 5);
 			info[0].message.addTxt(MetaString::ADVOB_TXT, 125);
 			info[0].message.addReplacement(MetaString::ART_NAMES, info[0].reward.artifacts.back());
 			info[0].reward.removeObject = true;
@@ -577,12 +577,12 @@ void CGPickable::initObj()
 		break;
 	case Obj::TREASURE_CHEST:
 		{
-			int hlp = cb->gameState()->getRandomGenerator().nextInt(99);
+			int hlp = rand.nextInt(99);
 			if(hlp >= 95)
 			{
 				soundID = soundBase::treasure;
 				info.resize(1);
-				loadRandomArtifact(info[0], 100, 0, 0, 0);
+				loadRandomArtifact(rand, info[0], 100, 0, 0, 0);
 				info[0].message.addTxt(MetaString::ADVOB_TXT,145);
 				info[0].message.addReplacement(MetaString::ART_NAMES, info[0].reward.artifacts.back());
 				info[0].reward.removeObject = true;
@@ -631,7 +631,7 @@ CGBonusingObject::CGBonusingObject()
 	selectMode = SELECT_FIRST;
 }
 
-void CGBonusingObject::initObj()
+void CGBonusingObject::initObj(CRandomGenerator & rand)
 {
 	auto configureBonusDuration = [&](CVisitInfo & visit, Bonus::BonusDuration duration, Bonus::BonusType type, si32 value, si32 descrID)
 	{
@@ -817,7 +817,7 @@ CGOnceVisitable::CGOnceVisitable()
 	selectMode = SELECT_FIRST;
 }
 
-void CGOnceVisitable::initObj()
+void CGOnceVisitable::initObj(CRandomGenerator & rand)
 {
 	switch(ID)
 	{
@@ -826,10 +826,10 @@ void CGOnceVisitable::initObj()
 			onEmpty.addTxt(MetaString::ADVOB_TXT, 38);
 			soundID = soundBase::MYSTERY;
 			blockVisit = true;
-			if(cb->gameState()->getRandomGenerator().nextInt(99) < 20)
+			if(rand.nextInt(99) < 20)
 			{
 				info.resize(1);
-				loadRandomArtifact(info[0], 10, 10, 10, 0);
+				loadRandomArtifact(rand, info[0], 10, 10, 10, 0);
 				info[0].message.addTxt(MetaString::ADVOB_TXT, 37);
 			}
 		}
@@ -839,8 +839,8 @@ void CGOnceVisitable::initObj()
 			soundID = soundBase::GENIE;
 			onEmpty.addTxt(MetaString::ADVOB_TXT, 65);
 			info.resize(1);
-			int type =  cb->gameState()->getRandomGenerator().nextInt(5); //any basic resource without gold
-			int value = cb->gameState()->getRandomGenerator().nextInt(1, 4);
+			int type =  rand.nextInt(5); //any basic resource without gold
+			int value = rand.nextInt(1, 4);
 			info[0].reward.resources[type] = value;
 			info[0].message.addTxt(MetaString::ADVOB_TXT, 64);
 		}
@@ -851,7 +851,7 @@ void CGOnceVisitable::initObj()
 			onSelect.addTxt(MetaString::ADVOB_TXT, 161);
 
 			info.resize(2);
-			loadRandomArtifact(info[0], 30, 50, 25, 5);
+			loadRandomArtifact(rand, info[0], 30, 50, 25, 5);
 
 			Bonus bonus(Bonus::ONE_BATTLE, Bonus::MORALE, Bonus::OBJECT, -3, ID);
 			info[0].reward.bonuses.push_back(bonus);
@@ -866,19 +866,19 @@ void CGOnceVisitable::initObj()
 			soundID = soundBase::GENIE;
 			onVisited.addTxt(MetaString::ADVOB_TXT, 156);
 
-			int hlp = cb->gameState()->getRandomGenerator().nextInt(99);
+			int hlp = rand.nextInt(99);
 
 			if(hlp < 40) //minor or treasure art
 			{
 				info.resize(1);
-				loadRandomArtifact(info[0], 10, 10, 0, 0);
+				loadRandomArtifact(rand, info[0], 10, 10, 0, 0);
 				info[0].message.addTxt(MetaString::ADVOB_TXT, 155);
 			}
 			else if(hlp < 90) //2 - 5 of non-gold resource
 			{
 				info.resize(1);
-				int type  = cb->gameState()->getRandomGenerator().nextInt(5);
-				int value = cb->gameState()->getRandomGenerator().nextInt(2, 5);
+				int type  = rand.nextInt(5);
+				int value = rand.nextInt(2, 5);
 				info[0].reward.resources[type] = value;
 				info[0].message.addTxt(MetaString::ADVOB_TXT, 154);
 			}
@@ -896,7 +896,7 @@ CGVisitableOPH::CGVisitableOPH()
 	selectMode = SELECT_PLAYER;
 }
 
-void CGVisitableOPH::initObj()
+void CGVisitableOPH::initObj(CRandomGenerator & rand)
 {
 	switch(ID)
 	{
@@ -951,7 +951,7 @@ void CGVisitableOPH::initObj()
 			info[0].reward.gainedLevels = 1;
 			onVisited.addTxt(MetaString::ADVOB_TXT, 147);
 			info.resize(1);
-			switch (cb->gameState()->getRandomGenerator().nextInt(2))
+			switch (rand.nextInt(2))
 			{
 			case 0: // free
 				onSelect.addTxt(MetaString::ADVOB_TXT, 148);
@@ -1029,7 +1029,7 @@ CGVisitableOPW::CGVisitableOPW()
 	resetDuration = 7;
 }
 
-void CGVisitableOPW::initObj()
+void CGVisitableOPW::initObj(CRandomGenerator & rand)
 {
 	switch (ID)
 	{
@@ -1087,7 +1087,7 @@ void CGVisitableOPW::setPropertyDer(ui8 what, ui32 val)
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-void CGMagicSpring::initObj()
+void CGMagicSpring::initObj(CRandomGenerator & rand)
 {
 	CVisitInfo visit; // TODO: "player above max mana" limiter. Use logical expressions for limiters?
 	visit.reward.manaPercentage = 200;

+ 7 - 7
lib/mapObjects/CRewardableObject.h

@@ -227,7 +227,7 @@ public:
 	void onHeroVisit(const CGHeroInstance *h) const override;
 
 	///possibly resets object state
-	void newTurn() const override;
+	void newTurn(CRandomGenerator & rand) const override;
 
 	/// gives second part of reward after hero level-ups for proper granting of spells/mana
 	void heroLevelUpDone(const CGHeroInstance *hero) const override;
@@ -255,7 +255,7 @@ public:
 class DLL_LINKAGE CGPickable : public CRewardableObject //campfire, treasure chest, Flotsam, Shipwreck Survivor, Sea Chest
 {
 public:
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 
 	CGPickable();
 
@@ -273,7 +273,7 @@ protected:
 	void grantReward(ui32 rewardID, const CGHeroInstance * hero) const override;
 
 public:
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 
 	CGBonusingObject();
 
@@ -290,7 +290,7 @@ public:
 class DLL_LINKAGE CGOnceVisitable : public CRewardableObject // wagon, corpse, lean to, warriors tomb
 {
 public:
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 
 	CGOnceVisitable();
 
@@ -303,7 +303,7 @@ public:
 class DLL_LINKAGE CGVisitableOPH : public CRewardableObject //objects visitable only once per hero
 {
 public:
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 
 	CGVisitableOPH();
 
@@ -316,7 +316,7 @@ public:
 class DLL_LINKAGE CGVisitableOPW : public CRewardableObject //objects visitable once per week
 {
 public:
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 
 	CGVisitableOPW();
 
@@ -335,7 +335,7 @@ protected:
 	std::vector<ui32> getAvailableRewards(const CGHeroInstance * hero) const override;
 
 public:
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 	std::vector<int3> getVisitableOffsets() const;
 	int3 getVisitableOffset() const override;
 

+ 37 - 37
lib/mapObjects/MiscObjects.cpp

@@ -201,7 +201,7 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
 
 }
 
-void CGCreature::initObj()
+void CGCreature::initObj(CRandomGenerator & rand)
 {
 	blockVisit = true;
 	switch(character)
@@ -210,13 +210,13 @@ void CGCreature::initObj()
 		character = -4;
 		break;
 	case 1:
-		character = cb->gameState()->getRandomGenerator().nextInt(1, 7);
+		character = rand.nextInt(1, 7);
 		break;
 	case 2:
-		character = cb->gameState()->getRandomGenerator().nextInt(1, 10);
+		character = rand.nextInt(1, 10);
 		break;
 	case 3:
-		character = cb->gameState()->getRandomGenerator().nextInt(4, 10);
+		character = rand.nextInt(4, 10);
 		break;
 	case 4:
 		character = 10;
@@ -228,7 +228,7 @@ void CGCreature::initObj()
 	CCreature &c = *VLC->creh->creatures[subID];
 	if(amount == 0)
 	{
-		amount = cb->gameState()->getRandomGenerator().nextInt(c.ammMin, c.ammMax);
+		amount = rand.nextInt(c.ammMin, c.ammMax);
 
 		if(amount == 0) //armies with 0 creatures are illegal
 		{
@@ -241,7 +241,7 @@ void CGCreature::initObj()
 	refusedJoining = false;
 }
 
-void CGCreature::newTurn() const
+void CGCreature::newTurn(CRandomGenerator & rand) const
 {//Works only for stacks of single type of size up to 2 millions
 	if (!notGrowingTeam)
 	{
@@ -434,7 +434,7 @@ void CGCreature::fight( const CGHeroInstance *h ) const
 			const auto & upgrades = getStack(slotID).type->upgrades;
 			if(!upgrades.empty())
 			{
-				auto it = RandomGeneratorUtil::nextItem(upgrades, cb->getRandomGenerator());
+				auto it = RandomGeneratorUtil::nextItem(upgrades, CRandomGenerator::getDefault());
 				cb->changeStackType(StackLocation(this, slotID), VLC->creh->creatures[*it]);
 			}
 		}
@@ -676,7 +676,7 @@ void CGMine::onHeroVisit( const CGHeroInstance * h ) const
 
 }
 
-void CGMine::newTurn() const
+void CGMine::newTurn(CRandomGenerator & rand) const
 {
 	if(cb->getDate() == 1)
 		return;
@@ -687,12 +687,12 @@ void CGMine::newTurn() const
 	cb->giveResource(tempOwner, producedResource, producedQuantity);
 }
 
-void CGMine::initObj()
+void CGMine::initObj(CRandomGenerator & rand)
 {
 	if(isAbandoned())
 	{
 		//set guardians
-		int howManyTroglodytes = cb->gameState()->getRandomGenerator().nextInt(100, 199);
+		int howManyTroglodytes = rand.nextInt(100, 199);
 		auto troglodytes = new CStackInstance(CreatureID::TROGLODYTES, howManyTroglodytes);
 		putStack(SlotID(0), troglodytes);
 
@@ -703,7 +703,7 @@ void CGMine::initObj()
 				possibleResources.push_back(static_cast<Res::ERes>(i));
 
 		assert(!possibleResources.empty());
-		producedResource = *RandomGeneratorUtil::nextItem(possibleResources, cb->gameState()->getRandomGenerator());
+		producedResource = *RandomGeneratorUtil::nextItem(possibleResources, rand);
 		tempOwner = PlayerColor::NEUTRAL;
 	}
 	else
@@ -859,7 +859,7 @@ CGResource::CGResource()
 	amount = 0;
 }
 
-void CGResource::initObj()
+void CGResource::initObj(CRandomGenerator & rand)
 {
 	blockVisit = true;
 
@@ -868,13 +868,13 @@ void CGResource::initObj()
 		switch(subID)
 		{
 		case 6:
-			amount = cb->gameState()->getRandomGenerator().nextInt(500, 1000);
+			amount = rand.nextInt(500, 1000);
 			break;
 		case 0: case 2:
-			amount = cb->gameState()->getRandomGenerator().nextInt(6, 10);
+			amount = rand.nextInt(6, 10);
 			break;
 		default:
-			amount = cb->gameState()->getRandomGenerator().nextInt(3, 5);
+			amount = rand.nextInt(3, 5);
 			break;
 		}
 	}
@@ -987,7 +987,7 @@ ObjectInstanceID CGTeleport::getRandomExit(const CGHeroInstance * h) const
 {
 	auto passableExits = getPassableExits(cb->gameState(), h, getAllExits(true));
 	if(passableExits.size())
-		return *RandomGeneratorUtil::nextItem(passableExits, cb->getRandomGenerator());
+		return *RandomGeneratorUtil::nextItem(passableExits, CRandomGenerator::getDefault());
 
 	return ObjectInstanceID();
 }
@@ -1119,7 +1119,7 @@ void CGMonolith::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer,
 	cb->moveHero(hero->id, dPos, true);
 }
 
-void CGMonolith::initObj()
+void CGMonolith::initObj(CRandomGenerator & rand)
 {
 	std::vector<Obj> IDs;
 	IDs.push_back(ID);
@@ -1164,7 +1164,7 @@ void CGSubterraneanGate::onHeroVisit( const CGHeroInstance * h ) const
 	cb->showTeleportDialog(&td);
 }
 
-void CGSubterraneanGate::initObj()
+void CGSubterraneanGate::initObj(CRandomGenerator & rand)
 {
 	type = BOTH;
 }
@@ -1283,7 +1283,7 @@ void CGWhirlpool::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer
 	{
 		auto obj = cb->getObj(getRandomExit(hero));
 		std::set<int3> tiles = obj->getBlockedPos();
-		dPos = CGHeroInstance::convertPosition(*RandomGeneratorUtil::nextItem(tiles, cb->getRandomGenerator()), true);
+		dPos = CGHeroInstance::convertPosition(*RandomGeneratorUtil::nextItem(tiles, CRandomGenerator::getDefault()), true);
 	}
 
 	cb->moveHero(hero->id, dPos, true);
@@ -1299,7 +1299,7 @@ bool CGWhirlpool::isProtected(const CGHeroInstance * h)
 	return false;
 }
 
-void CGArtifact::initObj()
+void CGArtifact::initObj(CRandomGenerator & rand)
 {
 	blockVisit = true;
 	if(ID == Obj::ARTIFACT)
@@ -1441,14 +1441,14 @@ void CGArtifact::serializeJsonOptions(JsonSerializeFormat& handler)
 	}
 }
 
-void CGWitchHut::initObj()
+void CGWitchHut::initObj(CRandomGenerator & rand)
 {
 	if (allowedAbilities.empty()) //this can happen for RMG. regular maps load abilities from map file
 	{
 		for (int i = 0; i < GameConstants::SKILL_QUANTITY; i++)
 			allowedAbilities.push_back(i);
 	}
-	ability = *RandomGeneratorUtil::nextItem(allowedAbilities, cb->gameState()->getRandomGenerator());
+	ability = *RandomGeneratorUtil::nextItem(allowedAbilities, rand);
 }
 
 void CGWitchHut::onHeroVisit( const CGHeroInstance * h ) const
@@ -1627,7 +1627,7 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const
 	cb->showInfoDialog(&iw);
 }
 
-void CGShrine::initObj()
+void CGShrine::initObj(CRandomGenerator & rand)
 {
 	if(spell == SpellID::NONE) //spell not set
 	{
@@ -1641,7 +1641,7 @@ void CGShrine::initObj()
 			return;
 		}
 
-		spell = *RandomGeneratorUtil::nextItem(possibilities, cb->gameState()->getRandomGenerator());
+		spell = *RandomGeneratorUtil::nextItem(possibilities, rand);
 	}
 }
 
@@ -1669,12 +1669,12 @@ void CGShrine::serializeJsonOptions(JsonSerializeFormat& handler)
 	handler.serializeId("spell", &CSpellHandler::decodeSpell, &CSpellHandler::encodeSpell, SpellID(SpellID::NONE), spell);
 }
 
-void CGSignBottle::initObj()
+void CGSignBottle::initObj(CRandomGenerator & rand)
 {
 	//if no text is set than we pick random from the predefined ones
 	if(message.empty())
 	{
-		message = *RandomGeneratorUtil::nextItem(VLC->generaltexth->randsign, cb->gameState()->getRandomGenerator());
+		message = *RandomGeneratorUtil::nextItem(VLC->generaltexth->randsign, rand);
 	}
 
 	if(ID == Obj::OCEAN_BOTTLE)
@@ -1714,7 +1714,7 @@ void CGScholar::onHeroVisit( const CGHeroInstance * h ) const
 		))) //hero doesn't have a spellbook or already knows the spell or doesn't have Wisdom
 	{
 		type = PRIM_SKILL;
-		bid = cb->getRandomGenerator().nextInt(GameConstants::PRIMARY_SKILLS - 1);
+		bid = CRandomGenerator::getDefault().nextInt(GameConstants::PRIMARY_SKILLS - 1);
 	}
 
 	InfoWindow iw;
@@ -1749,25 +1749,25 @@ void CGScholar::onHeroVisit( const CGHeroInstance * h ) const
 	cb->removeObject(this);
 }
 
-void CGScholar::initObj()
+void CGScholar::initObj(CRandomGenerator & rand)
 {
 	blockVisit = true;
 	if(bonusType == RANDOM)
 	{
-		bonusType = static_cast<EBonusType>(cb->gameState()->getRandomGenerator().nextInt(2));
+		bonusType = static_cast<EBonusType>(rand.nextInt(2));
 		switch(bonusType)
 		{
 		case PRIM_SKILL:
-			bonusID = cb->gameState()->getRandomGenerator().nextInt(GameConstants::PRIMARY_SKILLS -1);
+			bonusID = rand.nextInt(GameConstants::PRIMARY_SKILLS -1);
 			break;
 		case SECONDARY_SKILL:
-			bonusID = cb->gameState()->getRandomGenerator().nextInt(GameConstants::SKILL_QUANTITY -1);
+			bonusID = rand.nextInt(GameConstants::SKILL_QUANTITY -1);
 			break;
 		case SPELL:
 			std::vector<SpellID> possibilities;
 			for (int i = 1; i < 6; ++i)
 				cb->getAllowedSpells (possibilities, i);
-			bonusID = *RandomGeneratorUtil::nextItem(possibilities, cb->gameState()->getRandomGenerator());
+			bonusID = *RandomGeneratorUtil::nextItem(possibilities, rand);
 			break;
 		}
 	}
@@ -1874,7 +1874,7 @@ void CGMagi::reset()
 	eyelist.clear();
 }
 
-void CGMagi::initObj()
+void CGMagi::initObj(CRandomGenerator & rand)
 {
 	if (ID == Obj::EYE_OF_MAGI)
 	{
@@ -1920,12 +1920,12 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const
 	}
 
 }
-void CGBoat::initObj()
+void CGBoat::initObj(CRandomGenerator & rand)
 {
 	hero = nullptr;
 }
 
-void CGSirens::initObj()
+void CGSirens::initObj(CRandomGenerator & rand)
 {
 	blockVisit = true;
 }
@@ -2111,7 +2111,7 @@ void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const
 
 }
 
-void CGObelisk::initObj()
+void CGObelisk::initObj(CRandomGenerator & rand)
 {
 	obeliskCount++;
 }
@@ -2172,7 +2172,7 @@ void CGLighthouse::onHeroVisit( const CGHeroInstance * h ) const
 	}
 }
 
-void CGLighthouse::initObj()
+void CGLighthouse::initObj(CRandomGenerator & rand)
 {
 	if(tempOwner < PlayerColor::PLAYER_LIMIT)
 	{

+ 17 - 17
lib/mapObjects/MiscObjects.h

@@ -59,8 +59,8 @@ public:
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	std::string getHoverText(PlayerColor player) const override;
 	std::string getHoverText(const CGHeroInstance * hero) const override;
-	void initObj() override;
-	void newTurn() const override;
+	void initObj(CRandomGenerator & rand) override;
+	void newTurn(CRandomGenerator & rand) const override;
 	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
 	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
 
@@ -105,7 +105,7 @@ public:
 	std::string message;
 
 	void onHeroVisit(const CGHeroInstance * h) const override;
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -125,7 +125,7 @@ public:
 	std::string getHoverText(PlayerColor player) const override;
 	std::string getHoverText(const CGHeroInstance * hero) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CPlayersVisited&>(*this);
@@ -144,7 +144,7 @@ public:
 
 	CGScholar() : bonusType(EBonusType::RANDOM){};
 	void onHeroVisit(const CGHeroInstance * h) const override;
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CGObjectInstance&>(*this);
@@ -187,7 +187,7 @@ public:
 	std::string getObjectName() const override;
 
 	void pick( const CGHeroInstance * h ) const;
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -206,7 +206,7 @@ public:
 
 	CGResource();
 	void onHeroVisit(const CGHeroInstance * h) const override;
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
 	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
 	std::string getHoverText(PlayerColor player) const override;
@@ -227,7 +227,7 @@ class DLL_LINKAGE CGShrine : public CPlayersVisited
 public:
 	SpellID spell; //id of spell or NONE if random
 	void onHeroVisit(const CGHeroInstance * h) const override;
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 	std::string getHoverText(PlayerColor player) const override;
 	std::string getHoverText(const CGHeroInstance * hero) const override;
 
@@ -252,8 +252,8 @@ private:
 	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
 
 	void flagMine(PlayerColor player) const;
-	void newTurn() const override;
-	void initObj() override;
+	void newTurn(CRandomGenerator & rand) const override;
+	void initObj(CRandomGenerator & rand) override;
 
 	std::string getObjectName() const override;
 	std::string getHoverText(PlayerColor player) const override;
@@ -329,7 +329,7 @@ class DLL_LINKAGE CGMonolith : public CGTeleport
 protected:
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const override;
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 
 public:
 	template <typename Handler> void serialize(Handler &h, const int version)
@@ -341,7 +341,7 @@ public:
 class DLL_LINKAGE CGSubterraneanGate : public CGMonolith
 {
 	void onHeroVisit(const CGHeroInstance * h) const override;
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 
 public:
 	static void postInit();
@@ -382,7 +382,7 @@ class DLL_LINKAGE CGSirens : public CGObjectInstance
 public:
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	std::string getHoverText(const CGHeroInstance * hero) const override;
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -407,7 +407,7 @@ public:
 	ui8 direction;
 	const CGHeroInstance *hero;  //hero on board
 
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 
 	CGBoat()
 	{
@@ -443,7 +443,7 @@ public:
 
 	static void reset();
 
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
@@ -478,7 +478,7 @@ public:
 	static std::map<TeamID, ui8> visited; //map: team_id => how many obelisks has been visited
 
 	void onHeroVisit(const CGHeroInstance * h) const override;
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 	std::string getHoverText(PlayerColor player) const override;
 	static void reset();
 
@@ -494,7 +494,7 @@ class DLL_LINKAGE CGLighthouse : public CGObjectInstance
 {
 public:
 	void onHeroVisit(const CGHeroInstance * h) const override;
-	void initObj() override;
+	void initObj(CRandomGenerator & rand) override;
 	std::string getHoverText(PlayerColor player) const override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)

+ 9 - 4
server/CGameHandler.cpp

@@ -720,7 +720,7 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result )
 
 	if (necroSlot != SlotID())
 	{
-		finishingBattle->winnerHero->showNecromancyDialog(raisedStack);
+		finishingBattle->winnerHero->showNecromancyDialog(raisedStack, getRandomGenerator());
 		addToSlot(StackLocation(finishingBattle->winnerHero, necroSlot), raisedStack.type, raisedStack.count);
 	}
 
@@ -1717,7 +1717,7 @@ void CGameHandler::newTurn()
 	for(auto & elem : gs->map->objects)
 	{
 		if(elem)
-			elem->newTurn();
+			elem->newTurn(getRandomGenerator());
 	}
 
 	synchronizeArtifactHandlerLists(); //new day events may have changed them. TODO better of managing that
@@ -4177,8 +4177,8 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 			const Bonus * spellcaster = stack->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPELLCASTER, spellID));
 
 			//TODO special bonus for genies ability
-			if(randSpellcaster && battleGetRandomStackSpell(stack, CBattleInfoCallback::RANDOM_AIMED) < 0)
-				spellID = battleGetRandomStackSpell(stack, CBattleInfoCallback::RANDOM_GENIE);
+			if(randSpellcaster && battleGetRandomStackSpell(getRandomGenerator(), stack, CBattleInfoCallback::RANDOM_AIMED) < 0)
+				spellID = battleGetRandomStackSpell(getRandomGenerator(), stack, CBattleInfoCallback::RANDOM_GENIE);
 
 			if(spellID < 0)
 				complain("That stack can't cast spells!");
@@ -6247,6 +6247,11 @@ CGameHandler::FinishingBattleHelper::FinishingBattleHelper()
 	winnerHero = loserHero = nullptr;
 }
 
+CRandomGenerator & CGameHandler::getRandomGenerator()
+{
+	return CRandomGenerator::getDefault();
+}
+
 ///ServerSpellCastEnvironment
 ServerSpellCastEnvironment::ServerSpellCastEnvironment(CGameHandler * gh): gh(gh)
 {

+ 3 - 1
server/CGameHandler.h

@@ -249,7 +249,7 @@ public:
 		h & QID & states & finishingBattle;
 		if(version >= 761)
 		{
-			h & rand;
+			h & getRandomGenerator();
 		}
 	}
 
@@ -294,6 +294,8 @@ public:
 	void spawnWanderingMonsters(CreatureID creatureID);
 	friend class CVCMIServer;
 
+	CRandomGenerator & getRandomGenerator();
+
 private:
 	ServerSpellCastEnvironment * spellEnv;