diff --git a/config/schemas/skill.json b/config/schemas/skill.json index 0a191c61e..031195eb5 100644 --- a/config/schemas/skill.json +++ b/config/schemas/skill.json @@ -60,6 +60,14 @@ "type": "string", "description": "localizable skill name" }, + "obligatoryMajor":{ + "type": "boolean", + "description": "This skill is major obligatory (like H3 Wisdom)" + }, + "obligatoryMinor":{ + "type": "boolean", + "description": "This skill is minor obligatory (like H3 Magic school)" + }, "gainChance" : { "description" : "Chance for the skill to be offered on level-up (heroClass may override)", "anyOf" : [ diff --git a/config/skills.json b/config/skills.json index 1cc0466a6..6e7892702 100644 --- a/config/skills.json +++ b/config/skills.json @@ -196,6 +196,7 @@ }, "wisdom" : { "index" : 7, + "obligatoryMajor" : true, "base" : { "effects" : { "main" : { @@ -401,6 +402,7 @@ }, "fireMagic" : { "index" : 14, + "obligatoryMinor" : true, "base" : { "effects" : { "main" : { @@ -428,6 +430,7 @@ }, "airMagic" : { "index" : 15, + "obligatoryMinor" : true, "base" : { "effects" : { "main" : { @@ -455,6 +458,7 @@ }, "waterMagic" : { "index" : 16, + "obligatoryMinor" : true, "base" : { "effects" : { "main" : { @@ -482,6 +486,7 @@ }, "earthMagic" : { "index" : 17, + "obligatoryMinor" : true, "base" : { "effects" : { "main" : { diff --git a/lib/CSkillHandler.cpp b/lib/CSkillHandler.cpp index a1593012a..e4218e479 100644 --- a/lib/CSkillHandler.cpp +++ b/lib/CSkillHandler.cpp @@ -33,8 +33,8 @@ CSkill::LevelInfo::~LevelInfo() { } -CSkill::CSkill(SecondarySkill id, std::string identifier) - : id(id), identifier(identifier) +CSkill::CSkill(SecondarySkill id, std::string identifier, bool obligatoryMajor, bool obligatoryMinor) + : id(id), identifier(identifier), obligatoryMajor(obligatoryMajor), obligatoryMinor(obligatoryMinor) { gainChance[0] = gainChance[1] = 0; //affects CHeroClassHandler::afterLoadFinalization() levels.resize(NSecondarySkill::levels.size() - 1); @@ -207,8 +207,11 @@ CSkill * CSkillHandler::loadFromJson(const std::string & scope, const JsonNode & { assert(identifier.find(':') == std::string::npos); assert(!scope.empty()); + bool major, minor; - CSkill * skill = new CSkill(SecondarySkill((si32)index), identifier); + major = json["obligatoryMajor"].Bool(); + minor = json["obligatoryMinor"].Bool(); + CSkill * skill = new CSkill(SecondarySkill((si32)index), identifier, major, minor); skill->modScope = scope; VLC->generaltexth->registerString(scope, skill->getNameTextID(), json["name"].String()); diff --git a/lib/CSkillHandler.h b/lib/CSkillHandler.h index ec3b87ca5..d73c5bc39 100644 --- a/lib/CSkillHandler.h +++ b/lib/CSkillHandler.h @@ -51,9 +51,15 @@ private: std::string identifier; public: - CSkill(SecondarySkill id = SecondarySkill::DEFAULT, std::string identifier = "default"); + CSkill(SecondarySkill id = SecondarySkill::DEFAULT, std::string identifier = "default", bool obligatoryMajor = false, bool obligatoryMinor = false); ~CSkill(); + enum class Obligatory : ui8 + { + MAJOR = 0, + MINOR = 1, + }; + int32_t getIndex() const override; int32_t getIconIndex() const override; std::string getJsonKey() const override; @@ -70,6 +76,7 @@ public: LevelInfo & at(int level); std::string toString() const; + bool obligatory(Obligatory val) const { return val == Obligatory::MAJOR ? obligatoryMajor : obligatoryMinor; }; std::array gainChance; // gainChance[0/1] = default gain chance on level-up for might/magic heroes @@ -82,11 +89,16 @@ public: h & identifier; h & gainChance; h & levels; + h & obligatoryMajor; + h & obligatoryMinor; } friend class CSkillHandler; friend DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CSkill & skill); friend DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CSkill::LevelInfo & info); +private: + bool obligatoryMajor; + bool obligatoryMinor; }; class DLL_LINKAGE CSkillHandler: public CHandlerBase diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 0ab64f8c2..d6110012f 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -1145,28 +1145,41 @@ ArtBearer::ArtBearer CGHeroInstance::bearerType() const std::vector CGHeroInstance::getLevelUpProposedSecondarySkills() const { std::vector obligatorySkills; //hero is offered magic school or wisdom if possible - if (!skillsInfo.wisdomCounter) - { - if (canLearnSkill(SecondarySkill::WISDOM)) - obligatorySkills.emplace_back(SecondarySkill::WISDOM); - } - if (!skillsInfo.magicSchoolCounter) - { - std::vector ss = - { - SecondarySkill::FIRE_MAGIC, SecondarySkill::AIR_MAGIC, SecondarySkill::WATER_MAGIC, SecondarySkill::EARTH_MAGIC - }; + auto getObligatorySkills = [](CSkill::Obligatory obl){ + std::vector obligatory = {}; + for(int i = 0; i < VLC->skillh->size(); i++) + if((*VLC->skillh)[SecondarySkill(i)]->obligatory(obl)) + { + obligatory.emplace_back(i); + break; + } + return obligatory; + }; + + auto selectObligatorySkill = [&](std::vector& ss) -> void + { std::shuffle(ss.begin(), ss.end(), skillsInfo.rand.getStdGenerator()); for(const auto & skill : ss) { - if (canLearnSkill(skill)) //only schools hero doesn't know yet + if (canLearnSkill(skill)) //only skills hero doesn't know yet { obligatorySkills.push_back(skill); break; //only one } } + }; + + if (!skillsInfo.wisdomCounter) + { + auto obligatory = getObligatorySkills(CSkill::Obligatory::MAJOR); + selectObligatorySkill(obligatory); + } + if (!skillsInfo.magicSchoolCounter) + { + auto obligatory = getObligatorySkills(CSkill::Obligatory::MINOR); + selectObligatorySkill(obligatory); } std::vector skills; @@ -1339,20 +1352,12 @@ void CGHeroInstance::levelUp(const std::vector & skills) //deterministic secondary skills skillsInfo.magicSchoolCounter = (skillsInfo.magicSchoolCounter + 1) % maxlevelsToMagicSchool(); skillsInfo.wisdomCounter = (skillsInfo.wisdomCounter + 1) % maxlevelsToWisdom(); - if(vstd::contains(skills, SecondarySkill::WISDOM)) + for(const auto & skill : skills) { - skillsInfo.resetWisdomCounter(); - } - - SecondarySkill spellSchools[] = { - SecondarySkill::FIRE_MAGIC, SecondarySkill::AIR_MAGIC, SecondarySkill::WATER_MAGIC, SecondarySkill::EARTH_MAGIC}; - for(const auto & skill : spellSchools) - { - if(vstd::contains(skills, skill)) - { + if((*VLC->skillh)[skill]->obligatory(CSkill::Obligatory::MAJOR)) + skillsInfo.resetWisdomCounter(); + if((*VLC->skillh)[skill]->obligatory(CSkill::Obligatory::MINOR)) skillsInfo.resetMagicSchoolCounter(); - break; - } } //update specialty and other bonuses that scale with level