1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-02-11 13:15:38 +02:00

Roll first for hero class, and then - for actual hero for tavern

This commit is contained in:
Ivan Savenko 2023-07-11 18:49:19 +03:00
parent f8187ce1d8
commit a2d2ecc96f
3 changed files with 105 additions and 51 deletions

View File

@ -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;
}

View File

@ -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<const CHeroClass *> HeroPoolProcessor::findAvailableClassesFor(const PlayerColor & player) const
{
std::set<const CHeroClass *> 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<CGHeroInstance *> HeroPoolProcessor::findAvailableHeroesFor(const PlayerColor & player, const CHeroClass * heroClass) const
{
std::set<CGHeroInstance *> 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<CGHeroInstance *> pool;
std::set<const CHeroClass *> potentialClasses = findAvailableClassesFor(player);
std::set<const CHeroClass *> 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<CGHeroInstance *> 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<CGHeroInstance *> possibleHeroes = findAvailableHeroesFor(player, heroClass);
assert(!possibleHeroes.empty());
if(possibleHeroes.empty())
return nullptr;
return *RandomGeneratorUtil::nextItem(possibleHeroes, rand);
}

View File

@ -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<const CHeroClass *> findAvailableClassesFor(const PlayerColor & player) const;
std::set<CGHeroInstance *> 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();