1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-02 00:10:22 +02:00

Moved hero pool logic to the separate files

This commit is contained in:
Ivan Savenko 2023-07-11 15:16:02 +03:00
parent 1c55835b8b
commit 19ace6a849
26 changed files with 535 additions and 365 deletions

View File

@ -270,17 +270,10 @@ void CCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroIn
{ {
assert(townOrTavern); assert(townOrTavern);
assert(hero); assert(hero);
ui8 i=0;
for(; i<gs->players[*player].availableHeroes.size(); i++) HireHero pack(HeroTypeID(hero->subID), townOrTavern->id);
{ pack.player = *player;
if(gs->players[*player].availableHeroes[i] == hero) sendRequest(&pack);
{
HireHero pack(i, townOrTavern->id);
pack.player = *player;
sendRequest(&pack);
return;
}
}
} }
void CCallback::save( const std::string &fname ) void CCallback::save( const std::string &fname )

View File

@ -68,6 +68,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/gameState/CGameState.cpp ${MAIN_LIB_DIR}/gameState/CGameState.cpp
${MAIN_LIB_DIR}/gameState/CGameStateCampaign.cpp ${MAIN_LIB_DIR}/gameState/CGameStateCampaign.cpp
${MAIN_LIB_DIR}/gameState/InfoAboutArmy.cpp ${MAIN_LIB_DIR}/gameState/InfoAboutArmy.cpp
${MAIN_LIB_DIR}/gameState/TavernHeroesPool.cpp
${MAIN_LIB_DIR}/logging/CBasicLogConfigurator.cpp ${MAIN_LIB_DIR}/logging/CBasicLogConfigurator.cpp
${MAIN_LIB_DIR}/logging/CLogger.cpp ${MAIN_LIB_DIR}/logging/CLogger.cpp
@ -394,6 +395,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/gameState/EVictoryLossCheckResult.h ${MAIN_LIB_DIR}/gameState/EVictoryLossCheckResult.h
${MAIN_LIB_DIR}/gameState/InfoAboutArmy.h ${MAIN_LIB_DIR}/gameState/InfoAboutArmy.h
${MAIN_LIB_DIR}/gameState/SThievesGuildInfo.h ${MAIN_LIB_DIR}/gameState/SThievesGuildInfo.h
${MAIN_LIB_DIR}/gameState/TavernHeroesPool.h
${MAIN_LIB_DIR}/gameState/QuestInfo.h ${MAIN_LIB_DIR}/gameState/QuestInfo.h
${MAIN_LIB_DIR}/logging/CBasicLogConfigurator.h ${MAIN_LIB_DIR}/logging/CBasicLogConfigurator.h

View File

@ -13,6 +13,7 @@
#include "gameState/CGameState.h" #include "gameState/CGameState.h"
#include "gameState/InfoAboutArmy.h" #include "gameState/InfoAboutArmy.h"
#include "gameState/SThievesGuildInfo.h" #include "gameState/SThievesGuildInfo.h"
#include "gameState/TavernHeroesPool.h"
#include "CGeneralTextHandler.h" #include "CGeneralTextHandler.h"
#include "StartInfo.h" // for StartInfo #include "StartInfo.h" // for StartInfo
#include "battle/BattleInfo.h" // for BattleInfo #include "battle/BattleInfo.h" // for BattleInfo
@ -99,13 +100,6 @@ const PlayerState * CGameInfoCallback::getPlayerState(PlayerColor color, bool ve
} }
} }
const CTown * CGameInfoCallback::getNativeTown(PlayerColor color) const
{
const PlayerSettings *ps = getPlayerSettings(color);
ERROR_RET_VAL_IF(!ps, "There is no such player!", nullptr);
return (*VLC->townh)[ps->castle]->town;
}
const CGObjectInstance * CGameInfoCallback::getObjByQuestIdentifier(int identifier) const const CGObjectInstance * CGameInfoCallback::getObjByQuestIdentifier(int identifier) const
{ {
if(gs->map->questIdentifierToId.empty()) if(gs->map->questIdentifierToId.empty())
@ -486,13 +480,10 @@ std::vector<const CGHeroInstance *> CGameInfoCallback::getAvailableHeroes(const
//ERROR_RET_VAL_IF(!isOwnedOrVisited(townOrTavern), "Town or tavern must be owned or visited!", ret); //ERROR_RET_VAL_IF(!isOwnedOrVisited(townOrTavern), "Town or tavern must be owned or visited!", ret);
//TODO: town needs to be owned, advmap tavern needs to be visited; to be reimplemented when visit tracking is done //TODO: town needs to be owned, advmap tavern needs to be visited; to be reimplemented when visit tracking is done
const CGTownInstance * town = getTown(townOrTavern->id); const CGTownInstance * town = getTown(townOrTavern->id);
if(townOrTavern->ID == Obj::TAVERN || (town && town->hasBuilt(BuildingID::TAVERN))) if(townOrTavern->ID == Obj::TAVERN || (town && town->hasBuilt(BuildingID::TAVERN)))
{ return gs->hpool->getHeroesFor(*player);
range::copy(gs->players[*player].availableHeroes, std::back_inserter(ret));
vstd::erase_if(ret, [](const CGHeroInstance * h) {
return h == nullptr;
});
}
return ret; return ret;
} }

View File

@ -108,7 +108,6 @@ public:
// std::string getTavernRumor(const CGObjectInstance * townOrTavern) const; // std::string getTavernRumor(const CGObjectInstance * townOrTavern) const;
// EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID);//// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements // EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID);//// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
// virtual bool getTownInfo(const CGObjectInstance * town, InfoAboutTown & dest, const CGObjectInstance * selectedObject = nullptr) const; // virtual bool getTownInfo(const CGObjectInstance * town, InfoAboutTown & dest, const CGObjectInstance * selectedObject = nullptr) const;
// const CTown *getNativeTown(PlayerColor color) const;
//from gs //from gs
// const TeamState *getTeam(TeamID teamID) const; // const TeamState *getTeam(TeamID teamID) const;
@ -206,7 +205,6 @@ public:
virtual std::string getTavernRumor(const CGObjectInstance * townOrTavern) const; virtual std::string getTavernRumor(const CGObjectInstance * townOrTavern) const;
virtual EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID);//// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements virtual EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID);//// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
virtual bool getTownInfo(const CGObjectInstance * town, InfoAboutTown & dest, const CGObjectInstance * selectedObject = nullptr) const; virtual bool getTownInfo(const CGObjectInstance * town, InfoAboutTown & dest, const CGObjectInstance * selectedObject = nullptr) const;
virtual const CTown *getNativeTown(PlayerColor color) const;
//from gs //from gs
virtual const TeamState *getTeam(TeamID teamID) const; virtual const TeamState *getTeam(TeamID teamID) const;

View File

