1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-28 08:48:48 +02:00

Fix regressions

This commit is contained in:
Ivan Savenko 2023-07-11 17:51:14 +03:00
parent 19ace6a849
commit f8187ce1d8
6 changed files with 123 additions and 114 deletions

View File

@ -2012,6 +2012,9 @@ void NewTurn::applyGs(CGameState *gs)
logGlobal->error("Hero %d not found in NewTurn::applyGs", h.id.getNum());
continue;
}
hero->setMovementPoints(h.move);
hero->mana = h.mana;
}
gs->hpool->onNewDay();

View File

@ -385,7 +385,7 @@ int CGameState::getDate(Date::EDateType mode) const
CGameState::CGameState()
{
gs = this;
hpool = std::make_unique<TavernHeroesPool>(this);
hpool = std::make_unique<TavernHeroesPool>();
applier = std::make_shared<CApplier<CBaseForGSApply>>();
registerTypesClientPacks1(*applier);
registerTypesClientPacks2(*applier);

View File

@ -10,18 +10,9 @@
#include "StdInc.h"
#include "TavernHeroesPool.h"
#include "CGameState.h"
#include "CPlayerState.h"
#include "../mapObjects/CGHeroInstance.h"
#include "../CHeroHandler.h"
TavernHeroesPool::TavernHeroesPool() = default;
TavernHeroesPool::TavernHeroesPool(CGameState * gameState)
: gameState(gameState)
{
}
VCMI_LIB_NAMESPACE_BEGIN
TavernHeroesPool::~TavernHeroesPool()
{
@ -29,7 +20,7 @@ TavernHeroesPool::~TavernHeroesPool()
delete ptr.second;
}
std::map<HeroTypeID, CGHeroInstance*> TavernHeroesPool::unusedHeroesFromPool()
std::map<HeroTypeID, CGHeroInstance*> TavernHeroesPool::unusedHeroesFromPool() const
{
std::map<HeroTypeID, CGHeroInstance*> pool = heroesPool;
for(const auto & player : currentTavern)
@ -63,71 +54,6 @@ bool TavernHeroesPool::isHeroAvailableFor(HeroTypeID hero, PlayerColor color) co
return true;
}
CGHeroInstance * TavernHeroesPool::pickHeroFor(TavernHeroSlot slot,
const PlayerColor & player,
const FactionID & factionID,
CRandomGenerator & rand,
const CHeroClass * bannedClass) const
{
if(player>=PlayerColor::PLAYER_LIMIT)
{
logGlobal->error("Cannot pick hero for player %d. Wrong owner!", player.getStr());
return nullptr;
}
if(slot == TavernHeroSlot::NATIVE)
{
std::vector<CGHeroInstance *> pool;
for(auto & elem : heroesPool)
{
//get all available heroes
bool heroAvailable = 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 : heroesPool)
{
bool heroAvailable = 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)
{
logGlobal->error("There are no heroes available for player %s!", player.getStr());
return nullptr;
}
int roll = rand.nextInt(totalWeight - 1);
for (auto & elem : pool)
{
roll -= elem->type->heroClass->selectionProbability[factionID];
if(roll < 0)
{
return elem;
}
}
return pool.back();
}
std::vector<const CGHeroInstance *> TavernHeroesPool::getHeroesFor(PlayerColor color) const
{
std::vector<const CGHeroInstance *> result;
@ -174,3 +100,5 @@ void TavernHeroesPool::setAvailability(HeroTypeID hero, PlayerColor::Mask mask)
{
pavailable[hero] = mask;
}
VCMI_LIB_NAMESPACE_END

View File

@ -23,38 +23,34 @@ class CSimpleArmy;
enum class TavernHeroSlot
{
NATIVE,
RANDOM
NATIVE, // 1st / left slot in tavern, contains hero native to player's faction on new week
RANDOM // 2nd / right slot in tavern, contains hero of random class
};
class DLL_LINKAGE TavernHeroesPool
{
CGameState * gameState;
//[subID] - heroes available to buy; nullptr if not available
/// list of all heroes in pool, including those currently present in taverns
std::map<HeroTypeID, CGHeroInstance* > heroesPool;
// [subid] -> which players can recruit hero (binary flags)
/// list of which players are able to purchase specific hero
/// if hero is not present in list, he is available for everyone
std::map<HeroTypeID, PlayerColor::Mask> pavailable;
std::map<HeroTypeID, CGHeroInstance* > unusedHeroesFromPool(); //heroes pool without heroes that are available in taverns
/// list of heroes currently available in a tavern of a specific player
std::map<PlayerColor, std::map<TavernHeroSlot, CGHeroInstance*> > currentTavern;
bool isHeroAvailableFor(HeroTypeID hero, PlayerColor color) const;
public:
TavernHeroesPool();
TavernHeroesPool(CGameState * gameState);
~TavernHeroesPool();
CGHeroInstance * pickHeroFor(TavernHeroSlot slot,
const PlayerColor & player,
const FactionID & faction,
CRandomGenerator & rand,
const CHeroClass * bannedClass = nullptr) const;
/// Returns heroes currently availabe in tavern of a specific player
std::vector<const CGHeroInstance *> getHeroesFor(PlayerColor color) const;
/// returns heroes in pool without heroes that are available in taverns
std::map<HeroTypeID, CGHeroInstance* > unusedHeroesFromPool() const;
/// Returns true if hero is available to a specific player
bool isHeroAvailableFor(HeroTypeID hero, PlayerColor color) const;
CGHeroInstance * takeHero(HeroTypeID hero);
/// reset mana and movement points for all heroes in pool
@ -66,7 +62,6 @@ public:
template <typename Handler> void serialize(Handler &h, const int version)
{
h & gameState;
h & heroesPool;
h & pavailable;
}

View File

@ -58,19 +58,28 @@ void HeroPoolProcessor::clearHeroFromSlot(const PlayerColor & color, TavernHeroS
gameHandler->sendAndApply(&sah);
}
void HeroPoolProcessor::selectNewHeroForSlot(const PlayerColor & color, TavernHeroSlot slot)
void HeroPoolProcessor::selectNewHeroForSlot(const PlayerColor & color, TavernHeroSlot slot, bool needNativeHero, bool giveArmy)
{
SetAvailableHero sah;
sah.player = color;
sah.slotID = static_cast<int>(slot);
//first hero - native if possible, second hero -> any other class
CGHeroInstance *h = gameHandler->gameState()->hpool->pickHeroFor(slot, color, gameHandler->getPlayerSettings(color)->castle, gameHandler->getRandomGenerator());
CGHeroInstance *h = pickHeroFor(needNativeHero, color, gameHandler->getPlayerSettings(color)->castle, gameHandler->getRandomGenerator(), nullptr);
if (h)
{
sah.hid = h->subID;
h->initArmy(gameHandler->getRandomGenerator(), &sah.army);
if (giveArmy)
{
h->initArmy(gameHandler->getRandomGenerator(), &sah.army);
}
else
{
sah.army.clear();
sah.army.setCreature(SlotID(0), h->type->initialArmy[0].creature, 1);
}
}
else
{
@ -83,8 +92,8 @@ void HeroPoolProcessor::onNewWeek(const PlayerColor & color)
{
clearHeroFromSlot(color, TavernHeroSlot::NATIVE);
clearHeroFromSlot(color, TavernHeroSlot::RANDOM);
selectNewHeroForSlot(color, TavernHeroSlot::NATIVE);
selectNewHeroForSlot(color, TavernHeroSlot::RANDOM);
selectNewHeroForSlot(color, TavernHeroSlot::NATIVE, true, true);
selectNewHeroForSlot(color, TavernHeroSlot::RANDOM, false, true);
}
bool HeroPoolProcessor::hireHero(const CGObjectInstance *obj, const HeroTypeID & heroToRecruit, const PlayerColor & player)
@ -101,34 +110,34 @@ bool HeroPoolProcessor::hireHero(const CGObjectInstance *obj, const HeroTypeID &
if (gameHandler->getHeroCount(player, true) >= VLC->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP) && gameHandler->complain("Cannot hire hero, too many heroes garrizoned and wandering already!"))
return false;
if (town) //tavern in town
if(town) //tavern in town
{
if (!town->hasBuilt(BuildingID::TAVERN) && gameHandler->complain("No tavern!"))
if(!town->hasBuilt(BuildingID::TAVERN) && gameHandler->complain("No tavern!"))
return false;
if (town->visitingHero && gameHandler->complain("There is visiting hero - no place!"))
if(town->visitingHero && gameHandler->complain("There is visiting hero - no place!"))
return false;
}
if (obj->ID == Obj::TAVERN)
if(obj->ID == Obj::TAVERN)
{
if (gameHandler->getTile(obj->visitablePos())->visitableObjects.back() != obj && gameHandler->complain("Tavern entry must be unoccupied!"))
if(gameHandler->getTile(obj->visitablePos())->visitableObjects.back() != obj && gameHandler->complain("Tavern entry must be unoccupied!"))
return false;
}
auto recruitableHeroes = gameHandler->gameState()->hpool->getHeroesFor(player);
const CGHeroInstance *recruitedHero = nullptr;;
const CGHeroInstance * recruitedHero = nullptr;
for(const auto & hero : recruitableHeroes)
{
if (hero->subID == heroToRecruit)
if(hero->subID == heroToRecruit)
recruitedHero = hero;
}
if (!recruitedHero)
if(!recruitedHero)
{
gameHandler->complain ("Hero is not available for hiring!");
gameHandler->complain("Hero is not available for hiring!");
return false;
}
@ -137,19 +146,21 @@ bool HeroPoolProcessor::hireHero(const CGObjectInstance *obj, const HeroTypeID &
hr.hid = recruitedHero->subID;
hr.player = player;
hr.tile = recruitedHero->convertFromVisitablePos(obj->visitablePos());
if (gameHandler->getTile(hr.tile)->isWater())
if(gameHandler->getTile(hr.tile)->isWater())
{
//Create a new boat for hero
gameHandler->createObject(obj->visitablePos(), Obj::BOAT, recruitedHero->getBoatType().getNum());
hr.boatId = gameHandler->getTopObj(hr.tile)->id;
}
// apply netpack -> this will remove hired hero from tavern slot
gameHandler->sendAndApply(&hr);
if (recruitableHeroes[0] == recruitedHero)
selectNewHeroForSlot(player, TavernHeroSlot::NATIVE);
if(recruitableHeroes[0] == recruitedHero)
selectNewHeroForSlot(player, TavernHeroSlot::NATIVE, false, false);
else
selectNewHeroForSlot(player, TavernHeroSlot::RANDOM);
selectNewHeroForSlot(player, TavernHeroSlot::RANDOM, false, false);
gameHandler->giveResource(player, EGameResID::GOLD, -GameConstants::HERO_GOLD_COST);
@ -160,3 +171,70 @@ 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
{
if(player >= PlayerColor::PLAYER_LIMIT)
{
logGlobal->error("Cannot pick hero for player %d. Wrong owner!", player.getStr());
return nullptr;
}
const auto & hpool = gameHandler->gameState()->hpool;
if(isNative)
{
std::vector<CGHeroInstance *> pool;
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)
{
logGlobal->error("There are no heroes available for player %s!", player.getStr());
return nullptr;
}
int roll = rand.nextInt(totalWeight - 1);
for(auto & elem : pool)
{
roll -= elem->type->heroClass->selectionProbability[factionID];
if(roll < 0)
{
return elem;
}
}
return pool.back();
}

View File

@ -16,6 +16,9 @@ class PlayerColor;
class CGHeroInstance;
class HeroTypeID;
class CGObjectInstance;
class FactionID;
class CRandomGenerator;
class CHeroClass;
VCMI_LIB_NAMESPACE_END
@ -26,7 +29,9 @@ class HeroPoolProcessor : boost::noncopyable
CGameHandler * gameHandler;
void clearHeroFromSlot(const PlayerColor & color, TavernHeroSlot slot);
void selectNewHeroForSlot(const PlayerColor & color, TavernHeroSlot slot);
void selectNewHeroForSlot(const PlayerColor & color, TavernHeroSlot slot, bool needNativeHero, bool giveStartingArmy);
CGHeroInstance * pickHeroFor(bool isNative, const PlayerColor & player, const FactionID & faction, CRandomGenerator & rand, const CHeroClass * bannedClass) const;
public:
HeroPoolProcessor();
HeroPoolProcessor(CGameHandler * gameHandler);