1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

- random number generation refactoring

- fixed mantis #1743
This commit is contained in:
beegee1 2014-04-10 19:11:09 +02:00
parent d234f8627b
commit 1d57b75bc5
27 changed files with 466 additions and 273 deletions

1
.gitignore vendored
View File

@ -7,3 +7,4 @@
*.layout *.layout
*.pro.user *.pro.user
*.pro.user.* *.pro.user.*
/CMakeLists.txt.user

View File

@ -1,6 +1,8 @@
#include "StdInc.h" #include "StdInc.h"
#include "CEmptyAI.h" #include "CEmptyAI.h"
#include "CRandomGenerator.h"
void CEmptyAI::init(shared_ptr<CCallback> CB) void CEmptyAI::init(shared_ptr<CCallback> CB)
{ {
cb = CB; cb = CB;
@ -15,12 +17,12 @@ void CEmptyAI::yourTurn()
void CEmptyAI::heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID) void CEmptyAI::heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID)
{ {
cb->selectionMade(rand() % skills.size(), queryID); cb->selectionMade(CRandomGenerator::getDefault().nextInt(skills.size() - 1), queryID);
} }
void CEmptyAI::commanderGotLevel(const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) void CEmptyAI::commanderGotLevel(const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID)
{ {
cb->selectionMade(rand() % skills.size(), queryID); cb->selectionMade(CRandomGenerator::getDefault().nextInt(skills.size() - 1), queryID);
} }
void CEmptyAI::showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel) void CEmptyAI::showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel)

View File