@ -35,7 +35,6 @@ PlayerState::PlayerState(PlayerState && other) noexcept:
std::swap(visitedObjects, other.visitedObjects); std::swap(visitedObjects, other.visitedObjects);
std::swap(heroes, other.heroes); std::swap(heroes, other.heroes);
std::swap(towns, other.towns); std::swap(towns, other.towns);
std::swap(availableHeroes, other.availableHeroes);
std::swap(dwellings, other.dwellings); std::swap(dwellings, other.dwellings);
std::swap(quests, other.quests); std::swap(quests, other.quests);
} }

View File

@ -33,7 +33,6 @@ public:
std::set<ObjectInstanceID> visitedObjects; // as a std::set, since most accesses here will be from visited status checks std::set<ObjectInstanceID> visitedObjects; // as a std::set, since most accesses here will be from visited status checks
std::vector<ConstTransitivePtr<CGHeroInstance> > heroes; std::vector<ConstTransitivePtr<CGHeroInstance> > heroes;
std::vector<ConstTransitivePtr<CGTownInstance> > towns; std::vector<ConstTransitivePtr<CGTownInstance> > towns;
std::vector<ConstTransitivePtr<CGHeroInstance> > availableHeroes; //heroes available in taverns
std::vector<ConstTransitivePtr<CGDwelling> > dwellings; //used for town growth std::vector<ConstTransitivePtr<CGDwelling> > dwellings; //used for town growth
std::vector<QuestInfo> quests; //store info about all received quests std::vector<QuestInfo> quests; //store info about all received quests
@ -74,7 +73,6 @@ public:
h & status; h & status;
h & heroes; h & heroes;
h & towns; h & towns;
h & availableHeroes;
h & dwellings; h & dwellings;
h & quests; h & quests;
h & visitedObjects; h & visitedObjects;

View File

@ -357,9 +357,12 @@ class PlayerColor : public BaseForID<PlayerColor, ui8>
enum EPlayerColor enum EPlayerColor
{ {
PLAYER_LIMIT_I = 8 PLAYER_LIMIT_I = 8,
ALL_PLAYERS_MASK = 0xff
}; };
using Mask = uint8_t;
DLL_LINKAGE static const PlayerColor SPECTATOR; //252 DLL_LINKAGE static const PlayerColor SPECTATOR; //252
DLL_LINKAGE static const PlayerColor CANNOT_DETERMINE; //253 DLL_LINKAGE static const PlayerColor CANNOT_DETERMINE; //253
DLL_LINKAGE static const PlayerColor UNFLAGGABLE; //254 - neutral objects (pandora, banks) DLL_LINKAGE static const PlayerColor UNFLAGGABLE; //254 - neutral objects (pandora, banks)

View File

@ -36,6 +36,7 @@
#include "StartInfo.h" #include "StartInfo.h"
#include "gameState/CGameState.h" #include "gameState/CGameState.h"
#include "gameState/CGameStateCampaign.h" #include "gameState/CGameStateCampaign.h"
#include "gameState/TavernHeroesPool.h"
#include "mapping/CMap.h" #include "mapping/CMap.h"
#include "CPlayerState.h" #include "CPlayerState.h"
#include "GameSettings.h" #include "GameSettings.h"

View File

@ -36,7 +36,7 @@ public:
virtual void visitSetMana(SetMana & pack) {} virtual void visitSetMana(SetMana & pack) {}
virtual void visitSetMovePoints(SetMovePoints & pack) {} virtual void visitSetMovePoints(SetMovePoints & pack) {}
virtual void visitFoWChange(FoWChange & pack) {} virtual void visitFoWChange(FoWChange & pack) {}
virtual void visitSetAvailableHeroes(SetAvailableHeroes & pack) {} virtual void visitSetAvailableHeroes(SetAvailableHero & pack) {}
virtual void visitGiveBonus(GiveBonus & pack) {} virtual void visitGiveBonus(GiveBonus & pack) {}
virtual void visitChangeObjPos(ChangeObjPos & pack) {} virtual void visitChangeObjPos(ChangeObjPos & pack) {}
virtual void visitPlayerEndsGame(PlayerEndsGame & pack) {} virtual void visitPlayerEndsGame(PlayerEndsGame & pack) {}

View File

