From 39f9069ed95326fa466477a28f93060d5a34b3eb Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Mon, 9 Sep 2013 15:23:59 +0000 Subject: [PATCH] Implemented deterministic secondary skills, #1166. --- lib/CHeroHandler.cpp | 5 +++ lib/CHeroHandler.h | 1 + lib/CObjectHandler.cpp | 70 +++++++++++++++++++++++++++++++++++++++-- lib/CObjectHandler.h | 19 ++++++++++- lib/NetPacksLib.cpp | 15 +++++++++ server/CGameHandler.cpp | 6 +++- 6 files changed, 111 insertions(+), 5 deletions(-) diff --git a/lib/CHeroHandler.cpp b/lib/CHeroHandler.cpp index 93715e9a2..0d96e6d32 100644 --- a/lib/CHeroHandler.cpp +++ b/lib/CHeroHandler.cpp @@ -42,6 +42,11 @@ SecondarySkill CHeroClass::chooseSecSkill(const std::set & possi return *possibles.begin(); } +bool CHeroClass::isMagicHero() const +{ + return id % 2; // 0 - might, 1 - magic +} + EAlignment::EAlignment CHeroClass::getAlignment() const { return EAlignment::EAlignment(VLC->townh->factions[faction]->alignment); diff --git a/lib/CHeroHandler.h b/lib/CHeroHandler.h index 7da6624e3..ce531365c 100644 --- a/lib/CHeroHandler.h +++ b/lib/CHeroHandler.h @@ -116,6 +116,7 @@ public: std::string imageMapMale; std::string imageMapFemale; + bool isMagicHero() const; SecondarySkill chooseSecSkill(const std::set & possibles) const; //picks secondary skill out from given possibilities template void serialize(Handler &h, const int version) diff --git a/lib/CObjectHandler.cpp b/lib/CObjectHandler.cpp index e90b565be..6f2e12bd2 100644 --- a/lib/CObjectHandler.cpp +++ b/lib/CObjectHandler.cpp @@ -986,7 +986,26 @@ const std::string & CGHeroInstance::getBiography() const return biography; return type->biography; } -void CGHeroInstance::initObj() //TODO: use bonus system + +ui8 CGHeroInstance::maxlevelsToMagicSchool() const +{ + return type->heroClass->isMagicHero() ? 3 : 4; +} +ui8 CGHeroInstance::maxlevelsToWisdom() const +{ + return type->heroClass->isMagicHero() ? 3 : 6; +} + +void CGHeroInstance::SecondarySkillsInfo::resetMagicSchoolCounter() +{ + magicSchoolCounter = 1; +} +void CGHeroInstance::SecondarySkillsInfo::resetWisdomCounter() +{ + wisdomCounter = 1; +} + +void CGHeroInstance::initObj() { blockVisit = true; auto hs = new HeroSpecial(); @@ -996,6 +1015,10 @@ void CGHeroInstance::initObj() //TODO: use bonus system if(!type) initHero(); //TODO: set up everything for prison before specialties are configured + skillsInfo.randomSeed = rand(); + skillsInfo.resetMagicSchoolCounter(); + skillsInfo.resetWisdomCounter(); + for(const auto &spec : type->spec) //TODO: unfity with bonus system { auto bonus = new Bonus(); @@ -1636,6 +1659,33 @@ ArtBearer::ArtBearer CGHeroInstance::bearerType() const std::vector CGHeroInstance::levelUpProposedSkills() const { + std::vector obligatorySkills; //hero is offered magic school or wisdom if possible + if (!skillsInfo.magicSchoolCounter) + { + std::vector ss; + ss += SecondarySkill::FIRE_MAGIC, SecondarySkill::WATER_MAGIC, SecondarySkill::EARTH_MAGIC, SecondarySkill::EARTH_MAGIC; + + auto rng = [=](ui32 val)-> ui32 + { + return skillsInfo.randomSeed % val; //must be determined + }; + std::random_shuffle(ss.begin(), ss.end(), rng); + + for (auto skill : ss) + { + if (cb->isAllowed(2, skill) && !getSecSkillLevel(skill)) //only schools hero doesn't know yet + { + obligatorySkills.push_back(skill); + break; //only one + } + } + } + if (!skillsInfo.wisdomCounter) + { + if (cb->isAllowed(2, SecondarySkill::WISDOM) && !getSecSkillLevel(SecondarySkill::WISDOM)) + obligatorySkills.push_back(SecondarySkill::WISDOM); + } + std::vector skills; //picking sec. skills for choice std::set basicAndAdv, expert, none; @@ -1651,9 +1701,19 @@ std::vector CGHeroInstance::levelUpProposedSkills() const expert.insert(elem.first); none.erase(elem.first); } + for (auto s : obligatorySkills) //don't duplicate them + { + none.erase (s); + basicAndAdv.erase (s); + expert.erase (s); + } //first offered skill - if(basicAndAdv.size()) + if (canLearnSkill() && obligatorySkills.size()) //offer always if possible + { + skills.push_back (obligatorySkills[0]); + } + else if(basicAndAdv.size()) { SecondarySkill s = type->heroClass->chooseSecSkill(basicAndAdv);//upgrade existing skills.push_back(s); @@ -1666,7 +1726,11 @@ std::vector CGHeroInstance::levelUpProposedSkills() const } //second offered skill - if(none.size() && canLearnSkill()) //hero have free skill slot + if (canLearnSkill() && obligatorySkills.size() > 1) + { + skills.push_back (obligatorySkills[1]); + } + else if(none.size() && canLearnSkill()) //hero have free skill slot { skills.push_back(type->heroClass->chooseSecSkill(none)); //new skill } diff --git a/lib/CObjectHandler.h b/lib/CObjectHandler.h index 82c025ca6..27e0e6da9 100644 --- a/lib/CObjectHandler.h +++ b/lib/CObjectHandler.h @@ -346,6 +346,21 @@ public: std::vector specialty; + struct DLL_LINKAGE SecondarySkillsInfo + { + ui32 randomSeed; //skills are determined, initialized at map start + ui8 magicSchoolCounter; + ui8 wisdomCounter; + + void resetMagicSchoolCounter(); + void resetWisdomCounter(); + + template void serialize(Handler &h, const int version) + { + h & randomSeed & magicSchoolCounter & wisdomCounter; + } + } skillsInfo; + //BonusList bonuses; ////////////////////////////////////////////////////////////////////////// @@ -355,7 +370,7 @@ public: h & static_cast(*this); h & static_cast(*this); h & exp & level & name & biography & portrait & mana & secSkills & movement - & sex & inTownGarrison & spells & patrol & moveDir; + & sex & inTownGarrison & spells & patrol & moveDir & skillsInfo; h & visitedTown & boat; h & type & specialty & commander; BONUS_TREE_DESERIALIZATION_FIX @@ -415,6 +430,8 @@ public: //void giveArtifact (ui32 aid); void initHeroDefInfo(); void pushPrimSkill(PrimarySkill::PrimarySkill which, int val); + ui8 maxlevelsToMagicSchool() const; + ui8 maxlevelsToWisdom() const; void Updatespecialty(); void updateSkill(SecondarySkill which, int val); diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 0aa7f1526..8a1e878f0 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -1010,6 +1010,21 @@ DLL_LINKAGE void HeroLevelUp::applyGs( CGameState *gs ) { CGHeroInstance* h = gs->getHero(hero->id); h->level = level; + //deterministic secondary skills + h->skillsInfo.magicSchoolCounter = (++h->skillsInfo.magicSchoolCounter) % h->maxlevelsToMagicSchool(); + h->skillsInfo.wisdomCounter = (++h->skillsInfo.wisdomCounter) % h->maxlevelsToWisdom(); + if (vstd::contains(skills, SecondarySkill::WISDOM)) + h->skillsInfo.resetWisdomCounter(); + SecondarySkill spellSchools[] = { + SecondarySkill::FIRE_MAGIC, SecondarySkill::WATER_MAGIC, SecondarySkill::EARTH_MAGIC, SecondarySkill::EARTH_MAGIC}; + for (auto skill : spellSchools) + { + if (vstd::contains(skills, SecondarySkill::WISDOM)) + { + h->skillsInfo.resetMagicSchoolCounter(); + break; + } + } //specialty h->Updatespecialty(); } diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 08fb611ad..a4c951db7 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -205,7 +205,11 @@ void CGameHandler::levelUpHero(const CGHeroInstance * hero) else if(hlu.skills.size() == 1 || hero->tempOwner == PlayerColor::NEUTRAL) //choose skill automatically { sendAndApply(&hlu); - levelUpHero(hero, vstd::pickRandomElementOf (hlu.skills, rand)); + auto rng = [&]()-> ui32 + { + return hero->skillsInfo.randomSeed; //must be determined + }; + levelUpHero(hero, vstd::pickRandomElementOf (hlu.skills, rng)); } else if(hlu.skills.size() > 1) {