From a2d2ecc96f13b31d1dc8f6f29b6d9006882b48d7 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 11 Jul 2023 18:49:19 +0300 Subject: [PATCH] Roll first for hero class, and then - for actual hero for tavern --- lib/gameState/TavernHeroesPool.cpp | 2 +- server/HeroPoolProcessor.cpp | 149 +++++++++++++++++++---------- server/HeroPoolProcessor.h | 5 + 3 files changed, 105 insertions(+), 51 deletions(-) diff --git a/lib/gameState/TavernHeroesPool.cpp b/lib/gameState/TavernHeroesPool.cpp index 0b6693a9a..ea5bbc996 100644 --- a/lib/gameState/TavernHeroesPool.cpp +++ b/lib/gameState/TavernHeroesPool.cpp @@ -16,7 +16,7 @@ VCMI_LIB_NAMESPACE_BEGIN TavernHeroesPool::~TavernHeroesPool() { - for(auto ptr : heroesPool) // clean hero pool + for(const auto & ptr : heroesPool) // clean hero pool delete ptr.second; } diff --git a/server/HeroPoolProcessor.cpp b/server/HeroPoolProcessor.cpp index a05664946..12da68b5c 100644 --- a/server/HeroPoolProcessor.cpp +++ b/server/HeroPoolProcessor.cpp @@ -21,10 +21,13 @@ #include "../lib/gameState/CGameState.h" #include "../lib/gameState/TavernHeroesPool.h" -HeroPoolProcessor::HeroPoolProcessor() = default; +HeroPoolProcessor::HeroPoolProcessor() + : gameHandler(nullptr) +{ +} -HeroPoolProcessor::HeroPoolProcessor(CGameHandler * gameHandler): - gameHandler(gameHandler) +HeroPoolProcessor::HeroPoolProcessor(CGameHandler * gameHandler) + : gameHandler(gameHandler) { } @@ -172,11 +175,44 @@ bool HeroPoolProcessor::hireHero(const CGObjectInstance *obj, const HeroTypeID & return true; } -CGHeroInstance * HeroPoolProcessor::pickHeroFor(bool isNative, - const PlayerColor & player, - const FactionID & factionID, - CRandomGenerator & rand, - const CHeroClass * bannedClass) const +std::set HeroPoolProcessor::findAvailableClassesFor(const PlayerColor & player) const +{ + std::set result; + + const auto & hpool = gameHandler->gameState()->hpool; + FactionID factionID = gameHandler->getPlayerSettings(player)->castle; + + for(auto & elem : hpool->unusedHeroesFromPool()) + { + bool heroAvailable = hpool->isHeroAvailableFor(elem.first, player); + bool heroClassBanned = elem.second->type->heroClass->selectionProbability[factionID] == 0; + + if(heroAvailable && !heroClassBanned) + result.insert(elem.second->type->heroClass); + } + + return result; +} + +std::set HeroPoolProcessor::findAvailableHeroesFor(const PlayerColor & player, const CHeroClass * heroClass) const +{ + std::set result; + + const auto & hpool = gameHandler->gameState()->hpool; + + for(auto & elem : hpool->unusedHeroesFromPool()) + { + bool heroAvailable = hpool->isHeroAvailableFor(elem.first, player); + bool heroClassMatches = elem.second->type->heroClass == heroClass; + + if(heroAvailable && heroClassMatches) + result.insert(elem.second); + } + + return result; +} + +const CHeroClass * HeroPoolProcessor::pickClassFor(bool isNative, const PlayerColor & player, const FactionID & factionID, CRandomGenerator & rand) const { if(player >= PlayerColor::PLAYER_LIMIT) { @@ -185,56 +221,69 @@ CGHeroInstance * HeroPoolProcessor::pickHeroFor(bool isNative, } const auto & hpool = gameHandler->gameState()->hpool; + const auto & currentTavern = hpool->getHeroesFor(player); - if(isNative) - { - std::vector pool; + std::set potentialClasses = findAvailableClassesFor(player); + std::set possibleClasses; - for(auto & elem : hpool->unusedHeroesFromPool()) - { - //get all available heroes - bool heroAvailable = hpool->isHeroAvailableFor(elem.first, player); - bool heroClassNative = elem.second->type->heroClass->faction == factionID; - - if(heroAvailable && heroClassNative) - pool.push_back(elem.second); - } - - if(!pool.empty()) - return *RandomGeneratorUtil::nextItem(pool, rand); - - logGlobal->error("Cannot pick native hero for %s. Picking any...", player.getStr()); - } - - std::vector pool; - int totalWeight = 0; - - for(auto & elem : hpool->unusedHeroesFromPool()) - { - bool heroAvailable = hpool->isHeroAvailableFor(elem.first, player); - bool heroClassBanned = bannedClass && elem.second->type->heroClass == bannedClass; - - if(heroAvailable && !heroClassBanned) - { - pool.push_back(elem.second); - totalWeight += elem.second->type->heroClass->selectionProbability[factionID]; //total weight - } - } - if(pool.empty() || totalWeight == 0) + if(potentialClasses.empty()) { logGlobal->error("There are no heroes available for player %s!", player.getStr()); return nullptr; } - int roll = rand.nextInt(totalWeight - 1); - for(auto & elem : pool) + for(const auto & heroClass : potentialClasses) { - roll -= elem->type->heroClass->selectionProbability[factionID]; - if(roll < 0) - { - return elem; - } + if (isNative && heroClass->faction != factionID) + continue; + + bool hasSameClass = vstd::contains_if(currentTavern, [&](const CGHeroInstance * hero){ + return hero->type->heroClass == heroClass; + }); + + if (hasSameClass) + continue; + + possibleClasses.insert(heroClass); } - return pool.back(); + if (possibleClasses.empty()) + { + logGlobal->error("Cannot pick native hero for %s. Picking any...", player.getStr()); + possibleClasses = potentialClasses; + } + + int totalWeight = 0; + for(const auto & heroClass : possibleClasses) + totalWeight += heroClass->selectionProbability.at(factionID); + + int roll = rand.nextInt(totalWeight - 1); + for(const auto & heroClass : possibleClasses) + { + roll -= heroClass->selectionProbability.at(factionID); + if(roll < 0) + return heroClass; + } + + return *possibleClasses.rbegin(); +} + +CGHeroInstance * HeroPoolProcessor::pickHeroFor(bool isNative, + const PlayerColor & player, + const FactionID & factionID, + CRandomGenerator & rand, + const CHeroClass * bannedClass) const +{ + const CHeroClass * heroClass = pickClassFor(isNative, player, factionID, rand); + + if(!heroClass) + return nullptr; + + std::set possibleHeroes = findAvailableHeroesFor(player, heroClass); + + assert(!possibleHeroes.empty()); + if(possibleHeroes.empty()) + return nullptr; + + return *RandomGeneratorUtil::nextItem(possibleHeroes, rand); } diff --git a/server/HeroPoolProcessor.h b/server/HeroPoolProcessor.h index eeacd1444..671a48102 100644 --- a/server/HeroPoolProcessor.h +++ b/server/HeroPoolProcessor.h @@ -31,6 +31,11 @@ class HeroPoolProcessor : boost::noncopyable void clearHeroFromSlot(const PlayerColor & color, TavernHeroSlot slot); void selectNewHeroForSlot(const PlayerColor & color, TavernHeroSlot slot, bool needNativeHero, bool giveStartingArmy); + std::set findAvailableClassesFor(const PlayerColor & player) const; + std::set findAvailableHeroesFor(const PlayerColor & player, const CHeroClass * heroClass) const; + + const CHeroClass * pickClassFor(bool isNative, const PlayerColor & player, const FactionID & faction, CRandomGenerator & rand) const; + CGHeroInstance * pickHeroFor(bool isNative, const PlayerColor & player, const FactionID & faction, CRandomGenerator & rand, const CHeroClass * bannedClass) const; public: HeroPoolProcessor();