@ -330,23 +330,24 @@ struct DLL_LINKAGE FoWChange : public CPackForClient
} }
}; };
struct DLL_LINKAGE SetAvailableHeroes : public CPackForClient struct DLL_LINKAGE SetAvailableHero : public CPackForClient
{ {
SetAvailableHeroes() SetAvailableHero()
{ {
for(auto & i : army) army.clear();
i.clear();
} }
void applyGs(CGameState * gs); void applyGs(CGameState * gs);
uint8_t slotID;
PlayerColor player; PlayerColor player;
si32 hid[GameConstants::AVAILABLE_HEROES_PER_PLAYER]; //-1 if no hero HeroTypeID hid; //-1 if no hero
CSimpleArmy army[GameConstants::AVAILABLE_HEROES_PER_PLAYER]; CSimpleArmy army;
virtual void visitTyped(ICPackVisitor & visitor) override; virtual void visitTyped(ICPackVisitor & visitor) override;
template <typename Handler> void serialize(Handler & h, const int version) template <typename Handler> void serialize(Handler & h, const int version)
{ {
h & slotID;
h & player; h & player;
h & hid; h & hid;
h & army; h & army;
@ -692,7 +693,7 @@ struct DLL_LINKAGE HeroRecruited : public CPackForClient
{ {
void applyGs(CGameState * gs) const; void applyGs(CGameState * gs) const;
si32 hid = -1; //subID of hero HeroTypeID hid; //subID of hero
ObjectInstanceID tid; ObjectInstanceID tid;
ObjectInstanceID boatId; ObjectInstanceID boatId;
int3 tile; int3 tile;
@ -2437,12 +2438,12 @@ struct DLL_LINKAGE SetFormation : public CPackForServer
struct DLL_LINKAGE HireHero : public CPackForServer struct DLL_LINKAGE HireHero : public CPackForServer
{ {
HireHero() = default; HireHero() = default;
HireHero(si32 HID, const ObjectInstanceID & TID) HireHero(HeroTypeID HID, const ObjectInstanceID & TID)
: hid(HID) : hid(HID)
, tid(TID) , tid(TID)
{ {
} }
si32 hid = 0; //available hero serial HeroTypeID hid; //available hero serial
ObjectInstanceID tid; //town (tavern) id ObjectInstanceID tid; //town (tavern) id
PlayerColor player; PlayerColor player;

View File

@ -20,6 +20,7 @@
#include "spells/CSpellHandler.h" #include "spells/CSpellHandler.h"
#include "CCreatureHandler.h" #include "CCreatureHandler.h"
#include "gameState/CGameState.h" #include "gameState/CGameState.h"
#include "gameState/TavernHeroesPool.h"
#include "CStack.h" #include "CStack.h"
#include "battle/BattleInfo.h" #include "battle/BattleInfo.h"
#include "CTownHandler.h" #include "CTownHandler.h"
@ -151,7 +152,7 @@ void FoWChange::visitTyped(ICPackVisitor & visitor)
visitor.visitFoWChange(*this); visitor.visitFoWChange(*this);
} }
void SetAvailableHeroes::visitTyped(ICPackVisitor & visitor) void SetAvailableHero::visitTyped(ICPackVisitor & visitor)
{ {
visitor.visitSetAvailableHeroes(*this); visitor.visitSetAvailableHeroes(*this);
} }
@ -939,18 +940,9 @@ void FoWChange::applyGs(CGameState *gs)
} }
} }
void SetAvailableHeroes::applyGs(CGameState *gs) void SetAvailableHero::applyGs(CGameState *gs)
{ {
PlayerState *p = gs->getPlayerState(player); gs->hpool->setHeroForPlayer(player, TavernHeroSlot(slotID), hid, army);
p->availableHeroes.clear();
for (int i = 0; i < GameConstants::AVAILABLE_HEROES_PER_PLAYER; i++)
{
CGHeroInstance *h = (hid[i]>=0 ? gs->hpool.heroesPool[hid[i]].get() : nullptr);
if(h && army[i])
h->setToArmy(army[i]);
p->availableHeroes.emplace_back(h);
}
} }
void GiveBonus::applyGs(CGameState *gs) void GiveBonus::applyGs(CGameState *gs)
@ -1150,11 +1142,8 @@ void RemoveObject::applyGs(CGameState *gs)
beatenHero->inTownGarrison = false; beatenHero->inTownGarrison = false;
} }
//return hero to the pool, so he may reappear in tavern //return hero to the pool, so he may reappear in tavern
gs->hpool.heroesPool[beatenHero->subID] = beatenHero;
if(!vstd::contains(gs->hpool.pavailable, beatenHero->subID))
gs->hpool.pavailable[beatenHero->subID] = 0xff;
gs->hpool->addHeroToPool(beatenHero);
gs->map->objects[id.getNum()] = nullptr; gs->map->objects[id.getNum()] = nullptr;
//If hero on Boat is removed, the Boat disappears //If hero on Boat is removed, the Boat disappears
@ -1379,8 +1368,7 @@ void SetHeroesInTown::applyGs(CGameState * gs) const
void HeroRecruited::applyGs(CGameState * gs) const void HeroRecruited::applyGs(CGameState * gs) const
{ {
assert(vstd::contains(gs->hpool.heroesPool, hid)); CGHeroInstance *h = gs->hpool->takeHero(hid);
CGHeroInstance *h = gs->hpool.heroesPool[hid];
CGTownInstance *t = gs->getTown(tid); CGTownInstance *t = gs->getTown(tid);
PlayerState *p = gs->getPlayerState(player); PlayerState *p = gs->getPlayerState(player);
@ -1411,7 +1399,6 @@ void HeroRecruited::applyGs(CGameState * gs) const
} }
} }
gs->hpool.heroesPool.erase(hid);
if(h->id == ObjectInstanceID()) if(h->id == ObjectInstanceID())
{ {
h->id = ObjectInstanceID(static_cast<si32>(gs->map->objects.size())); h->id = ObjectInstanceID(static_cast<si32>(gs->map->objects.size()));
@ -2021,26 +2008,14 @@ void NewTurn::applyGs(CGameState *gs)
{ {
CGHeroInstance *hero = gs->getHero(h.id); CGHeroInstance *hero = gs->getHero(h.id);
if(!hero) if(!hero)
{
// retreated or surrendered hero who has not been reset yet
for(auto& hp : gs->hpool.heroesPool)
{
if(hp.second->id == h.id)
{
hero = hp.second;
break;
}
}
}
if(!hero)
{ {
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();
for(const auto & re : res) for(const auto & re : res)
{ {
assert(re.first < PlayerColor::PLAYER_LIMIT); assert(re.first < PlayerColor::PLAYER_LIMIT);

View File

@ -35,9 +35,9 @@ struct DLL_LINKAGE PlayerSettings
}; };
Ebonus bonus; Ebonus bonus;
si16 castle; FactionID castle;
si32 hero, HeroTypeID hero;
heroPortrait; //-1 if default, else ID HeroTypeID heroPortrait; //-1 if default, else ID
std::string heroName; std::string heroName;
PlayerColor color; //from 0 - PlayerColor color; //from 0 -

View File

@ -12,6 +12,7 @@
#include "EVictoryLossCheckResult.h" #include "EVictoryLossCheckResult.h"
#include "InfoAboutArmy.h" #include "InfoAboutArmy.h"
#include "TavernHeroesPool.h"
#include "CGameStateCampaign.h" #include "CGameStateCampaign.h"
#include "SThievesGuildInfo.h" #include "SThievesGuildInfo.h"
@ -102,81 +103,6 @@ static CGObjectInstance * createObject(const Obj & id, int subid, const int3 & p
return nobj; return nobj;
} }
CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native,
const PlayerColor & player,
const CTown * town,
std::map<ui32, ConstTransitivePtr<CGHeroInstance>> & available,
CRandomGenerator & rand,
const CHeroClass * bannedClass) const
{
CGHeroInstance *ret = nullptr;
if(player>=PlayerColor::PLAYER_LIMIT)
{
logGlobal->error("Cannot pick hero for faction %s. Wrong owner!", town->faction->getJsonKey());
return nullptr;
}
std::vector<CGHeroInstance *> pool;
if(native)
{
for(auto & elem : available)
{
if(pavailable.find(elem.first)->second & 1<<player.getNum()
&& elem.second->type->heroClass->faction == town->faction->getIndex())
{
pool.push_back(elem.second); //get all available heroes
}
}
if(pool.empty())
{
logGlobal->error("Cannot pick native hero for %s. Picking any...", player.getStr());
return pickHeroFor(false, player, town, available, rand);
}
else
{
ret = *RandomGeneratorUtil::nextItem(pool, rand);
}
}
else
{
int sum = 0;
int r;
for(auto & elem : available)
{
if (pavailable.find(elem.first)->second & (1<<player.getNum()) && // hero is available
( !bannedClass || elem.second->type->heroClass != bannedClass) ) // and his class is not same as other hero
{
pool.push_back(elem.second);
sum += elem.second->type->heroClass->selectionProbability[town->faction->getId()]; //total weight
}
}
if(pool.empty() || sum == 0)
{
logGlobal->error("There are no heroes available for player %s!", player.getStr());
return nullptr;
}
r = rand.nextInt(sum - 1);
for (auto & elem : pool)
{
r -= elem->type->heroClass->selectionProbability[town->faction->getId()];
if(r < 0)
{
ret = elem;
break;
}
}
if(!ret)
ret = pool.back();
}
available.erase(ret->subID);
return ret;
}
HeroTypeID CGameState::pickNextHeroType(const PlayerColor & owner) HeroTypeID CGameState::pickNextHeroType(const PlayerColor & owner)
{ {
const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(owner); const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(owner);
@ -459,6 +385,7 @@ int CGameState::getDate(Date::EDateType mode) const
CGameState::CGameState() CGameState::CGameState()
{ {
gs = this; gs = this;
hpool = std::make_unique<TavernHeroesPool>(this);
applier = std::make_shared<CApplier<CBaseForGSApply>>(); applier = std::make_shared<CApplier<CBaseForGSApply>>();
registerTypesClientPacks1(*applier); registerTypesClientPacks1(*applier);
registerTypesClientPacks2(*applier); registerTypesClientPacks2(*applier);
@ -469,9 +396,6 @@ CGameState::~CGameState()
{ {
map.dellNull(); map.dellNull();
curB.dellNull(); curB.dellNull();
for(auto ptr : hpool.heroesPool) // clean hero pool
ptr.second.dellNull();
} }
void CGameState::preInit(Services * services) void CGameState::preInit(Services * services)
@ -951,8 +875,7 @@ void CGameState::initHeroes()
if(!vstd::contains(heroesToCreate, HeroTypeID(ph->subID))) if(!vstd::contains(heroesToCreate, HeroTypeID(ph->subID)))
continue; continue;
ph->initHero(getRandomGenerator()); ph->initHero(getRandomGenerator());
hpool.heroesPool[ph->subID] = ph; hpool->addHeroToPool(ph);
hpool.pavailable[ph->subID] = 0xff;
heroesToCreate.erase(ph->type->getId()); heroesToCreate.erase(ph->type->getId());
map->allHeroes[ph->subID] = ph; map->allHeroes[ph->subID] = ph;
@ -965,14 +888,11 @@ void CGameState::initHeroes()
int typeID = htype.getNum(); int typeID = htype.getNum();
map->allHeroes[typeID] = vhi; map->allHeroes[typeID] = vhi;
hpool.heroesPool[typeID] = vhi; hpool->addHeroToPool(vhi);
hpool.pavailable[typeID] = 0xff;
} }
for(auto & elem : map->disposedHeroes) for(auto & elem : map->disposedHeroes)
{ hpool->setAvailability(elem.heroId, elem.players);
hpool.pavailable[elem.heroId] = elem.players;
}
if (campaign) if (campaign)
campaign->initHeroes(); campaign->initHeroes();
@ -2067,17 +1987,6 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
#undef FILL_FIELD #undef FILL_FIELD
} }
std::map<ui32, ConstTransitivePtr<CGHeroInstance> > CGameState::unusedHeroesFromPool()
{
std::map<ui32, ConstTransitivePtr<CGHeroInstance> > pool = hpool.heroesPool;
for(const auto & player : players)
for(auto availableHero : player.second.availableHeroes)
if(availableHero)
pool.erase((*availableHero).subID);
return pool;
}
void CGameState::buildBonusSystemTree() void CGameState::buildBonusSystemTree()
{ {
buildGlobalTeamPlayerTree(); buildGlobalTeamPlayerTree();

View File

@ -29,6 +29,7 @@ struct EventCondition;
struct CampaignTravel; struct CampaignTravel;
class CStackInstance; class CStackInstance;
class CGameStateCampaign; class CGameStateCampaign;
class TavernHeroesPool;
struct SThievesGuildInfo; struct SThievesGuildInfo;
template<typename T> class CApplier; template<typename T> class CApplier;
@ -78,25 +79,10 @@ DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EVictoryLossCheck
class DLL_LINKAGE CGameState : public CNonConstInfoCallback class DLL_LINKAGE CGameState : public CNonConstInfoCallback
{ {
friend class CGameStateCampaign; friend class CGameStateCampaign;
public: public:
struct DLL_LINKAGE HeroesPool //we have here all heroes available on this map that are not hired
{ std::unique_ptr<TavernHeroesPool> hpool;
std::map<ui32, ConstTransitivePtr<CGHeroInstance> > heroesPool; //[subID] - heroes available to buy; nullptr if not available
std::map<ui32,ui8> pavailable; // [subid] -> which players can recruit hero (binary flags)
CGHeroInstance * pickHeroFor(bool native,
const PlayerColor & player,
const CTown * town,
std::map<ui32, ConstTransitivePtr<CGHeroInstance>> & available,
CRandomGenerator & rand,
const CHeroClass * bannedClass = nullptr) const;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & heroesPool;
h & pavailable;
}
} hpool; //we have here all heroes available on this map that are not hired
CGameState(); CGameState();
virtual ~CGameState(); virtual ~CGameState();
@ -142,7 +128,6 @@ public:
bool checkForStandardLoss(const PlayerColor & player) const; //checks if given player lost the game bool checkForStandardLoss(const PlayerColor & player) const; //checks if given player lost the game
void obtainPlayersStats(SThievesGuildInfo & tgi, int level); //fills tgi with info about other players that is available at given level of thieves' guild void obtainPlayersStats(SThievesGuildInfo & tgi, int level); //fills tgi with info about other players that is available at given level of thieves' guild
std::map<ui32, ConstTransitivePtr<CGHeroInstance> > unusedHeroesFromPool(); //heroes pool without heroes that are available in taverns
bool isVisible(int3 pos, const std::optional<PlayerColor> & player) const override; bool isVisible(int3 pos, const std::optional<PlayerColor> & player) const override;
bool isVisible(const CGObjectInstance * obj, const std::optional<PlayerColor> & player) const override; bool isVisible(const CGObjectInstance * obj, const std::optional<PlayerColor> & player) const override;

View File

@ -0,0 +1,176 @@
/*
* TavernHeroesPool.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#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)
{
}
TavernHeroesPool::~TavernHeroesPool()
{
for(auto ptr : heroesPool) // clean hero pool
delete ptr.second;
}
std::map<HeroTypeID, CGHeroInstance*> TavernHeroesPool::unusedHeroesFromPool()
{
std::map<HeroTypeID, CGHeroInstance*> pool = heroesPool;
for(const auto & player : currentTavern)
for(auto availableHero : player.second)
if(availableHero.second)
pool.erase(HeroTypeID(availableHero.second->subID));
return pool;
}
void TavernHeroesPool::setHeroForPlayer(PlayerColor player, TavernHeroSlot slot, HeroTypeID hero, CSimpleArmy & army)
{
currentTavern[player].erase(slot);
if (hero == HeroTypeID::NONE)
return;
CGHeroInstance * h = heroesPool[hero];
if (h && army)
h->setToArmy(army);
currentTavern[player][slot] = h;
}
bool TavernHeroesPool::isHeroAvailableFor(HeroTypeID hero, PlayerColor color) const
{
if (pavailable.count(hero))
return pavailable.at(hero) & (1 << color.getNum());
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;
if(!currentTavern.count(color))
return result;
for(const auto & hero : currentTavern.at(color))
result.push_back(hero.second);
return result;
}
CGHeroInstance * TavernHeroesPool::takeHero(HeroTypeID hero)
{
assert(heroesPool.count(hero));
CGHeroInstance * result = heroesPool[hero];
heroesPool.erase(hero);
assert(result);
return result;
}
void TavernHeroesPool::onNewDay()
{
for(auto & hero : heroesPool)
{
assert(hero.second);
if(!hero.second)
continue;
hero.second->setMovementPoints(hero.second->movementPointsLimit(true));
hero.second->mana = hero.second->manaLimit();
}
}
void TavernHeroesPool::addHeroToPool(CGHeroInstance * hero)
{
heroesPool[HeroTypeID(hero->subID)] = hero;
}
void TavernHeroesPool::setAvailability(HeroTypeID hero, PlayerColor::Mask mask)
{
pavailable[hero] = mask;
}

View File

@ -0,0 +1,75 @@
/*
* TavernHeroesPool.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../ConstTransitivePtr.h"
#include "../GameConstants.h"
VCMI_LIB_NAMESPACE_BEGIN
class CGHeroInstance;
class CTown;
class CRandomGenerator;
class CHeroClass;
class CGameState;
class CSimpleArmy;
enum class TavernHeroSlot
{
NATIVE,
RANDOM
};
class DLL_LINKAGE TavernHeroesPool
{
CGameState * gameState;
//[subID] - heroes available to buy; nullptr if not available
std::map<HeroTypeID, CGHeroInstance* > heroesPool;
// [subid] -> which players can recruit hero (binary flags)
std::map<HeroTypeID, PlayerColor::Mask> pavailable;
std::map<HeroTypeID, CGHeroInstance* > unusedHeroesFromPool(); //heroes pool without heroes that are available in taverns
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;
std::vector<const CGHeroInstance *> getHeroesFor(PlayerColor color) const;
CGHeroInstance * takeHero(HeroTypeID hero);
/// reset mana and movement points for all heroes in pool
void onNewDay();
void addHeroToPool(CGHeroInstance * hero);
void setAvailability(HeroTypeID hero, PlayerColor::Mask mask);
void setHeroForPlayer(PlayerColor player, TavernHeroSlot slot, HeroTypeID hero, CSimpleArmy & army);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & gameState;
h & heroesPool;
h & pavailable;
}
};
VCMI_LIB_NAMESPACE_END

View File

@ -56,10 +56,10 @@ struct DLL_LINKAGE DisposedHero
{ {
DisposedHero(); DisposedHero();
ui32 heroId; HeroTypeID heroId;
ui32 portrait; /// The portrait id of the hero, -1 is default. ui32 portrait; /// The portrait id of the hero, -1 is default.
std::string name; std::string name;
ui8 players; /// Who can hire this hero (bitfield). PlayerColor::Mask players; /// Who can hire this hero (bitfield).
template <typename Handler> template <typename Handler>
void serialize(Handler & h, const int version) void serialize(Handler & h, const int version)

View File

@ -239,7 +239,7 @@ void registerTypesClientPacks1(Serializer &s)
s.template registerType<CPackForClient, SetMana>(); s.template registerType<CPackForClient, SetMana>();
s.template registerType<CPackForClient, SetMovePoints>(); s.template registerType<CPackForClient, SetMovePoints>();
s.template registerType<CPackForClient, FoWChange>(); s.template registerType<CPackForClient, FoWChange>();
s.template registerType<CPackForClient, SetAvailableHeroes>(); s.template registerType<CPackForClient, SetAvailableHero>();
s.template registerType<CPackForClient, GiveBonus>(); s.template registerType<CPackForClient, GiveBonus>();
s.template registerType<CPackForClient, ChangeObjPos>(); s.template registerType<CPackForClient, ChangeObjPos>();
s.template registerType<CPackForClient, PlayerEndsGame>(); s.template registerType<CPackForClient, PlayerEndsGame>();

View File

@ -14,6 +14,7 @@
#include "../StartInfo.h" #include "../StartInfo.h"
#include "../gameState/CGameState.h" #include "../gameState/CGameState.h"
#include "../gameState/CGameStateCampaign.h" #include "../gameState/CGameStateCampaign.h"
#include "../gameState/TavernHeroesPool.h"
#include "../mapping/CMap.h" #include "../mapping/CMap.h"
#include "../CModHandler.h" #include "../CModHandler.h"
#include "../mapObjects/CObjectHandler.h" #include "../mapObjects/CObjectHandler.h"

View File

@ -8,6 +8,12 @@
* *
*/ */
#include "StdInc.h" #include "StdInc.h"
#include "CGameHandler.h"
#include "HeroPoolProcessor.h"
#include "ServerNetPackVisitors.h"
#include "ServerSpellCastEnvironment.h"
#include "CVCMIServer.h"
#include "../lib/filesystem/Filesystem.h" #include "../lib/filesystem/Filesystem.h"
#include "../lib/filesystem/FileInfo.h" #include "../lib/filesystem/FileInfo.h"
@ -35,7 +41,6 @@
#include "../lib/GameSettings.h" #include "../lib/GameSettings.h"
#include "../lib/battle/BattleInfo.h" #include "../lib/battle/BattleInfo.h"
#include "../lib/CondSh.h" #include "../lib/CondSh.h"
#include "ServerNetPackVisitors.h"
#include "../lib/VCMI_Lib.h" #include "../lib/VCMI_Lib.h"
#include "../lib/mapping/CMap.h" #include "../lib/mapping/CMap.h"
#include "../lib/mapping/CMapService.h" #include "../lib/mapping/CMapService.h"
@ -44,9 +49,6 @@
#include "../lib/ScopeGuard.h" #include "../lib/ScopeGuard.h"
#include "../lib/CSoundBase.h" #include "../lib/CSoundBase.h"
#include "../lib/TerrainHandler.h" #include "../lib/TerrainHandler.h"
#include "CGameHandler.h"
#include "ServerSpellCastEnvironment.h"
#include "CVCMIServer.h"
#include "../lib/CCreatureSet.h" #include "../lib/CCreatureSet.h"
#include "../lib/CThreadHelper.h" #include "../lib/CThreadHelper.h"
#include "../lib/GameConstants.h" #include "../lib/GameConstants.h"
@ -868,24 +870,12 @@ void CGameHandler::battleAfterLevelUp(const BattleResult &result)
std::set<PlayerColor> playerColors = {finishingBattle->loser, finishingBattle->victor}; std::set<PlayerColor> playerColors = {finishingBattle->loser, finishingBattle->victor};
checkVictoryLossConditions(playerColors); checkVictoryLossConditions(playerColors);
if (result.result == BattleResult::SURRENDER || result.result == BattleResult::ESCAPE) //loser has escaped or surrendered if (result.result == BattleResult::SURRENDER)
{ heroPool->onHeroSurrendered(finishingBattle->loser, finishingBattle->loserHero);
SetAvailableHeroes sah;
sah.player = finishingBattle->loser;
sah.hid[0] = finishingBattle->loserHero->subID;
if (result.result == BattleResult::ESCAPE) //retreat
{
sah.army[0].clear();
sah.army[0].setCreature(SlotID(0), finishingBattle->loserHero->type->initialArmy.at(0).creature, 1);
}
if (const CGHeroInstance *another = getPlayerState(finishingBattle->loser)->availableHeroes.at(0)) if (result.result == BattleResult::ESCAPE)
sah.hid[1] = another->subID; heroPool->onHeroEscaped(finishingBattle->loser, finishingBattle->loserHero);
else
sah.hid[1] = -1;
sendAndApply(&sah);
}
if (result.winner != 2 && finishingBattle->winnerHero && finishingBattle->winnerHero->stacks.empty() if (result.winner != 2 && finishingBattle->winnerHero && finishingBattle->winnerHero->stacks.empty()
&& (!finishingBattle->winnerHero->commander || !finishingBattle->winnerHero->commander->alive)) && (!finishingBattle->winnerHero->commander || !finishingBattle->winnerHero->commander->alive))
{ {
@ -893,20 +883,7 @@ void CGameHandler::battleAfterLevelUp(const BattleResult &result)
sendAndApply(&ro); sendAndApply(&ro);
if (VLC->settings()->getBoolean(EGameSettings::HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS)) if (VLC->settings()->getBoolean(EGameSettings::HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS))
{ heroPool->onHeroEscaped(finishingBattle->victor, finishingBattle->winnerHero);
SetAvailableHeroes sah;
sah.player = finishingBattle->victor;
sah.hid[0] = finishingBattle->winnerHero->subID;
sah.army[0].clear();
sah.army[0].setCreature(SlotID(0), finishingBattle->winnerHero->type->initialArmy.at(0).creature, 1);
if (const CGHeroInstance *another = getPlayerState(finishingBattle->victor)->availableHeroes.at(0))
sah.hid[1] = another->subID;
else
sah.hid[1] = -1;
sendAndApply(&sah);
}
} }
finishingBattle.reset(); finishingBattle.reset();
@ -1576,6 +1553,7 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
CGameHandler::CGameHandler(CVCMIServer * lobby) CGameHandler::CGameHandler(CVCMIServer * lobby)
: lobby(lobby) : lobby(lobby)
, heroPool(std::make_unique<HeroPoolProcessor>(this))
, complainNoCreatures("No creatures to split") , complainNoCreatures("No creatures to split")
, complainNotEnoughCreatures("Cannot split that stack, not enough creatures!") , complainNotEnoughCreatures("Cannot split that stack, not enough creatures!")
, complainInvalidSlot("Invalid slot accessed!") , complainInvalidSlot("Invalid slot accessed!")
@ -1765,27 +1743,6 @@ void CGameHandler::newTurn()
} }
} }
std::map<ui32, ConstTransitivePtr<CGHeroInstance> > pool = gs->hpool.heroesPool;
for (auto& hp : pool)
{
auto hero = hp.second;
if (hero->isInitialized() && hero->stacks.size())
{
// reset retreated or surrendered heroes
auto maxmove = hero->movementPointsLimit(true);
// if movement is greater than maxmove, we should decrease it
if (hero->movementPointsRemaining() != maxmove || hero->mana < hero->manaLimit())
{
NewTurn::Hero hth;
hth.id = hero->id;
hth.move = maxmove;
hth.mana = hero->getManaNewTurn();
n.heroes.insert(hth);
}
}
}
for (auto & elem : gs->players) for (auto & elem : gs->players)
{ {
if (elem.first == PlayerColor::NEUTRAL) if (elem.first == PlayerColor::NEUTRAL)
@ -1797,29 +1754,7 @@ void CGameHandler::newTurn()
hadGold.insert(playerGold); hadGold.insert(playerGold);
if (newWeek) //new heroes in tavern if (newWeek) //new heroes in tavern
{ heroPool->onNewWeek(elem.first);
SetAvailableHeroes sah;
sah.player = elem.first;
//pick heroes and their armies
CHeroClass *banned = nullptr;
for (int j = 0; j < GameConstants::AVAILABLE_HEROES_PER_PLAYER; j++)
{
//first hero - native if possible, second hero -> any other class
if (CGHeroInstance *h = gs->hpool.pickHeroFor(j == 0, elem.first, getNativeTown(elem.first), pool, getRandomGenerator(), banned))
{
sah.hid[j] = h->subID;
h->initArmy(getRandomGenerator(), &sah.army[j]);
banned = h->type->heroClass;
}
else
{
sah.hid[j] = -1;
}
}
sendAndApply(&sah);
}
n.res[elem.first] = elem.second.resources; n.res[elem.first] = elem.second.resources;
@ -4383,94 +4318,6 @@ bool CGameHandler::setFormation(ObjectInstanceID hid, ui8 formation)
return true; return true;
} }
bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor player)
{
const PlayerState * p = getPlayerState(player);
const CGTownInstance * t = getTown(obj->id);
//common preconditions
// if ((p->resources.at(EGameResID::GOLD)<GOLD_NEEDED && complain("Not enough gold for buying hero!"))
// || (getHeroCount(player, false) >= GameConstants::MAX_HEROES_PER_PLAYER && complain("Cannot hire hero, only 8 wandering heroes are allowed!")))
if ((p->resources[EGameResID::GOLD] < GameConstants::HERO_GOLD_COST && complain("Not enough gold for buying hero!"))
|| ((getHeroCount(player, false) >= VLC->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP) && complain("Cannot hire hero, too many wandering heroes already!")))
|| ((getHeroCount(player, true) >= VLC->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP) && complain("Cannot hire hero, too many heroes garrizoned and wandering already!"))))
{
return false;
}
if (t) //tavern in town
{
if ((!t->hasBuilt(BuildingID::TAVERN) && complain("No tavern!"))
|| (t->visitingHero && complain("There is visiting hero - no place!")))
{
return false;
}
}
else if (obj->ID == Obj::TAVERN)
{
if (getTile(obj->visitablePos())->visitableObjects.back() != obj && complain("Tavern entry must be unoccupied!"))
{
return false;
}
}
const CGHeroInstance *nh = p->availableHeroes.at(hid);
if (!nh)
{
complain ("Hero is not available for hiring!");
return false;
}
HeroRecruited hr;
hr.tid = obj->id;
hr.hid = nh->subID;
hr.player = player;
hr.tile = nh->convertFromVisitablePos(obj->visitablePos());
if (getTile(hr.tile)->isWater())
{
//Create a new boat for hero
createObject(obj->visitablePos(), Obj::BOAT, nh->getBoatType().getNum());
hr.boatId = getTopObj(hr.tile)->id;
}
sendAndApply(&hr);
std::map<ui32, ConstTransitivePtr<CGHeroInstance> > pool = gs->unusedHeroesFromPool();
const CGHeroInstance *theOtherHero = p->availableHeroes.at(!hid);
const CGHeroInstance *newHero = nullptr;
if (theOtherHero) //on XXL maps all heroes can be imprisoned :(
{
newHero = gs->hpool.pickHeroFor(false, player, getNativeTown(player), pool, getRandomGenerator(), theOtherHero->type->heroClass);
}
SetAvailableHeroes sah;
sah.player = player;
if (newHero)
{
sah.hid[hid] = newHero->subID;
sah.army[hid].clear();
sah.army[hid].setCreature(SlotID(0), newHero->type->initialArmy[0].creature, 1);
}
else
{
sah.hid[hid] = -1;
}
sah.hid[!hid] = theOtherHero ? theOtherHero->subID : -1;
sendAndApply(&sah);
giveResource(player, EGameResID::GOLD, -GameConstants::HERO_GOLD_COST);
if(t)
{
visitCastleObjects(t, nh);
giveSpells (t,nh);
}
return true;
}
bool CGameHandler::queryReply(QueryID qid, const JsonNode & answer, PlayerColor player) bool CGameHandler::queryReply(QueryID qid, const JsonNode & answer, PlayerColor player)
{ {
boost::unique_lock<boost::recursive_mutex> lock(gsm); boost::unique_lock<boost::recursive_mutex> lock(gsm);

View File

@ -46,6 +46,7 @@ template<typename T> class CApplier;
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END
class HeroPoolProcessor;
class CGameHandler; class CGameHandler;
class CVCMIServer; class CVCMIServer;
class CBaseForGHApply; class CBaseForGHApply;
@ -97,7 +98,10 @@ class CGameHandler : public IGameCallback, public CBattleInfoCallback, public En
CVCMIServer * lobby; CVCMIServer * lobby;
std::shared_ptr<CApplier<CBaseForGHApply>> applier; std::shared_ptr<CApplier<CBaseForGHApply>> applier;
std::unique_ptr<boost::thread> battleThread; std::unique_ptr<boost::thread> battleThread;
public: public:
std::unique_ptr<HeroPoolProcessor> heroPool;
using FireShieldInfo = std::vector<std::pair<const CStack *, int64_t>>; using FireShieldInfo = std::vector<std::pair<const CStack *, int64_t>>;
//use enums as parameters, because doMove(sth, true, false, true) is not readable //use enums as parameters, because doMove(sth, true, false, true) is not readable
enum EGuardLook {CHECK_FOR_GUARDS, IGNORE_GUARDS}; enum EGuardLook {CHECK_FOR_GUARDS, IGNORE_GUARDS};
@ -145,6 +149,7 @@ public:
void setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance *heroes[2], bool creatureBank, const CGTownInstance *town); void setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance *heroes[2], bool creatureBank, const CGTownInstance *town);
void setBattleResult(BattleResult::EResult resultType, int victoriusSide); void setBattleResult(BattleResult::EResult resultType, int victoriusSide);
CGameHandler() = default;
CGameHandler(CVCMIServer * lobby); CGameHandler(CVCMIServer * lobby);
~CGameHandler(); ~CGameHandler();
@ -240,7 +245,6 @@ public:
void removeObstacle(const CObstacleInstance &obstacle); void removeObstacle(const CObstacleInstance &obstacle);
bool queryReply( QueryID qid, const JsonNode & answer, PlayerColor player ); bool queryReply( QueryID qid, const JsonNode & answer, PlayerColor player );
bool hireHero( const CGObjectInstance *obj, ui8 hid, PlayerColor player );
bool buildBoat( ObjectInstanceID objid, PlayerColor player ); bool buildBoat( ObjectInstanceID objid, PlayerColor player );
bool setFormation( ObjectInstanceID hid, ui8 formation ); bool setFormation( ObjectInstanceID hid, ui8 formation );
bool tradeResources(const IMarket *market, ui32 val, PlayerColor player, ui32 id1, ui32 id2); bool tradeResources(const IMarket *market, ui32 val, PlayerColor player, ui32 id1, ui32 id2);
@ -283,6 +287,7 @@ public:
h & QID; h & QID;
h & states; h & states;
h & finishingBattle; h & finishingBattle;
h & heroPool;
h & getRandomGenerator(); h & getRandomGenerator();
#if SCRIPTING_ENABLED #if SCRIPTING_ENABLED

