diff --git a/CCallback.cpp b/CCallback.cpp index 6dd134f07..0266f6b31 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -270,17 +270,10 @@ void CCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroIn { assert(townOrTavern); assert(hero); - ui8 i=0; - for(; iplayers[*player].availableHeroes.size(); i++) - { - if(gs->players[*player].availableHeroes[i] == hero) - { - HireHero pack(i, townOrTavern->id); - pack.player = *player; - sendRequest(&pack); - return; - } - } + + HireHero pack(HeroTypeID(hero->subID), townOrTavern->id); + pack.player = *player; + sendRequest(&pack); } void CCallback::save( const std::string &fname ) diff --git a/cmake_modules/VCMI_lib.cmake b/cmake_modules/VCMI_lib.cmake index f325bfa4f..3ddf40f68 100644 --- a/cmake_modules/VCMI_lib.cmake +++ b/cmake_modules/VCMI_lib.cmake @@ -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 diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index 97be80869..940c33489 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -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 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; } diff --git a/lib/CGameInfoCallback.h b/lib/CGameInfoCallback.h index 6e57fa34a..8bca8a99d 100644 --- a/lib/CGameInfoCallback.h +++ b/lib/CGameInfoCallback.h @@ -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; diff --git a/lib/CPlayerState.cpp b/lib/CPlayerState.cpp index 4abfe8f3d..41d2676d9 100644 --- a/lib/CPlayerState.cpp +++ b/lib/CPlayerState.cpp @@ -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); } diff --git a/lib/CPlayerState.h b/lib/CPlayerState.h index 26f7075b5..cfb99617c 100644 --- a/lib/CPlayerState.h +++ b/lib/CPlayerState.h @@ -33,7 +33,6 @@ public: std::set visitedObjects; // as a std::set, since most accesses here will be from visited status checks std::vector > heroes; std::vector > towns; - std::vector > availableHeroes; //heroes available in taverns std::vector > dwellings; //used for town growth std::vector 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; diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 4a58b77a5..bb0acfd91 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -357,9 +357,12 @@ class PlayerColor : public BaseForID 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) diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index a898f5367..3e0a9c543 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -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" diff --git a/lib/NetPackVisitor.h b/lib/NetPackVisitor.h index 226b4a18a..f312339d2 100644 --- a/lib/NetPackVisitor.h +++ b/lib/NetPackVisitor.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) {} @@ -162,4 +162,4 @@ public: virtual void visitLobbyShowMessage(LobbyShowMessage & pack) {} }; -VCMI_LIB_NAMESPACE_END \ No newline at end of file +VCMI_LIB_NAMESPACE_END diff --git a/lib/NetPacks.h b/lib/NetPacks.h index e3a6f25c5..2071079c7 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -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 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; diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index b8368d741..e2eadfd64 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -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(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); diff --git a/lib/StartInfo.h b/lib/StartInfo.h index 495892d82..31b4dfe15 100644 --- a/lib/StartInfo.h +++ b/lib/StartInfo.h @@ -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 - diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index 52c0ebc53..545dd06e3 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -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> & 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 pool; - - if(native) - { - for(auto & elem : available) - { - if(pavailable.find(elem.first)->second & 1<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<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(this); applier = std::make_shared>(); 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 > CGameState::unusedHeroesFromPool() -{ - std::map > 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(); diff --git a/lib/gameState/CGameState.h b/lib/gameState/CGameState.h index 99548f5b7..e11bd8a4f 100644 --- a/lib/gameState/CGameState.h +++ b/lib/gameState/CGameState.h @@ -29,6 +29,7 @@ struct EventCondition; struct CampaignTravel; class CStackInstance; class CGameStateCampaign; +class TavernHeroesPool; struct SThievesGuildInfo; template 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 > heroesPool; //[subID] - heroes available to buy; nullptr if not available - std::map pavailable; // [subid] -> which players can recruit hero (binary flags) - - CGHeroInstance * pickHeroFor(bool native, - const PlayerColor & player, - const CTown * town, - std::map> & available, - CRandomGenerator & rand, - const CHeroClass * bannedClass = nullptr) const; - - template 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 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 > unusedHeroesFromPool(); //heroes pool without heroes that are available in taverns bool isVisible(int3 pos, const std::optional & player) const override; bool isVisible(const CGObjectInstance * obj, const std::optional & player) const override; diff --git a/lib/gameState/TavernHeroesPool.cpp b/lib/gameState/TavernHeroesPool.cpp new file mode 100644 index 000000000..0634bdde8 --- /dev/null +++ b/lib/gameState/TavernHeroesPool.cpp @@ -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 TavernHeroesPool::unusedHeroesFromPool() +{ + std::map 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 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 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 TavernHeroesPool::getHeroesFor(PlayerColor color) const +{ + std::vector 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; +} diff --git a/lib/gameState/TavernHeroesPool.h b/lib/gameState/TavernHeroesPool.h new file mode 100644 index 000000000..d9e636949 --- /dev/null +++ b/lib/gameState/TavernHeroesPool.h @@ -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 heroesPool; + + // [subid] -> which players can recruit hero (binary flags) + std::map pavailable; + + std::map unusedHeroesFromPool(); //heroes pool without heroes that are available in taverns + + std::map > 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 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 void serialize(Handler &h, const int version) + { + h & gameState; + h & heroesPool; + h & pavailable; + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index 58a3568d2..dc8dc60af 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -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 void serialize(Handler & h, const int version) diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index c23a60074..6d3d78bb3 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -239,7 +239,7 @@ void registerTypesClientPacks1(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); - s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); diff --git a/lib/registerTypes/TypesLobbyPacks.cpp b/lib/registerTypes/TypesLobbyPacks.cpp index a347447f4..8ac2734a7 100644 --- a/lib/registerTypes/TypesLobbyPacks.cpp +++ b/lib/registerTypes/TypesLobbyPacks.cpp @@ -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" diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 19dbeb8e2..a2e988392 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -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 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(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 > 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)= 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 > 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 lock(gsm); diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 24e97067d..7da768eaa 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -46,6 +46,7 @@ template 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> applier; std::unique_ptr battleThread; + public: + std::unique_ptr heroPool; + using FireShieldInfo = std::vector>; //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 diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 4c09889c0..76030afd0 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -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 diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index b49cef5a2..892869efd 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -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; } diff --git a/server/HeroPoolProcessor.cpp b/server/HeroPoolProcessor.cpp new file mode 100644 index 000000000..f4278508c --- /dev/null +++ b/server/HeroPoolProcessor.cpp @@ -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(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(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; +} diff --git a/server/HeroPoolProcessor.h b/server/HeroPoolProcessor.h new file mode 100644 index 000000000..c37d6cb42 --- /dev/null +++ b/server/HeroPoolProcessor.h @@ -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 void serialize(Handler &h, const int version) + { + h & gameHandler; + } +}; diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index 892be093d..d4ceffb47 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -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)