1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-28 08:48:48 +02:00

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.
This commit is contained in:
Arseniy Shestakov 2016-09-09 20:30:36 +03:00
parent 70abae9b51
commit c8faca8f39
28 changed files with 225 additions and 219 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
{

View File

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

View File

@ -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)
{

View File

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