View File

@ -2,6 +2,7 @@ set(server_SRCS
StdInc.cpp StdInc.cpp
CGameHandler.cpp CGameHandler.cpp
HeroPoolProcessor.cpp
ServerSpellCastEnvironment.cpp ServerSpellCastEnvironment.cpp
CQuery.cpp CQuery.cpp
CVCMIServer.cpp CVCMIServer.cpp
@ -13,6 +14,7 @@ set(server_HEADERS
StdInc.h StdInc.h
CGameHandler.h CGameHandler.h
HeroPoolProcessor.h
ServerSpellCastEnvironment.h ServerSpellCastEnvironment.h
CQuery.h CQuery.h
CVCMIServer.h CVCMIServer.h

View File

@ -822,7 +822,7 @@ void CVCMIServer::setPlayer(PlayerColor clickedColor)
void CVCMIServer::optionNextCastle(PlayerColor player, int dir) void CVCMIServer::optionNextCastle(PlayerColor player, int dir)
{ {
PlayerSettings & s = si->playerInfos[player]; PlayerSettings & s = si->playerInfos[player];
si16 & cur = s.castle; FactionID & cur = s.castle;
auto & allowed = getPlayerInfo(player.getNum()).allowedFactions; auto & allowed = getPlayerInfo(player.getNum()).allowedFactions;
const bool allowRandomTown = getPlayerInfo(player.getNum()).isFactionRandom; const bool allowRandomTown = getPlayerInfo(player.getNum()).isFactionRandom;
@ -856,7 +856,7 @@ void CVCMIServer::optionNextCastle(PlayerColor player, int dir)
else else
{ {
assert(dir >= -1 && dir <= 1); //othervice std::advance may go out of range assert(dir >= -1 && dir <= 1); //othervice std::advance may go out of range
auto iter = allowed.find(FactionID(cur)); auto iter = allowed.find(cur);
std::advance(iter, dir); std::advance(iter, dir);
cur = *iter; cur = *iter;
} }

View File

@ -0,0 +1,162 @@
/*
* HeroPoolProcessor.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "HeroPoolProcessor.h"
#include "CGameHandler.h"
#include "../lib/CHeroHandler.h"
#include "../lib/CPlayerState.h"
#include "../lib/GameSettings.h"
#include "../lib/NetPacks.h"
#include "../lib/StartInfo.h"
#include "../lib/mapObjects/CGTownInstance.h"
#include "../lib/gameState/CGameState.h"
#include "../lib/gameState/TavernHeroesPool.h"
HeroPoolProcessor::HeroPoolProcessor() = default;
HeroPoolProcessor::HeroPoolProcessor(CGameHandler * gameHandler):
gameHandler(gameHandler)
{
}
void HeroPoolProcessor::onHeroSurrendered(const PlayerColor & color, const CGHeroInstance * hero)
{
SetAvailableHero sah;
sah.slotID = 0;
sah.player = color;
sah.hid = hero->subID;
sah.army.clear();
sah.army.setCreature(SlotID(0), hero->type->initialArmy.at(0).creature, 1);
gameHandler->sendAndApply(&sah);
}
void HeroPoolProcessor::onHeroEscaped(const PlayerColor & color, const CGHeroInstance * hero)
{
SetAvailableHero sah;
sah.slotID = 0;
sah.player = color;
sah.hid = hero->subID;
gameHandler->sendAndApply(&sah);
}
void HeroPoolProcessor::clearHeroFromSlot(const PlayerColor & color, TavernHeroSlot slot)
{
SetAvailableHero sah;
sah.player = color;
sah.slotID = static_cast<int>(slot);
sah.hid = HeroTypeID::NONE;
gameHandler->sendAndApply(&sah);
}
void HeroPoolProcessor::selectNewHeroForSlot(const PlayerColor & color, TavernHeroSlot slot)
{
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());
if (h)
{
sah.hid = h->subID;
h->initArmy(gameHandler->getRandomGenerator(), &sah.army);
}
else
{
sah.hid = -1;
}
gameHandler->sendAndApply(&sah);
}
void HeroPoolProcessor::onNewWeek(const PlayerColor & color)
{
clearHeroFromSlot(color, TavernHeroSlot::NATIVE);
clearHeroFromSlot(color, TavernHeroSlot::RANDOM);
selectNewHeroForSlot(color, TavernHeroSlot::NATIVE);
selectNewHeroForSlot(color, TavernHeroSlot::RANDOM);
}
bool HeroPoolProcessor::hireHero(const CGObjectInstance *obj, const HeroTypeID & heroToRecruit, const PlayerColor & player)
{
const PlayerState * playerState = gameHandler->getPlayerState(player);
const CGTownInstance * town = gameHandler->getTown(obj->id);
if (playerState->resources[EGameResID::GOLD] < GameConstants::HERO_GOLD_COST && gameHandler->complain("Not enough gold for buying hero!"))
return false;
if (gameHandler->getHeroCount(player, false) >= VLC->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP) && gameHandler->complain("Cannot hire hero, too many wandering heroes already!"))
return false;
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->hasBuilt(BuildingID::TAVERN) && gameHandler->complain("No tavern!"))
return false;
if (town->visitingHero && gameHandler->complain("There is visiting hero - no place!"))
return false;
}
if (obj->ID == Obj::TAVERN)
{
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;;
for(const auto & hero : recruitableHeroes)
{
if (hero->subID == heroToRecruit)
recruitedHero = hero;
}
if (!recruitedHero)
{
gameHandler->complain ("Hero is not available for hiring!");
return false;
}
HeroRecruited hr;
hr.tid = obj->id;
hr.hid = recruitedHero->subID;
hr.player = player;
hr.tile = recruitedHero->convertFromVisitablePos(obj->visitablePos());
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;
}
gameHandler->sendAndApply(&hr);
if (recruitableHeroes[0] == recruitedHero)
selectNewHeroForSlot(player, TavernHeroSlot::NATIVE);
else
selectNewHeroForSlot(player, TavernHeroSlot::RANDOM);
gameHandler->giveResource(player, EGameResID::GOLD, -GameConstants::HERO_GOLD_COST);
if(town)
{
gameHandler->visitCastleObjects(town, recruitedHero);
gameHandler->giveSpells(town, recruitedHero);
}
return true;
}

View File

@ -0,0 +1,45 @@
/*
* HeroPoolProcessor.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
VCMI_LIB_NAMESPACE_BEGIN
enum class TavernHeroSlot;
class PlayerColor;
class CGHeroInstance;
class HeroTypeID;
class CGObjectInstance;
VCMI_LIB_NAMESPACE_END
class CGameHandler;
class HeroPoolProcessor : boost::noncopyable
{
CGameHandler * gameHandler;
void clearHeroFromSlot(const PlayerColor & color, TavernHeroSlot slot);
void selectNewHeroForSlot(const PlayerColor & color, TavernHeroSlot slot);
public:
HeroPoolProcessor();
HeroPoolProcessor(CGameHandler * gameHandler);
void onHeroSurrendered(const PlayerColor & color, const CGHeroInstance * hero);
void onHeroEscaped(const PlayerColor & color, const CGHeroInstance * hero);
void onNewWeek(const PlayerColor & color);
bool hireHero(const CGObjectInstance *obj, const HeroTypeID & hid, const PlayerColor & player);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & gameHandler;
}
};

View File

@ -11,6 +11,8 @@
#include "ServerNetPackVisitors.h" #include "ServerNetPackVisitors.h"
#include "CGameHandler.h" #include "CGameHandler.h"
#include "HeroPoolProcessor.h"
#include "../lib/IGameCallback.h" #include "../lib/IGameCallback.h"
#include "../lib/mapObjects/CGTownInstance.h" #include "../lib/mapObjects/CGTownInstance.h"
#include "../lib/gameState/CGameState.h" #include "../lib/gameState/CGameState.h"
@ -251,7 +253,7 @@ void ApplyGhNetPackVisitor::visitHireHero(HireHero & pack)
if(town && PlayerRelations::ENEMIES == gh.getPlayerRelations(obj->tempOwner, gh.getPlayerAt(pack.c))) if(town && PlayerRelations::ENEMIES == gh.getPlayerRelations(obj->tempOwner, gh.getPlayerAt(pack.c)))
gh.throwAndComplain(&pack, "Can't buy hero in enemy town!"); gh.throwAndComplain(&pack, "Can't buy hero in enemy town!");
result = gh.hireHero(obj, pack.hid, pack.player); result = gh.heroPool->hireHero(obj, pack.hid, pack.player);
} }
void ApplyGhNetPackVisitor::visitBuildBoat(BuildBoat & pack) void ApplyGhNetPackVisitor::visitBuildBoat(BuildBoat & pack)