@ -104,8 +104,9 @@ BattleAction CStupidAI::activeStack( const CStack * stack )
if(stack->type->idNumber == CreatureID::CATAPULT) if(stack->type->idNumber == CreatureID::CATAPULT)
{ {
BattleAction attack; BattleAction attack;
static const int wallHexes[] = {50, 183, 182, 130, 62, 29, 12, 95}; static const std::vector<int> wallHexes = boost::assign::list_of(50)(183)(182)(130)(62)(29)(12)(95);
attack.destinationTile = wallHexes[ rand()%ARRAY_COUNT(wallHexes) ];
attack.destinationTile = *RandomGeneratorUtil::nextItem(wallHexes, CRandomGenerator::getDefault());
attack.actionType = Battle::CATAPULT; attack.actionType = Battle::CATAPULT;
attack.additionalInfo = 0; attack.additionalInfo = 0;
attack.side = side; attack.side = side;

View File

@ -553,19 +553,6 @@ namespace vstd
}); });
} }
static inline int retreiveRandNum(const std::function<int()> &randGen)
{
if (randGen)
return randGen();
else
return rand();
}
template <typename T> const T & pickRandomElementOf(const std::vector<T> &v, const std::function<int()> &randGen)
{
return v.at(retreiveRandNum(randGen) % v.size());
}
template<typename T> template<typename T>
void advance(T &obj, int change) void advance(T &obj, int change)
{ {

View File

@ -4,6 +4,7 @@
#include "../lib/filesystem/Filesystem.h" #include "../lib/filesystem/Filesystem.h"
#include "../lib/filesystem/ISimpleResourceLoader.h" #include "../lib/filesystem/ISimpleResourceLoader.h"
#include "../lib/JsonNode.h" #include "../lib/JsonNode.h"
#include "../lib/CRandomGenerator.h"
#include "CBitmapHandler.h" #include "CBitmapHandler.h"
#include "Graphics.h" #include "Graphics.h"
@ -1432,7 +1433,7 @@ void CCreatureAnim::loopPreview(bool warMachine)
if (anim.size(elem)) if (anim.size(elem))
available.push_back(elem); available.push_back(elem);
size_t rnd = rand()%(available.size()*2); size_t rnd = CRandomGenerator::getDefault().nextInt(available.size() * 2 - 1);
if (rnd >= available.size()) if (rnd >= available.size())
{ {

View File

@ -9,6 +9,7 @@
#include "../lib/GameConstants.h" #include "../lib/GameConstants.h"
#include "../lib/filesystem/Filesystem.h" #include "../lib/filesystem/Filesystem.h"
#include "../lib/StringConstants.h" #include "../lib/StringConstants.h"
#include "../lib/CRandomGenerator.h"
/* /*
* CMusicHandler.cpp, part of VCMI engine * CMusicHandler.cpp, part of VCMI engine
@ -230,7 +231,7 @@ int CSoundHandler::playSound(std::string sound, int repeats)
// Helper. Randomly select a sound from an array and play it // Helper. Randomly select a sound from an array and play it
int CSoundHandler::playSoundFromSet(std::vector<soundBase::soundID> &sound_vec) int CSoundHandler::playSoundFromSet(std::vector<soundBase::soundID> &sound_vec)
{ {
return playSound(sound_vec[rand() % sound_vec.size()]); return playSound(*RandomGeneratorUtil::nextItem(sound_vec, CRandomGenerator::getDefault()));
} }
void CSoundHandler::stopSound( int handler ) void CSoundHandler::stopSound( int handler )
@ -504,10 +505,7 @@ bool MusicEntry::play()
if (!setName.empty()) if (!setName.empty())
{ {
auto set = owner->musicsSet[setName]; auto set = owner->musicsSet[setName];
size_t entryID = rand() % set.size(); load(RandomGeneratorUtil::nextItem(set, CRandomGenerator::getDefault())->second);
auto iterator = set.begin();
std::advance(iterator, entryID);
load(iterator->second);
} }
logGlobal->traceStream()<<"Playing music file "<<currentName; logGlobal->traceStream()<<"Playing music file "<<currentName;

View File

@ -43,6 +43,7 @@
#include "gui/CIntObjectClasses.h" #include "gui/CIntObjectClasses.h"
#include "../lib/mapping/CMapService.h" #include "../lib/mapping/CMapService.h"
#include "../lib/mapping/CMap.h" #include "../lib/mapping/CMap.h"
#include "../lib/CRandomGenerator.h"
/* /*
* CPreGame.cpp, part of VCMI engine * CPreGame.cpp, part of VCMI engine
@ -607,7 +608,7 @@ CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMulti
bordered = true; bordered = true;
//load random background //load random background
const JsonVector & bgNames = CGPreGameConfig::get().getConfig()["game-select"].Vector(); const JsonVector & bgNames = CGPreGameConfig::get().getConfig()["game-select"].Vector();
bg = new CPicture(bgNames[rand() % bgNames.size()].String(), 0, 0); bg = new CPicture(RandomGeneratorUtil::nextItem(bgNames, CRandomGenerator::getDefault())->String(), 0, 0);
pos = bg->center(); pos = bg->center();
} }
@ -4136,7 +4137,7 @@ std::string CLoadingScreen::getBackground()
} }
else else
{ {
return conf[ rand() % conf.size() ].String(); return RandomGeneratorUtil::nextItem(conf, CRandomGenerator::getDefault())->String();
} }
} }

View File

@ -26,6 +26,7 @@
#include "../CVideoHandler.h" #include "../CVideoHandler.h"
#include "../../lib/CTownHandler.h" #include "../../lib/CTownHandler.h"
#include "../../lib/mapping/CMap.h" #include "../../lib/mapping/CMap.h"
#include "../../lib/CRandomGenerator.h"
#include "CBattleAnimations.h" #include "CBattleAnimations.h"
#include "CBattleInterfaceClasses.h" #include "CBattleInterfaceClasses.h"
@ -59,7 +60,7 @@ static void onAnimationFinished(const CStack *stack, CCreatureAnimation * anim)
if (anim->framesInGroup(CCreatureAnim::MOUSEON) > 0) if (anim->framesInGroup(CCreatureAnim::MOUSEON) > 0)
{ {
if (float(rand() % 100) < creature->animation.timeBetweenFidgets * 10) if (CRandomGenerator::getDefault().nextDouble(99.0) < creature->animation.timeBetweenFidgets * 10)
anim->playOnce(CCreatureAnim::MOUSEON); anim->playOnce(CCreatureAnim::MOUSEON);
else else
anim->setType(CCreatureAnim::HOLDING); anim->setType(CCreatureAnim::HOLDING);
@ -194,7 +195,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
logGlobal->errorStream() << bfieldType << " battlefield type does not have any backgrounds!"; logGlobal->errorStream() << bfieldType << " battlefield type does not have any backgrounds!";
else else
{ {
const std::string bgName = vstd::pickRandomElementOf(graphics->battleBacks[bfieldType], rand); const std::string bgName = *RandomGeneratorUtil::nextItem(graphics->battleBacks[bfieldType], CRandomGenerator::getDefault());
background = BitmapHandler::loadBitmap(bgName, false); background = BitmapHandler::loadBitmap(bgName, false);
} }
} }

View File

@ -17,6 +17,7 @@
#include "../lib/GameConstants.h" #include "../lib/GameConstants.h"
#include "../lib/CStopWatch.h" #include "../lib/CStopWatch.h"
#include "CMT.h" #include "CMT.h"
#include "../lib/CRandomGenerator.h"
/* /*
* mapHandler.cpp, part of VCMI engine * mapHandler.cpp, part of VCMI engine
@ -129,7 +130,7 @@ void CMapHandler::prepareFOWDefs()
elem[j].resize(sizes.z); elem[j].resize(sizes.z);
for(int k = 0; k < sizes.z; ++k) for(int k = 0; k < sizes.z; ++k)
{ {
elem[j][k] = rand()%graphics->FoWfullHide->ourImages.size(); elem[j][k] = CRandomGenerator::getDefault().nextInt(graphics->FoWfullHide->ourImages.size() - 1);
} }
} }
} }
@ -199,6 +200,8 @@ void CMapHandler::borderAndTerrainBitmapInit()
{ {
int terBitmapNum = -1; int terBitmapNum = -1;
auto & rand = CRandomGenerator::getDefault();
if(i==-1 && j==-1) if(i==-1 && j==-1)
terBitmapNum = 16; terBitmapNum = 16;
else if(i==-1 && j==(sizes.y)) else if(i==-1 && j==(sizes.y))
@ -208,15 +211,15 @@ void CMapHandler::borderAndTerrainBitmapInit()
else if(i==(sizes.x) && j==(sizes.y)) else if(i==(sizes.x) && j==(sizes.y))
terBitmapNum = 18; terBitmapNum = 18;
else if(j == -1 && i > -1 && i < sizes.y) else if(j == -1 && i > -1 && i < sizes.y)
terBitmapNum = 22+rand()%2; terBitmapNum = rand.nextInt(22, 23);
else if(i == -1 && j > -1 && j < sizes.y) else if(i == -1 && j > -1 && j < sizes.y)
terBitmapNum = 33+rand()%2; terBitmapNum = rand.nextInt(33, 34);
else if(j == sizes.y && i >-1 && i < sizes.x) else if(j == sizes.y && i >-1 && i < sizes.x)
terBitmapNum = 29+rand()%2; terBitmapNum = rand.nextInt(29, 30);
else if(i == sizes.x && j > -1 && j < sizes.y) else if(i == sizes.x && j > -1 && j < sizes.y)
terBitmapNum = 25+rand()%2; terBitmapNum = rand.nextInt(25, 26);
else else
terBitmapNum = rand()%16; terBitmapNum = rand.nextInt(15);
if(terBitmapNum != -1) if(terBitmapNum != -1)
{ {
@ -1083,7 +1086,7 @@ ui8 CMapHandler::getPhaseShift(const CGObjectInstance *object) const
auto i = animationPhase.find(object); auto i = animationPhase.find(object);
if(i == animationPhase.end()) if(i == animationPhase.end())
{ {
ui8 ret = rand() % 255; ui8 ret = CRandomGenerator::getDefault().nextInt(254);
animationPhase[object] = ret; animationPhase[object] = ret;
return ret; return ret;
} }

View File

@ -87,7 +87,7 @@ std::pair< std::vector<BattleHex>, int > BattleInfo::getPath(BattleHex start, Ba
} }
ui32 BattleInfo::calculateDmg( const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, ui32 BattleInfo::calculateDmg( const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero,
bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg ) bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg, CRandomGenerator & rand )
{ {
TDmgRange range = calculateDmgRange(attacker, defender, shooting, charge, lucky, unlucky, deathBlow, ballistaDoubleDmg); TDmgRange range = calculateDmgRange(attacker, defender, shooting, charge, lucky, unlucky, deathBlow, ballistaDoubleDmg);
@ -97,7 +97,7 @@ ui32 BattleInfo::calculateDmg( const CStack* attacker, const CStack* defender, c
int howManyToAv = std::min<ui32>(10, attacker->count); int howManyToAv = std::min<ui32>(10, attacker->count);
for (int g=0; g<howManyToAv; ++g) for (int g=0; g<howManyToAv; ++g)
{ {
valuesToAverage[g] = range.first + rand() % (range.second - range.first + 1); valuesToAverage[g] = rand.nextInt(range.first, range.second);
} }
return std::accumulate(valuesToAverage, valuesToAverage + howManyToAv, 0) / howManyToAv; return std::accumulate(valuesToAverage, valuesToAverage + howManyToAv, 0) / howManyToAv;
@ -723,7 +723,9 @@ const CGHeroInstance * BattleInfo::getHero( PlayerColor player ) const
return nullptr; return nullptr;
} }
std::vector<ui32> BattleInfo::calculateResistedStacks(const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::vector<const CStack*> & affectedCreatures, PlayerColor casterSideOwner, ECastingMode::ECastingMode mode, int usedSpellPower, int spellLevel) const std::vector<ui32> BattleInfo::calculateResistedStacks(const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2,
const std::vector<const CStack*> & affectedCreatures, PlayerColor casterSideOwner, ECastingMode::ECastingMode mode,
int usedSpellPower, int spellLevel, CRandomGenerator & rand) const
{ {
std::vector<ui32> ret; std::vector<ui32> ret;
for(auto & affectedCreature : affectedCreatures) for(auto & affectedCreature : affectedCreatures)
@ -749,8 +751,11 @@ std::vector<ui32> BattleInfo::calculateResistedStacks(const CSpell * sp, const C
if(prob > 100) prob = 100; if(prob > 100) prob = 100;
if(rand()%100 < prob) //immunity from resistance //immunity from resistance
if(rand.nextInt(99) < prob)
{
ret.push_back((affectedCreature)->ID); ret.push_back((affectedCreature)->ID);
}
} }

View File

@ -129,7 +129,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb
shared_ptr<CObstacleInstance> getObstacleOnTile(BattleHex tile) const; shared_ptr<CObstacleInstance> getObstacleOnTile(BattleHex tile) const;
std::set<BattleHex> getStoppers(bool whichSidePerspective) const; std::set<BattleHex> getStoppers(bool whichSidePerspective) const;
ui32 calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg); //charge - number of hexes travelled before attack (for champion's jousting) ui32 calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg, CRandomGenerator & rand); //charge - number of hexes travelled before attack (for champion's jousting)
void calculateCasualties(std::map<ui32,si32> *casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount) void calculateCasualties(std::map<ui32,si32> *casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount)
//void getPotentiallyAttackableHexes(AttackableTiles &at, const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos); //hexes around target that could be attacked in melee //void getPotentiallyAttackableHexes(AttackableTiles &at, const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos); //hexes around target that could be attacked in melee
@ -144,7 +144,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb
const CGHeroInstance * getHero(PlayerColor player) const; //returns fighting hero that belongs to given player const CGHeroInstance * getHero(PlayerColor player) const; //returns fighting hero that belongs to given player
std::vector<ui32> calculateResistedStacks(const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::vector<const CStack*> & affectedCreatures, PlayerColor casterSideOwner, ECastingMode::ECastingMode mode, int usedSpellPower, int spellLevel) const; std::vector<ui32> calculateResistedStacks(const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::vector<const CStack*> & affectedCreatures, PlayerColor casterSideOwner, ECastingMode::ECastingMode mode, int usedSpellPower, int spellLevel, CRandomGenerator & rand) const;
const CStack * battleGetStack(BattleHex pos, bool onlyAlive); //returns stack at given tile const CStack * battleGetStack(BattleHex pos, bool onlyAlive); //returns stack at given tile
const CGHeroInstance * battleGetOwner(const CStack * stack) const; //returns hero that owns given stack; nullptr if none const CGHeroInstance * battleGetOwner(const CStack * stack) const; //returns hero that owns given stack; nullptr if none

View File

@ -2251,10 +2251,14 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) co
} }
} }
if (possibleSpells.size()) if(!possibleSpells.empty())
return possibleSpells[rand() % possibleSpells.size()]; {
return *RandomGeneratorUtil::nextItem(possibleSpells, gs->getRandomGenerator());
}
else else
{
return SpellID::NONE; return SpellID::NONE;
}
} }
SpellID CBattleInfoCallback::getRandomCastedSpell(const CStack * caster) const SpellID CBattleInfoCallback::getRandomCastedSpell(const CStack * caster) const
@ -2269,7 +2273,7 @@ SpellID CBattleInfoCallback::getRandomCastedSpell(const CStack * caster) const
{ {
totalWeight += std::max(b->additionalInfo, 1); //minimal chance to cast is 1 totalWeight += std::max(b->additionalInfo, 1); //minimal chance to cast is 1
} }
int randomPos = rand() % totalWeight; int randomPos = gs->getRandomGenerator().nextInt(totalWeight - 1);
for(Bonus * b : *bl) for(Bonus * b : *bl)
{ {
randomPos -= std::max(b->additionalInfo, 1); randomPos -= std::max(b->additionalInfo, 1);

View File

@ -458,7 +458,7 @@ public:
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & scenarioOps & initialOpts & currentPlayer & day & map & players & teams & hpool & globalEffects; h & scenarioOps & initialOpts & currentPlayer & day & map & players & teams & hpool & globalEffects & rand;
BONUS_TREE_DESERIALIZATION_FIX BONUS_TREE_DESERIALIZATION_FIX
} }

View File

@ -23,7 +23,7 @@
* *
*/ */
SecondarySkill CHeroClass::chooseSecSkill(const std::set<SecondarySkill> & possibles, std::minstd_rand & distr) const //picks secondary skill out from given possibilities SecondarySkill CHeroClass::chooseSecSkill(const std::set<SecondarySkill> & possibles, CRandomGenerator & rand) const //picks secondary skill out from given possibilities
{ {
int totalProb = 0; int totalProb = 0;
for(auto & possible : possibles) for(auto & possible : possibles)
@ -32,12 +32,14 @@ SecondarySkill CHeroClass::chooseSecSkill(const std::set<SecondarySkill> & possi
} }
if (totalProb != 0) // may trigger if set contains only banned skills (0 probability) if (totalProb != 0) // may trigger if set contains only banned skills (0 probability)
{ {
int ran = distr()%totalProb; auto ran = rand.nextInt(totalProb - 1);
for(auto & possible : possibles) for(auto & possible : possibles)
{ {
ran -= secSkillProbability[possible]; ran -= secSkillProbability[possible];
if(ran<0) if(ran < 0)
{
return possible; return possible;
}
} }
} }
// FIXME: select randomly? How H3 handles such rare situation? // FIXME: select randomly? How H3 handles such rare situation?

View File

@ -21,6 +21,7 @@ class CGameInfo;
class CGHeroInstance; class CGHeroInstance;
struct BattleHex; struct BattleHex;
class JsonNode; class JsonNode;
class CRandomGenerator;
struct SSpecialtyInfo struct SSpecialtyInfo
{ si32 type; { si32 type;
@ -132,7 +133,7 @@ public:
CHeroClass(); CHeroClass();
bool isMagicHero() const; bool isMagicHero() const;
SecondarySkill chooseSecSkill(const std::set<SecondarySkill> & possibles, std::minstd_rand & distr) const; //picks secondary skill out from given possibilities SecondarySkill chooseSecSkill(const std::set<SecondarySkill> & possibles, CRandomGenerator & rand) const; //picks secondary skill out from given possibilities
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {

View File

@ -68,6 +68,7 @@ set(lib_SRCS
CHeroHandler.cpp CHeroHandler.cpp
CModHandler.cpp CModHandler.cpp
CObstacleInstance.cpp CObstacleInstance.cpp
CRandomGenerator.cpp
CSpellHandler.cpp CSpellHandler.cpp
CThreadHelper.cpp CThreadHelper.cpp
CTownHandler.cpp CTownHandler.cpp
@ -91,7 +92,6 @@ set(lib_HEADERS
CondSh.h CondSh.h
ConstTransitivePtr.h ConstTransitivePtr.h
CBonusTypeHandler.h CBonusTypeHandler.h
CRandomGenerator.h
CScriptingModule.h CScriptingModule.h
CStopWatch.h CStopWatch.h
FunctionList.h FunctionList.h

View File

@ -767,17 +767,14 @@ void CGHeroInstance::initHero()
} }
assert(validTypes()); assert(validTypes());
if (exp == 0xffffffff) level = 1;
if(exp == 0xffffffff)
{ {
initExp(); initExp();
} }
else if (ID != Obj::PRISON) else
{ {
level = VLC->heroh->level(exp); levelUpAutomatically();
}
else //warp hero at the beginning of next turn
{
level = 1;
} }
if (VLC->modh->modules.COMMANDERS && !commander) if (VLC->modh->modules.COMMANDERS && !commander)
@ -953,7 +950,7 @@ void CGHeroInstance::initObj()
if(!type) if(!type)
initHero(); //TODO: set up everything for prison before specialties are configured initHero(); //TODO: set up everything for prison before specialties are configured
skillsInfo.distribution.seed(rand()); skillsInfo.rand.setSeed(cb->gameState()->getRandomGenerator().nextInt());
skillsInfo.resetMagicSchoolCounter(); skillsInfo.resetMagicSchoolCounter();
skillsInfo.resetWisdomCounter(); skillsInfo.resetWisdomCounter();
@ -1552,7 +1549,6 @@ EAlignment::EAlignment CGHeroInstance::getAlignment() const
void CGHeroInstance::initExp() void CGHeroInstance::initExp()
{ {
exp = cb->gameState()->getRandomGenerator().nextInt(40, 89); exp = cb->gameState()->getRandomGenerator().nextInt(40, 89);
level = 1;
} }
std::string CGHeroInstance::nodeName() const std::string CGHeroInstance::nodeName() const
@ -1630,7 +1626,7 @@ ArtBearer::ArtBearer CGHeroInstance::bearerType() const
return ArtBearer::HERO; return ArtBearer::HERO;
} }
std::vector<SecondarySkill> CGHeroInstance::levelUpProposedSkills() const std::vector<SecondarySkill> CGHeroInstance::getLevelUpProposedSecondarySkills() const
{ {
std::vector<SecondarySkill> obligatorySkills; //hero is offered magic school or wisdom if possible std::vector<SecondarySkill> obligatorySkills; //hero is offered magic school or wisdom if possible
if (!skillsInfo.wisdomCounter) if (!skillsInfo.wisdomCounter)
@ -1643,11 +1639,7 @@ std::vector<SecondarySkill> CGHeroInstance::levelUpProposedSkills() const
std::vector<SecondarySkill> ss; std::vector<SecondarySkill> ss;
ss += SecondarySkill::FIRE_MAGIC, SecondarySkill::AIR_MAGIC, SecondarySkill::WATER_MAGIC, SecondarySkill::EARTH_MAGIC; ss += SecondarySkill::FIRE_MAGIC, SecondarySkill::AIR_MAGIC, SecondarySkill::WATER_MAGIC, SecondarySkill::EARTH_MAGIC;
auto rng = [=](ui32 val) mutable -> ui32 std::shuffle(ss.begin(), ss.end(), skillsInfo.rand.getStdGenerator());
{
return skillsInfo.distribution() % val; //must be determined
};
std::random_shuffle(ss.begin(), ss.end(), rng);
for (auto skill : ss) for (auto skill : ss)
{ {
@ -1691,12 +1683,12 @@ std::vector<SecondarySkill> CGHeroInstance::levelUpProposedSkills() const
} }
else if(none.size() && canLearnSkill()) //hero have free skill slot else if(none.size() && canLearnSkill()) //hero have free skill slot
{ {
skills.push_back(type->heroClass->chooseSecSkill(none, skillsInfo.distribution)); //new skill skills.push_back(type->heroClass->chooseSecSkill(none, skillsInfo.rand)); //new skill
none.erase(skills.back()); none.erase(skills.back());
} }
else if(!basicAndAdv.empty()) else if(!basicAndAdv.empty())
{ {
skills.push_back(type->heroClass->chooseSecSkill(basicAndAdv, skillsInfo.distribution)); //upgrade existing skills.push_back(type->heroClass->chooseSecSkill(basicAndAdv, skillsInfo.rand)); //upgrade existing
basicAndAdv.erase(skills.back()); basicAndAdv.erase(skills.back());
} }
@ -1706,7 +1698,7 @@ std::vector<SecondarySkill> CGHeroInstance::levelUpProposedSkills() const
//3) give any other new skill //3) give any other new skill
if(!basicAndAdv.empty()) if(!basicAndAdv.empty())
{ {
SecondarySkill s = type->heroClass->chooseSecSkill(basicAndAdv, skillsInfo.distribution);//upgrade existing SecondarySkill s = type->heroClass->chooseSecSkill(basicAndAdv, skillsInfo.rand);//upgrade existing
skills.push_back(s); skills.push_back(s);
basicAndAdv.erase(s); basicAndAdv.erase(s);
} }
@ -1716,18 +1708,147 @@ std::vector<SecondarySkill> CGHeroInstance::levelUpProposedSkills() const
} }
else if(none.size() && canLearnSkill()) else if(none.size() && canLearnSkill())
{ {
skills.push_back(type->heroClass->chooseSecSkill(none, skillsInfo.distribution)); //give new skill skills.push_back(type->heroClass->chooseSecSkill(none, skillsInfo.rand)); //give new skill
none.erase(skills.back()); none.erase(skills.back());
} }
return skills; return skills;
} }
PrimarySkill::PrimarySkill CGHeroInstance::nextPrimarySkill() const
{
assert(gainsLevel());
int randomValue = cb->gameState()->getRandomGenerator().nextInt(99), pom = 0, primarySkill = 0;
const auto & skillChances = (level > 9) ? type->heroClass->primarySkillLowLevel : type->heroClass->primarySkillHighLevel;
for(; primarySkill < GameConstants::PRIMARY_SKILLS; ++primarySkill)
{
pom += skillChances[primarySkill];
if(randomValue < pom)
{
break;
}
}
logGlobal->traceStream() << "The hero gets the primary skill " << primarySkill << " with a probability of " << randomValue << "%.";
return static_cast<PrimarySkill::PrimarySkill>(primarySkill);
}
boost::optional<SecondarySkill> CGHeroInstance::nextSecondarySkill() const
{
assert(gainsLevel());
boost::optional<SecondarySkill> chosenSecondarySkill;
const auto proposedSecondarySkills = getLevelUpProposedSecondarySkills();
if(!proposedSecondarySkills.empty())
{
std::vector<SecondarySkill> learnedSecondarySkills;
for(auto secondarySkill : proposedSecondarySkills)
{
if(getSecSkillLevel(secondarySkill) > 0)
{
learnedSecondarySkills.push_back(secondarySkill);
}
}
auto & rand = cb->gameState()->getRandomGenerator();
if(learnedSecondarySkills.empty())
{
// there are only new skills to learn, so choose anyone of them
chosenSecondarySkill = *RandomGeneratorUtil::nextItem(proposedSecondarySkills, rand);
}
else
{
// preferably upgrade a already learned secondary skill
chosenSecondarySkill = *RandomGeneratorUtil::nextItem(learnedSecondarySkills, rand);
}
}
return chosenSecondarySkill;
}
void CGHeroInstance::setPrimarySkill(PrimarySkill::PrimarySkill primarySkill, si64 value, ui8 abs)
{
if(primarySkill < PrimarySkill::EXPERIENCE)
{
Bonus * skill = getBonusLocalFirst(Selector::type(Bonus::PRIMARY_SKILL)
.And(Selector::subtype(primarySkill))
.And(Selector::sourceType(Bonus::HERO_BASE_SKILL)));
assert(skill);
if(abs)
{
skill->val = value;
}
else
{
skill->val += value;
}
}
else if(primarySkill == PrimarySkill::EXPERIENCE)
{
if(abs)
{
exp = value;
}
else
{
exp += value;
}
}
}
bool CGHeroInstance::gainsLevel() const bool CGHeroInstance::gainsLevel() const
{ {
return exp >= VLC->heroh->reqExp(level+1); return exp >= VLC->heroh->reqExp(level+1);
} }
void CGHeroInstance::levelUp(std::vector<SecondarySkill> skills)
{
++level;
//deterministic secondary skills
skillsInfo.magicSchoolCounter = (skillsInfo.magicSchoolCounter + 1) % maxlevelsToMagicSchool();
skillsInfo.wisdomCounter = (skillsInfo.wisdomCounter + 1) % maxlevelsToWisdom();
if(vstd::contains(skills, SecondarySkill::WISDOM))
{
skillsInfo.resetWisdomCounter();
}
SecondarySkill spellSchools[] = {
SecondarySkill::FIRE_MAGIC, SecondarySkill::AIR_MAGIC, SecondarySkill::WATER_MAGIC, SecondarySkill::EARTH_MAGIC};
for(auto skill : spellSchools)
{
if(vstd::contains(skills, skill))
{
skillsInfo.resetMagicSchoolCounter();
break;
}
}
//specialty
Updatespecialty();
}
void CGHeroInstance::levelUpAutomatically()
{
while(gainsLevel())
{
const auto primarySkill = nextPrimarySkill();
setPrimarySkill(primarySkill, 1, false);
auto proposedSecondarySkills = getLevelUpProposedSecondarySkills();
const auto secondarySkill = nextSecondarySkill();
if(secondarySkill)
{
setSecSkillLevel(*secondarySkill, 1, false);
}
//TODO why has the secondary skills to be passed to the method?
levelUp(proposedSecondarySkills);
}
}
void CGDwelling::initObj() void CGDwelling::initObj()
{ {
switch(ID) switch(ID)
@ -2303,13 +2424,15 @@ void CGTownInstance::newTurn() const
{ {
if (cb->getDate(Date::DAY_OF_WEEK) == 1) //reset on new week if (cb->getDate(Date::DAY_OF_WEEK) == 1) //reset on new week
{ {
auto & rand = cb->gameState()->getRandomGenerator();
//give resources for Rampart, Mystic Pond //give resources for Rampart, Mystic Pond
if (hasBuilt(BuildingID::MYSTIC_POND, ETownType::RAMPART) if (hasBuilt(BuildingID::MYSTIC_POND, ETownType::RAMPART)
&& cb->getDate(Date::DAY) != 1 && (tempOwner < PlayerColor::PLAYER_LIMIT)) && cb->getDate(Date::DAY) != 1 && (tempOwner < PlayerColor::PLAYER_LIMIT))
{ {
int resID = rand()%4+2;//bonus to random rare resource int resID = rand.nextInt(2, 5); //bonus to random rare resource
resID = (resID==2)?1:resID; resID = (resID==2)?1:resID;
int resVal = rand()%4+1;//with size 1..4 int resVal = rand.nextInt(1, 4);//with size 1..4
cb->giveResource(tempOwner, static_cast<Res::ERes>(resID), resVal); cb->giveResource(tempOwner, static_cast<Res::ERes>(resID), resVal);
cb->setObjProperty (id, ObjProperty::BONUS_VALUE_FIRST, resID); cb->setObjProperty (id, ObjProperty::BONUS_VALUE_FIRST, resID);
cb->setObjProperty (id, ObjProperty::BONUS_VALUE_SECOND, resVal); cb->setObjProperty (id, ObjProperty::BONUS_VALUE_SECOND, resVal);
@ -2334,11 +2457,11 @@ void CGTownInstance::newTurn() const
} }
if (nativeCrits.size()) if (nativeCrits.size())
{ {
SlotID pos = nativeCrits[rand() % nativeCrits.size()]; SlotID pos = *RandomGeneratorUtil::nextItem(nativeCrits, rand);
StackLocation sl(this, pos); StackLocation sl(this, pos);
const CCreature *c = getCreature(pos); const CCreature *c = getCreature(pos);
if (rand()%100 < 90 || c->upgrades.empty()) //increase number if no upgrade available if (rand.nextInt(99) < 90 || c->upgrades.empty()) //increase number if no upgrade available
{ {
cb->changeStackCount(sl, c->growth); cb->changeStackCount(sl, c->growth);
} }
@ -2347,9 +2470,9 @@ void CGTownInstance::newTurn() const
cb->changeStackType(sl, VLC->creh->creatures[*c->upgrades.begin()]); cb->changeStackType(sl, VLC->creh->creatures[*c->upgrades.begin()]);
} }
} }
if ((stacksCount() < GameConstants::ARMY_SIZE && rand()%100 < 25) || Slots().empty()) //add new stack if ((stacksCount() < GameConstants::ARMY_SIZE && rand.nextInt(99) < 25) || Slots().empty()) //add new stack
{ {
int i = rand() % std::min (GameConstants::CREATURES_PER_TOWN, cb->getDate(Date::MONTH)<<1); int i = rand.nextInt(std::min(GameConstants::CREATURES_PER_TOWN, cb->getDate(Date::MONTH) << 1) - 1);
if (!town->creatures[i].empty()) if (!town->creatures[i].empty())
{ {
CreatureID c = town->creatures[i][0]; CreatureID c = town->creatures[i][0];
@ -3316,7 +3439,7 @@ void CGCreature::initObj()
amount = 1; amount = 1;
} }
} }
formation.randomFormation = rand(); formation.randomFormation = cb->gameState()->getRandomGenerator().nextInt();
temppower = stacks[SlotID(0)]->count * 1000; temppower = stacks[SlotID(0)]->count * 1000;
refusedJoining = false; refusedJoining = false;
@ -3532,10 +3655,10 @@ void CGCreature::fight( const CGHeroInstance *h ) const
if (formation.randomFormation % 100 < 50) //upgrade if (formation.randomFormation % 100 < 50) //upgrade
{ {
SlotID slotId = SlotID(stacks.size() / 2); SlotID slotId = SlotID(stacks.size() / 2);
if(ui32 upgradesSize = getStack(slotId).type->upgrades.size()) const auto & upgrades = getStack(slotId).type->upgrades;
if(!upgrades.empty())
{ {
auto it = getStack(slotId).type->upgrades.cbegin(); //pick random in case there are more auto it = RandomGeneratorUtil::nextItem(upgrades, cb->gameState()->getRandomGenerator());
std::advance (it, rand() % upgradesSize);
cb->changeStackType(StackLocation(this, slotId), VLC->creh->creatures[*it]); cb->changeStackType(StackLocation(this, slotId), VLC->creh->creatures[*it]);
} }
} }
@ -3869,11 +3992,12 @@ void CGVisitableOPW::onHeroVisit( const CGHeroInstance * h ) const
Component::EComponentType type = Component::RESOURCE; Component::EComponentType type = Component::RESOURCE;
Res::ERes sub=Res::WOOD; Res::ERes sub=Res::WOOD;
int val=0; int val=0;
auto & rand = cb->gameState()->getRandomGenerator();
switch (ID) switch (ID)
{ {
case Obj::MYSTICAL_GARDEN: case Obj::MYSTICAL_GARDEN:
if (rand()%2) if(rand.nextInt(1) == 1)
{ {
sub = Res::GEMS; sub = Res::GEMS;
val = 5; val = 5;
@ -3886,8 +4010,8 @@ void CGVisitableOPW::onHeroVisit( const CGHeroInstance * h ) const
break; break;
case Obj::WINDMILL: case Obj::WINDMILL:
mid = 170; mid = 170;
sub = static_cast<Res::ERes>((rand() % 5) + 1); sub = static_cast<Res::ERes>(rand.nextInt(1, 5));
val = (rand() % 4) + 3; val = rand.nextInt(3, 6);
break; break;
case Obj::WATER_WHEEL: case Obj::WATER_WHEEL:
mid = 164; mid = 164;
@ -3923,16 +4047,27 @@ void CGTeleport::onHeroVisit( const CGHeroInstance * h ) const
switch(ID) switch(ID)
{ {
case Obj::MONOLITH1: //one way - find corresponding exit monolith case Obj::MONOLITH1: //one way - find corresponding exit monolith
{
if(vstd::contains(objs,Obj::MONOLITH2) && vstd::contains(objs[Obj::MONOLITH2],subID) && objs[Obj::MONOLITH2][subID].size()) if(vstd::contains(objs,Obj::MONOLITH2) && vstd::contains(objs[Obj::MONOLITH2],subID) && objs[Obj::MONOLITH2][subID].size())
destinationid = objs[Obj::MONOLITH2][subID][rand()%objs[Obj::MONOLITH2][subID].size()]; {
destinationid = *RandomGeneratorUtil::nextItem(objs[Obj::MONOLITH2][subID], cb->gameState()->getRandomGenerator());
}
else else
{
logGlobal->warnStream() << "Cannot find corresponding exit monolith for "<< id; logGlobal->warnStream() << "Cannot find corresponding exit monolith for "<< id;
}
break; break;
}
case Obj::MONOLITH3://two way monolith - pick any other one case Obj::MONOLITH3://two way monolith - pick any other one
case Obj::WHIRLPOOL: //Whirlpool case Obj::WHIRLPOOL: //Whirlpool
if(vstd::contains(objs,ID) && vstd::contains(objs[ID],subID) && objs[ID][subID].size()>1) if(vstd::contains(objs,ID) && vstd::contains(objs[ID],subID) && objs[ID][subID].size()>1)
{ {
while ((destinationid = objs[ID][subID][rand()%objs[ID][subID].size()]) == id); //choose another exit //choose another exit
do
{
destinationid = *RandomGeneratorUtil::nextItem(objs[ID][subID], cb->gameState()->getRandomGenerator());
} while(destinationid == id);
if (ID == Obj::WHIRLPOOL) if (ID == Obj::WHIRLPOOL)
{ {
if (!h->hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION)) if (!h->hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION))
@ -3983,9 +4118,8 @@ void CGTeleport::onHeroVisit( const CGHeroInstance * h ) const
if (ID == Obj::WHIRLPOOL) if (ID == Obj::WHIRLPOOL)
{ {
std::set<int3> tiles = cb->getObj(destinationid)->getBlockedPos(); std::set<int3> tiles = cb->getObj(destinationid)->getBlockedPos();
auto it = tiles.begin(); auto & tile = *RandomGeneratorUtil::nextItem(tiles, cb->gameState()->getRandomGenerator());
std::advance (it, rand() % tiles.size()); //picking random element of set is tiring cb->moveHero(h->id, tile + int3(1,0,0), true);
cb->moveHero (h->id, *it + int3(1,0,0), true);
} }
else else
cb->moveHero (h->id,CGHeroInstance::convertPosition(cb->getObj(destinationid)->pos,true) - getVisitableOffset(), true); cb->moveHero (h->id,CGHeroInstance::convertPosition(cb->getObj(destinationid)->pos,true) - getVisitableOffset(), true);
@ -5156,7 +5290,7 @@ void CGBonusingObject::onHeroVisit( const CGHeroInstance * h ) const
messageID = 55; messageID = 55;
iw.soundID = soundBase::LUCK; iw.soundID = soundBase::LUCK;
gbonus.bonus.type = Bonus::LUCK; gbonus.bonus.type = Bonus::LUCK;
gbonus.bonus.val = rand()%5 - 1; gbonus.bonus.val = cb->gameState()->getRandomGenerator().nextInt(-1, 3);
descr_id = 69; descr_id = 69;
gbonus.bdescr.addReplacement((gbonus.bonus.val<0 ? "-" : "+") + boost::lexical_cast<std::string>(gbonus.bonus.val)); gbonus.bdescr.addReplacement((gbonus.bonus.val<0 ? "-" : "+") + boost::lexical_cast<std::string>(gbonus.bonus.val));
break; break;

View File

@ -8,6 +8,7 @@
#include "int3.h" #include "int3.h"
#include "GameConstants.h" #include "GameConstants.h"
#include "ResourceSet.h" #include "ResourceSet.h"
#include "CRandomGenerator.h"
/* /*
* CObjectHandler.h, part of VCMI engine * CObjectHandler.h, part of VCMI engine
@ -387,8 +388,8 @@ public:
struct DLL_LINKAGE SecondarySkillsInfo struct DLL_LINKAGE SecondarySkillsInfo
{ {
//skills are determined, initialized at map start //skills are determined, initialized at map start
//FIXME: remove mutable? //FIXME remove mutable
mutable std::minstd_rand distribution; mutable CRandomGenerator rand;
ui8 magicSchoolCounter; ui8 magicSchoolCounter;
ui8 wisdomCounter; ui8 wisdomCounter;
@ -397,39 +398,10 @@ public:
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & magicSchoolCounter & wisdomCounter; h & magicSchoolCounter & wisdomCounter & rand;
if (h.saving)
{
std::ostringstream stream;
stream << distribution;
std::string str = stream.str();
h & str;
}
else
{
std::string str;
h & str;
std::istringstream stream(str);
stream >> distribution;
}
} }
} skillsInfo; } skillsInfo;
//////////////////////////////////////////////////////////////////////////
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CArmedInstance&>(*this);
h & static_cast<CArtifactSet&>(*this);
h & exp & level & name & biography & portrait & mana & secSkills & movement
& sex & inTownGarrison & spells & patrol & moveDir & skillsInfo;
h & visitedTown & boat;
h & type & specialty & commander;
BONUS_TREE_DESERIALIZATION_FIX
//visitied town pointer will be restored by map serialization method
}
//////////////////////////////////////////////////////////////////////////
int3 getSightCenter() const; //"center" tile from which the sight distance is calculated int3 getSightCenter() const; //"center" tile from which the sight distance is calculated
int getSightRadious() const; //sight distance (should be used if player-owned structure) int getSightRadious() const; //sight distance (should be used if player-owned structure)
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -451,9 +423,28 @@ public:
int getCurrentLuck(int stack=-1, bool town=false) const; int getCurrentLuck(int stack=-1, bool town=false) const;
int getSpellCost(const CSpell *sp) const; //do not use during battles -> bonuses from army would be ignored int getSpellCost(const CSpell *sp) const; //do not use during battles -> bonuses from army would be ignored
// ----- primary and secondary skill, experience, level handling -----
/// Returns true if hero has lower level than should upon his experience.
bool gainsLevel() const;
/// Returns the next primary skill on level up. Can only be called if hero can gain a level up.
PrimarySkill::PrimarySkill nextPrimarySkill() 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;
/// Gets 0, 1 or 2 secondary skills which are proposed on hero level up.
std::vector<SecondarySkill> getLevelUpProposedSecondarySkills() const;
ui8 getSecSkillLevel(SecondarySkill skill) const; //0 - no skill ui8 getSecSkillLevel(SecondarySkill skill) const; //0 - no skill
/// Returns true if hero has free secondary skill slot.
bool canLearnSkill() const;
void setPrimarySkill(PrimarySkill::PrimarySkill primarySkill, si64 value, ui8 abs);
void setSecSkillLevel(SecondarySkill which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value void setSecSkillLevel(SecondarySkill which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value
bool canLearnSkill() const; ///true if hero has free secondary skill slot void levelUp(std::vector<SecondarySkill> skills);
int maxMovePoints(bool onLand) const; int maxMovePoints(bool onLand) const;
int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false) const; int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false) const;
@ -470,8 +461,6 @@ public:
CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const; CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const;
void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const; void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const;
ECanDig diggingStatus() const; //0 - can dig; 1 - lack of movement; 2 - ECanDig diggingStatus() const; //0 - can dig; 1 - lack of movement; 2 -
std::vector<SecondarySkill> levelUpProposedSkills() const;
bool gainsLevel() const; //true if hero has lower level than should upon his experience
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -506,6 +495,22 @@ public:
const std::string & getHoverText() const override; const std::string & getHoverText() const override;
protected: protected:
void setPropertyDer(ui8 what, ui32 val) override;//synchr void setPropertyDer(ui8 what, ui32 val) override;//synchr
private:
void levelUpAutomatically();
public:
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CArmedInstance&>(*this);
h & static_cast<CArtifactSet&>(*this);
h & exp & level & name & biography & portrait & mana & secSkills & movement
& sex & inTownGarrison & spells & patrol & moveDir & skillsInfo;
h & visitedTown & boat;
h & type & specialty & commander;
BONUS_TREE_DESERIALIZATION_FIX
//visitied town pointer will be restored by map serialization method
}
}; };
class DLL_LINKAGE CSpecObjInfo class DLL_LINKAGE CSpecObjInfo

86
lib/CRandomGenerator.cpp Normal file
View File

@ -0,0 +1,86 @@
/*
* CRandomGenerator.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "CRandomGenerator.h"
boost::thread_specific_ptr<CRandomGenerator> CRandomGenerator::defaultRand;
CRandomGenerator::CRandomGenerator()
{
resetSeed();
}
void CRandomGenerator::setSeed(int seed)
{
rand.seed(seed);
}
void CRandomGenerator::resetSeed()
{
boost::hash<std::string> stringHash;
auto threadIdHash = stringHash(boost::lexical_cast<std::string>(boost::this_thread::get_id()));
setSeed(threadIdHash * std::time(nullptr));
}
TRandI CRandomGenerator::getIntRange(int lower, int upper)
{
return boost::bind(TIntDist(lower, upper), boost::ref(rand));
}
int CRandomGenerator::nextInt(int upper)
{
return getIntRange(0, upper)();
}
int CRandomGenerator::nextInt(int lower, int upper)
{
return getIntRange(lower, upper)();
}
int CRandomGenerator::nextInt()
{
return TIntDist()(rand);
}
TRand CRandomGenerator::getDoubleRange(double lower, double upper)
{
return boost::bind(TRealDist(lower, upper), boost::ref(rand));
}
double CRandomGenerator::nextDouble(double upper)
{
return getDoubleRange(0, upper)();
}
double CRandomGenerator::nextDouble(double lower, double upper)
{
return getDoubleRange(lower, upper)();
}
double CRandomGenerator::nextDouble()
{
return TRealDist()(rand);
}
CRandomGenerator & CRandomGenerator::getDefault()
{
if(!defaultRand.get())
{
defaultRand.reset(new CRandomGenerator());
}
return *defaultRand.get();
}
TGenerator & CRandomGenerator::getStdGenerator()
{
return rand;
}

View File

@ -19,77 +19,80 @@ typedef std::function<double()> TRand;
/// The random generator randomly generates integers and real numbers("doubles") between /// The random generator randomly generates integers and real numbers("doubles") between
/// a given range. This is a header only class and mainly a wrapper for /// a given range. This is a header only class and mainly a wrapper for
/// convenient usage of the standard random API. /// convenient usage of the standard random API. An instance of this RNG is not thread safe.
class CRandomGenerator : boost::noncopyable class DLL_LINKAGE CRandomGenerator : boost::noncopyable
{ {
public: public:
/// Seeds the generator with the current time by default. /// Seeds the generator by default with the product of the current time in milliseconds and the
CRandomGenerator() /// current thread ID.
{ CRandomGenerator();
rand.seed(static_cast<unsigned long>(std::time(nullptr)));
}
void setSeed(int seed) void setSeed(int seed);
{
rand.seed(seed); /// Resets the seed to the product of the current time in milliseconds and the
} /// current thread ID.
void resetSeed();
/// Generate several integer numbers within the same range. /// Generate several integer numbers within the same range.
/// e.g.: auto a = gen.getIntRange(0,10); a(); a(); a(); /// e.g.: auto a = gen.getIntRange(0,10); a(); a(); a();
/// requires: lower <= upper /// requires: lower <= upper
TRandI getIntRange(int lower, int upper) TRandI getIntRange(int lower, int upper);
{
return boost::bind(TIntDist(lower, upper), boost::ref(rand));
}
/// Generates an integer between 0 and upper. /// Generates an integer between 0 and upper.
/// requires: 0 <= upper /// requires: 0 <= upper
int nextInt(int upper) int nextInt(int upper);
{
return getIntRange(0, upper)();
}
/// requires: lower <= upper /// requires: lower <= upper
int nextInt(int lower, int upper) int nextInt(int lower, int upper);
{
return getIntRange(lower, upper)();
}
/// Generates an integer between 0 and the maximum value it can hold. /// Generates an integer between 0 and the maximum value it can hold.
int nextInt() int nextInt();
{
return TIntDist()(rand);
}
/// Generate several double/real numbers within the same range. /// Generate several double/real numbers within the same range.
/// e.g.: auto a = gen.getDoubleRange(4.5,10.2); a(); a(); a(); /// e.g.: auto a = gen.getDoubleRange(4.5,10.2); a(); a(); a();
/// requires: lower <= upper /// requires: lower <= upper
TRand getDoubleRange(double lower, double upper) TRand getDoubleRange(double lower, double upper);
{
return boost::bind(TRealDist(lower, upper), boost::ref(rand));
}
/// Generates a double between 0 and upper. /// Generates a double between 0 and upper.
/// requires: 0 <= upper /// requires: 0 <= upper
double nextDouble(double upper) double nextDouble(double upper);
{
return getDoubleRange(0, upper)();
}
/// requires: lower <= upper /// requires: lower <= upper
double nextDouble(double lower, double upper) double nextDouble(double lower, double upper);
{
return getDoubleRange(lower, upper)();
}
/// Generates a double between 0.0 and 1.0. /// Generates a double between 0.0 and 1.0.
double nextDouble() double nextDouble();
{
return TRealDist()(rand); /// Gets a globally accessible RNG which will be constructed once per thread. For the
} /// seed a combination of the thread ID and current time in milliseconds will be used.
static CRandomGenerator & getDefault();
/// Provide method so that this RNG can be used with legacy std:: API
TGenerator & getStdGenerator();
private: private:
TGenerator rand; TGenerator rand;
static boost::thread_specific_ptr<CRandomGenerator> defaultRand;
public:
template <typename Handler>
void serialize(Handler & h, const int version)
{
if(h.saving)
{
std::ostringstream stream;
stream << rand;
std::string str = stream.str();
h & str;
}
else
{
std::string str;
h & str;
std::istringstream stream(str);
stream >> rand;
}
}
}; };
namespace RandomGeneratorUtil namespace RandomGeneratorUtil

View File

@ -1068,14 +1068,13 @@ struct HeroLevelUp : public Query//2000
const CGHeroInstance *hero; const CGHeroInstance *hero;
PrimarySkill::PrimarySkill primskill; PrimarySkill::PrimarySkill primskill;
ui8 level;
std::vector<SecondarySkill> skills; std::vector<SecondarySkill> skills;
HeroLevelUp(){type = 2000;}; HeroLevelUp(){type = 2000;};
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & queryID & hero & primskill & level & skills; h & queryID & hero & primskill & skills;
} }
}; };

View File

@ -49,28 +49,9 @@ DLL_LINKAGE void SetResources::applyGs( CGameState *gs )
DLL_LINKAGE void SetPrimSkill::applyGs( CGameState *gs ) DLL_LINKAGE void SetPrimSkill::applyGs( CGameState *gs )
{ {
CGHeroInstance *hero = gs->getHero(id); CGHeroInstance * hero = gs->getHero(id);
assert(hero); assert(hero);
hero->setPrimarySkill(which, val, abs);
if(which < PrimarySkill::EXPERIENCE)
{
Bonus *skill = hero->getBonusLocalFirst(Selector::type(Bonus::PRIMARY_SKILL)
.And(Selector::subtype(which))
.And(Selector::sourceType(Bonus::HERO_BASE_SKILL)));
assert(skill);
if(abs)
skill->val = val;
else
skill->val += val;
}
else if(which == PrimarySkill::EXPERIENCE)
{
if(abs)
hero->exp = val;
else
hero->exp += val;
}
} }
DLL_LINKAGE void SetSecSkill::applyGs( CGameState *gs ) DLL_LINKAGE void SetSecSkill::applyGs( CGameState *gs )
@ -1020,25 +1001,8 @@ DLL_LINKAGE void SetHoverName::applyGs( CGameState *gs )
DLL_LINKAGE void HeroLevelUp::applyGs( CGameState *gs ) DLL_LINKAGE void HeroLevelUp::applyGs( CGameState *gs )
{ {
CGHeroInstance* h = gs->getHero(hero->id); CGHeroInstance * h = gs->getHero(hero->id);
h->level = level; h->levelUp(skills);
//deterministic secondary skills
h->skillsInfo.magicSchoolCounter = (h->skillsInfo.magicSchoolCounter + 1) % h->maxlevelsToMagicSchool();
h->skillsInfo.wisdomCounter = (h->skillsInfo.wisdomCounter + 1) % h->maxlevelsToWisdom();
if (vstd::contains(skills, SecondarySkill::WISDOM))
h->skillsInfo.resetWisdomCounter();
SecondarySkill spellSchools[] = {
SecondarySkill::FIRE_MAGIC, SecondarySkill::AIR_MAGIC, SecondarySkill::WATER_MAGIC, SecondarySkill::EARTH_MAGIC};
for (auto skill : spellSchools)
{
if (vstd::contains(skills, skill))
{
h->skillsInfo.resetMagicSchoolCounter();
break;
}
}
//specialty
h->Updatespecialty();
} }
DLL_LINKAGE void CommanderLevelUp::applyGs (CGameState *gs) DLL_LINKAGE void CommanderLevelUp::applyGs (CGameState *gs)

View File

@ -107,6 +107,7 @@
<Unit filename="CObjectHandler.h" /> <Unit filename="CObjectHandler.h" />
<Unit filename="CObstacleInstance.cpp" /> <Unit filename="CObstacleInstance.cpp" />
<Unit filename="CObstacleInstance.h" /> <Unit filename="CObstacleInstance.h" />
<Unit filename="CRandomGenerator.cpp" />
<Unit filename="CRandomGenerator.h" /> <Unit filename="CRandomGenerator.h" />
<Unit filename="CScriptingModule.h" /> <Unit filename="CScriptingModule.h" />
<Unit filename="CSpellHandler.cpp" /> <Unit filename="CSpellHandler.cpp" />

View File

@ -185,6 +185,7 @@
<ClCompile Include="CSpellHandler.cpp" /> <ClCompile Include="CSpellHandler.cpp" />
<ClCompile Include="CThreadHelper.cpp" /> <ClCompile Include="CThreadHelper.cpp" />
<ClCompile Include="CTownHandler.cpp" /> <ClCompile Include="CTownHandler.cpp" />
<ClCompile Include="CRandomGenerator.cpp" />
<ClCompile Include="filesystem\AdapterLoaders.cpp" /> <ClCompile Include="filesystem\AdapterLoaders.cpp" />
<ClCompile Include="filesystem\CArchiveLoader.cpp" /> <ClCompile Include="filesystem\CArchiveLoader.cpp" />
<ClCompile Include="filesystem\CBinaryReader.cpp" /> <ClCompile Include="filesystem\CBinaryReader.cpp" />
@ -323,4 +324,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
</ImportGroup> </ImportGroup>
</Project> </Project>

View File

@ -39,6 +39,7 @@
<ClCompile Include="CCreatureSet.cpp" /> <ClCompile Include="CCreatureSet.cpp" />
<ClCompile Include="CGameState.cpp" /> <ClCompile Include="CGameState.cpp" />
<ClCompile Include="Connection.cpp" /> <ClCompile Include="Connection.cpp" />
<ClCompile Include="CRandomGenerator.cpp" />
<ClCompile Include="HeroBonus.cpp" /> <ClCompile Include="HeroBonus.cpp" />
<ClCompile Include="IGameCallback.cpp" /> <ClCompile Include="IGameCallback.cpp" />
<ClCompile Include="NetPacksLib.cpp" /> <ClCompile Include="NetPacksLib.cpp" />
@ -393,4 +394,4 @@
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -183,31 +183,21 @@ void CGameHandler::levelUpHero(const CGHeroInstance * hero)
return; return;
} }
//give prim skill // give primary skill
logGlobal->traceStream() << hero->name <<" got level "<<hero->level; logGlobal->traceStream() << hero->name << " got level "<< hero->level;
int r = rand()%100, pom=0, x=0; auto primarySkill = hero->nextPrimarySkill();
auto & skillChances = (hero->level>9) ? hero->type->heroClass->primarySkillLowLevel : hero->type->heroClass->primarySkillHighLevel;
for(;x<GameConstants::PRIMARY_SKILLS;x++)
{
pom += skillChances[x];
if(r<pom)
break;
}
logGlobal->traceStream() << "The hero gets the primary skill with the no. " << x << " with a probability of " << r << "%.";
SetPrimSkill sps; SetPrimSkill sps;
sps.id = hero->id; sps.id = hero->id;
sps.which = static_cast<PrimarySkill::PrimarySkill>(x); sps.which = primarySkill;
sps.abs = false; sps.abs = false;
sps.val = 1; sps.val = 1;
sendAndApply(&sps); sendAndApply(&sps);
HeroLevelUp hlu; HeroLevelUp hlu;
hlu.hero = hero; hlu.hero = hero;
hlu.primskill = static_cast<PrimarySkill::PrimarySkill>(x); hlu.primskill = primarySkill;
hlu.level = hero->level+1; hlu.skills = hero->getLevelUpProposedSecondarySkills();
hlu.skills = hero->levelUpProposedSkills();
if(hlu.skills.size() == 0) if(hlu.skills.size() == 0)
{ {
@ -217,11 +207,7 @@ void CGameHandler::levelUpHero(const CGHeroInstance * hero)
else if(hlu.skills.size() == 1 || hero->tempOwner == PlayerColor::NEUTRAL) //choose skill automatically else if(hlu.skills.size() == 1 || hero->tempOwner == PlayerColor::NEUTRAL) //choose skill automatically
{ {
sendAndApply(&hlu); sendAndApply(&hlu);
auto rng = [&]() mutable -> ui32 levelUpHero(hero, *RandomGeneratorUtil::nextItem(hlu.skills, hero->skillsInfo.rand));
{
return hero->skillsInfo.distribution(); //must be determined
};
levelUpHero(hero, vstd::pickRandomElementOf (hlu.skills, rng));
} }
else if(hlu.skills.size() > 1) else if(hlu.skills.size() > 1)
{ {
@ -359,7 +345,7 @@ void CGameHandler::levelUpCommander(const CCommanderInstance * c)
else if(skillAmount == 1 || hero->tempOwner == PlayerColor::NEUTRAL) //choose skill automatically else if(skillAmount == 1 || hero->tempOwner == PlayerColor::NEUTRAL) //choose skill automatically
{ {
sendAndApply(&clu); sendAndApply(&clu);
levelUpCommander(c, vstd::pickRandomElementOf (clu.skills, rand)); levelUpCommander(c, *RandomGeneratorUtil::nextItem(clu.skills, gs->getRandomGenerator()));
} }
else if(skillAmount > 1) //apply and ask for secondary skill else if(skillAmount > 1) //apply and ask for secondary skill
{ {
@ -520,7 +506,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
int maxLevel = eagleEyeLevel + 1; int maxLevel = eagleEyeLevel + 1;
double eagleEyeChance = finishingBattle->winnerHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::EAGLE_EYE); double eagleEyeChance = finishingBattle->winnerHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::EAGLE_EYE);
for(const CSpell *sp : gs->curB->sides.at(!battleResult.data->winner).usedSpellsHistory) for(const CSpell *sp : gs->curB->sides.at(!battleResult.data->winner).usedSpellsHistory)
if(sp->level <= maxLevel && !vstd::contains(finishingBattle->winnerHero->spells, sp->id) && rand() % 100 < eagleEyeChance) if(sp->level <= maxLevel && !vstd::contains(finishingBattle->winnerHero->spells, sp->id) && gs->getRandomGenerator().nextInt(99) < eagleEyeChance)
cs.spells.insert(sp->id); cs.spells.insert(sp->id);
} }
} }
@ -754,20 +740,20 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
if(!vstd::contains_if(gs->curB->sides, sideHeroBlocksLuck)) if(!vstd::contains_if(gs->curB->sides, sideHeroBlocksLuck))
{ {
if(attackerLuck > 0 && rand()%24 < attackerLuck) if(attackerLuck > 0 && gs->getRandomGenerator().nextInt(23) < attackerLuck)
{ {
bat.flags |= BattleAttack::LUCKY; bat.flags |= BattleAttack::LUCKY;
} }
if (VLC->modh->settings.data["hardcodedFeatures"]["NEGATIVE_LUCK"].Bool()) // negative luck enabled if (VLC->modh->settings.data["hardcodedFeatures"]["NEGATIVE_LUCK"].Bool()) // negative luck enabled
{ {
if (attackerLuck < 0 && rand()%24 < abs(attackerLuck)) if (attackerLuck < 0 && gs->getRandomGenerator().nextInt(23) < abs(attackerLuck))
{ {
bat.flags |= BattleAttack::UNLUCKY; bat.flags |= BattleAttack::UNLUCKY;
} }
} }
} }
if (rand()%100 < att->valOfBonuses(Bonus::DOUBLE_DAMAGE_CHANCE)) if(gs->getRandomGenerator().nextInt(99) < att->valOfBonuses(Bonus::DOUBLE_DAMAGE_CHANCE))
{ {
bat.flags |= BattleAttack::DEATH_BLOW; bat.flags |= BattleAttack::DEATH_BLOW;
} }
@ -777,7 +763,7 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
static const int artilleryLvlToChance[] = {0, 50, 75, 100}; static const int artilleryLvlToChance[] = {0, 50, 75, 100};
const CGHeroInstance * owner = gs->curB->getHero(att->owner); const CGHeroInstance * owner = gs->curB->getHero(att->owner);
int chance = artilleryLvlToChance[owner->getSecSkillLevel(SecondarySkill::ARTILLERY)]; int chance = artilleryLvlToChance[owner->getSecSkillLevel(SecondarySkill::ARTILLERY)];
if(chance > rand() % 100) if(chance > gs->getRandomGenerator().nextInt(99))
{ {
bat.flags |= BattleAttack::BALLISTA_DOUBLE_DMG; bat.flags |= BattleAttack::BALLISTA_DOUBLE_DMG;
} }
@ -823,8 +809,9 @@ void CGameHandler::applyBattleEffects(BattleAttack &bat, const CStack *att, cons
bsa.flags |= BattleStackAttacked::SECONDARY; //all other targets do not suffer from spells & spell-like abilities bsa.flags |= BattleStackAttacked::SECONDARY; //all other targets do not suffer from spells & spell-like abilities
bsa.attackerID = att->ID; bsa.attackerID = att->ID;
bsa.stackAttacked = def->ID; bsa.stackAttacked = def->ID;
bsa.damageAmount = gs->curB->calculateDmg(att, def, gs->curB->battleGetOwner(att), gs->curB->battleGetOwner(def), bat.shot(), distance, bat.lucky(), bat.unlucky(), bat.deathBlow(), bat.ballistaDoubleDmg()); bsa.damageAmount = gs->curB->calculateDmg(att, def, gs->curB->battleGetOwner(att), gs->curB->battleGetOwner(def),
def->prepareAttacked(bsa, gameState()->getRandomGenerator()); //calculate casualties bat.shot(), distance, bat.lucky(), bat.unlucky(), bat.deathBlow(), bat.ballistaDoubleDmg(), gs->getRandomGenerator());
def->prepareAttacked(bsa, gs->getRandomGenerator()); //calculate casualties
//life drain handling //life drain handling
if (att->hasBonusOfType(Bonus::LIFE_DRAIN) && def->isLiving()) if (att->hasBonusOfType(Bonus::LIFE_DRAIN) && def->isLiving())
@ -865,7 +852,6 @@ void CGameHandler::applyBattleEffects(BattleAttack &bat, const CStack *att, cons
void CGameHandler::handleConnection(std::set<PlayerColor> players, CConnection &c) void CGameHandler::handleConnection(std::set<PlayerColor> players, CConnection &c)
{ {
setThreadName("CGameHandler::handleConnection"); setThreadName("CGameHandler::handleConnection");
srand(time(nullptr));
try try
{ {
@ -1089,6 +1075,9 @@ void CGameHandler::init(StartInfo *si)
gs->init(si); gs->init(si);
logGlobal->infoStream() << "Gamestate initialized!"; logGlobal->infoStream() << "Gamestate initialized!";
// reset seed, so that clients can't predict any following random values
gs->getRandomGenerator().resetSeed();
for(auto & elem : gs->players) for(auto & elem : gs->players)
{ {
states.addPlayer(elem.first); states.addPlayer(elem.first);
@ -1123,15 +1112,20 @@ void CGameHandler::setPortalDwelling(const CGTownInstance * town, bool forced=fa
return; return;
} }
ui32 dwellpos = rand()%dwellings.size();//take random dwelling auto dwelling = *RandomGeneratorUtil::nextItem(dwellings, gs->getRandomGenerator());
ui32 creapos = rand()%dwellings.at(dwellpos)->creatures.size();//for multi-creature dwellings like Golem Factory
CreatureID creature = dwellings.at(dwellpos)->creatures.at(creapos).second[0];
if (clear) // for multi-creature dwellings like Golem Factory
ssi.creatures[GameConstants::CREATURES_PER_TOWN].first = std::max((ui32)1, (VLC->creh->creatures.at(creature)->growth)/2); auto creatureId = RandomGeneratorUtil::nextItem(dwelling->creatures, gs->getRandomGenerator())->second[0];
if(clear)
{
ssi.creatures[GameConstants::CREATURES_PER_TOWN].first = std::max((ui32)1, (VLC->creh->creatures.at(creatureId)->growth)/2);
}
else else
ssi.creatures[GameConstants::CREATURES_PER_TOWN].first = VLC->creh->creatures.at(creature)->growth; {
ssi.creatures[GameConstants::CREATURES_PER_TOWN].second.push_back(creature); ssi.creatures[GameConstants::CREATURES_PER_TOWN].first = VLC->creh->creatures.at(creatureId)->growth;
}
ssi.creatures[GameConstants::CREATURES_PER_TOWN].second.push_back(creatureId);
sendAndApply(&ssi); sendAndApply(&ssi);
} }
} }
@ -1149,7 +1143,6 @@ void CGameHandler::newTurn()
bool newMonth = getDate(Date::DAY_OF_MONTH) == 28; bool newMonth = getDate(Date::DAY_OF_MONTH) == 28;
std::map<PlayerColor, si32> hadGold;//starting gold - for buildings like dwarven treasury std::map<PlayerColor, si32> hadGold;//starting gold - for buildings like dwarven treasury
srand(time(nullptr));
if (firstTurn) if (firstTurn)
{ {
@ -1182,7 +1175,7 @@ void CGameHandler::newTurn()
} }
else else
{ {
int monthType = rand()%100; int monthType = gs->getRandomGenerator().nextInt(99);
if(newMonth) //new month if(newMonth) //new month
{ {
if (monthType < 40) //double growth if (monthType < 40) //double growth
@ -1196,7 +1189,7 @@ void CGameHandler::newTurn()
else if(VLC->creh->doubledCreatures.size()) else if(VLC->creh->doubledCreatures.size())
{ {
const std::vector<CreatureID> doubledCreatures (VLC->creh->doubledCreatures.begin(), VLC->creh->doubledCreatures.end()); const std::vector<CreatureID> doubledCreatures (VLC->creh->doubledCreatures.begin(), VLC->creh->doubledCreatures.end());
n.creatureid = vstd::pickRandomElementOf(doubledCreatures, []{ return rand(); }); n.creatureid = *RandomGeneratorUtil::nextItem(doubledCreatures, gs->getRandomGenerator());
} }
else else
{ {
@ -1427,12 +1420,12 @@ void CGameHandler::newTurn()
if (newMonth) if (newMonth)
{ {
iw.text.addTxt(MetaString::ARRAY_TXT, (130)); iw.text.addTxt(MetaString::ARRAY_TXT, (130));
iw.text.addReplacement(MetaString::ARRAY_TXT, 32 + rand()%10); iw.text.addReplacement(MetaString::ARRAY_TXT, gs->getRandomGenerator().nextInt(32, 41));
} }
else else
{ {
iw.text.addTxt(MetaString::ARRAY_TXT, (133)); iw.text.addTxt(MetaString::ARRAY_TXT, (133));
iw.text.addReplacement(MetaString::ARRAY_TXT, 43 + rand()%15); iw.text.addReplacement(MetaString::ARRAY_TXT, gs->getRandomGenerator().nextInt(43, 57));
} }
} }
for (auto & elem : gs->players) for (auto & elem : gs->players)
@ -3592,7 +3585,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
{ {
if(currentHP.at(attackedPart) != EWallState::DESTROYED && // this part can be hit if(currentHP.at(attackedPart) != EWallState::DESTROYED && // this part can be hit
currentHP.at(attackedPart) != EWallState::NONE && currentHP.at(attackedPart) != EWallState::NONE &&
rand()%100 < getCatapultHitChance(attackedPart, sbi))//hit is successful gs->getRandomGenerator().nextInt(99) < getCatapultHitChance(attackedPart, sbi))//hit is successful
{ {
hitSuccessfull = true; hitSuccessfull = true;
} }
@ -3607,7 +3600,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
} }
if (allowedTargets.empty()) if (allowedTargets.empty())
break; break;
attackedPart = allowedTargets.at(rand()%allowedTargets.size()); attackedPart = *RandomGeneratorUtil::nextItem(allowedTargets, gs->getRandomGenerator());
} }
} }
while (!hitSuccessfull); while (!hitSuccessfull);
@ -3623,7 +3616,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
int dmgChance[] = { sbi.noDmg, sbi.oneDmg, sbi.twoDmg }; //dmgChance[i] - chance for doing i dmg when hit is successful int dmgChance[] = { sbi.noDmg, sbi.oneDmg, sbi.twoDmg }; //dmgChance[i] - chance for doing i dmg when hit is successful
int dmgRand = rand()%100; int dmgRand = gs->getRandomGenerator().nextInt(99);
//accumulating dmgChance //accumulating dmgChance
dmgChance[1] += dmgChance[0]; dmgChance[1] += dmgChance[0];
dmgChance[2] += dmgChance[1]; dmgChance[2] += dmgChance[1];
@ -4071,7 +4064,7 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex
} }
//checking if creatures resist //checking if creatures resist
sc.resisted = gs->curB->calculateResistedStacks(spell, caster, secHero, attackedCres, casterColor, mode, usedSpellPower, spellLvl); sc.resisted = gs->curB->calculateResistedStacks(spell, caster, secHero, attackedCres, casterColor, mode, usedSpellPower, spellLvl, gs->getRandomGenerator());
//calculating dmg to display //calculating dmg to display
if (spellID == SpellID::DEATH_STARE || spellID == SpellID::ACID_BREATH_DAMAGE) if (spellID == SpellID::DEATH_STARE || spellID == SpellID::ACID_BREATH_DAMAGE)
@ -4465,7 +4458,7 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex
for(auto & attackedCre : attackedCres) for(auto & attackedCre : attackedCres)
{ {
int mirrorChance = (attackedCre)->valOfBonuses(Bonus::MAGIC_MIRROR); int mirrorChance = (attackedCre)->valOfBonuses(Bonus::MAGIC_MIRROR);
if(mirrorChance > rand()%100) if(mirrorChance > gs->getRandomGenerator().nextInt(99))
{ {
std::vector<CStack *> mirrorTargets; std::vector<CStack *> mirrorTargets;
std::vector<CStack *> & battleStacks = gs->curB->stacks; std::vector<CStack *> & battleStacks = gs->curB->stacks;
@ -4479,7 +4472,7 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex
} }
if (!mirrorTargets.empty()) if (!mirrorTargets.empty())
{ {
int targetHex = mirrorTargets.at(rand() % mirrorTargets.size())->position; int targetHex = (*RandomGeneratorUtil::nextItem(mirrorTargets, gs->getRandomGenerator()))->position;
handleSpellCasting(spellID, 0, targetHex, 1 - casterSide, (attackedCre)->owner, nullptr, (caster ? caster : nullptr), usedSpellPower, ECastingMode::MAGIC_MIRROR, (attackedCre)); handleSpellCasting(spellID, 0, targetHex, 1 - casterSide, (attackedCre)->owner, nullptr, (caster ? caster : nullptr), usedSpellPower, ECastingMode::MAGIC_MIRROR, (attackedCre));
} }
} }
@ -4652,7 +4645,7 @@ void CGameHandler::stackTurnTrigger(const CStack * st)
} }
if (fearsomeCreature) if (fearsomeCreature)
{ {
if (rand() % 100 < 10) //fixed 10% if (gs->getRandomGenerator().nextInt(99) < 10) //fixed 10%
{ {
bte.effect = Bonus::FEAR; bte.effect = Bonus::FEAR;
sendAndApply(&bte); sendAndApply(&bte);
@ -4663,18 +4656,18 @@ void CGameHandler::stackTurnTrigger(const CStack * st)
int side = gs->curB->whatSide(st->owner); int side = gs->curB->whatSide(st->owner);
if (bl.size() && st->casts && !gs->curB->sides.at(side).enchanterCounter) if (bl.size() && st->casts && !gs->curB->sides.at(side).enchanterCounter)
{ {
int index = rand() % bl.size(); auto bonus = *RandomGeneratorUtil::nextItem(bl, gs->getRandomGenerator());
SpellID spellID = SpellID(bl[index]->subtype); auto spellID = SpellID(bonus->subtype);
if (gs->curB->battleCanCastThisSpell(st->owner, SpellID(spellID).toSpell(), ECastingMode::ENCHANTER_CASTING) == ESpellCastProblem::OK) //TODO: select another available? if (gs->curB->battleCanCastThisSpell(st->owner, SpellID(spellID).toSpell(), ECastingMode::ENCHANTER_CASTING) == ESpellCastProblem::OK) //TODO: select another available?
{ {
int spellLeveL = bl[index]->val; //spell level int spellLeveL = bonus->val; //spell level
const CGHeroInstance * enemyHero = gs->curB->getHero(gs->curB->theOtherPlayer(st->owner)); const CGHeroInstance * enemyHero = gs->curB->getHero(gs->curB->theOtherPlayer(st->owner));
handleSpellCasting(spellID, spellLeveL, -1, side, st->owner, nullptr, enemyHero, 0, ECastingMode::ENCHANTER_CASTING, st); handleSpellCasting(spellID, spellLeveL, -1, side, st->owner, nullptr, enemyHero, 0, ECastingMode::ENCHANTER_CASTING, st);
BattleSetStackProperty ssp; BattleSetStackProperty ssp;
ssp.which = BattleSetStackProperty::ENCHANTER_COUNTER; ssp.which = BattleSetStackProperty::ENCHANTER_COUNTER;
ssp.absolute = false; ssp.absolute = false;
ssp.val = bl[index]->additionalInfo; //increase cooldown counter ssp.val = bonus->additionalInfo; //increase cooldown counter
ssp.stackID = st->ID; ssp.stackID = st->ID;
sendAndApply(&ssp); sendAndApply(&ssp);
} }
@ -5345,7 +5338,7 @@ void CGameHandler::attackCasting(const BattleAttack & bat, Bonus::BonusType atta
continue; continue;
//check if spell should be casted (probability handling) //check if spell should be casted (probability handling)
if(rand()%100 >= chance) if(gs->getRandomGenerator().nextInt(99) >= chance)
continue; continue;
//casting //TODO: check if spell can be blocked or target is immune //casting //TODO: check if spell can be blocked or target is immune
@ -5383,10 +5376,10 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
double chanceToKill = attacker->valOfBonuses(Bonus::DEATH_STARE, 0) / 100.0f; double chanceToKill = attacker->valOfBonuses(Bonus::DEATH_STARE, 0) / 100.0f;
vstd::amin(chanceToKill, 1); //cap at 100% vstd::amin(chanceToKill, 1); //cap at 100%
std::binomial_distribution<> distr(attacker->count, chanceToKill); std::binomial_distribution<> distribution(attacker->count, chanceToKill);
std::mt19937 rng(rand()); std::mt19937 rng(std::time(nullptr));
int staredCreatures = distr(rng); int staredCreatures = distribution(rng);
double cap = 1 / std::max(chanceToKill, (double)(0.01));//don't divide by 0 double cap = 1 / std::max(chanceToKill, (double)(0.01));//don't divide by 0
int maxToKill = (attacker->count + cap - 1) / cap; //not much more than chance * count int maxToKill = (attacker->count + cap - 1) / cap; //not much more than chance * count
@ -5405,7 +5398,7 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
TBonusListPtr acidBreath = attacker->getBonuses(Selector::type(Bonus::ACID_BREATH)); TBonusListPtr acidBreath = attacker->getBonuses(Selector::type(Bonus::ACID_BREATH));
for(const Bonus *b : *acidBreath) for(const Bonus *b : *acidBreath)
{ {
if (b->additionalInfo > rand()%100) if (b->additionalInfo > gs->getRandomGenerator().nextInt(99))
acidDamage += b->val; acidDamage += b->val;
} }
if (acidDamage) if (acidDamage)
@ -5439,7 +5432,7 @@ bool CGameHandler::castSpell(const CGHeroInstance *h, SpellID spellID, const int
case SpellID::SUMMON_BOAT: case SpellID::SUMMON_BOAT:
{ {
//check if spell works at all //check if spell works at all
if(rand() % 100 >= s->getPower(schoolLevel)) //power is % chance of success if(gs->getRandomGenerator().nextInt(99) >= s->getPower(schoolLevel)) //power is % chance of success
{ {
InfoWindow iw; InfoWindow iw;
iw.player = h->tempOwner; iw.player = h->tempOwner;
@ -5501,7 +5494,7 @@ bool CGameHandler::castSpell(const CGHeroInstance *h, SpellID spellID, const int
case SpellID::SCUTTLE_BOAT: case SpellID::SCUTTLE_BOAT:
{ {
//check if spell works at all //check if spell works at all
if(rand() % 100 >= s->getPower(schoolLevel)) //power is % chance of success if(gs->getRandomGenerator().nextInt(99) >= s->getPower(schoolLevel)) //power is % chance of success
{ {
InfoWindow iw; InfoWindow iw;
iw.player = h->tempOwner; iw.player = h->tempOwner;
@ -5918,7 +5911,7 @@ void CGameHandler::runBattle()
|| NBonus::hasOfType(gs->curB->battleGetFightingHero(1), Bonus::BLOCK_MORALE)) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses) || NBonus::hasOfType(gs->curB->battleGetFightingHero(1), Bonus::BLOCK_MORALE)) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses)
) )
{ {
if( rand()%24 < -2 * nextStackMorale) if(gs->getRandomGenerator().nextInt(23) < -2 * nextStackMorale)
{ {
//unit loses its turn - empty freeze action //unit loses its turn - empty freeze action
BattleAction ba; BattleAction ba;
@ -5983,7 +5976,7 @@ void CGameHandler::runBattle()
if(!attackableBattleHexes.empty()) if(!attackableBattleHexes.empty())
{ {
BattleAction attack; BattleAction attack;
attack.destinationTile = attackableBattleHexes[rand() % attackableBattleHexes.size()]; attack.destinationTile = *RandomGeneratorUtil::nextItem(attackableBattleHexes, gs->getRandomGenerator());
attack.actionType = Battle::CATAPULT; attack.actionType = Battle::CATAPULT;
attack.additionalInfo = 0; attack.additionalInfo = 0;
attack.side = !next->attackerOwned; attack.side = !next->attackerOwned;
@ -6077,7 +6070,7 @@ void CGameHandler::runBattle()
|| NBonus::hasOfType(gs->curB->battleGetFightingHero(1), Bonus::BLOCK_MORALE)) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses || NBonus::hasOfType(gs->curB->battleGetFightingHero(1), Bonus::BLOCK_MORALE)) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses
) )
{ {
if(rand()%24 < nextStackMorale) //this stack hasn't got morale this turn if(gs->getRandomGenerator().nextInt(23) < nextStackMorale) //this stack hasn't got morale this turn
{ {
BattleTriggerEffect bte; BattleTriggerEffect bte;

View File

@ -8,7 +8,6 @@
//#include <boost/interprocess/shared_memory_object.hpp> //#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/random/linear_congruential.hpp> #include <boost/random/linear_congruential.hpp>
#include <boost/random/mersenne_twister.hpp> #include <boost/random/mersenne_twister.hpp>
#include <boost/random/poisson_distribution.hpp>
#include <boost/random/variate_generator.hpp> #include <boost/random/variate_generator.hpp>
#include <boost/system/system_error.hpp> #include <boost/system/system_error.hpp>
//#include <boost/asio.hpp> //#include <boost/asio.hpp>