Przeglądaj źródła

Implemented deterministic secondary skills, #1166.

DjWarmonger 12 lat temu
rodzic
commit
39f9069ed9

+ 5 - 0
lib/CHeroHandler.cpp

@@ -42,6 +42,11 @@ SecondarySkill CHeroClass::chooseSecSkill(const std::set<SecondarySkill> & 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);

+ 1 - 0
lib/CHeroHandler.h

@@ -116,6 +116,7 @@ public:
 	std::string imageMapMale;
 	std::string imageMapFemale;
 
+	bool isMagicHero() const;
 	SecondarySkill chooseSecSkill(const std::set<SecondarySkill> & possibles) const; //picks secondary skill out from given possibilities
 
 	template <typename Handler> void serialize(Handler &h, const int version)

+ 67 - 3
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<SecondarySkill> CGHeroInstance::levelUpProposedSkills() const
 {
+	std::vector<SecondarySkill> obligatorySkills; //hero is offered magic school or wisdom if possible
+	if (!skillsInfo.magicSchoolCounter)
+	{
+		std::vector<SecondarySkill> 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<SecondarySkill> skills;
 	//picking sec. skills for choice
 	std::set<SecondarySkill> basicAndAdv, expert, none;
@@ -1651,9 +1701,19 @@ std::vector<SecondarySkill> 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<SecondarySkill> 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
 	}

+ 18 - 1
lib/CObjectHandler.h

@@ -346,6 +346,21 @@ public:
 
 	std::vector<HeroSpecial*> specialty;
 
+	struct DLL_LINKAGE SecondarySkillsInfo
+	{
+		ui32 randomSeed; //skills are determined, initialized at map start
+		ui8 magicSchoolCounter;
+		ui8 wisdomCounter;
+
+		void resetMagicSchoolCounter();
+		void resetWisdomCounter();
+
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & randomSeed & magicSchoolCounter & wisdomCounter;
+		}
+	} skillsInfo;
+
 	//BonusList bonuses;
 	//////////////////////////////////////////////////////////////////////////
 
@@ -355,7 +370,7 @@ public:
 		h & static_cast<CArmedInstance&>(*this);
 		h & static_cast<CArtifactSet&>(*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);
 

+ 15 - 0
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();
 }

+ 5 - 1
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)
 	{