1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-23 00:28:08 +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()); logGlobal->error("Hero %d not found in NewTurn::applyGs", h.id.getNum());
continue; continue;
} }
hero->setMovementPoints(h.move);
hero->mana = h.mana;
} }
gs->hpool->onNewDay(); gs->hpool->onNewDay();

View File

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

View File

@ -10,18 +10,9 @@
#include "StdInc.h" #include "StdInc.h"
#include "TavernHeroesPool.h" #include "TavernHeroesPool.h"
#include "CGameState.h"
#include "CPlayerState.h"
#include "../mapObjects/CGHeroInstance.h" #include "../mapObjects/CGHeroInstance.h"
#include "../CHeroHandler.h"
TavernHeroesPool::TavernHeroesPool() = default; VCMI_LIB_NAMESPACE_BEGIN
TavernHeroesPool::TavernHeroesPool(CGameState * gameState)
: gameState(gameState)
{
}
TavernHeroesPool::~TavernHeroesPool() TavernHeroesPool::~TavernHeroesPool()
{ {
@ -29,7 +20,7 @@ TavernHeroesPool::~TavernHeroesPool()
delete ptr.second; delete ptr.second;
} }
std::map<HeroTypeID, CGHeroInstance*> TavernHeroesPool::unusedHeroesFromPool() std::map<HeroTypeID, CGHeroInstance*> TavernHeroesPool::unusedHeroesFromPool() const
{ {
std::map<HeroTypeID, CGHeroInstance*> pool = heroesPool; std::map<HeroTypeID, CGHeroInstance*> pool = heroesPool;
for(const auto & player : currentTavern) for(const auto & player : currentTavern)
@ -63,71 +54,6 @@ bool TavernHeroesPool::isHeroAvailableFor(HeroTypeID hero, PlayerColor color) co
return true; 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 *> TavernHeroesPool::getHeroesFor(PlayerColor color) const
{ {
std::vector<const CGHeroInstance *> result; std::vector<const CGHeroInstance *> result;
@ -174,3 +100,5 @@ void TavernHeroesPool::setAvailability(HeroTypeID hero, PlayerColor::Mask mask)
{ {
pavailable[hero] = mask; pavailable[hero] = mask;
} }
VCMI_LIB_NAMESPACE_END

View File

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

View File

@ -58,19 +58,28 @@ void HeroPoolProcessor::clearHeroFromSlot(const PlayerColor & color, TavernHeroS
gameHandler->sendAndApply(&sah); 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; SetAvailableHero sah;
sah.player = color; sah.player = color;
sah.slotID = static_cast<int>(slot); sah.slotID = static_cast<int>(slot);
//first hero - native if possible, second hero -> any other class //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) if (h)
{ {
sah.hid = h->subID; 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 else
{ {
@ -83,8 +92,8 @@ void HeroPoolProcessor::onNewWeek(const PlayerColor & color)
{ {
clearHeroFromSlot(color, TavernHeroSlot::NATIVE); clearHeroFromSlot(color, TavernHeroSlot::NATIVE);
clearHeroFromSlot(color, TavernHeroSlot::RANDOM); clearHeroFromSlot(color, TavernHeroSlot::RANDOM);
selectNewHeroForSlot(color, TavernHeroSlot::NATIVE); selectNewHeroForSlot(color, TavernHeroSlot::NATIVE, true, true);
selectNewHeroForSlot(color, TavernHeroSlot::RANDOM); selectNewHeroForSlot(color, TavernHeroSlot::RANDOM, false, true);
} }
bool HeroPoolProcessor::hireHero(const CGObjectInstance *obj, const HeroTypeID & heroToRecruit, const PlayerColor & player) 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!")) 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; 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; 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; 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; return false;
} }
auto recruitableHeroes = gameHandler->gameState()->hpool->getHeroesFor(player); auto recruitableHeroes = gameHandler->gameState()->hpool->getHeroesFor(player);
const CGHeroInstance *recruitedHero = nullptr;; const CGHeroInstance * recruitedHero = nullptr;
for(const auto & hero : recruitableHeroes) for(const auto & hero : recruitableHeroes)
{ {
if (hero->subID == heroToRecruit) if(hero->subID == heroToRecruit)
recruitedHero = hero; recruitedHero = hero;
} }
if (!recruitedHero) if(!recruitedHero)
{ {
gameHandler->complain ("Hero is not available for hiring!"); gameHandler->complain("Hero is not available for hiring!");
return false; return false;
} }
@ -137,19 +146,21 @@ bool HeroPoolProcessor::hireHero(const CGObjectInstance *obj, const HeroTypeID &
hr.hid = recruitedHero->subID; hr.hid = recruitedHero->subID;
hr.player = player; hr.player = player;
hr.tile = recruitedHero->convertFromVisitablePos(obj->visitablePos()); hr.tile = recruitedHero->convertFromVisitablePos(obj->visitablePos());
if (gameHandler->getTile(hr.tile)->isWater()) if(gameHandler->getTile(hr.tile)->isWater())
{ {
//Create a new boat for hero //Create a new boat for hero
gameHandler->createObject(obj->visitablePos(), Obj::BOAT, recruitedHero->getBoatType().getNum()); gameHandler->createObject(obj->visitablePos(), Obj::BOAT, recruitedHero->getBoatType().getNum());
hr.boatId = gameHandler->getTopObj(hr.tile)->id; hr.boatId = gameHandler->getTopObj(hr.tile)->id;
} }
// apply netpack -> this will remove hired hero from tavern slot
gameHandler->sendAndApply(&hr); gameHandler->sendAndApply(&hr);
if (recruitableHeroes[0] == recruitedHero) if(recruitableHeroes[0] == recruitedHero)
selectNewHeroForSlot(player, TavernHeroSlot::NATIVE); selectNewHeroForSlot(player, TavernHeroSlot::NATIVE, false, false);
else else
selectNewHeroForSlot(player, TavernHeroSlot::RANDOM); selectNewHeroForSlot(player, TavernHeroSlot::RANDOM, false, false);
gameHandler->giveResource(player, EGameResID::GOLD, -GameConstants::HERO_GOLD_COST); gameHandler->giveResource(player, EGameResID::GOLD, -GameConstants::HERO_GOLD_COST);
@ -160,3 +171,70 @@ bool HeroPoolProcessor::hireHero(const CGObjectInstance *obj, const HeroTypeID &
} }
return true; 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 CGHeroInstance;
class HeroTypeID; class HeroTypeID;
class CGObjectInstance; class CGObjectInstance;
class FactionID;
class CRandomGenerator;
class CHeroClass;
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END
@ -26,7 +29,9 @@ class HeroPoolProcessor : boost::noncopyable
CGameHandler * gameHandler; CGameHandler * gameHandler;
void clearHeroFromSlot(const PlayerColor & color, TavernHeroSlot slot); 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: public:
HeroPoolProcessor(); HeroPoolProcessor();
HeroPoolProcessor(CGameHandler * gameHandler); HeroPoolProcessor(CGameHandler * gameHandler);