mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-20 20:23:03 +02:00
Moved hero pool logic to the separate files
This commit is contained in:
parent
1c55835b8b
commit
19ace6a849
@ -270,17 +270,10 @@ void CCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroIn
|
||||
{
|
||||
assert(townOrTavern);
|
||||
assert(hero);
|
||||
ui8 i=0;
|
||||
for(; i<gs->players[*player].availableHeroes.size(); i++)
|
||||
{
|
||||
if(gs->players[*player].availableHeroes[i] == hero)
|
||||
{
|
||||
HireHero pack(i, townOrTavern->id);
|
||||
|
||||
HireHero pack(HeroTypeID(hero->subID), townOrTavern->id);
|
||||
pack.player = *player;
|
||||
sendRequest(&pack);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCallback::save( const std::string &fname )
|
||||
|
@ -68,6 +68,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
||||
${MAIN_LIB_DIR}/gameState/CGameState.cpp
|
||||
${MAIN_LIB_DIR}/gameState/CGameStateCampaign.cpp
|
||||
${MAIN_LIB_DIR}/gameState/InfoAboutArmy.cpp
|
||||
${MAIN_LIB_DIR}/gameState/TavernHeroesPool.cpp
|
||||
|
||||
${MAIN_LIB_DIR}/logging/CBasicLogConfigurator.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/InfoAboutArmy.h
|
||||
${MAIN_LIB_DIR}/gameState/SThievesGuildInfo.h
|
||||
${MAIN_LIB_DIR}/gameState/TavernHeroesPool.h
|
||||
${MAIN_LIB_DIR}/gameState/QuestInfo.h
|
||||
|
||||
${MAIN_LIB_DIR}/logging/CBasicLogConfigurator.h
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "gameState/CGameState.h"
|
||||
#include "gameState/InfoAboutArmy.h"
|
||||
#include "gameState/SThievesGuildInfo.h"
|
||||
#include "gameState/TavernHeroesPool.h"
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "StartInfo.h" // for StartInfo
|
||||
#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
|
||||
{
|
||||
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);
|
||||
//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);
|
||||
|
||||
if(townOrTavern->ID == Obj::TAVERN || (town && town->hasBuilt(BuildingID::TAVERN)))
|
||||
{
|
||||
range::copy(gs->players[*player].availableHeroes, std::back_inserter(ret));
|
||||
vstd::erase_if(ret, [](const CGHeroInstance * h) {
|
||||
return h == nullptr;
|
||||
});
|
||||
}
|
||||
return gs->hpool->getHeroesFor(*player);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,6 @@ public:
|
||||
// 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
|
||||
// virtual bool getTownInfo(const CGObjectInstance * town, InfoAboutTown & dest, const CGObjectInstance * selectedObject = nullptr) const;
|
||||
// const CTown *getNativeTown(PlayerColor color) const;
|
||||
|
||||
//from gs
|
||||
// const TeamState *getTeam(TeamID teamID) const;
|
||||
@ -206,7 +205,6 @@ public:
|
||||
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 bool getTownInfo(const CGObjectInstance * town, InfoAboutTown & dest, const CGObjectInstance * selectedObject = nullptr) const;
|
||||
virtual const CTown *getNativeTown(PlayerColor color) const;
|
||||
|
||||
//from gs
|
||||
virtual const TeamState *getTeam(TeamID teamID) const;
|
||||
|
@ -35,7 +35,6 @@ PlayerState::PlayerState(PlayerState && other) noexcept:
|
||||
std::swap(visitedObjects, other.visitedObjects);
|
||||
std::swap(heroes, other.heroes);
|
||||
std::swap(towns, other.towns);
|
||||
std::swap(availableHeroes, other.availableHeroes);
|
||||
std::swap(dwellings, other.dwellings);
|
||||
std::swap(quests, other.quests);
|
||||
}
|
||||
|
@ -33,7 +33,6 @@ public:
|
||||
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<CGTownInstance> > towns;
|
||||
std::vector<ConstTransitivePtr<CGHeroInstance> > availableHeroes; //heroes available in taverns
|
||||
std::vector<ConstTransitivePtr<CGDwelling> > dwellings; //used for town growth
|
||||
std::vector<QuestInfo> quests; //store info about all received quests
|
||||
|
||||
@ -74,7 +73,6 @@ public:
|
||||
h & status;
|
||||
h & heroes;
|
||||
h & towns;
|
||||
h & availableHeroes;
|
||||
h & dwellings;
|
||||
h & quests;
|
||||
h & visitedObjects;
|
||||
|
@ -357,9 +357,12 @@ class PlayerColor : public BaseForID<PlayerColor, ui8>
|
||||
|
||||
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 CANNOT_DETERMINE; //253
|
||||
DLL_LINKAGE static const PlayerColor UNFLAGGABLE; //254 - neutral objects (pandora, banks)
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "StartInfo.h"
|
||||
#include "gameState/CGameState.h"
|
||||
#include "gameState/CGameStateCampaign.h"
|
||||
#include "gameState/TavernHeroesPool.h"
|
||||
#include "mapping/CMap.h"
|
||||
#include "CPlayerState.h"
|
||||
#include "GameSettings.h"
|
||||
|
@ -36,7 +36,7 @@ public:
|
||||
virtual void visitSetMana(SetMana & pack) {}
|
||||
virtual void visitSetMovePoints(SetMovePoints & pack) {}
|
||||
virtual void visitFoWChange(FoWChange & pack) {}
|
||||
virtual void visitSetAvailableHeroes(SetAvailableHeroes & pack) {}
|
||||
virtual void visitSetAvailableHeroes(SetAvailableHero & pack) {}
|
||||
virtual void visitGiveBonus(GiveBonus & pack) {}
|
||||
virtual void visitChangeObjPos(ChangeObjPos & pack) {}
|
||||
virtual void visitPlayerEndsGame(PlayerEndsGame & pack) {}
|
||||
|
@ -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)
|
||||
i.clear();
|
||||
army.clear();
|
||||
}
|
||||
void applyGs(CGameState * gs);
|
||||
|
||||
uint8_t slotID;
|
||||
PlayerColor player;
|
||||
si32 hid[GameConstants::AVAILABLE_HEROES_PER_PLAYER]; //-1 if no hero
|
||||
CSimpleArmy army[GameConstants::AVAILABLE_HEROES_PER_PLAYER];
|
||||
HeroTypeID hid; //-1 if no hero
|
||||
CSimpleArmy army;
|
||||
|
||||
virtual void visitTyped(ICPackVisitor & visitor) override;
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & slotID;
|
||||
h & player;
|
||||
h & hid;
|
||||
h & army;
|
||||
@ -692,7 +693,7 @@ struct DLL_LINKAGE HeroRecruited : public CPackForClient
|
||||
{
|
||||
void applyGs(CGameState * gs) const;
|
||||
|
||||
si32 hid = -1; //subID of hero
|
||||
HeroTypeID hid; //subID of hero
|
||||
ObjectInstanceID tid;
|
||||
ObjectInstanceID boatId;
|
||||
int3 tile;
|
||||
@ -2437,12 +2438,12 @@ struct DLL_LINKAGE SetFormation : public CPackForServer
|
||||
struct DLL_LINKAGE HireHero : public CPackForServer
|
||||
{
|
||||
HireHero() = default;
|
||||
HireHero(si32 HID, const ObjectInstanceID & TID)
|
||||
HireHero(HeroTypeID HID, const ObjectInstanceID & TID)
|
||||
: hid(HID)
|
||||
, tid(TID)
|
||||
{
|
||||
}
|
||||
si32 hid = 0; //available hero serial
|
||||
HeroTypeID hid; //available hero serial
|
||||
ObjectInstanceID tid; //town (tavern) id
|
||||
PlayerColor player;
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "spells/CSpellHandler.h"
|
||||
#include "CCreatureHandler.h"
|
||||
#include "gameState/CGameState.h"
|
||||
#include "gameState/TavernHeroesPool.h"
|
||||
#include "CStack.h"
|
||||
#include "battle/BattleInfo.h"
|
||||
#include "CTownHandler.h"
|
||||
@ -151,7 +152,7 @@ void FoWChange::visitTyped(ICPackVisitor & visitor)
|
||||
visitor.visitFoWChange(*this);
|
||||
}
|
||||
|
||||
void SetAvailableHeroes::visitTyped(ICPackVisitor & visitor)
|
||||
void SetAvailableHero::visitTyped(ICPackVisitor & visitor)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
}
|
||||
gs->hpool->setHeroForPlayer(player, TavernHeroSlot(slotID), hid, army);
|
||||
}
|
||||
|
||||
void GiveBonus::applyGs(CGameState *gs)
|
||||
@ -1150,11 +1142,8 @@ void RemoveObject::applyGs(CGameState *gs)
|
||||
beatenHero->inTownGarrison = false;
|
||||
}
|
||||
//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;
|
||||
|
||||
//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
|
||||
{
|
||||
assert(vstd::contains(gs->hpool.heroesPool, hid));
|
||||
CGHeroInstance *h = gs->hpool.heroesPool[hid];
|
||||
CGHeroInstance *h = gs->hpool->takeHero(hid);
|
||||
CGTownInstance *t = gs->getTown(tid);
|
||||
PlayerState *p = gs->getPlayerState(player);
|
||||
|
||||
@ -1411,7 +1399,6 @@ void HeroRecruited::applyGs(CGameState * gs) const
|
||||
}
|
||||
}
|
||||
|
||||
gs->hpool.heroesPool.erase(hid);
|
||||
if(h->id == ObjectInstanceID())
|
||||
{
|
||||
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);
|
||||
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());
|
||||
continue;
|
||||
}
|
||||
hero->setMovementPoints(h.move);
|
||||
hero->mana = h.mana;
|
||||
}
|
||||
|
||||
gs->hpool->onNewDay();
|
||||
|
||||
for(const auto & re : res)
|
||||
{
|
||||
assert(re.first < PlayerColor::PLAYER_LIMIT);
|
||||
|
@ -35,9 +35,9 @@ struct DLL_LINKAGE PlayerSettings
|
||||
};
|
||||
|
||||
Ebonus bonus;
|
||||
si16 castle;
|
||||
si32 hero,
|
||||
heroPortrait; //-1 if default, else ID
|
||||
FactionID castle;
|
||||
HeroTypeID hero;
|
||||
HeroTypeID heroPortrait; //-1 if default, else ID
|
||||
|
||||
std::string heroName;
|
||||
PlayerColor color; //from 0 -
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "EVictoryLossCheckResult.h"
|
||||
#include "InfoAboutArmy.h"
|
||||
#include "TavernHeroesPool.h"
|
||||
#include "CGameStateCampaign.h"
|
||||
#include "SThievesGuildInfo.h"
|
||||
|
||||
@ -102,81 +103,6 @@ static CGObjectInstance * createObject(const Obj & id, int subid, const int3 & p
|
||||
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)
|
||||
{
|
||||
const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(owner);
|
||||
@ -459,6 +385,7 @@ int CGameState::getDate(Date::EDateType mode) const
|
||||
CGameState::CGameState()
|
||||
{
|
||||
gs = this;
|
||||
hpool = std::make_unique<TavernHeroesPool>(this);
|
||||
applier = std::make_shared<CApplier<CBaseForGSApply>>();
|
||||
registerTypesClientPacks1(*applier);
|
||||
registerTypesClientPacks2(*applier);
|
||||
@ -469,9 +396,6 @@ CGameState::~CGameState()
|
||||
{
|
||||
map.dellNull();
|
||||
curB.dellNull();
|
||||
|
||||
for(auto ptr : hpool.heroesPool) // clean hero pool
|
||||
ptr.second.dellNull();
|
||||
}
|
||||
|
||||
void CGameState::preInit(Services * services)
|
||||
@ -951,8 +875,7 @@ void CGameState::initHeroes()
|
||||
if(!vstd::contains(heroesToCreate, HeroTypeID(ph->subID)))
|
||||
continue;
|
||||
ph->initHero(getRandomGenerator());
|
||||
hpool.heroesPool[ph->subID] = ph;
|
||||
hpool.pavailable[ph->subID] = 0xff;
|
||||
hpool->addHeroToPool(ph);
|
||||
heroesToCreate.erase(ph->type->getId());
|
||||
|
||||
map->allHeroes[ph->subID] = ph;
|
||||
@ -965,14 +888,11 @@ void CGameState::initHeroes()
|
||||
|
||||
int typeID = htype.getNum();
|
||||
map->allHeroes[typeID] = vhi;
|
||||
hpool.heroesPool[typeID] = vhi;
|
||||
hpool.pavailable[typeID] = 0xff;
|
||||
hpool->addHeroToPool(vhi);
|
||||
}
|
||||
|
||||
for(auto & elem : map->disposedHeroes)
|
||||
{
|
||||
hpool.pavailable[elem.heroId] = elem.players;
|
||||
}
|
||||
hpool->setAvailability(elem.heroId, elem.players);
|
||||
|
||||
if (campaign)
|
||||
campaign->initHeroes();
|
||||
@ -2067,17 +1987,6 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
|
||||
#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()
|
||||
{
|
||||
buildGlobalTeamPlayerTree();
|
||||
|
@ -29,6 +29,7 @@ struct EventCondition;
|
||||
struct CampaignTravel;
|
||||
class CStackInstance;
|
||||
class CGameStateCampaign;
|
||||
class TavernHeroesPool;
|
||||
struct SThievesGuildInfo;
|
||||
|
||||
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
|
||||
{
|
||||
friend class CGameStateCampaign;
|
||||
|
||||
public:
|
||||
struct DLL_LINKAGE HeroesPool
|
||||
{
|
||||
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
|
||||
//we have here all heroes available on this map that are not hired
|
||||
std::unique_ptr<TavernHeroesPool> hpool;
|
||||
|
||||
CGameState();
|
||||
virtual ~CGameState();
|
||||
@ -142,7 +128,6 @@ public:
|
||||
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
|
||||
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(const CGObjectInstance * obj, const std::optional<PlayerColor> & player) const override;
|
||||
|
176
lib/gameState/TavernHeroesPool.cpp
Normal file
176
lib/gameState/TavernHeroesPool.cpp
Normal 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;
|
||||
}
|
75
lib/gameState/TavernHeroesPool.h
Normal file
75
lib/gameState/TavernHeroesPool.h
Normal 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
|
@ -56,10 +56,10 @@ struct DLL_LINKAGE DisposedHero
|
||||
{
|
||||
DisposedHero();
|
||||
|
||||
ui32 heroId;
|
||||
HeroTypeID heroId;
|
||||
ui32 portrait; /// The portrait id of the hero, -1 is default.
|
||||
std::string name;
|
||||
ui8 players; /// Who can hire this hero (bitfield).
|
||||
PlayerColor::Mask players; /// Who can hire this hero (bitfield).
|
||||
|
||||
template <typename Handler>
|
||||
void serialize(Handler & h, const int version)
|
||||
|
@ -239,7 +239,7 @@ void registerTypesClientPacks1(Serializer &s)
|
||||
s.template registerType<CPackForClient, SetMana>();
|
||||
s.template registerType<CPackForClient, SetMovePoints>();
|
||||
s.template registerType<CPackForClient, FoWChange>();
|
||||
s.template registerType<CPackForClient, SetAvailableHeroes>();
|
||||
s.template registerType<CPackForClient, SetAvailableHero>();
|
||||
s.template registerType<CPackForClient, GiveBonus>();
|
||||
s.template registerType<CPackForClient, ChangeObjPos>();
|
||||
s.template registerType<CPackForClient, PlayerEndsGame>();
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "../StartInfo.h"
|
||||
#include "../gameState/CGameState.h"
|
||||
#include "../gameState/CGameStateCampaign.h"
|
||||
#include "../gameState/TavernHeroesPool.h"
|
||||
#include "../mapping/CMap.h"
|
||||
#include "../CModHandler.h"
|
||||
#include "../mapObjects/CObjectHandler.h"
|
||||
|
@ -8,6 +8,12 @@
|
||||
*
|
||||
*/
|
||||
#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/FileInfo.h"
|
||||
@ -35,7 +41,6 @@
|
||||
#include "../lib/GameSettings.h"
|
||||
#include "../lib/battle/BattleInfo.h"
|
||||
#include "../lib/CondSh.h"
|
||||
#include "ServerNetPackVisitors.h"
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
#include "../lib/mapping/CMap.h"
|
||||
#include "../lib/mapping/CMapService.h"
|
||||
@ -44,9 +49,6 @@
|
||||
#include "../lib/ScopeGuard.h"
|
||||
#include "../lib/CSoundBase.h"
|
||||
#include "../lib/TerrainHandler.h"
|
||||
#include "CGameHandler.h"
|
||||
#include "ServerSpellCastEnvironment.h"
|
||||
#include "CVCMIServer.h"
|
||||
#include "../lib/CCreatureSet.h"
|
||||
#include "../lib/CThreadHelper.h"
|
||||
#include "../lib/GameConstants.h"
|
||||
@ -868,24 +870,12 @@ void CGameHandler::battleAfterLevelUp(const BattleResult &result)
|
||||
std::set<PlayerColor> playerColors = {finishingBattle->loser, finishingBattle->victor};
|
||||
checkVictoryLossConditions(playerColors);
|
||||
|
||||
if (result.result == BattleResult::SURRENDER || result.result == BattleResult::ESCAPE) //loser has escaped or surrendered
|
||||
{
|
||||
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 (result.result == BattleResult::SURRENDER)
|
||||
heroPool->onHeroSurrendered(finishingBattle->loser, finishingBattle->loserHero);
|
||||
|
||||
if (const CGHeroInstance *another = getPlayerState(finishingBattle->loser)->availableHeroes.at(0))
|
||||
sah.hid[1] = another->subID;
|
||||
else
|
||||
sah.hid[1] = -1;
|
||||
if (result.result == BattleResult::ESCAPE)
|
||||
heroPool->onHeroEscaped(finishingBattle->loser, finishingBattle->loserHero);
|
||||
|
||||
sendAndApply(&sah);
|
||||
}
|
||||
if (result.winner != 2 && finishingBattle->winnerHero && finishingBattle->winnerHero->stacks.empty()
|
||||
&& (!finishingBattle->winnerHero->commander || !finishingBattle->winnerHero->commander->alive))
|
||||
{
|
||||
@ -893,20 +883,7 @@ void CGameHandler::battleAfterLevelUp(const BattleResult &result)
|
||||
sendAndApply(&ro);
|
||||
|
||||
if (VLC->settings()->getBoolean(EGameSettings::HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS))
|
||||
{
|
||||
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);
|
||||
}
|
||||
heroPool->onHeroEscaped(finishingBattle->victor, finishingBattle->winnerHero);
|
||||
}
|
||||
|
||||
finishingBattle.reset();
|
||||
@ -1576,6 +1553,7 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
|
||||
|
||||
CGameHandler::CGameHandler(CVCMIServer * lobby)
|
||||
: lobby(lobby)
|
||||
, heroPool(std::make_unique<HeroPoolProcessor>(this))
|
||||
, complainNoCreatures("No creatures to split")
|
||||
, complainNotEnoughCreatures("Cannot split that stack, not enough creatures!")
|
||||
, 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)
|
||||
{
|
||||
if (elem.first == PlayerColor::NEUTRAL)
|
||||
@ -1797,29 +1754,7 @@ void CGameHandler::newTurn()
|
||||
hadGold.insert(playerGold);
|
||||
|
||||
if (newWeek) //new heroes in tavern
|
||||
{
|
||||
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);
|
||||
}
|
||||
heroPool->onNewWeek(elem.first);
|
||||
|
||||
n.res[elem.first] = elem.second.resources;
|
||||
|
||||
@ -4383,94 +4318,6 @@ bool CGameHandler::setFormation(ObjectInstanceID hid, ui8 formation)
|
||||
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)
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> lock(gsm);
|
||||
|
@ -46,6 +46,7 @@ template<typename T> class CApplier;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class HeroPoolProcessor;
|
||||
class CGameHandler;
|
||||
class CVCMIServer;
|
||||
class CBaseForGHApply;
|
||||
@ -97,7 +98,10 @@ class CGameHandler : public IGameCallback, public CBattleInfoCallback, public En
|
||||
CVCMIServer * lobby;
|
||||
std::shared_ptr<CApplier<CBaseForGHApply>> applier;
|
||||
std::unique_ptr<boost::thread> battleThread;
|
||||
|
||||
public:
|
||||
std::unique_ptr<HeroPoolProcessor> heroPool;
|
||||
|
||||
using FireShieldInfo = std::vector<std::pair<const CStack *, int64_t>>;
|
||||
//use enums as parameters, because doMove(sth, true, false, true) is not readable
|
||||
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 setBattleResult(BattleResult::EResult resultType, int victoriusSide);
|
||||
|
||||
CGameHandler() = default;
|
||||
CGameHandler(CVCMIServer * lobby);
|
||||
~CGameHandler();
|
||||
|
||||
@ -240,7 +245,6 @@ public:
|
||||
|
||||
void removeObstacle(const CObstacleInstance &obstacle);
|
||||
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 setFormation( ObjectInstanceID hid, ui8 formation );
|
||||
bool tradeResources(const IMarket *market, ui32 val, PlayerColor player, ui32 id1, ui32 id2);
|
||||
@ -283,6 +287,7 @@ public:
|
||||
h & QID;
|
||||
h & states;
|
||||
h & finishingBattle;
|
||||
h & heroPool;
|
||||
h & getRandomGenerator();
|
||||
|
||||
#if SCRIPTING_ENABLED
|
||||
|
@ -2,6 +2,7 @@ set(server_SRCS
|
||||
StdInc.cpp
|
||||
|
||||
CGameHandler.cpp
|
||||
HeroPoolProcessor.cpp
|
||||
ServerSpellCastEnvironment.cpp
|
||||
CQuery.cpp
|
||||
CVCMIServer.cpp
|
||||
@ -13,6 +14,7 @@ set(server_HEADERS
|
||||
StdInc.h
|
||||
|
||||
CGameHandler.h
|
||||
HeroPoolProcessor.h
|
||||
ServerSpellCastEnvironment.h
|
||||
CQuery.h
|
||||
CVCMIServer.h
|
||||
|
@ -822,7 +822,7 @@ void CVCMIServer::setPlayer(PlayerColor clickedColor)
|
||||
void CVCMIServer::optionNextCastle(PlayerColor player, int dir)
|
||||
{
|
||||
PlayerSettings & s = si->playerInfos[player];
|
||||
si16 & cur = s.castle;
|
||||
FactionID & cur = s.castle;
|
||||
auto & allowed = getPlayerInfo(player.getNum()).allowedFactions;
|
||||
const bool allowRandomTown = getPlayerInfo(player.getNum()).isFactionRandom;
|
||||
|
||||
@ -856,7 +856,7 @@ void CVCMIServer::optionNextCastle(PlayerColor player, int dir)
|
||||
else
|
||||
{
|
||||
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);
|
||||
cur = *iter;
|
||||
}
|
||||
|
162
server/HeroPoolProcessor.cpp
Normal file
162
server/HeroPoolProcessor.cpp
Normal 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;
|
||||
}
|
45
server/HeroPoolProcessor.h
Normal file
45
server/HeroPoolProcessor.h
Normal 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;
|
||||
}
|
||||
};
|
@ -11,6 +11,8 @@
|
||||
#include "ServerNetPackVisitors.h"
|
||||
|
||||
#include "CGameHandler.h"
|
||||
#include "HeroPoolProcessor.h"
|
||||
|
||||
#include "../lib/IGameCallback.h"
|
||||
#include "../lib/mapObjects/CGTownInstance.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)))
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user