mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-25 22:42:04 +02:00
GameRandomizer is now in lib, add implementation
This commit is contained in:
@@ -354,10 +354,10 @@ void CGHeroInstance::initObj(IGameRandomizer & gameRandomizer)
|
||||
updateAppearance();
|
||||
}
|
||||
|
||||
void CGHeroInstance::initHero(vstd::RNG & rand, const HeroTypeID & SUBID)
|
||||
void CGHeroInstance::initHero(IGameRandomizer & gameRandomizer, const HeroTypeID & SUBID)
|
||||
{
|
||||
subID = SUBID.getNum();
|
||||
initHero(rand);
|
||||
initHero(gameRandomizer);
|
||||
}
|
||||
|
||||
TObjectTypeHandler CGHeroInstance::getObjectHandler() const
|
||||
@@ -377,7 +377,7 @@ void CGHeroInstance::updateAppearance()
|
||||
appearance = app;
|
||||
}
|
||||
|
||||
void CGHeroInstance::initHero(vstd::RNG & rand)
|
||||
void CGHeroInstance::initHero(IGameRandomizer & gameRandomizer)
|
||||
{
|
||||
assert(validTypes(true));
|
||||
|
||||
@@ -430,7 +430,7 @@ void CGHeroInstance::initHero(vstd::RNG & rand)
|
||||
setFormation(EArmyFormation::LOOSE);
|
||||
if (!stacksCount()) //standard army//initial army
|
||||
{
|
||||
initArmy(rand);
|
||||
initArmy(gameRandomizer.getDefault());
|
||||
}
|
||||
assert(validTypes());
|
||||
|
||||
@@ -439,11 +439,11 @@ void CGHeroInstance::initHero(vstd::RNG & rand)
|
||||
|
||||
if(exp == UNINITIALIZED_EXPERIENCE)
|
||||
{
|
||||
initExp(rand);
|
||||
initExp(gameRandomizer.getDefault());
|
||||
}
|
||||
else
|
||||
{
|
||||
levelUpAutomatically(rand);
|
||||
levelUpAutomatically(gameRandomizer);
|
||||
}
|
||||
|
||||
// load base hero bonuses, TODO: per-map loading of base hero bonuses
|
||||
@@ -468,8 +468,6 @@ void CGHeroInstance::initHero(vstd::RNG & rand)
|
||||
commander->giveTotalStackExperience(exp); //after our exp is set
|
||||
}
|
||||
|
||||
skillsInfo = SecondarySkillsInfo();
|
||||
|
||||
//copy active (probably growing) bonuses from hero prototype to hero object
|
||||
for(const std::shared_ptr<Bonus> & b : getHeroType()->specialty)
|
||||
addNewBonus(b);
|
||||
@@ -650,20 +648,6 @@ ui8 CGHeroInstance::maxlevelsToWisdom() const
|
||||
return getHeroClass()->isMagicHero() ? 3 : 6;
|
||||
}
|
||||
|
||||
CGHeroInstance::SecondarySkillsInfo::SecondarySkillsInfo():
|
||||
magicSchoolCounter(1),
|
||||
wisdomCounter(1)
|
||||
{}
|
||||
|
||||
void CGHeroInstance::SecondarySkillsInfo::resetMagicSchoolCounter()
|
||||
{
|
||||
magicSchoolCounter = 0;
|
||||
}
|
||||
void CGHeroInstance::SecondarySkillsInfo::resetWisdomCounter()
|
||||
{
|
||||
wisdomCounter = 0;
|
||||
}
|
||||
|
||||
void CGHeroInstance::pickRandomObject(IGameRandomizer & gameRandomizer)
|
||||
{
|
||||
assert(ID == Obj::HERO || ID == Obj::PRISON || ID == Obj::RANDOM_HERO);
|
||||
@@ -1426,34 +1410,18 @@ ArtBearer CGHeroInstance::bearerType() const
|
||||
return ArtBearer::HERO;
|
||||
}
|
||||
|
||||
std::vector<SecondarySkill> CGHeroInstance::getLevelUpProposedSecondarySkills(vstd::RNG & rand) const
|
||||
std::vector<SecondarySkill> CGHeroInstance::getLevelupSkillCandidates(IGameRandomizer & gameRandomizer) const
|
||||
{
|
||||
auto getObligatorySkills = [](CSkill::Obligatory obl){
|
||||
std::set<SecondarySkill> obligatory;
|
||||
for(auto i = 0; i < LIBRARY->skillh->size(); i++)
|
||||
if((*LIBRARY->skillh)[SecondarySkill(i)]->obligatory(obl))
|
||||
obligatory.insert(i); //Always return all obligatory skills
|
||||
|
||||
return obligatory;
|
||||
};
|
||||
|
||||
auto intersect = [](const std::set<SecondarySkill> & left, const std::set<SecondarySkill> & right)
|
||||
{
|
||||
std::set<SecondarySkill> intersect;
|
||||
std::set_intersection(left.begin(), left.end(), right.begin(), right.end(),
|
||||
std::inserter(intersect, intersect.begin()));
|
||||
return intersect;
|
||||
};
|
||||
|
||||
std::set<SecondarySkill> wisdomList = getObligatorySkills(CSkill::Obligatory::MAJOR);
|
||||
std::set<SecondarySkill> schoolList = getObligatorySkills(CSkill::Obligatory::MINOR);
|
||||
|
||||
std::set<SecondarySkill> basicAndAdv;
|
||||
std::set<SecondarySkill> none;
|
||||
std::vector<SecondarySkill> skills;
|
||||
|
||||
for(int i = 0; i < LIBRARY->skillh->size(); i++)
|
||||
if (canLearnSkill(SecondarySkill(i)))
|
||||
none.insert(SecondarySkill(i));
|
||||
if (canLearnSkill())
|
||||
{
|
||||
for(int i = 0; i < LIBRARY->skillh->size(); i++)
|
||||
if (canLearnSkill(SecondarySkill(i)))
|
||||
none.insert(SecondarySkill(i));
|
||||
}
|
||||
|
||||
for(const auto & elem : secSkills)
|
||||
{
|
||||
@@ -1462,96 +1430,33 @@ std::vector<SecondarySkill> CGHeroInstance::getLevelUpProposedSecondarySkills(vs
|
||||
none.erase(elem.first);
|
||||
}
|
||||
|
||||
bool wantsWisdom = skillsInfo.wisdomCounter + 1 >= maxlevelsToWisdom();
|
||||
bool wantsSchool = skillsInfo.magicSchoolCounter + 1 >= maxlevelsToMagicSchool();
|
||||
|
||||
std::vector<SecondarySkill> skills;
|
||||
|
||||
auto chooseSkill = [&](std::set<SecondarySkill> & options)
|
||||
{
|
||||
bool selectWisdom = wantsWisdom && !intersect(options, wisdomList).empty();
|
||||
bool selectSchool = wantsSchool && !intersect(options, schoolList).empty();
|
||||
SecondarySkill selection;
|
||||
|
||||
if (selectWisdom)
|
||||
selection = getHeroClass()->chooseSecSkill(intersect(options, wisdomList), rand);
|
||||
else if (selectSchool)
|
||||
selection = getHeroClass()->chooseSecSkill(intersect(options, schoolList), rand);
|
||||
else
|
||||
selection = getHeroClass()->chooseSecSkill(options, rand);
|
||||
|
||||
skills.push_back(selection);
|
||||
options.erase(selection);
|
||||
|
||||
if (wisdomList.count(selection))
|
||||
wisdomList.clear();
|
||||
|
||||
if (schoolList.count(selection))
|
||||
schoolList.clear();
|
||||
};
|
||||
|
||||
if (!basicAndAdv.empty())
|
||||
chooseSkill(basicAndAdv);
|
||||
{
|
||||
skills.push_back(gameRandomizer.rollSecondarySkillForLevelup(this, basicAndAdv));
|
||||
basicAndAdv.erase(skills.back());
|
||||
}
|
||||
|
||||
if (canLearnSkill() && !none.empty())
|
||||
chooseSkill(none);
|
||||
if (!none.empty())
|
||||
{
|
||||
skills.push_back(gameRandomizer.rollSecondarySkillForLevelup(this, none));
|
||||
none.erase(skills.back());
|
||||
}
|
||||
|
||||
if (!basicAndAdv.empty() && skills.size() < 2)
|
||||
chooseSkill(basicAndAdv);
|
||||
{
|
||||
skills.push_back(gameRandomizer.rollSecondarySkillForLevelup(this, basicAndAdv));
|
||||
basicAndAdv.erase(skills.back());
|
||||
}
|
||||
|
||||
if (canLearnSkill() && !none.empty() && skills.size() < 2)
|
||||
chooseSkill(none);
|
||||
if (!none.empty() && skills.size() < 2)
|
||||
{
|
||||
skills.push_back(gameRandomizer.rollSecondarySkillForLevelup(this, none));
|
||||
none.erase(skills.back());
|
||||
}
|
||||
|
||||
return skills;
|
||||
}
|
||||
|
||||
PrimarySkill CGHeroInstance::nextPrimarySkill(vstd::RNG & rand) const
|
||||
{
|
||||
assert(gainsLevel());
|
||||
const auto isLowLevelHero = level < GameConstants::HERO_HIGH_LEVEL;
|
||||
const auto & skillChances = isLowLevelHero ? getHeroClass()->primarySkillLowLevel : getHeroClass()->primarySkillHighLevel;
|
||||
|
||||
if (isCampaignYog())
|
||||
{
|
||||
// Yog can only receive Attack or Defence on level-up
|
||||
std::vector<int> yogChances = { skillChances[0], skillChances[1]};
|
||||
return static_cast<PrimarySkill>(RandomGeneratorUtil::nextItemWeighted(yogChances, rand));
|
||||
}
|
||||
|
||||
return static_cast<PrimarySkill>(RandomGeneratorUtil::nextItemWeighted(skillChances, rand));
|
||||
}
|
||||
|
||||
std::optional<SecondarySkill> CGHeroInstance::nextSecondarySkill(vstd::RNG & rand) const
|
||||
{
|
||||
assert(gainsLevel());
|
||||
|
||||
std::optional<SecondarySkill> chosenSecondarySkill;
|
||||
const auto proposedSecondarySkills = getLevelUpProposedSecondarySkills(rand);
|
||||
if(!proposedSecondarySkills.empty())
|
||||
{
|
||||
std::vector<SecondarySkill> learnedSecondarySkills;
|
||||
for(const auto & secondarySkill : proposedSecondarySkills)
|
||||
{
|
||||
if(getSecSkillLevel(secondarySkill) > 0)
|
||||
{
|
||||
learnedSecondarySkills.push_back(secondarySkill);
|
||||
}
|
||||
}
|
||||
|
||||
if(learnedSecondarySkills.empty())
|
||||
{
|
||||
// there are only new skills to learn, so choose anyone of them
|
||||
chosenSecondarySkill = std::make_optional(*RandomGeneratorUtil::nextItem(proposedSecondarySkills, rand));
|
||||
}
|
||||
else
|
||||
{
|
||||
// preferably upgrade a already learned secondary skill
|
||||
chosenSecondarySkill = std::make_optional(*RandomGeneratorUtil::nextItem(learnedSecondarySkills, rand));
|
||||
}
|
||||
}
|
||||
return chosenSecondarySkill;
|
||||
}
|
||||
|
||||
void CGHeroInstance::setPrimarySkill(PrimarySkill primarySkill, si64 value, ChangeValueMode mode)
|
||||
{
|
||||
if(primarySkill < PrimarySkill::EXPERIENCE)
|
||||
@@ -1589,22 +1494,9 @@ bool CGHeroInstance::gainsLevel() const
|
||||
return level < LIBRARY->heroh->maxSupportedLevel() && exp >= static_cast<TExpType>(LIBRARY->heroh->reqExp(level+1));
|
||||
}
|
||||
|
||||
void CGHeroInstance::levelUp(const std::vector<SecondarySkill> & skills)
|
||||
void CGHeroInstance::levelUp()
|
||||
{
|
||||
++level;
|
||||
|
||||
//deterministic secondary skills
|
||||
++skillsInfo.magicSchoolCounter;
|
||||
++skillsInfo.wisdomCounter;
|
||||
|
||||
for(const auto & skill : skills)
|
||||
{
|
||||
if((*LIBRARY->skillh)[skill]->obligatory(CSkill::Obligatory::MAJOR))
|
||||
skillsInfo.resetWisdomCounter();
|
||||
if((*LIBRARY->skillh)[skill]->obligatory(CSkill::Obligatory::MINOR))
|
||||
skillsInfo.resetMagicSchoolCounter();
|
||||
}
|
||||
|
||||
//update specialty and other bonuses that scale with level
|
||||
nodeHasChanged();
|
||||
}
|
||||
@@ -1615,23 +1507,18 @@ void CGHeroInstance::attachCommanderToArmy()
|
||||
commander->setArmy(this);
|
||||
}
|
||||
|
||||
void CGHeroInstance::levelUpAutomatically(vstd::RNG & rand)
|
||||
void CGHeroInstance::levelUpAutomatically(IGameRandomizer & gameRandomizer)
|
||||
{
|
||||
while(gainsLevel())
|
||||
{
|
||||
const auto primarySkill = nextPrimarySkill(rand);
|
||||
const auto primarySkill = gameRandomizer.rollPrimarySkillForLevelup(this);
|
||||
const auto proposedSecondarySkills = getLevelupSkillCandidates(gameRandomizer);
|
||||
|
||||
setPrimarySkill(primarySkill, 1, ChangeValueMode::RELATIVE);
|
||||
if(!proposedSecondarySkills.empty())
|
||||
setSecSkillLevel(proposedSecondarySkills.front(), 1, ChangeValueMode::RELATIVE);
|
||||
|
||||
auto proposedSecondarySkills = getLevelUpProposedSecondarySkills(rand);
|
||||
|
||||
const auto secondarySkill = nextSecondarySkill(rand);
|
||||
if(secondarySkill)
|
||||
{
|
||||
setSecSkillLevel(*secondarySkill, 1, ChangeValueMode::RELATIVE);
|
||||
}
|
||||
|
||||
//TODO why has the secondary skills to be passed to the method?
|
||||
levelUp(proposedSecondarySkills);
|
||||
levelUp();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user