1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-07-15 01:24:45 +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

View File

@ -767,17 +767,14 @@ void CGHeroInstance::initHero()
}
assert(validTypes());
if (exp == 0xffffffff)
level = 1;
if(exp == 0xffffffff)
{
initExp();
}
else if (ID != Obj::PRISON)
else
{
level = VLC->heroh->level(exp);
}
else //warp hero at the beginning of next turn
{
level = 1;
levelUpAutomatically();
}
if (VLC->modh->modules.COMMANDERS && !commander)
@ -953,7 +950,7 @@ void CGHeroInstance::initObj()
if(!type)
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.resetWisdomCounter();
@ -1552,7 +1549,6 @@ EAlignment::EAlignment CGHeroInstance::getAlignment() const
void CGHeroInstance::initExp()
{
exp = cb->gameState()->getRandomGenerator().nextInt(40, 89);
level = 1;
}
std::string CGHeroInstance::nodeName() const
@ -1630,7 +1626,7 @@ ArtBearer::ArtBearer CGHeroInstance::bearerType() const
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
if (!skillsInfo.wisdomCounter)
@ -1643,11 +1639,7 @@ std::vector<SecondarySkill> CGHeroInstance::levelUpProposedSkills() const
std::vector<SecondarySkill> ss;
ss += SecondarySkill::FIRE_MAGIC, SecondarySkill::AIR_MAGIC, SecondarySkill::WATER_MAGIC, SecondarySkill::EARTH_MAGIC;
auto rng = [=](ui32 val) mutable -> ui32
{
return skillsInfo.distribution() % val; //must be determined
};
std::random_shuffle(ss.begin(), ss.end(), rng);
std::shuffle(ss.begin(), ss.end(), skillsInfo.rand.getStdGenerator());
for (auto skill : ss)
{
@ -1691,12 +1683,12 @@ std::vector<SecondarySkill> CGHeroInstance::levelUpProposedSkills() const
}
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());
}
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());
}
@ -1706,7 +1698,7 @@ std::vector<SecondarySkill> CGHeroInstance::levelUpProposedSkills() const
//3) give any other new skill
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);
basicAndAdv.erase(s);
}
@ -1716,18 +1708,147 @@ std::vector<SecondarySkill> CGHeroInstance::levelUpProposedSkills() const
}
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());
}
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
{
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()
{
switch(ID)
@ -2303,13 +2424,15 @@ void CGTownInstance::newTurn() const
{
if (cb->getDate(Date::DAY_OF_WEEK) == 1) //reset on new week
{
auto & rand = cb->gameState()->getRandomGenerator();
//give resources for Rampart, Mystic Pond
if (hasBuilt(BuildingID::MYSTIC_POND, ETownType::RAMPART)
&& 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;
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->setObjProperty (id, ObjProperty::BONUS_VALUE_FIRST, resID);
cb->setObjProperty (id, ObjProperty::BONUS_VALUE_SECOND, resVal);
@ -2334,11 +2457,11 @@ void CGTownInstance::newTurn() const
}
if (nativeCrits.size())
{
SlotID pos = nativeCrits[rand() % nativeCrits.size()];
SlotID pos = *RandomGeneratorUtil::nextItem(nativeCrits, rand);
StackLocation sl(this, 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);
}
@ -2347,9 +2470,9 @@ void CGTownInstance::newTurn() const
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())
{
CreatureID c = town->creatures[i][0];
@ -3316,7 +3439,7 @@ void CGCreature::initObj()
amount = 1;
}
}
formation.randomFormation = rand();
formation.randomFormation = cb->gameState()->getRandomGenerator().nextInt();
temppower = stacks[SlotID(0)]->count * 1000;
refusedJoining = false;
@ -3532,10 +3655,10 @@ void CGCreature::fight( const CGHeroInstance *h ) const
if (formation.randomFormation % 100 < 50) //upgrade
{
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
std::advance (it, rand() % upgradesSize);
auto it = RandomGeneratorUtil::nextItem(upgrades, cb->gameState()->getRandomGenerator());
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;
Res::ERes sub=Res::WOOD;
int val=0;
auto & rand = cb->gameState()->getRandomGenerator();
switch (ID)
{
case Obj::MYSTICAL_GARDEN:
if (rand()%2)
if(rand.nextInt(1) == 1)
{
sub = Res::GEMS;
val = 5;
@ -3886,8 +4010,8 @@ void CGVisitableOPW::onHeroVisit( const CGHeroInstance * h ) const
break;
case Obj::WINDMILL:
mid = 170;
sub = static_cast<Res::ERes>((rand() % 5) + 1);
val = (rand() % 4) + 3;
sub = static_cast<Res::ERes>(rand.nextInt(1, 5));
val = rand.nextInt(3, 6);
break;
case Obj::WATER_WHEEL:
mid = 164;
@ -3923,16 +4047,27 @@ void CGTeleport::onHeroVisit( const CGHeroInstance * h ) const
switch(ID)
{
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())
destinationid = objs[Obj::MONOLITH2][subID][rand()%objs[Obj::MONOLITH2][subID].size()];
{
destinationid = *RandomGeneratorUtil::nextItem(objs[Obj::MONOLITH2][subID], cb->gameState()->getRandomGenerator());
}
else
{
logGlobal->warnStream() << "Cannot find corresponding exit monolith for "<< id;
}
break;
}
case Obj::MONOLITH3://two way monolith - pick any other one
case Obj::WHIRLPOOL: //Whirlpool
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 (!h->hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION))
@ -3983,9 +4118,8 @@ void CGTeleport::onHeroVisit( const CGHeroInstance * h ) const
if (ID == Obj::WHIRLPOOL)
{
std::set<int3> tiles = cb->getObj(destinationid)->getBlockedPos();
auto it = tiles.begin();
std::advance (it, rand() % tiles.size()); //picking random element of set is tiring
cb->moveHero (h->id, *it + int3(1,0,0), true);
auto & tile = *RandomGeneratorUtil::nextItem(tiles, cb->gameState()->getRandomGenerator());
cb->moveHero(h->id, tile + int3(1,0,0), true);
}
else
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;
iw.soundID = soundBase::LUCK;
gbonus.bonus.type = Bonus::LUCK;
gbonus.bonus.val = rand()%5 - 1;
gbonus.bonus.val = cb->gameState()->getRandomGenerator().nextInt(-1, 3);
descr_id = 69;
gbonus.bdescr.addReplacement((gbonus.bonus.val<0 ? "-" : "+") + boost::lexical_cast<std::string>(gbonus.bonus.val));
break;